🆔

ERC-165: Standard Interface Detection

2023/01/29に公開

ERC-165: Standard Interface Detection

なんなの?

スマートコントラクトが実装するインターフェイスを公開および検出するための標準メソッドを定義
= インターフェイス識別子を算出する方法を定義

なぜ必要なの?

対象のコントラクトがERC-20などのインターフェイスを実装しているか確認する手段がなかったため

どうやってインターフェイス識別子を取得するの?

インターフェイス内のすべての関数セレクタの排他的論理和(XOR)。
関数セレクタとは、関数の名前と引数をKeccak-256でハッシュにして最初の4バイトを取ったもの。
https://docs.soliditylang.org/en/v0.8.17/abi-spec.html?highlight=selector#function-selector

pragma solidity ^0.4.20;

interface Solidity101 {
    function hello() external pure;
    function world(int) external pure;
}

contract Selector {
    function calculateSelector() public pure returns (bytes4) {
        Solidity101 i;
        return i.hello.selector ^ i.world.selector;
    }
}

どうやって実装するの?

pragma solidity ^0.4.20;

interface ERC165 {
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

上のインターフェイスのインターフェイス識別子は
bytes4(keccak256('supportsInterface(bytes4)'));
で求めることができる。(関数が1つなのでこのような形だが、複数あればXORでつなげる)
結果は0x01ffc9a7である。

詳細な定義

  • 引数interfaceID0x01ffc9a7だったらtrueを返す
  • 引数interfaceID0xffffffffだったらfalseを返す
  • このインターフェイスを実装しているコントラクトだったら、trueを返す
  • それ以外の場合falseを返す
  • このメソッドの実行に使用するgasは30000以下にする必要がある

OpenZeppelinのEIP165の実装

abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/ERC165.sol

type(I).interfaceIdは以下を返す

与えられたインターフェイスIのEIP-165インターフェイス識別子を含むbytes4値。この識別子は、インターフェイス自体で定義されたすべての関数セレクタのXORとして定義されます - 継承されたすべての関数を除く。

https://docs.soliditylang.org/en/v0.8.17/units-and-global-variables.html#meta-type

OpenZeppelinのEIP721の実装

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

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol

参照

https://eips.ethereum.org/EIPS/eip-165

Discussion