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(). A malicious receiver contract can reenter mintSnowman() during the onERC721Received() callback before s_TokenCounter is incremented, causing the same token ID to be minted twice or the counter to be manipulated.
Likelihood:
Requires receiver to be a malicious contract implementing onERC721Received
SnowmanAirdrop has nonReentrant guard but mintSnowman() itself does not
Impact:
Reentrant call during onERC721Received can mint duplicate token IDs
s_TokenCounter manipulation could cause future mints to collide with existing token IDs
The s_TokenCounter increment occurs after _safeMint(), violating the
Checks-Effects-Interactions pattern. While mintSnowman() has onlyOwner
protection limiting direct exploitation, the reentrancy via onERC721Received
can still corrupt the token counter state if a malicious contract is ever
set as the airdrop owner, or if the onlyOwner restriction is removed in a
future upgrade. More immediately, the costly-loop pattern means each
iteration performs an external call (_safeMint) before updating state,
creating a window where s_TokenCounter is stale within the same transaction.
The fix applies the Checks-Effects-Interactions pattern by capturing and
incrementing s_TokenCounter before calling _safeMint. The token ID is
stored in a local variable first, the counter is incremented, then the
mint executes. Any reentrant call during onERC721Received will now use
the already-incremented counter value, producing a new unique token ID
instead of colliding with the current one. This eliminates the reentrancy
vector without requiring a nonReentrant modifier.
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.