_safeMint / safeTransferFrom invokes onERC721Received / onERC1155Received on the recipient before the surrounding state is finalised. A malicious recipient re-enters (e.g. mints again below a per-wallet cap, or claims twice) during that callback.
// mint(): _safeMint(msg.sender, id); minted[msg.sender]=true; // attacker onERC721Received() { if(count<10) Victim.mint(); } // re-enters before minted flag is set -> bypasses the 1-per-wallet cap.
// checks-effects-interactions + guard around the safe-mint function mint() external nonReentrant { require(!minted[msg.sender]); minted[msg.sender] = true; // effects first _safeMint(msg.sender, id); // interaction last }
Largest recorded losses in the same failure class — related, not the same bug:
The SaferICO scanner runs 201 detectors over your Solidity source, SAFE-0147 among them. Paste an address or the source itself — a small per-scan fee, shown before you sign, or unlimited on any plan.