Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EIP: Versioned TokenIDs for Dynamic NFTs #6912

Closed
wants to merge 11 commits into from
124 changes: 124 additions & 0 deletions EIPS/eip-6912.md
@@ -0,0 +1,124 @@
---
eip: 6912
title: Versioned TokenIDs for Dynamic NFTs
description: A minimal, backwards-compatible specification for versioned token IDs for dynamic NFTs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your description doesn't really add any information over the title. Answering the question of "what is a versioned token ID" would be a reasonable goal for your description.

author: James Wenzel (@jameswenzel)
discussions-to: https://ethereum-magicians.org/t/eip-6912-versioned-tokenids-for-dynamic-nfts/13897
status: Draft
type: Standards Track
category: ERC
created: 2023-04-19
requires: 165, 721
---

## Abstract

This EIP proposes an extension to the [ERC-721](./eip-721.md) non-fungible token standard by introducing a Versioned TokenId standard for "dynamic" NFTs with on or offchain properties that may change over time. The new "versioned tokenId" is meant to track both the "identifier" of a token as well as its current "version number" so that old outstanding orders and approvals for updated tokens are automatically invalidated.

A `uint256 versionedTokenId` MUST encode both the true token "identifier" along with a "version number". Encoding, storage, and retrieval specific details should be left up to individual token implementations, and this EIP does not make specific recommendations.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't introduce any requirements ("MUST") outside of the Specification section. Give an overview in the abstract, but don't use the RFC 2119 keywords.


When any onchain action occurs that changes a dynamic token's metadata (on or offchain), the token contract MUST emit a transfer event for the current `versionedTokenId` from the current owner to the null address (i.e., "burning" the old token). It also then MUST emit a transfer event of an updated `versionedTokenId` (which encodes the new "version number" and the same "identifier") from the null address to the original owner. The actual encoded token "identifier" MUST not change, and the encoded "version number" MUST change.

## Motivation

The primary motivation for this EIP is to prevent unintentional sales or approved transfers of tokens whose metadata has been updated, while maintaining backward compatibility with all known marketplaces.

Currently, if a token has a metadata update that makes it subjectively more or less valuable, all open orders, bids, and asks will still be valid. This standard aims to prevent malicious or unintended sales and previously approved transfers of NFTs after they have been changed on or offchain. By implementing this EIP, developers can create NFTs that automatically update their `versionedTokenId` when metadata changes occur, which will invalidate open orders and ensure that only the latest version of a token is used in onchain transactions.

## Specification

**Smart contracts implementing the [ERC-6912](./eip-6912.md) standard MUST implement all methods in the `IERC6912` interface.**

**Smart contracts implementing the ERC-6912 standard MUST implement the [ERC-165](./eip-165.md) `supportsInterface` method and MUST return the constant value `true` if `0xe98e3e5e` is passed through the interfaceID argument.**

### Interface

```solidity
/**
* @title ERC6912 Versioned TokenIDs for Dynamic NFTs
* @dev See https://eips.ethereum.org/EIPS/eip-6912
* Note: The ERC-165 identifier for this interface is 0xe98e3e5e.
*/
interface IERC6912 /* is ERC165 */ {

/**
* @notice This method is meant to ensure that it is always possible to find the current valid `cversionedTokenid` for a given
* token, even with an out-of-date or otherwise incorrect `versionedTokenId`, so long as the encoded "identifier" is valid.
* MUST return the current `versionedTokenId` for the "identifier" encoded by a given `tokenId`.
* MUST revert if passed a `tokenId` with an invalid or non-existent "identifier."
* MUST return the current "version number" for the "identifier" encoded by the `tokenId`.
* MUST NOT revert if the encoded "version number" is incorrect or outdated.
* Implementation details about storage and retrieval of versioned token IDs SHOULD be left up to individual implementations.
*/
function getCurrentVersionedTokenId(uint256 tokenId) external view returns (uint256);

/**
* @notice This method is meant to ensure it is always possible to find the encoded "identifier" for a given `versionedTokenId`,
* even with an out-of-date or otherwise incorrect `versionedTokenId`.
* MUST return the encoded "identifier" for a given `versionedTokenId`.
* MUST NOT revert if the encoded "version number" is incorrect or outdated.
* MAY revert if no token exists for the encoded "identifier" or the "identifier" is otherwise invalid.
*/
function getEncodedTokenIdentifier(uint256 versionedTokenId) external view returns (uint256);

}
```

### Changes to ERC-721 Events

#### `event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`

The standard ERC-721 `Transfer` event MUST be emitted when a token's metadata changes, first for the current `versionedTokenId` (including its encoded "token version") from the current owner to the null address, then for the updated `versionedTokenId` (which encodes the new "token version" and the same "identifier") from the null address to the original owner. This is equivalent to a "burn" event for the old `versionedTokenId`, followed by a "mint" event for the new `versionedTokenId`.


### Changes to ERC-721 Methods

The following methods from ERC-721 MUST be updated to handle `versionedTokenIds`:

```solidity
function approve(address, uint256) external;

function getApproved(uint256) external returns (address) external view;

function transferFrom(address, address, uint256) external;

function safeTransferFrom(address, address, uint256) external;

function safeTransferFrom(address, address, uint256, bytes) external;

function tokenURI(uint256) external view returns (string); // from the optional ERC721Metadata extension
```

Each method MUST revert when called with a `versionedTokenId` that encodes an incorrect or outdated "version number".

Each method SHOULD behave normally when called with a `versionedTokenId` that encodes the current "version number" for a valid "identifier."

```solidity
function ownerOf(uint256) external returns (address) external view;
```

The `ownerOf` method MUST return the current owner of the token with the given `versionedTokenId` if the encoded "version number" is current or `0`. If the `versionedTokenId` encodes an incorrect or outdated (non-zero) "version number", the method MUST revert.

For compatibility with onchain protocols that assume a 1-to-1 mapping of a `tokenId` to a conceptual "identifier" (such as [ERC-6551](./eip-6551.md)), the `ownerOf` method MAY revert for any `versionedTokenId` that encodes a non-zero "version number," only when called by a specific caller or set of callers (such as the ERC-6551 registry contract) to be determined by the implementer.


## Rationale

The introduction of versioned token IDs allows for better and safer marketplace handling of NFTs with changing metadata, and ensures that only the latest "version number" of a token is used in onchain transactions. All indexers that track token burns and mints should be compatible with ERC-6912 tokens. The specified behavior in this EIP minimizes the changes to the ERC-721 standard while maintaining full backwards-compatibility with all known marketplaces.

## Backwards Compatibility

This EIP aims to be fully backwards-compatible with ERC-721 and ERC-721-compatible marketplaces.

To maintain compatibility with onchain protocols that rely on constant `tokenId`s, implementers may refer to the optional behavior described for the `ownerOf` method.

## Security Considerations

Since some NFT marketplaces allow for bulk-signing of up to millions of orders, token developers who anticipate many metadata updates for their token SHOULD make individual token "version numbers" non-incremental but monotonically increasing. They SHOULD use a method of deriving new "version numbers" that makes it difficult or impossible to anticipate future `versionedTokenIds` far in advance, in order to make bulk signature phishing impractical.

As an example, one method would be to use the current BLOCKHASH or PREVRANDAO to increment the current version number, truncated to the desired number of bits. While BLOCKHASH and PREVRANDAO are deterministic and able to be influenced, they are impractical to predict indefinitely far in advance, and are not easily influenced by a single actor.


## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).