Normal behavior: mintSnowman() mints Snowman NFTs to the receiver and increments s_TokenCounter for each NFT minted.
The issue: _safeMint() triggers onERC721Received() on the recipient if it is a contract. The s_TokenCounter++ state update occurs AFTER _safeMint(), violating the Checks-Effects-Interactions pattern. A malicious receiver contract can reenter mintSnowman() during the callback before s_TokenCounter is incremented, causing the same token ID to be used for two separate mint operations.
Likelihood:
Requires the receiver to be a malicious contract implementing onERC721Received — not possible through the standard airdrop flow since SnowmanAirdrop has nonReentrant.
Exploitable only when mintSnowman() is called directly by the owner with a malicious receiver address.
Impact:
Reentrant call mints duplicate token IDs, corrupting NFT ownership records.
s_TokenCounter manipulation causes future mints to collide with existing token IDs.
The following test demonstrates that a malicious ERC721 receiver reenters mintSnowman() during the onERC721Received callback before s_TokenCounter is incremented. The counter state is corrupted and the first recipient's ownership is overwritten by the second mint using the same token ID.
The fix applies CEI by capturing and incrementing s_TokenCounter before calling _safeMint. The token ID is stored in a local variable, the counter increments, then the mint executes. Any reentrant call uses the already-incremented counter producing a new unique token ID. Adding nonReentrant is recommended as defence in depth.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.