Skip to main content
Sigvex

ERC-721 Standard Violations Remediation

Implement safe transfer callbacks, correct events, and ERC-165 support to make NFT contracts standard-compliant.

ERC-721 Standard Violations Remediation

Overview

NFT contracts that deviate from ERC-721 break in marketplaces, wallets, and bridges that expect standard behavior. The most damaging deviation is a safeTransferFrom that never calls onERC721Received on a contract recipient: NFTs sent to a contract that cannot handle them become permanently locked. Missing ERC-165 interface declarations and malformed Transfer events compound the problem.

Related Detector: ERC-721 Standard Violations

Before (Vulnerable)

function safeTransferFrom(address from, address to, uint256 tokenId) public {
    _transfer(from, to, tokenId);
    // No onERC721Received check -- NFTs can be locked in contracts
}

After (Fixed)

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract MyNFT is ERC721 {
    constructor() ERC721("MyNFT", "MNFT") {}
    // safeTransferFrom, ERC-165 support, and correct events are inherited
}

Inheriting from a reviewed ERC-721 implementation guarantees that safeTransferFrom invokes the recipient callback, that supportsInterface reports the correct interface IDs, and that Transfer and Approval events carry the standard indexed parameters. The callback check rejects transfers to contracts that do not implement IERC721Receiver, preventing silent loss.

Alternative Mitigations

  • If you must write a custom implementation, call IERC721Receiver(to).onERC721Received(...) whenever to is a contract and require the returned magic value 0x150b7a02.
  • Declare ERC-165 support for both the IERC721 interface ID and the IERC165 interface ID so external integrations can detect capability.
  • Emit Transfer(from, to, tokenId) on every mint, transfer, and burn, with all three parameters indexed.

Common Mistakes

  • Implementing transferFrom correctly but leaving safeTransferFrom without the receiver callback.
  • Returning true from supportsInterface for interfaces the contract does not actually implement, which misleads integrators.
  • Skipping the Transfer event on mint or burn, which breaks indexers and marketplace inventory.

References