🐵

ERC-721: Non-Fungible Token Standard

2023/03/05に公開

ERC-721: Non-Fungible Token Standard

概要

代替不可能なトークンの標準インターフェース

2つのユースケースが考慮されている

  • 個人同士で取引する場合
  • 第三者に委託して取引する場合
    • オペレーターと呼んでいる
      • オペレーター = ブローカー, ウォレット, オークショナー

NFTはデジタル資産や物理的な資産の所有権を表す。
NFTは区別可能で、それぞれの所有権を個別に追跡できる

動機

標準的なインターフェイスを用意することで、イーサリアム上のNFTとの連携をあらゆるアプリケーションができるようになる。
ERC-721はERC-20に触発されて作成された。
EIP-20はNFTの追跡には不十分であった。なぜなら、NFTは資産を区別する必要があるが、FTは区別しないからである。

コード

以下のコード内のコメント部分を意訳する
https://eips.ethereum.org/EIPS/eip-721#specification

用途によって実装すべきインターフェイスは異なる

  • ERC721: MUSTで実装しなければならない
  • ERC721TokenReceiver: NFTの送付を受け入れるcontractが実装すべきインターフェイス
  • ERC721Metadata: NFTが表す資産に関する詳細を実装できる。任意実装のインターフェイス
  • ERC721Enumerable: NFTの完全なリストを公開し、それらを発見できるようにすることができる。任意実装のインターフェイス

ERC721

pragma solidity ^0.4.20;

/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://eips.ethereum.org/EIPS/eip-721
///  Note: the ERC-165 identifier for this interface is 0x80ac58cd.
// 以下のインターフェイスはMUSTで実装する必要がある
interface ERC721 /* is ERC165 */ {
    ////////////////////////////
    // event
    ////////////////////////////


    // NFTの所有権が変更された場合に発生する。
    // NFTが作成されたとき(from == 0)や破棄されるときにも発生する(to == 0)
    // 例外として、contractの作成時に作成されたNFTはこのイベントを発生させない。
    // このイベントを発生させたとき、そのNFTの承認はリセットされる
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

    // 1つのNFTの承認されたアドレスが変更 or 再確認された場合に発生する。
    // ゼロアドレスは承認されたアドレスが無いことを示す。
    // `Transfer`イベントが発生したらこのイベントも発生して、承認されたアドレスがあればリセットする。
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);

    // オペレータがオーナーの代わりにすべてのNFTを管理できるようにするために有効または無効にしたときに発生する。
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
  
    ////////////////////////////
    // function
    ////////////////////////////

    // オーナーに割り当てられたすべてのNFTを数える
    // ゼロアドレスに割り当てられたNFTは無効として、例外を発生させる
    function balanceOf(address _owner) external view returns (uint256);

    // NFTのオーナーを見つける
    // ゼロアドレスに割り当てられたNFTは無効として、例外を発生させる
    function ownerOf(uint256 _tokenId) external view returns (address);

    // NFTの所有権の移転をする
    // 以下をチェックする
    // - `msg.sender`が以下のいずれかでなければ例外を投げる
    //   - 現在のオーナー
    //   - 承認されたオペレーター
    //   - このNFTの承認されたアドレス
    // - `_from`が現在のオーナーでない場合は例外を投げる
    // - `_to`がゼロアドレスの場合は例外を投げる
    // - `_tokenId`が有効なNFTではない場合は例外を投げる
    // 転送が完了したら`_to`がスマートコントラクトであるかどうか確認する
    // ※ コードサイズをチェックして`コードサイズ > 0`であればスマートコントラクト。
    // `_to`がスマートコントラクトの場合、`onERC721Received`を`_to`で呼び出し、返された値が`bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`でない場合は例外を投げる。
    /// @param _from: 現在のNFTの所有者
    /// @param _to: 新しい所有者
    /// @param _tokenId: 転送するNFT
    /// @param data: フォーマットの指定されていない追加データ
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;

    // NFTの所有権の移転をする
    // 上記の引数の`data`が存在しない関数
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

    // NFTの所有権の移転をする
    // この関数では`_to`が`safeTransferFrom`のようにNFTを受信できることを確認しない。
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    // NFTの承認アドレスを変更または再確認する
    // ゼロアドレスは承認されたアドレスが無いことを示す。
    // `msg.sender`が現在のNFTのオーナー or 現在のオーナーの承認されたオペレーターでない場合は例外を投げる
    /// @param _approved: 新しく承認するNFTコントローラー
    /// @param _tokenId: 承認するNFT
    function approve(address _approved, uint256 _tokenId) external payable;

    // `msg.sender`のすべての資産を管理するためのサードパーティの承認を有効 or 無効にする
    // `ApprovalForAll`イベントを発生させる。
    /// @param _operator: 承認するオペーレーターのアドレス
    /// @param _approved: 承認する場合は`True`, 承認を取り消す場合は`false`
    function setApprovalForAll(address _operator, bool _approved) external;

    // 単一のNFTに対する承認されたアドレスを取得する
    // `_tokenId`が有効なNFTでない場合は例外を投げる
    // 該当アドレスがない場合はゼロアドレスを返す
    function getApproved(uint256 _tokenId) external view returns (address);

    // ownerに対して`_operator`が承認されたアドレスかどうか確認する
    // 承認されていれば`True`, そうでなければ`False`を返す
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

