The WeatherNft::requestMintWeatherNFT
function processes LINK deposits for Chainlink Automation by converting the initLinkDeposit
parameter (provided as uint256
) to uint96
before registering the upkeep. If a user supplies a value greater than type(uint96).max
, the entire amount is transferred to the contract, but only the lower 96 bits are actually used for the upkeep registration. The excess LINK above the uint96
limit is permanently locked in the contract, becoming inaccessible to both the user and the protocol.
The contract does not enforce an upper bound on the initLinkDeposit
parameter.
If a user supplies an initLinkDeposit
greater than 2^96-1
, the entire amount is transferred from the user to the contract.
Only the least significant 96 bits of the deposit are used in the keeper registration (uint96
cast); any excess LINK above this limit is not refunded or accessible.
As a result, users can permanently lose any excess LINK they deposit over the uint96
maximum.
Likelihood:
Any user can accidentally or intentionally supply an initLinkDeposit
that exceeds the uint96
limit, especially when handling large values or using automated tooling.
In practice, most users are unlikely to hold such a large amount of LINK tokens, so this scenario is uncommon in typical user behavior.
Impact:
The excess LINK is permanently locked in the contract, leading to loss of funds.
There is no way for users or protocol admins to recover or refund the locked LINK.
This PoC demonstrates that if a user submits an initLinkDeposit
exceeding the uint96 maximum, the full amount is transferred, but only 2^96-1
LINK (plus 1 for rounding) is actually credited to the upkeep, and the excess LINK remains stuck in the contract.
The attacker funds their account with LINK exceeding the uint96
maximum.
The attacker calls requestMintWeatherNFT
with a large initLinkDeposit
value.
The full amount is transferred to the contract.
Only the lower 96 bits of initLinkDeposit
are used in the keeper registration.
The excess LINK above 2^96-1
cannot be withdrawn or used.
Manual Review
Foundry Unit Testing
Enforce Upper Bound in requestMintWeatherNFT
Add a require
at the top of requestMintWeatherNFT
to reject any initLinkDeposit
exceeding the uint96 limit:
This ensures the full LINK deposit will always fit into a uint96 without truncation, preventing any excess LINK from becoming permanently locked in the contract.
After discussion with the sponsor, this turns out to be invalid. This is because the Chainlink has a capped maximum supply of 1 billion LINK tokens. This means that the total number of LINK tokens will never exceed 1 billion. The token has 18 decimals, so the max scaled value that is required to represent all LINK tokens is 1e27. The max value of `uint96` is 2**96 - 1, that is around 79e27 and it is sufficient to store all LINK tokens. Therefore, the cast from uint256 to uint96 is safe and there is no possibility of token truncation/loss of tokens.
After discussion with the sponsor, this turns out to be invalid. This is because the Chainlink has a capped maximum supply of 1 billion LINK tokens. This means that the total number of LINK tokens will never exceed 1 billion. The token has 18 decimals, so the max scaled value that is required to represent all LINK tokens is 1e27. The max value of `uint96` is 2**96 - 1, that is around 79e27 and it is sufficient to store all LINK tokens. Therefore, the cast from uint256 to uint96 is safe and there is no possibility of token truncation/loss of tokens.
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.