ERC-721

We’ve discussed how you can make a fungible token using ERC-20, but what if not all tokens are alike? This comes up in situations like real estate, voting rights, or collectibles, where some items are valued more than others, due to their usefulness, rarity, etc. ERC-721 is a standard for representing ownership of non-fungible tokens, that is, where each token is unique.

ERC-721 is a more complex standard than ERC-20, with multiple optional extensions, and is split across a number of contracts. The OpenZeppelin Contracts provide flexibility regarding how these are combined, along with custom useful extensions. Check out the API reference to learn more about these.

Constructing an ERC-721 Token Contract

We’ll use ERC-721 to track items in our game, which will each have their own unique attributes. Whenever one is to be awarded to a player, it will be minted and sent to them. Players are free to keep their token or trade it with other people as they see fit, as they would any other asset on the blockchain! Please note any account can call awardItem to mint items. To restrict what accounts can be minted per item. We can use an Access Control extension.

Here’s what a contract for tokenized items might look like:

sol_storage! {
    #[entrypoint]
    struct GameItem {
        #[borrow]
        Erc721 erc721;
        #[borrow]
        Metadata metadata;
        uint256 _next_token_id;
    }
}

#[external]
#[inherit(Erc721, Metadata)]
impl GameItem {
    pub fn award_item(
        &mut self,
        player: Address,
    ) -> Result<U256, Vec<u8>> {
        let token_id = self._next_token_id.get() + uint!(1_U256);
        self._next_token_id.set(token_id);

        self.erc721._mint(player, token_id)?;

        Ok(token_id)
    }
}

The Erc721Metadata contract is an extension contract of ERC-721. It extends the contract itself with the name, symbol and base uri for the token.

Also note that, unlike ERC-20, ERC-721 lacks a decimals field, since each token is distinct and cannot be partitioned.

For more information about erc721 schema, check out the ERC-721 specification.

You’ll notice that the item’s information is included in the metadata, but that information isn’t on-chain! So a game developer could change the underlying metadata, changing the rules of the game!

Extensions

Additionally, there are multiple custom extensions, including:

  • ERC-721 Burnable: A way for token holders to burn their own tokens.

  • ERC-721 Consecutive: An implementation of ERC2309 for minting batches of tokens during construction, in accordance with ERC721.

  • ERC-721 Enumerable: Optional extension that allows enumerating the tokens on chain, often not included since it requires large gas overhead.

  • ERC-721 Metadata: Optional extension that adds name, symbol, and token URI, almost always included.

  • ERC-721 Pausable: A primitive to pause contract operation.

  • ERC-721 Uri Storage: A more flexible but more expensive way of storing metadata.