Eggstravaganza

First Flight #37
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: medium
Valid

Unsafe Minting (`_mint` vs `_safeMint`) in NFT contract

Vulnerability Details

Bug Description

In the EggstravaganzaNFT contract, the mintEgg function uses the internal _mint function from the OpenZeppelin ERC721 implementation:

function mintEgg(address to, uint256 tokenId) external returns (bool) {
require(msg.sender == gameContract, "Unauthorized minter");
_mint(to, tokenId);
totalSupply += 1;
return true;
}

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.

Impact

  • 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.

Tools Used

  • Manual Review

Recommended Mitigation Steps

  1. Replace _mint with ****_safeMint: Change the minting logic to use _safeMint instead of _mint:

    _safeMint(to, tokenId);

    This ensures that if the to address is a smart contract, it must implement onERC721Received correctly, or the minting will fail.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

Proof of Concept

Here’s a conceptual walkthrough of how this vulnerability would manifest in the current system:

  1. 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.

  2. 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.

  3. EggstravaganzaNFT Calls ****_mint:

    • _mint does not validate whether the recipient contract supports ERC721 tokens.

    • The NFT is minted and transferred to the contract address.

  4. 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.

  5. 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.

Updates

Lead Judging Commences

m3dython Lead Judge 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Unsafe ERC721 Minting

Protocol doesn't check if recipient contracts can handle ERC721 tokens

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.