selectWinner() mints the puppy NFT to the winner using _safeMint, which calls onERC721Received on contract recipients and reverts if it is absent or returns the wrong value.
Because the winner is chosen pseudo-randomly from all players, a participating contract that does not implement the ERC721 receiver hook can be selected. When that happens, _safeMint reverts, reverting the whole selectWinner() transaction. Since this is the only way to conclude the round, the raffle becomes stuck.
Likelihood:
Occurs when a participating contract address that lacks onERC721Received is selected as winner; contract entrants are allowed and selection is uniform, so the probability scales with how many such contracts enter.
Impact:
The round cannot be finalized: no winner is paid, no fees are bankable, and there is no separate refund path once the raffle period has ended; funds are stranded.
The test below enters a contract with no onERC721Received hook as a player; when the pseudo-random selection lands on that address, _safeMint reverts and selectWinner() reverts with it, leaving the round unable to conclude. The commented loop shows how to brute-force msg.sender/block.timestamp until index 0 is selected, then assert the revert.
Either mint with _mint (accepting the recipient's responsibility) or decouple prize delivery from winner selection via a pull-based claim. (Note: a pull pattern (winner calls claimPrize() separately) is the more robust fix, as it also avoids the ETH-transfer failure path stalling the draw).
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.