In the EggstravaganzaNFT
contract, the mintEgg
function uses the internal _mint
function from the OpenZeppelin ERC721
implementation:
This approach mints the token and assigns it to the to
address without checking whether the recipient is a contract — or, if it is, whether the contract is capable of handling ERC721 tokens. If a token is minted to a contract that does not implement onERC721Received
, the NFT will be locked and permanently inaccessible unless that contract happens to implement a custom retrieval or recovery mechanism (which is rare and typically not the case).
The OpenZeppelin _safeMint
function mitigates this risk by including a contract compatibility check. If the recipient is a contract, it performs a low-level call to onERC721Received
to confirm the contract understands how to safely receive NFTs. If not, the minting transaction is reverted.
This is especially dangerous in games like EggHunt, where NFTs are programmatically minted based on player interactions. If user input, frontend glitches, game logic bugs, or malicious actors cause NFTs to be minted to non-compliant smart contracts (such as multisigs, proxies, or vaults), those tokens become permanently unusable, breaking user expectations and potentially harming game mechanics tied to ownership, burning, or transfers.
Permanent Loss of NFTs: NFTs minted to non-ERC721-compatible contracts are effectively stuck and inaccessible. This is irreversible and impacts the user's ability to use, transfer, or interact with the token.
Game Logic Breakage: If the EggHunt game later relies on NFTs being owned by users (for rewards, progression, claiming, etc.), having tokens stuck in contracts will break assumptions. The player may appear to "own" the egg but cannot perform any in-game action requiring it.
Bad UX and Loss of Trust: Players may think they've received an NFT, only to find it's missing or inaccessible. This degrades the trustworthiness of the game and could spark user complaints or support overhead.
Marketplace and Wallet Compatibility Issues: Marketplaces and wallets might misrepresent stuck NFTs or not display them at all, causing further confusion.
Front-running or Attack Surface: Attackers could intentionally mint eggs to known non-compliant contracts to sabotage gameplay or cause denial-of-service-like behavior in downstream logic.
Manual Review
Replace _mint
with ****_safeMint
: Change the minting logic to use _safeMint
instead of _mint
:
This ensures that if the to
address is a smart contract, it must implement onERC721Received
correctly, or the minting will fail.
Validate Recipient Addresses: Add check to prevent minting to known dangerous addresses, such as address(0)
or specific contracts that have failed to return a valid selector in the past.
Add Tests for Non-compliant Contracts: Unit tests should be written to simulate minting to both compliant and non-compliant smart contracts. Ensure that minting fails for incompatible contracts.
Explicitly Document Token Compatibility Requirements: Clearly state in the EggstravaganzaNFT and EggHunt documentation that tokens are minted using _safeMint
and will fail if the recipient is not ERC721-compatible. This avoids confusion for integrators and developers.
Handle Edge Cases in Game Logic: Ensure that EggHuntGame
does not attempt to mint tokens to unexpected addresses, such as player-defined delegate contracts, third-party agents, or temporary contracts. Implement checks to validate user-supplied recipients.
Here’s a conceptual walkthrough of how this vulnerability would manifest in the current system:
Setup:
A player completes an in-game action in EggHuntGame
that results in minting an egg.
The EggHuntGame
contract calls mintEgg(to, tokenId)
on the EggstravaganzaNFT
.
User Submits a Contract Address:
Either intentionally or due to a bug, the to
address is a contract that does not implement onERC721Received
.
This could be a multisig wallet, an upgradable proxy, or even a contract created via CREATE2
for which the logic isn’t yet deployed.
EggstravaganzaNFT Calls ****_mint
:
_mint
does not validate whether the recipient contract supports ERC721 tokens.
The NFT is minted and transferred to the contract address.
Token Becomes Inaccessible:
Because the recipient contract has no fallback mechanism or onERC721Received
hook, the token cannot be transferred, interacted with, or reclaimed.
The owner cannot call transferFrom
or safeTransferFrom
unless the receiving contract provides a manual escape hatch.
Player Experience Breaks:
The player does not see the egg in their wallet.
The game may not register them as owning the egg (if balance checks rely on ERC721 balances).
In-game progression, burning mechanisms, or reward systems that rely on ownership break.
This can be fully avoided by switching to _safeMint
.
Let me know when you’re ready to move on to the next one.
Protocol doesn't check if recipient contracts can handle ERC721 tokens
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.