Creating non-fungible tokens (NFTs) has become a cornerstone of blockchain innovation, especially on the Ethereum network. At the heart of this revolution lies the ERC721 standard, a powerful and widely adopted protocol for building unique digital assets. Whether you're a developer exploring decentralized applications or an enthusiast diving into NFT creation, understanding how to program an ERC721 token using Solidity is essential.
This guide walks you through the structure, core functions, and step-by-step deployment of an ERC721 smart contract — all while maintaining code clarity and real-world applicability.
What Is ERC721?
ERC721 stands for Ethereum Request for Comments, with 721 being the proposal identifier. It defines a standard interface for Non-Fungible Tokens (NFTs) on the Ethereum blockchain. Unlike fungible tokens such as ETH or ERC20 tokens, each ERC721 token is unique, indivisible, and not interchangeable. This makes them ideal for representing ownership of digital art, collectibles, virtual real estate, and more.
The standard ensures compatibility across wallets, marketplaces, and platforms by defining a common set of rules and functions that every compliant contract must implement.
Core Functions of ERC721
An ERC721-compliant smart contract includes several key functions divided into categories: general token information, ownership management, and metadata handling.
Basic Token Information (ERC-20 Like)
These functions are familiar to those who have worked with ERC20 tokens:
name()– Returns the full name of the token (e.g., "GeeksforGeeks NFT").symbol()– Provides a short ticker symbol (e.g., "GFG").totalSupply()– Retrieves the total number of minted tokens.balanceOf(address owner)– Shows how many NFTs a specific address owns.
👉 Learn how to build your first NFT collection with step-by-step guidance.
Ownership Management
These functions handle transfer permissions and ownership tracking:
ownerOf(uint256 tokenId)– Returns the owner address of a given token ID.approve(address to, uint256 tokenId)– Grants another address permission to transfer a specific token.getApproved(uint256 tokenId)– Checks which address is approved to manage a token.setApprovalForAll(address operator, bool approved)– Allows an operator to manage all tokens of the caller.isApprovedForAll(address owner, address operator)– Verifies if an operator is approved for full control.transferFrom(address from, address to, uint256 tokenId)– Transfers a token from one address to another (must be called by the owner or approved party).safeTransferFrom(...)– A safer version that ensures the receiving contract can handle NFTs.
Additionally:
tokenOfOwnerByIndex(address owner, uint256 index)– Lets you iterate through all tokens owned by an address using an index-based lookup.
Metadata Function
One of the most important aspects of an NFT is its metadata, which describes its attributes (like image URL, name, properties, etc.).
tokenURI(uint256 tokenId)– Returns a JSON-formatted metadata link for a given token ID. This URI often points to an IPFS hash or HTTPS endpoint hosting the asset details.
For example:
{
"name": "Crypto Art #1",
"description": "A digital masterpiece",
"image": "https://ipfs.io/ipfs/QmXy...",
"attributes": [ ... ]
}This function enables platforms like OpenSea to display rich content linked to your NFT.
Events in ERC721
Smart contracts emit events to notify external applications about state changes. The two primary events in ERC721 are:
- Transfer – Triggered when a token is sent from one address to another. Includes
from,to, andtokenId. - Approval – Fired when an address is granted permission to take ownership of a token.
These events allow dApps and indexers to track ownership and activity in real time.
How to Create and Deploy an ERC721 Token
Let’s walk through creating a simple NFT smart contract using Solidity and deploying it via Remix IDE on the Ropsten testnet (now deprecated; we’ll use Goerli as a modern alternative).
Prerequisites
- A Web3 wallet (e.g., MetaMask)
- Test ETH from a faucet (e.g., Goerli faucet)
- Access to Remix IDE
Step-by-Step Implementation
Step 1: Specify License Identifier
// SPDX-License-Identifier: MITThis line declares the open-source license used for transparency and compliance.
Step 2: Define Solidity Version
pragma solidity ^0.8.0;Using Solidity 0.8.x ensures built-in overflow protection and improved security.
Step 3: Import Standard Libraries
Instead of raw imports from GitHub, use OpenZeppelin’s trusted libraries:
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";These provide secure implementations of ERC721 with metadata support and ownership controls.
Step 4: Define Your Contract
contract MyNFT is ERC721URIStorage, Ownable {
constructor() ERC721("GeeksNFT", "GFG") Ownable(msg.sender) {}This initializes the NFT with a name ("GeeksNFT") and symbol ("GFG"), while assigning ownership to the deployer.
Step 5: Implement Minting Function
function mintNFT(address recipient, uint256 tokenId, string memory tokenURI) public onlyOwner {
_mint(recipient, tokenId);
_setTokenURI(tokenId, tokenURI);
}This function allows the owner to mint new tokens and assign metadata via URI.
👉 Start minting your own NFTs with tools designed for developers and creators.
Full Smart Contract Code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyNFT is ERC721URIStorage, Ownable {
constructor() ERC721("GeeksNFT", "GFG") Ownable(msg.sender) {}
function mintNFT(address recipient, uint256 tokenId, string memory tokenURI) public onlyOwner {
_mint(recipient, tokenId);
_setTokenURI(tokenId, tokenURI);
}
}Deployment Steps
- Open Remix IDE
- Create a new file (
MyNFT.sol) and paste the code - Compile using the Solidity compiler
- Switch environment to Injected Web3 (connects MetaMask)
- Deploy on Goerli testnet
- Interact with functions: call
mintNFTwith recipient address, token ID, and metadata URI
After deployment, verify the transaction on Etherscan and view your NFT on marketplaces like OpenSea (testnet mode).
Frequently Asked Questions (FAQ)
Q: Can ERC721 tokens be split or divided?
A: No. ERC721 tokens are non-fungible and indivisible — each represents a single unique asset.
Q: What’s the difference between ERC721 and ERC1155?
A: ERC721 supports only one unique token per ID, while ERC1155 allows both fungible and non-fungible tokens in a single contract.
Q: Where should I store NFT metadata?
A: Use decentralized storage like IPFS or Arweave to ensure permanence and avoid centralization risks.
Q: Do I need real ETH to deploy an ERC721 contract?
A: Not for testing. Use testnets like Goerli with free test ETH from faucets before deploying on mainnet.
Q: Can someone else mint my NFTs after deployment?
A: Only if you allow it. Using onlyOwner restricts minting to the contract owner unless explicitly changed.
Q: How do I make my NFT tradable on marketplaces?
A: Ensure your contract follows ERC721 standards and includes proper tokenURI metadata — platforms like OpenSea automatically detect compliant contracts.
Final Thoughts
Programming an ERC721 token in Solidity opens doors to innovative digital ownership models. With standardized interfaces, robust tooling from OpenZeppelin, and accessible IDEs like Remix, creating NFTs is now within reach for developers at all levels.
Whether you're launching a digital art project or building a game with rare in-game items, mastering ERC721 is your first step toward blockchain-powered uniqueness.
👉 Explore advanced NFT development tools and resources to accelerate your journey.