Weather Witness

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

Insufficient initLinkDeposit Allows DoS and Permanently Locks LINK

Summary

The WeatherNft::requestMintWeatherNFT function accepts an initLinkDeposit parameter used for funding Chainlink Automation upkeep registrations. If the provided LINK deposit is too small, the keeper registration may fail or rapidly exhaust its balance, effectively causing a Denial of Service (DoS) condition for the NFT's automated weather updates. Additionally, any LINK transferred to the contract under these conditions becomes permanently locked and irrecoverable by both the user and the protocol.


Vulnerability Details

// @audit-issue Insufficient LINK deposit causes DoS and locks funds
@> weatherNft.requestMintWeatherNFT{
value: weatherNft.s_currentMintPrice()
}(
pincode, isoCode, true, heartbeat, 1
);

Issue Identified

  • The contract does not enforce a minimum bound on the initLinkDeposit parameter.

  • A user can supply a minimal LINK deposit (e.g., 1 wei LINK) insufficient for covering the operational costs of Chainlink Automation.

  • This minimal deposit leads to immediate depletion, causing the automated upkeep to become non-functional (DoS condition).

  • The LINK tokens deposited are permanently locked within the contract, as there is no refund mechanism.


Risk

Likelihood:

  • Users could inadvertently or maliciously set an extremely small initLinkDeposit, easily achievable by user error or attacker manipulation.

Impact:

  • NFT's automatic updates permanently cease functioning, directly harming user experience and protocol functionality (DoS).

  • Deposited LINK tokens remain permanently inaccessible, resulting in a permanent loss of user funds.


Proof of Concept (PoC)

The provided PoC demonstrates that supplying an overly small initLinkDeposit prevents proper Chainlink keeper registration, causing NFT automation to fail permanently. The minimal LINK transferred remains locked in the contract, inaccessible by the depositor or protocol administrators.

PoC Explanation

  1. User provides a minimal LINK deposit (1 wei LINK) when minting the NFT.

  2. The mint request and initial LINK deposit transfer succeed.

  3. The Chainlink Automation setup either fails or quickly exhausts the tiny balance.

  4. Automated weather updates cease, creating a DoS scenario.

  5. Deposited LINK remains locked indefinitely within the contract.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.29;
import {Test, console} from "forge-std/Test.sol";
import {WeatherNft, WeatherNftStore} from "src/WeatherNft.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol";
import {Vm} from "forge-std/Vm.sol";
contract WeatherNftForkTest is Test {
WeatherNft weatherNft;
LinkTokenInterface linkToken;
address functionsRouter;
address user = makeAddr("user");
function setUp() external {
weatherNft = WeatherNft(0x4fF356bB2125886d048038386845eCbde022E15e);
linkToken = LinkTokenInterface(0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846);
functionsRouter = 0xA9d587a00A31A52Ed70D6026794a8FC5E2F5dCb0;
vm.deal(user, 1000e18);
deal(address(linkToken), user, 1000e18);
// Fund the subscription required by Chainlink Functions
vm.prank(user);
linkToken.transferAndCall(functionsRouter, 100e18, abi.encode(15459));
}
function test_InitLinkDeposit_TooSmall_Causes_DoS_And_Locks_Funds() public {
string memory pincode = "125001";
string memory isoCode = "IN";
bool registerKeeper = true;
uint256 heartbeat = 12 hours;
uint256 initLinkDeposit = 1; // Setting initLinkDeposit too small can cause DoS and funds lock issues.
uint256 tokenId = weatherNft.s_tokenCounter();
// Step 1: User initiates a Weather NFT mint request with too small initLinkDeposit.
vm.startPrank(user);
linkToken.approve(address(weatherNft), initLinkDeposit);
vm.recordLogs();
weatherNft.requestMintWeatherNFT{value: weatherNft.s_currentMintPrice()}(
pincode, isoCode, registerKeeper, heartbeat, initLinkDeposit
);
vm.stopPrank();
// Step 2: Extract requestId from the WeatherNFTMintRequestSent event
Vm.Log[] memory logs = vm.getRecordedLogs();
bytes32 reqId;
for (uint256 i; i < logs.length; i++) {
if (logs[i].topics[0] == keccak256("WeatherNFTMintRequestSent(address,string,string,bytes32)")) {
(,,, reqId) = abi.decode(logs[i].data, (address, string, string, bytes32));
break;
}
}
// Step 3: Simulate successful Chainlink oracle fulfillment
vm.prank(functionsRouter);
bytes memory weatherResponse = abi.encode(WeatherNftStore.Weather.RAINY);
weatherNft.handleOracleFulfillment(reqId, weatherResponse, "");
// Step 4: User calls fulfillMintRequest once —
// If initLinkDeposit is too small, the upkeep may not function or register properly,
// causing a denial of service (DoS) for the NFT's weather updates.
// Any LINK sent is locked in the contract and cannot be recovered.
vm.startPrank(user);
vm.expectRevert();
weatherNft.fulfillMintRequest(reqId); // Should revert due to insufficient deposit for keeper registration.
// Verify that the small initLinkDeposit remains locked in the contract.
uint256 expectedLocked = 1;
vm.assertEq(linkToken.balanceOf(address(weatherNft)), expectedLocked);
}
}

Tools Used

  • Manual Review

  • Foundry Unit Testing


Recommendations

Enforce Minimum LINK Deposit

Add a require check within requestMintWeatherNFT to ensure a sufficient minimum LINK deposit:

function requestMintWeatherNFT(
string memory _pincode,
string memory _isoCode,
bool _registerKeeper,
uint256 _heartbeat,
uint256 _initLinkDeposit
) external payable returns (bytes32 _reqId) {
+ require(_initLinkDeposit >= MIN_LINK_DEPOSIT, "initLinkDeposit too small");
// ... existing logic ...
}

This ensures that upkeep registrations have enough LINK for proper operation, preventing both DoS conditions and permanent fund lock-ups.

Updates

Appeal created

bube Lead Judge 4 months 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.