The ERC-721 standard has redefined how ownership of both virtual and real-world assets is tracked, transferred, and traded on the blockchain.
It has been adopted across industries, from virtual collectibles to real-world assets like art, music, and real-estate.
In this article, we’ll break down what ERC-721 is, the problems it solves, its impact, and how to get started with implementing it.
ERC-721 is the technical standard that defines how smart contracts implement, track, and transfer non-fungible tokens (NFTs) on the Ethereum and other EVM-compatible blockchains.
Fungibility is the ability to exchange or replace an asset with another of the same kind and equal value. For example, dollar bills are fungible. If Alice has a $10 bill, she can exchange it with Bob for another $10 bill, because both have the same value.
A non-fungible asset, on the other hand, has unique characteristics and is not interchangeable with another asset. A piece of art owned by Alice is unique and does not have the same value as a different piece of artwork owned by Bob.
Before ERC-721, ERC-20 specified a standard API for how smart contracts implement, transfer, and track fungible tokens like Ether (ETH) and USDC. ERC-20 also specified an interface that allows wallets and exchanges to interact with and trade these tokens.
However, ERC-20 didn’t account for use cases where developers want a token to represent a unique asset. This limitation made it impossible to represent or trade unique items like houses, artworks, or digital collectibles using ERC-20.
ERC-721 solves the problem of uniqueness by introducing a standard interface that allows you to implement, transfer, and track tokenized proof of ownership. To make integration with wallets and exchanges easier, ERC-721 builds on the existing design of ERC-20.
The key difference between ERC-721 and ERC-20 tokens is that every ERC-721 token has both a tokenId
and a contract address
while ERC-20 tokens have only a contract address. The tokenId
parameter allows ERC-721s to be differentiated from other individual tokens.
For a token to be an NFT, the contract that creates it must implement both ERC-721 and ERC-165 interfaces.
ERC-165 enables contracts to declare their compatibility with ERC-721 which allows other contracts or wallets to check for compatibility before initiating a transfer. Without ERC-165, if an NFT supporting the ERC-721 standard is transferred to a smart contract that doesn't support it, the token may get stuck in the unsupported contract.
Here is an example implementation of the ERC-165’s supportsInterface
function from OpenZeppelin that allows a contract to make a compatibility check:
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
A series of functions are required from both the ERC-721 and ERC-165 standards to manage NFTs.
These functions ensure that each token within the contract is unique, with its own individual metadata—such as the name
, image
, and description—and that the token can be safely transferred to users within the contract and users in other contracts that support the ERC-721 standard.
When a new NFT is minted, the contract that creates it assigns a unique tokenId
to the NFT address. You can use the OpenZeppelin’s Counter utility to confirm the uniqueness of a token.
Below is an example of how we can assign a unique tokenId
to an NFT while minting it using OpenZeppelin Counters utility.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract SimpleERC721Token is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() ERC721("Updraft NFT", "Updraft") Ownable(msg.sender) {}
/**
* @dev Function to mint a new token.
* This function can only be called by the owner of the contract (due to the onlyOwner modifier).
* @param to The address that will own the newly minted token.
* @return The ID of the newly minted token.
*/
function mintToken(address to) public onlyOwner returns (uint256) {
// Increment the token ID counter to get a new unique ID
_tokenIds.increment();
// Get the current token ID
uint256 newItemId = _tokenIds.current();
// Mint the token to the specified address with the new token ID
_mint(to, newItemId);
// Return the newly minted token's ID
return newItemId;
}
}
To create a non-fungible token using the ERC-721 standard, you need to implement the the following required functions:
function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
function approve(address _approved, uint256 _tokenId) external payable;
function setApprovalForAll(address _operator, bool _approved) external;
function getApproved(uint256 _tokenId) external view returns (address);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
function supportsInterface(bytes4 interfaceID) external view returns (bool);
And the required events in the interface:
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
Now, let’s categorize the functions according to their purpose:
The function balanceOf
returns the number of tokens owned by a specific address, while ownerOf
returns the address that owns a particular tokenId
. These make ownership details easily accessible for any given NFT.
The functions transferFrom
and safeTransferFrom
are used to transfer tokens between addresses.
The distinction is that safeTransferFrom
checks whether the receiving contract supports ERC-721.
safeTransferFrom
also verifies if the address it is sending to is a contract address. If it is not a contract address, the call is not executed so tokens are not sent to incompatible addresses. On any transfer, a Transfer
event is emitted to signal the change of ownership on-chain.
ERC-721 provides approve
and setApprovalForAll
functions that allows a user to delegate control of their NFT to an operator.
The approve
function allows a specific address to manage a particular token, while setApprovalForAll
gives an operator full control over all the owner’s tokens.
These functions are essential for decentralized exchanges or marketplaces where users need to give temporary permissions for trading or listing NFTs. These actions trigger Approval
or ApprovalForAll
events.
These functions give you full management control and security over your NFTs. You can implement all the required functions yourself or choose to use well vetted and widely used implementations like OpenZeppelin and Solmate.
An example of how to create a simple NFT using OpenZeppelin is shown below:
In the above image, you can see that only three lines of code are needed for a simple implementation. The rest of the implementations come from the OpenZepplin contract (ERC721
) the contract MyNFT
is inheriting from.
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract MyNFT is ERC721 {
constructor() ERC721("NFTName", "NFTSymbol") {}
}
In the sidebar of the Remix screenshot above, you’ll see every function implemented using the OpenZepplin library. You can immediately interact with your newly minted NFT and use all the available functions: transfer, approve an operator, check for supported interfaces, etc.
ERC-721 has significantly impacted several sectors that relate to asset ownership. Including digital collectibles, artwork, and other real-world assets like real estate.
The first major use case of NFTs was CryptoKitties in 2017. Launched before ERC-721 was finalized, the project was built on the principles that would later be standardized in ERC-721.
CryptoKitties introduced blockchain-based gaming and demonstrated the potential of non-fungible tokens. It allowed users to buy, sell, and breed digital cats, each uniquely owned and verified on the Ethereum blockchain. It was so popular that it congested the Ethereum network.
Since then, new, innovative platforms that leverage ER721 have emerged such as OpenSea, Rarible, and Propy.
They allow users, artists, and collectors to create, buy, and sell assets while maintaining proof of ownership on-chain and immutable forever.
The formalization of ERC-721 also opened new avenues of commerce for the gaming industry. Game designers can now build unique assets into their worlds that users can own and trade on platforms like Axie Infinity, Decentraland, and Immutable.
Platforms like Propy also make it easy to tokenize property ownership using NFTs. You can buy real-world properties (like houses and land) on the platform and your ownership is recorded on-chain and transferred to you.
In 2023, development authorities in West Bengal, India, partnered with Polygon and Airchains to implement a blockchain-based land ownership and mutation system with NFTs.
The ERC-721 standard has also inspired other standards to improve or extend their functionality. Examples include:
Notable implementations of NFTs vary across sectors.
One of the earliest and most well-known applications of NFTs is digital art. NFT platforms like OpenSea and Rarible enable artists to tokenize and sell unique digital artworks.
Axie Infinity and CryptoKitties use NFTs for unique in-game assets that players can trade and own.
The Opulus and Audius platforms allow musical artists to sell their music as NFTs. In 2021, Kings of Leon released an album as NFTs and sold it to fans directly.
Platforms like The Sandbox and Decentraland allow users to purchase, own, and build on parcels of land within their digital worlds.
Brands like RTFKT create NFT fashion items for virtual and real-world use. From December 2021 to 2023, Nike-RTFKT NFTs have earned close to $1.4 billion.
As blockchain technology becomes more integrated into various sectors, NFTs will likely play a key role in reshaping how we think about ownership.
The scope of NFTs is expected to move well beyond art, and we’ll likely see increased impact in industries like real estate, finance, and intellectual property as mainstream adoption increases.
To further solidify your understanding and get hands-on experience creating NFTs, Cyfrin Updraft has a complete tutorial covering how to develop an NFT collection.