ERC721 is the standard for non-fungible tokens (NFTs). Its security model differs significantly from ERC20 — each token is unique, ownership tracking is more complex, and the safeTransferFrom mechanism introduces reentrancy vectors that surprise many developers.
When safeTransferFrom sends an NFT to a contract address, it calls onERC721Received on the recipient. This is an external call that can re-enter your contract before state updates complete — a classic reentrancy scenario.
// VULNERABLE — state update happens after the external call
function sell(uint256 tokenId) public {
nft.safeTransferFrom(address(this), msg.sender, tokenId);
balances[msg.sender] -= price; // too late — re-entered here
}
Always update state before transferring NFTs. Use OpenZeppelin's ReentrancyGuard on any function that calls safeTransferFrom. See reentrancy.
ERC2981 on-chain royalties are enforced by NFT marketplaces voluntarily — they are not enforced by the ERC721 contract itself. A custom marketplace or a direct transfer between wallets will not pay royalties. Protocols that assume royalties are always collected will have incorrect accounting.
A common bug in NFT contracts is leaving the mint function callable by anyone, or with an overly broad access control. Always restrict minting to authorized addresses and implement maximum supply caps to prevent infinite minting. See access control.
Using uint256 for tokenIds is standard, but iteration over all tokens owned by an address is expensive on-chain. Use the ERC721Enumerable extension carefully — the storage overhead grows linearly with NFT count and can make ownership tracking prohibitively expensive in loops.
Most NFT metadata is hosted off-chain (IPFS or centralized servers). Contracts with a mutable setBaseURI function allow the deployer to change the metadata — or delete it — after sale. Buyers may end up with NFTs pointing to empty or changed images. Prefer IPFS with immutable CIDs and remove URI mutability after reveal.
ERC721 or ERC721A as the base contractReentrancyGuard to any function that calls safeTransferFrommint with onlyOwner or a signature-based allow listMAX_SUPPLY constant enforced in the mint functionblock.timestampbaseURI after revealSmartContract.us audits ERC721 contracts for reentrancy, access control, and metadata security issues. Analyze an NFT contract →