⚠️ This EIP is not recommended for general use or implementation as it is likely to change.

EIP-5008: ERC-721 Nonce Extension Source

Add a `nonce` function to ERC-721.

AuthorAnders, Lance, Shrug
Discussions-Tohttps://ethereum-magicians.org/t/eip5008-erc-721-nonce-and-metadata-update-extension/8925
StatusDraft
TypeStandards Track
CategoryERC
Created2022-04-10
Requires 165, 721

Abstract

This standard is an extension of ERC-721. It proposes adding a nonce function to ERC-721 tokens.

Motivation

Some orders of NFT marketplaces have been attacked and the NFTs sold at a lower price than the current market floor price. This can happen when users transfer an NFT to another wallet and, later, back to the original wallet. This reactivates the order, which may list the token at a much lower price than the owner would have intended.

This EIP proposes adding a nonce property to ERC-721 tokens, and the nonce will be changed when a token is transferred. If a nonce is added to an order, the order can be checked to avoid attacks.

Specification

The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY” and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

interface IERC5008 is IERC165 {
    /// @notice Get the nonce of an NFT
    /// Throws if `tokenId` is not a valid NFT
    /// @param tokenId The id of the NFT
    /// @return The nonce of the NFT
    function nonce(uint256 tokenId) external view returns(uint256);
}

The nonce(uint256 tokenId) function MUST be implemented as view.

The supportsInterface method MUST return true when called with 0xce03fdab.

Rationale

At first transferCount was considered as function name, but there may some case to change the nonce besides transfer, such as important properties changed, then we changed transferCount to nonce.

Backwards Compatibility

This standard is compatible with ERC-721 standards.

Test Cases

Test Contract

pragma solidity 0.8.10;
import "./ERC5008.sol";

contract ERC5008Demo is ERC5008{
    mapping(uint256 => uint256) private _tokenData;

    constructor(string memory name_, string memory symbol_)ERC5008(name_, symbol_){
    }

    /// @notice mint a new NFT  
    /// @param to  The owner of the new token
    /// @param tokenId  The id of the new token
    function mint(address to, uint256 tokenId) public {
       _mint(to, id);
    }

}

Test Code

run in terminal: npm hardhat test

import { expect } from "chai";
import { ethers } from "hardhat";

describe("Test ERC5008 ", function () {

    let [alice, bob] = await ethers.getSigners();

    const ERC5008Demo = await ethers.getContractFactory("ERC5008Demo");

    let contract = await ERC5008Demo.deploy();

    let tokenId = 1;
    await contract.mint(alice.address, tokenId);

    expect(await contract.nonce(tokenId)).equals(1);

    await contract.transferFrom(alice.address, bob.address, tokenId);

    expect(await contract.nonce(tokenId)).equals(2);
    
});

Reference Implementation

// SPDX-License-Identifier: CC0
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "./IERC5008.sol";

contract ERC5008 is ERC721, IERC5008 {
    mapping(uint256 => uint256) private _tokenNonce;

    constructor(string memory name_, string memory symbol_)ERC721(name_, symbol_){
    }

    /// @notice Get the nonce of an NFT
    /// Throws if `tokenId` is not a valid NFT
    /// @param tokenId The NFT to get the nonce for
    /// @return The nonce of this NFT
    function nonce(uint256 tokenId) public virtual override view returns(uint256) {
        require(_exists(tokenId), "Error: query for nonexistent token");

        return  _tokenNonce[tokenId];
     }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override{
        super._beforeTokenTransfer(from, to, tokenId);
        _tokenNonce[tokenId]++;
    }

    /// @dev See {IERC165-supportsInterface}.
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
        return interfaceId == type(IERC5008).interfaceId || super.supportsInterface(interfaceId);
    }
}

Security Considerations

No security issues found.

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Anders, Lance, Shrug, "EIP-5008: ERC-721 Nonce Extension [DRAFT]," Ethereum Improvement Proposals, no. 5008, April 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5008.