# ERC165使用方法

By [xyyme.eth](https://paragraph.com/@xyyme) · 2022-03-11

---

了解过 NFT 合约的同学应该注意到，每个 NFT 合约都实现了 **supportsInterface** 方法，但是每个合约中的具体方法实现又不尽相同，那么在实际开发过程中，究竟应该怎么实现这个方法呢，这篇文章就来讲讲这个内容

> ERC165的具体内容这里就不再多说了，不了解的同学可以 Google 一下

首先我们需要知道计算一个接口的 interfaceId 都有几种方法。

假设对于如下接口：

    interface IExample {
        function foo() external view returns (uint);
    
        function bar() external view returns (bool);
    
        function baz() external view returns (bytes32);
    }
    

那么可以这样计算它的 interfaceId

### 使用硬编码函数签名计算（不推荐，容易出错且麻烦）

    function calcInterfaceId1() public view returns (bytes4) {
        return bytes4(keccak256("foo()"))
            ^ bytes4(keccak256("bar()"))
            ^ bytes4(keccak256("baz()"));
    }
    

### 使用方法 selector 计算（不推荐，不过在 0.6.7 之前的版本要用这个）

    function calcInterfaceId2() public view returns (bytes4) {
        IExample i;
        return i.foo.selector ^ i.bar.selector ^ i.baz.selector;
    }
    

### 直接使用 type 关键词（推荐，0.6.7版本及之后支持）

    function calcInterfaceId3() public view returns (bytes4) {
        return type(IExample).interfaceId;
    }
    

这三种方法都可以用来计算 interfaceId，结果相同，都是 **0x9bb235aa**。

接下来，我们看看实际开发中都是怎么实现 **supportsInterface** 方法的。

来看几个例子：

### 同时继承与实现包含 IERC165 的合约（ERC721）和接口（IERC2981）

[Invisible Friends](https://opensea.io/collection/invisiblefriends) → [代码](https://etherscan.io/address/0x59468516a8259058bad1ca5f8f4bff190d30e066#code)

    // 合约继承了 ERC721，实现了 IERC2981接口
    contract InvisibleFriends is ERC721, IERC2981, Ownable,                                                                ReentrancyGuard {
    }
    

    // override 后面包括 ERC721 和 IERC2981 的最上层基类 IERC165
    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, IERC165)
        returns (bool)
    {
        // 需要显式声明 IERC2981
        return
            interfaceId == type(IERC2981).interfaceId ||
            super.supportsInterface(interfaceId);
    }
    

合约实现的基类中，ERC721 和 IERC2981 中都包括 IERC165，因此需要显式重写 **supportsInterface** 方法。override后面的括号需要包含 ERC721，由于 IERC2981 是接口，因此要包含 IERC2981 的最上层基类 IERC165，同时需要在方法实现中显示声明 IERC2981 接口本身。

### 同时继承多个包含 IERC165接口的合约（ERC721，ERC721Enumerable）

[mfers](https://opensea.io/collection/mfers) → [代码](https://etherscan.io/address/0x79fcdef22feed20eddacbb2587640e45491b757f#code)

    // 合约继承了 ERC721，ERC721Enumerable
    contract mfers is ERC721, ERC721Enumerable, Ownable {
    }
    

    // override 后面包括 ERC721 和 ERC721Enumerable
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        // 无需显式声明接口
        return super.supportsInterface(interfaceId);
    }
    

合约实现的基类中，ERC721 和 ERC721Enumerable 中都包括 IERC165。因此需要显式重写 **supportsInterface** 方法。而由于两者均非接口，因此直接在 override 后面声明。在方法实现中无需显式声明。

### 只继承一个包含 IERC165 接口的合约（ERC721A）

[Tasty Bones](https://opensea.io/collection/tastybonesxyz) → [代码](https://etherscan.io/address/0x1b79c7832ed9358e024f9e46e9c8b6f56633691b#code)

    // 合约继承了 ERC721
    contract TastyBones is Ownable, ERC721A, ReentrancyGuard {
    }
    
    // 没有显式实现 supportsInterface 方法
    

合约实现的基类中，只有 ERC721A 一个合约包括 IERC165 接口，因此合约的最终实现无需显式实现 **supportsInterface** 方法。

这三个合约基本上代表了几种具体实现方式，总结一下：

*   如果合约继承的基类中只有一个合约包含 IERC165，即没有冲突，那么无需显式实现
    
*   如果基类中存在 IERC165 冲突，且均非接口，需要显式实现，override 之后需要包括这些基类名，方法实现中无需显式包括合约
    
*   如果基类中存在 IERC165 冲突，且存在接口，需要显式实现，override 之后需要包括合约以及接口的最上层基类 IERC165，方法实现中需要显式包括继承的接口
    

### 参考

[

ERC-165: Standard Interface Detection
-------------------------------------

Creates a standard method to publish and detect what interfaces a smart contract implements.

https://eips.ethereum.org



](https://eips.ethereum.org/EIPS/eip-165)

---

*Originally published on [xyyme.eth](https://paragraph.com/@xyyme/erc165)*
