The non-fungible tokens, or in short NFTs, have one the most influential roles in the crypto and Web3 scene. These are digital assets that can represent digital art, virtual collectibles, assets in games, and more. Ethereum Foundation introduced the ERC-721 standard in 2017, which helped all the crypto wallets, brokers, and protocols use this feature in a translatable way between any crypto solution. In this post, we will look more deeply into the ERC-721 standard and explore how it is used in the Solidity programming language for the Ethereum blockchain. What Is an NFT? Non-fungible tokens represent unique assets that can't be exchanged, copied, substituted, or divided. Each token has a unique identifier. Therefore, we can see who owns what. An NFT can represent but is not limited to: digital work of art; virtual collectibles; digital assets in games; ownership rights, for instance, to a real estate; tickets that have unique data like seating place; a negative loan like a mortgage. NFTs are like humans - there are no two people the same. The ERC-721? To represent an NFT, the Ethereum Foundation has implemented it with an ERC-721 token standard. This standard describes a table of who owns what. The standardized approach helps crypto wallets, brokers, and auctions to work with NFTs on Ethereum and other EVM blockchains. The ERC-721 standard was launched in 2017 and authored by William Entriken, Dieter Shirley, Jacob Evans, and Nastassia Sachs. I met William Entriken at one of the NFT conferences. Functions There are several functions defined in the ERC-721 standard. Let's look at the most important ones separately. Ownership Functions These functions describe ownership rights. returns a number of NFTS owned by address; balanceOf(address _owner) _owner returns an owner who owns a token with the identifier; ownerOf(uint256 _tokenId) _tokenId Transfer Functions These functions help to transfer a token. transfers the token with the identifier from the address to the address checking that recipient is aware of the ERC-721 standard; safeTransferFrom(address _from, address _to, uint256 _tokenId) tokenId _from _to similar to the function above with the option to send to the to in a call, can be useful to send additional data; safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) data _to gives rights to the to transfer the token with the identifier; approve(address _approved, uint256 _tokenId) _approved _tokenId returns the wallet address approved to transfer the token with identifier; getApproved(uint256 _tokenId) _tokenId approve or remove approval for all the assets to ; setApprovalForAll(address _operator, bool _approved) msg.sender _operator checks if the address has rights for the assets. isApprovedForAll(address _owner, address _operator) _operator _owner Events Events help to notify about changes with an NFT. For instance, the web frontend can be updated. emits when the token with the identifier is transferred from the to the . Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId) _tokenId _from _to emits when approval rights are given to the to the token with the _owner` who has ownership rights; Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId) _approved _tokenId' identifier by the emits when gives or removes rights to the for their assets. ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved) _owner _operator Metadata Extension A commonly used ERC-721 extension describes metadata in a JSON format. It represents information about a token, like a name, symbol, and token URI. Using this extension, crypto wallets can ask for the information using three functions: returns a descriptive name; function name() external view returns (string _name) returns token symbol; function symbol() external view returns (string _symbol) returns URL to JSON file that describes the NFT. Usually, this is stored on IPFS. function tokenURI(uint256 _tokenId) external view returns (string) The token URI points to a JSON file that conforms to the ERC721 Metadata JSON Schema: { "title": "Asset Metadata", "type": "object", "properties": { "name": { "type": "string", "description": "Identifies the asset to which this NFT represents" }, "description": { "type": "string", "description": "Describes the asset to which this NFT represents" }, "image": { "type": "string", "description": "A URI pointing to a resource with mime type image representing the asset to which this NFT represents. " } } } If you're unfamiliar with IPFS file storage, we will discuss that in one of the following posts. Putting It All Together The most straightforward way to implement NFTs is to use the OpenZepplin contracts suite that includes the and contracts. It fully conforms to the ERC-721 and metadata extension standards. ERC721 ERC721URIStorage Let's build an NFT ticketing smart contract. First, we must import OpenZepplin ERC-721 contract implementations and conform our smart contract to it. import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; contract Ticket is ERC721URIStorage { /// ... } At first, we need to define the NFT name and symbol. To do that, we pass the event name and description to smart contract the constructor. constructor( string memory eventName, string memory shortName ) ERC721(eventName, shortName) { // initializes the NFT storage } We can now start minting NFTs because the OpenZepplin hides away all the nitty gritty implementation details. function createTicket(address visitor, string memory tokenURI) external { tokenId++; // mint the NFT and assign to the visitor address _mint(visitor, tokenId); // set the token JSON file link that was uploaded to the IPFS _setTokenURI(tokenId, tokenURI); console.log(tokenId); } All the ERC-721 functions discussed above come with the OpenZepplin contracts. for free TL;DR The non-fungible tokens, or NFTs, differ much from regular ERC-20 tokens. They describe uniqueness. Remember what we discussed at the beginning of this post - NFTs are like humans. Or let me call it differently this time - NFTs are like kitties - there are no two kitties alike. The most commonly used standard that defines NFTs is ERC-721, with metadata extension. It helps to attach metadata to the token, like images which is the most used feature in the crypto world. Links Sample code EIP-721: Non-Fungible Token Standard What is ERC-721? The Ethereum NFT Token Standard How to create and deploy an ERC-721 (NFT) ERC721 by OpenZepplin