Weather Witness

First Flight #40
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Business Logic Flaw: Missing LINK Deposit Validation Leads to Keeper Registration Break

Root + Impact

Description

Normally, when a user calls requestMintWeatherNFTThe i contract expects a precise msg.value equal to s_currentMintPrice and optionally registers a Chainlink Keeper by transferring LINK tokens (s_link) from the caller if _registerKeeper is true.

However, the function only enforces msg.value == s_currentMintPrice, with no validation on the sufficiency of _initLinkDeposit. This allows an attacker to pass the mint price and set _registerKeeper = true, but submit 0 LINK or insufficient LINK, causing downstream Chainlink Keeper registration to fail — all without immediate reversion, because the function does not validate the result or even whether s_link.safeTransferFrom(...) succeeds.

function requestMintWeatherNFT(
string memory _pincode,
string memory _isoCode,
bool _registerKeeper,
uint256 _heartbeat,
uint256 _initLinkDeposit
) external payable returns (bytes32 _reqId) {
require(
@> msg.value == s_currentMintPrice,
WeatherNft__InvalidAmountSent()
);
s_currentMintPrice += s_stepIncreasePerMint;
if (_registerKeeper) {
@> IERC20(s_link).safeTransferFrom(
msg.sender,
address(this),
_initLinkDeposit
);
// No check if _initLinkDeposit == 0 or LINK allowance is valid
}
_reqId = _sendFunctionsWeatherFetchRequest(_pincode, _isoCode);
emit WeatherNFTMintRequestSent(msg.sender, _pincode, _isoCode, _reqId);
s_funcReqIdToUserMintReq[_reqId] = UserMintRequest({
user: msg.sender,
pincode: _pincode,
isoCode: _isoCode,
registerKeeper: _registerKeeper,
heartbeat: _heartbeat,
initLinkDeposit: _initLinkDeposit
});
}

Risk

Likelihood:

  • This occurs whenever a malicious or careless user sets _registerKeeper = true but provides a zero or very small _initLinkDeposit.

  • Since safeTransferFrom does not revert if allowance or balance is insufficient (if not properly handled via try/catch or return value), the LINK may not be transferred, yet the process proceeds.

Impact:

  • Chainlink Keeper registration will fail silently, leading to unfulfilled weather NFT functionality or automated upkeep mechanisms.

  • The system will record registerKeeper = true, misleading any future logic or external processes that rely on that flag.

  • Wasted gas fees for both user and protocol, and potential trust degradation if minting fails or data is not fetched as expected.

Proof of Concept

// Approve LINK and Mint NFT
vm.startBroadcast(userPrivateKey);
linkToken.approve(address(weatherNft), 0.1);
weatherNft.requestMintWeatherNFT{value: weatherNft.s_currentMintPrice()}(
pincode,
isoCode,
registerKeeper,
heartbeat,
0.1
);
vm.stopBroadcast();

Recommended Mitigation

Set MIN_LINK_DEPOSIT based on Chainlink Keeper requirements (typically 5-10 LINK).

if (_registerKeeper) {
+ require(_initLinkDeposit >= MIN_LINK_DEPOSIT, "LINK deposit too low");
+ uint256 allowance = IERC20(s_link).allowance(msg.sender, address(this));
+ require(allowance >= _initLinkDeposit, "Insufficient LINK allowance");
+ uint256 balance = IERC20(s_link).balanceOf(msg.sender);
+ require(balance >= _initLinkDeposit, "Insufficient LINK balance");
IERC20(s_link).safeTransferFrom(
msg.sender,
address(this),
_initLinkDeposit
);
}
Updates

Appeal created

bube Lead Judge 23 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

[Invalid] The LINK deposit is not checked

This is informational/invalid. If the LINK deposit is not enough, the function `registerUpkeep` will revert and it is responsibility of the user to provide the correct amount of `_initLinkDeposit`, if the user wants automated weather updates.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.