ERC721
ERC721 is a standard for representing ownership of non-fungible tokens, that is, where each token is unique. This makes ERC721 tokens useful for things like real estate, voting rights, or collectibles, where some items are valued more than others, due to their usefulness, rarity, etc
Constructing an ERC721 Token Contract
We’ll use ERC721 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 mint items we can add Access
Control.
Here’s what a contract for tokenized items might look like:
// contracts/GameItem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ERC721URIStorage} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
contract GameItem is ERC721URIStorage {
uint256 private _nextTokenId;
constructor() ERC721("GameItem", "ITM") {}
function awardItem(address player, string memory tokenURI)
public
returns (uint256)
{
uint256 tokenId = _nextTokenId++;
_mint(player, tokenId);
_setTokenURI(tokenId, tokenURI);
return tokenId;
}
}
The ERC721URIStorage
contract is an implementation of ERC721 that includes the metadata
standard extensions
(IERC721Metadata
) as well as
a mechanism for per-token metadata. That’s where the
_setTokenURI
method comes from: we use it to store an item’s metadata.
Also note that, unlike ERC20, ERC721 lacks a decimals
field, since
each token is distinct and cannot be partitioned.
New items can be created:
> gameItem.awardItem(playerAddress, "https://game.example/item-id-8u5h2m.json")
Transaction successful. Transaction hash: 0x...
Events emitted:
- Transfer(0x0000000000000000000000000000000000000000, playerAddress, 7)
And the owner and metadata of each item queried:
> gameItem.ownerOf(7)
playerAddress
> gameItem.tokenURI(7)
"https://game.example/item-id-8u5h2m.json"
This tokenURI
should resolve to a JSON document that might look
something like:
{
"name": "Thor's hammer",
"description": "Mjölnir, the legendary hammer of the Norse god of thunder.",
"image": "https://game.example/item-id-8u5h2m.png",
"strength": 20
}
For more information about the tokenURI
metadata JSON Schema, check
out the ERC721 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!
If you’d like to put all item information on-chain, you can extend
ERC721 to do so (though it will be rather costly) by providing a
Base64
Data URI with the JSON schema
encoded. You could also leverage IPFS to store the tokenURI information,
but these techniques are out of the scope of this overview guide.