When a user mints an NFT and opts for automated weather updates, they specify a _heartbeat interval. This interval determines how frequently Chainlink Automation should check and update the NFT's weather data. The contract does not validate the _heartbeat value provided by the user.
If a user provides _heartbeat = 0, the condition (block.timestamp >= s_weatherNftInfo[_tokenId].lastFulfilledAt + s_weatherNftInfo[_tokenId].heartbeat) in checkUpkeep simplifies to (block.timestamp >= s_weatherNftInfo[_tokenId].lastFulfilledAt). This condition will be true in the same block performUpkeep updates lastFulfilledAt, or in the very next block.
Consequently, checkUpkeep will continuously indicate that an update is needed. Each time performUpkeep is called, it initiates a new Chainlink Functions request and consumes LINK from the NFT's dedicated keeper subscription for the performUpkeep transaction itself. This rapid, continuous triggering will quickly deplete the _initLinkDeposit provided for the NFT's automation.
Likelihood: Medium
A user might accidentally input 0 if a UI allows it, or a front-end might default to 0 if not handled.
A user might intentionally set it to 0 if they misunderstand the implications, leading to self-inflicted LINK loss for their automation.
Impact: Medium
Rapid Depletion of Automation LINK Funds: The _initLinkDeposit for the specific NFT's automation upkeep will be consumed very quickly due to frequent performUpkeep calls. This renders the automation feature short-lived and wastes the user's deposit.
Increased Load on Chainlink Services: Frequent, unnecessary calls to performUpkeep and subsequently to Chainlink Functions can put a small, but potentially cumulative, load on the Chainlink network.
Wasted Gas and Oracle Fees: Each cycle of performUpkeep and the Functions call costs gas (paid by keeper, funded by user's LINK deposit) and LINK for the Functions request itself (paid from the contract's Functions subscription, which might be shared or owner-funded).
Alice mints an NFT and calls requestMintWeatherNFT with _pincode = "12345", _isoCode = "US", _registerKeeper = true, _heartbeat = 0, and _initLinkDeposit = 5 * 10**18 (5 LINK).
The NFT is minted (assuming fulfillMintRequest is called and completes). s_weatherNftInfo[tokenId].heartbeat is set to 0. s_weatherNftInfo[tokenId].lastFulfilledAt is set to T1 (current block timestamp).
A Chainlink Keeper node calls checkUpkeep for this NFT in block T1 or T1 + few seconds (e.g., next block).
The condition block.timestamp >= T1 + 0 is true.
checkUpkeep returns upkeepNeeded = true.
The Keeper node calls performUpkeep. This transaction costs LINK from Alice's upkeep subscription.
s_weatherNftInfo[tokenId].lastFulfilledAt is updated to T2 (current block timestamp during performUpkeep).
A Chainlink Functions request is sent.
Another (or the same) Keeper node calls checkUpkeep shortly after, in block T2 or T2 + few seconds.
The condition block.timestamp >= T2 + 0 is true.
checkUpkeep returns upkeepNeeded = true.
This cycle repeats rapidly, each performUpkeep call consuming LINK, until Alice's 5 LINK deposit for automation is exhausted. The shared Chainlink Functions subscription (s_functionsConfig.subId) will also be charged for each weather API request.
Add a require statement in requestMintWeatherNFT to ensure that _heartbeat is above a reasonable minimum value (e.g., a few minutes or hours, depending on the desired operational parameters and Chainlink node capabilities). This prevents a zero or extremely small heartbeat interval that would lead to excessive updates.
This is informational. It is user's responsibility to provide correct input arguments. If the user provides incorrect arguments, it will lead to incorrect results, lost funds or failed transaction.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.