Snowman Merkle Airdrop

AI First Flight #10
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Snowman.mintSnowman iterates up to balanceOf times — large Snow balance causes out-of-gas and permanent claim failure

Snowman.mintSnowman iterates up to balanceOf times — large Snow balance causes OOG

Impact

Medium Impact

Likelihood

Medium Likelihood

Root + Impact

Description

  • Snowman.mintSnowman is expected to mint a batch of NFTs corresponding to the claimer's Snow balance.

  • The function iterates amount times in a single transaction, calling _safeMint on each iteration. The amount value comes from i_snow.balanceOf(receiver) in SnowmanAirdrop.claimSnowman. A user who accumulates a large Snow balance would trigger a proportionally large loop. At extreme balances the transaction exceeds the block gas limit and permanently reverts, making the claim impossible.

// src/Snowman.sol
function mintSnowman(address receiver, uint256 amount) external {
@> for (uint256 i = 0; i < amount; i++) { // unbounded — no cap
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}

Risk

Likelihood:

  • Any user who buys or earns more Snow than the safe loop limit triggers this.

  • Since mintSnowman has no cap on amount, the problem scales with Snow supply.

Impact:

  • Legitimate claimers with large Snow balances are permanently denied their Snowman NFTs due to OOG.

  • A malicious user can transfer large amounts of Snow to a target to intentionally brick their claim.

Proof of Concept

1) Alice buys 10,000 Snow (amount = 10,000 in current unit system)
2) claimSnowman passes amount = 10,000 to mintSnowman
3) mintSnowman attempts 10,000 _safeMint iterations -> approaches block gas limit -> OOG revert
4) Alice can never claim her Snowman NFTs

Recommended Mitigation

+ uint256 public constant MAX_MINT_PER_TX = 100;
function mintSnowman(address receiver, uint256 amount) external {
+ require(amount <= MAX_MINT_PER_TX, "Exceeds per-tx mint cap");
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}

A better long-term fix is to mint exactly one NFT per claim, or use a pull-pattern where claimers call mintBatch in multiple transactions.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 4 hours 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!