interface ERC165 {
    // コントラクトがインターフェースを実装しているかどうかをクエリする
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

ERC721TokenReceiver

ウォレット/ブローカー/オークション アプリケーションはsafe transferを受け入れる場合、以下のインターフェイスを実装する必要がある。

/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
interface ERC721TokenReceiver {
    // NFTの受領処理を処理する
    // この関数は例外を発生させてトランザクションをrevertすることで転送を拒否することがある
    /// @return 例外を発生しない場合、`bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
    function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4);
}

ERC721Metadata

NFTが表す資産に関する詳細を実装できる任意実装のインターフェイス

/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev 詳細は https://eips.ethereum.org/EIPS/eip-721 を参照してください。
///  注意: このインターフェースのERC-165識別子は0x5b5e139fです。
interface ERC721Metadata /* is ERC721 */ {
    /// @notice このコントラクトのNFTコレクションの説明的な名前
    function name() external view returns (string _name);

    /// @notice このコントラクトのNFTの略称名
    function symbol() external view returns (string _symbol);

    /// @notice 特定のアセットに対する一意のUniform Resource Identifier(URI)
    /// @dev `_tokenId`が有効なNFTでない場合はthrowします。
    ///  URIはRFC 3986で定義されています。URIは、「ERC721 Metadata JSONスキーマ」に準拠するJSONファイルを指すことができます。
    function tokenURI(uint256 _tokenId) external view returns (string);
}

ERC721Enumerable

NFTの完全なリストを公開し、それらを発見できるようにすることができる。任意実装のインターフェイス

/// @title ERC-721非代替可能トークン標準、任意の列挙拡張
/// @dev 詳細は https://eips.ethereum.org/EIPS/eip-721 を参照してください。
///  注意: このインターフェースのERC-165識別子は0x780e9d63です。
interface ERC721Enumerable /* is ERC721 */ {
    /// @notice このコントラクトでトラッキングされているNFTの数を数える
    /// @return このコントラクトでトラッキングされている有効なNFTの数。
    ///  それぞれに、割り当て可能でクエリ可能なオーナーがゼロアドレスとは異なるアドレスに割り当てられています
    function totalSupply() external view returns (uint256);

    /// @notice 有効なNFTを列挙する
    /// @dev `_index` >= `totalSupply()`の場合はthrowします。
    /// @param _index `totalSupply()`未満のカウンター
    /// @return `_index`番目のNFTのトークン識別子、(ソート順序は指定されていません)
    function tokenByIndex(uint256 _index) external view returns (uint256);

    /// @notice オーナーに割り当てられたNFTを列挙する
    /// @dev `_index` >= `balanceOf(_owner)`または`_owner`がゼロアドレスである場合はthrowします。
    /// これは、無効なNFTを表します。
    /// @param _owner 彼らが所有するNFTに興味があるアドレス
    /// @param _index `balanceOf(_owner)`未満のカウンター
    /// @return `_owner`に割り当てられた`_index`番目のNFTのトークン識別子、(ソート順序は指定されていません)
    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}

NFT作成・破壊は仕様に含まれない

Mint, Burnは仕様に含まれていない。
独自で実装する必要がある。

Discussion