第4节:ERC165(识别合约类型)

概述

在opensea中,我们能够知道一个NFT的具体协议类型,即这个collection到底是ERC721还是ERC1155,

而之所以能够判断出NFT的协议类型,是因为标准的NFT的代码都会遵循一个标准,即EIP-165

EIP-165接口定义

EIP165提供了一种检测智能合约类型的方法,在实现上它只定义了一个接口,同时也明确了计算interfaceId的规则:对该协议(如NFT721)的所有接口selector的hash值做^运算(异或,XOR,^),再取前四字节作为interfaceId。

interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

EIP165协议的interfaceId为0x01ffc9a7,计算公式:

0x01ffc9a7=bytes4(keccak256(supportsInterface.selector))

NFT721的interfaceid

// Note: the ERC-165 identifier for this interface is 0x80ac58cd.
interface ERC721 /* is ERC165 */ {
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    function balanceOf(address _owner) external view returns (uint256);
    function ownerOf(uint256 _tokenId) external view returns (address);
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
    function approve(address _approved, uint256 _tokenId) external payable;
    function setApprovalForAll(address _operator, bool _approved) external;
    function getApproved(uint256 _tokenId) external view returns (address);
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

NFT721的interfaceId为:0x80ac58cd,计算公式为:

0x80ac58cd=bytes4(keccak256(ERC721.Transfer.selector)^keccak256(ERC721.Approval.selector) ^ ··· ^keccak256(ERC721.isApprovedForAll.selector))

当我们想判断一个合约的类型是否为NFT721时,只需检查一下supportsInterface(0x80ac58cd)是否返回true即可。

NFT721Meta的interfaceId

// Note: the ERC-165 identifier for this interface is 0x5b5e139f.
interface ERC721Metadata /* is ERC721 */ {
    function name() external view returns (string _name);
    function symbol() external view returns (string _symbol);
    function tokenURI(uint256 _tokenId) external view returns (string); 
}

NFT721Meta的interfaceId为:0x5b5e139f,计算公式为:

0x5b5e139f=bytes4(keccak256(ERC721Metadata.name.selector)^keccak256(ERC721Metadata.symbol.selector)^keccak256(ERC721Metadata.tokenURI.selector))

在NFT721中实现supportsInterface

下面这个接口是NFT721中的标准实现

    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
                  // 0x80ac58cd
            interfaceId == type(IERC721).interfaceId ||
            // 0x5b5e139f
            interfaceId == type(IERC721Metadata).interfaceId ||
            // 0x01ffc9a7
            super.supportsInterface(interfaceId);
    }

总结

  1. 当想检查一个NFT721是否实现了EIP165标准时,只需要查询:supportsInterface(0x01ffc9a7)的返回值即可,true表示支持,false表示不支持;
  2. 同样的道理:0x5b5e139f返回true表示支持IERC721Metadata,0x80ac58cd返回true表示为标准NFT721协议。