Describe the normal behavior in one or more sentences
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.
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
function testCannotClaimTwice() public {
// First claim
vm.prank(alice);
snow.approve(address(airdrop), 1);
}
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.
To prevent duplicate claims, the claimSnowman
function should explicitly track and restrict users who have already claimed their NFT.
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:
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.
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.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.