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
10 months ago
yeahchibyke Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!