🌏

[solidity]ERC165でトークンの種類を判定する

2023/09/17に公開

スマートコントラクトを利用するとき

どんな種類のスマートコントラクトなのか

という判定をしたい場合があります。

例えばNFTマーケットのopenseaでは、urlのスマートコントラクトアドレスからNFTの種類がERC721かERC1155かを判定しています。

このとき役立つ規格が

ERC165

になります。

この記事では

ERC165の説明、実装について説明します。

ERC165とは

コントラクトが任意のインターフェースを実装しているかどうかを検出する規格です。

  • ERC-165を実装している場合は、supportsInterface(interfaceID)関数を呼び出すだけで、使用できるインターフェイスを実装しているかどうかを判断できます。
  • ERC-165を実装していない場合は、そのコントラクトがどのメソッドを使用しているかを昔ながらの方法で確認する必要があります。

開発環境

  • solidity
  • hardhat
  • web3(1.10)
  • vscode

インターフェース

ERC165のインターフェースは以下のようになります。

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
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);
}

使い方は簡単で、コードにIERC165を継承して、supportsInterface(bytes4 interfaceId)に内容を記載するだけです。

実装

具体的に実装してみましょう。

Hoge.sol
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/interfaces/IERC165.sol";

contract Hoge is IERC165 {

    function supportsInterface(bytes4 interfaceId) external view returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }

    function greet()public view returns (string) {
        return "Hello"
    }
}

インターフェースの識別

インターフェースを実装しているかどうかを判別するにはinterfaceIdが必要です。

interfaceIdはEthereum ABIによって定義された関数セレクタのセットになります。

具体的にはERC165

function supportsInterface(bytes4 interfaceID) external view returns (bool);

の識別子は

bytes4(keccak256('supportsInterface(bytes4)'));

で計算できます。

上記の結果は

0x01ffc9a7

になります。

例えば

interface Test {
    function isUser() external returns (bool);
}

というinterfaceを使った場合は

bytes4(keccak256('isUser()'));

で計算します。

上記の結果は

0x4c22c119

になります。

この結果を確認するには

type(Test).interfaceId

で値を確認することができます。

テストコード

ERC165のテストコードは以下のように記載します。

hoge.js
const { expect } = require("chai");
const { ethers } = require("hardhat");

// 省略

  describe("IERC165", function () {
    it("should be true when ERC165 is implemented", async function () {
      expect(await Hoge.supportsInterface("0x01ffc9a7")).to.be.true;
    });
  });

フロントエンド

フロントでは以下のように呼び出します。

hoge.tsx
contract.methods.supportsInterface("0x5b5e139f").call().then(function(result: boolean){
  if (result) {
    // something
  }
});

まとめ

ERC165はNFTマーケットなどを作成する場合に必須の仕様になります。

きちんと理解しておきましょう。

参考記事

Discussion