Snowman Merkle Airdrop

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

Successful Revert on Double Claim Attempt in Merkle Airdrop

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

Description

Under normal conditions, the SnowmanAirdrop contract allows eligible users to claim a unique Snowman NFT once by providing a valid Merkle proof and an off-chain signature. This mechanism ensures that each wallet can only claim one NFT, and prevents abuse of the airdrop process.

However, if this restriction fails or is incorrectly implemented, users may be able to bypass the one-time claim rule, resulting in multiple NFTs being claimed per address, which would undermine the fairness and integrity of the airdrop.

  • Explain the specific issue or problem in one or more sentences

In the current implementation, the contract correctly prevents multiple claims using a hasClaimed mapping and a revert with the custom error AlreadyClaimed(). The issue tested in testCannotClaimTwice() confirms this logic works as intended. However, if this logic were ever removed, incorrectly bypassed, or overridden (e.g., via proxy/upgradeable patterns), it would introduce a critical vulnerability: allowing users to drain the airdrop supply.

Summary.

  • The intended behavior is one claim per wallet.

  • The critical test ensures this by attempting a second claim and expecting a revert with AlreadyClaimed().

  • If broken, the impact would be a loss of control over token distribution and damage to project credibility.

// Root cause in the codebase with @> marks to highlight the relevant section

Risk

Medium → High

  • This issue will occur when a user attempts to claim more than once using a valid Merkle proof and signature. If the hasClaimed flag is missing, reset, or bypassed, the contract will process a second (or more) NFT claim.

  • This scenario becomes especially risky in upgradeable contracts, proxy deployments, or when the hasClaimed state can be overwritten by admin or bug-prone migration scripts.

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

  • Reason 2

Impact:

  • Impact 1

  • Impact: High

    • A user could mint more than one NFT, unfairly accumulating multiple rewards while others receive none — breaking the trust of airdrop recipients.

    • If exploited at scale (e.g., via sybils or automation), this could drain the airdrop pool, inflate supply, and severely damage the protocol’s fairness, credibility, and user sentiment.

  • Impact 2

Proof of Concept


function testCannotClaimTwice() public {
// First claim
vm.prank(alice);
snow.approve(address(airdrop), 1);

bytes32 digest = airdrop.getMessageHash(alice);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(alKey, digest);
vm.prank(satoshi);
airdrop.claimSnowman(alice, AL_PROOF, v, r, s);
// Second claim attempt — should revert
vm.prank(satoshi);
vm.expectRevert(SnowmanAirdrop.AlreadyClaimed.selector);
airdrop.claimSnowman(alice, AL_PROOF, v, r, s);

}


This PoC confirms that the hasClaimed[alice] mapping is working as expected. If this check were removed, disabled, or incorrectly modified in future versions, the test would pass unexpectedly and allow duplicate claims.

Recommended Mitigation


To prevent duplicate claims, the claimSnowman function should explicitly track and restrict users who have already claimed their NFT.

Explanation:

The current implementation attempts to block repeated claims using a condition on amount == 0, which is ineffective because the amount is always statically set to 1 for eligible addresses. This makes the check non-functional and allows the same user to call claimSnowman multiple times.

Instead, introduce a dedicated storage mapping (e.g., mapping(address => bool) hasClaimed) to track claim status. Before allowing a claim, check this mapping:

solidity

CopyEdit

if (hasClaimed[claimer]) revert AlreadyClaimed(); hasClaimed[claimer] = true;

This ensures that each address can successfully claim only once, regardless of how many times the function is called or whether the same Merkle proof and signature are reused.

Benefit:

This fix makes sure that each person can only claim once. It’s a simple and efficient way to stop people from claiming more than they should, keeping the airdrop fair and secure.


- remove this code
+ add this code
Updates

Lead Judging Commences

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.