Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: low
Valid

No Replay Protection / Idempotency

Root + Impact

Description

  • Normal Behavior: Each eligible address should only be able to claim their Snowman NFT once using a valid Merkle proof and signature.

  • Issue: The contract does not check if a user has already claimed. The s_hasClaimedSnowman mapping is updated only after state changes and is never read before execution. This allows any valid claimant to repeatedly call claimSnowman() and mint multiple NFTs.

function claimSnowman(...) external nonReentrant {
...
@> s_hasClaimedSnowman[receiver] = true;
...
@> i_snowman.mintSnowman(receiver, amount);
}

Risk

Likelihood:

  • The Merkle tree and signature are static; users can re-submit them any time.

  • There's no guard preventing repeated calls to the claim function

Impact:

  • Any eligible user can infinitely repeat the claim and mint function.

  • Token distribution becomes exploitable, leading to over-minting and broken economics.


Proof of Concept

// First claim (valid)
airdrop.claimSnowman(receiver, proof, v, r, s); // mints 1 Snowman
// Replay
airdrop.claimSnowman(receiver, proof, v, r, s); // mints again — unlimited!
// Front-run others' claims if proofs are leaked or shared

Recommended Mitigation

function claimSnowman(...) external nonReentrant {
+ if (s_hasClaimedSnowman[receiver]) {
+ revert AlreadyClaimed();
+ }
...
s_hasClaimedSnowman[receiver] = true;
...
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 19 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Lack of claim check

The claim function of the Snowman Airdrop contract doesn't check that a recipient has already claimed a Snowman. This poses no significant risk as is as farming period must have been long concluded before snapshot, creation of merkle script, and finally claiming.

Support

FAQs

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