ERC-4906 EIP-721 Metadata Update Extension
Final · NFTs & multi-token · created 2022-03-13
Defines a standard event that signals when an NFT's metadata (descriptive information such as image and attributes) has changed. External services such as NFT marketplaces can reflect the changed information in time.
Why it is needed
NFT metadata such as images and attributes is not final once set and can change later. Many EIP-721 contracts emit an event when a token's metadata changes, but the name and shape of that event differ from collection to collection. It is not impossible for third-party platforms such as NFT marketplaces to track these changes. However, they must build separate handling for each collection, so the extra work keeps growing. If a single standard event is defined, a platform can detect metadata changes across many NFTs in the same way and update them in time. This proposal adds that standard event as an optional extension to EIP-721.
What to implement
event MetadataUpdate(uint256 _tokenId);: Emitted when the metadata of a single token changes. It lets third-party platforms such as NFT marketplaces update images and attributes in time.event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);: Emitted when the metadata of a range of consecutive token IDs changes. It announces the start ID and end ID together.
In depth
An optional extension of EIP-721
This standard is an extension of EIP-721 and is OPTIONAL for EIP-721 contracts. The interface IERC4906 inherits IERC165 and IERC721, so a contract that implements it must provide both EIP-165 interface detection and the basic EIP-721 functionality. Requirement keywords (MUST, SHOULD, RECOMMENDED, etc.) follow RFC 2119. The extension adds only two events and one supportsInterface response, and no new functions.
MetadataUpdate: change notification for a single token
When a token's JSON metadata changes, the MetadataUpdate(uint256 _tokenId) event MUST be emitted. The event carries only the token ID and does not say what changed or how. A third party that wants to know the changes receives the event and then calls EIP-721's tokenURI(uint256 _tokenId) to fetch the new metadata. In other words, the event serves only as a signal to re-read that token.
BatchMetadataUpdate: change notification for a consecutive range
When the metadata of a range of consecutive token IDs changes, BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId) is emitted. As with a single-token change, when JSON metadata changes, either MetadataUpdate or BatchMetadataUpdate MUST be emitted. When a range is specified, the third-party platform queries tokenURI individually for each token in the range and updates it. The original text defines the range as a consecutive range, so the event is not meant for grouping several scattered token IDs into one.
Three cases where not emitting the event is recommended
The original text makes it RECOMMENDED not to emit MetadataUpdate in three situations. First, when a token is minted. Second, when a token is burned. Third, when tokenURI has changed but the JSON metadata the URI points to is unchanged. In other words, whether to emit the event is decided by the actual change in the JSON metadata, not by whether the URI string changed. These three cases are RECOMMENDED rather than MUST NOT, so they are not prohibitions that implementers must follow.
supportsInterface and interface ID 0x49064906
A contract that supports this extension MUST return true when supportsInterface is called with 0x49064906. The reference implementation is an ERC4906 contract that inherits OpenZeppelin's ERC721 and overrides supportsInterface to return true if interfaceId is bytes4(0x49064906) and otherwise pass it to super.supportsInterface(interfaceId). The constructor takes name_ and symbol_ and passes them straight to ERC721. The events are declared only in the interface, and the reference implementation contains no code that emits them.
Design rationale: why the event does not carry the change
Each NFT has a different metadata structure with several fields. The changed values could be carried in the event as something like bytes data, but it is hard for third-party platforms to interpret the many varying kinds of bytes data. To avoid unnecessary complexity, the MetadataUpdate event does not include arbitrary metadata. Instead, it takes the approach of having the receiver re-read the full metadata through tokenURI. No backwards compatibility issues were found, and the status in the original text is Final.
Things to watch out for
- Access control for functions that signal off-chain metadata changes: When metadata changes off-chain, a function that emits MetadataUpdate can be added to the contract. In that case, you must make sure the access control of that function is set up correctly.