Snowman Merkle Airdrop

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

Gas Limit Exhaustion in NFT Minting

Root + Impact

Transaction reverts due to gas overflow when staking large token amounts, permanently locking user funds.

Description

  • Normal Behavior: Users should be able to stake Snow tokens to receive proportional Snowman NFTs (1 NFT per 1e18 tokens)

  • Issue: The minting loop processes token amounts in wei (10^18 per token), causing astronomical iteration counts that always exceed block gas limits

// Snowman.sol
function mintSnowman(address receiver, uint256 amount) external {
@>for (uint256 i = 0; i < amount; i++) { // Loops over token amount in wei
_safeMint(receiver, s_TokenCounter++);
}
}

Risk

Likelihood:

  • Occurs for any stake ≥0.001 tokens (1000 iterations) due to wei conversion

  • Affects 100% of staking transactions with non-zero amounts

Impact:

  • Permanent lock of transferred tokens

  • Complete protocol functionality denial

  • Irreversible loss of user funds

Proof of Concept

// Demonstrates gas exhaustion with minimal stake
function testGasExhaustion() public {
uint256 smallStake = 0.001 ether; // 0.001 tokens
deal(address(snow), user, smallStake);
vm.startPrank(user);
snow.approve(address(airdrop), smallStake);
// Transaction reverts due to 1000+ iterations
vm.expectRevert();
airdrop.claimSnowman(user, proof, v, r, s);
// Tokens permanently locked
assertEq(snow.balanceOf(address(airdrop)), smallStake);
}

Explanation: Even small stakes (0.001 tokens) require 1000+ loop iterations, exceeding Ethereum's 30M gas limit since each _safeMint consumes ~50k gas. The test verifies failure with minimal stake.

Recommended Mitigation

// Snowman.sol
+ uint256 public constant MAX_MINT_PER_TX = 100;
function mintSnowman(address receiver, uint256 amount) external {
+ uint256 nfts = amount / 1e18;
+ require(nfts > 0, "Min 1 NFT");
+ require(nfts <= MAX_MINT_PER_TX, "Exceeds cap");
- for (uint256 i = 0; i < amount; i++) {
+ for (uint256 i = 0; i < nfts; i++) {
_safeMint(receiver, s_TokenCounter++);
}
}

Explanation: Converts token amount to NFT count and adds cap to prevent gas exhaustion. Each iteration now represents one NFT instead of one wei.

Updates

Lead Judging Commences

yeahchibyke Lead Judge
3 months ago
yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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