はじめに

初めまして。 CryptoGamesというブロックチェーンゲーム企業でエンジニアをしている cardene(かるでね) です! スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

代表的なゲームはクリプトスペルズというブロックチェーンゲームです。

今回は、ERC721を拡張して、他のERC721やERC20トークンを所有する提案している規格であるERC998についてまとめていきます!

以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。

998は現在(2023年10月28日)では「Draft」段階です。

他にも様々なERCについてまとめています。

概要

この規格は、ERC721トークンが他のERC721トークンとERC20トークンを所有できるようにするものです。 また、ERC20やERC223トークンがERC721トークンによって所有できるようにもします。 これにより、4つの種類の合成可能なトークンが生まれます。

  1. ERC998ERC721 Top-Down Composable
    • ERC721トークンがERC721トークンを所有し、保持し、転送できるトークン。
  2. ERC998ERC20 Top-Down Composable
    • ERC721トークンがERC20トークンを所有し、保持し、転送できるトークン。
  3. ERC998ERC721 Bottom-Up Composable
    • ERC721トークンを他のERC721トークンにアタッチできるトークン。
  4. ERC998ERC20 Bottom-Up Composable
    • ERC20トークンをERC721トークンにアタッチできるトークン。

ERC20については以下を参考にしてください。

ERC721については以下を参考にしてください。

これらのトークンの特徴を簡単に説明します。

  • ERC998ERC721 Top-Down Composableは、ERC721トークンに追加の機能を持たせ、他のERC721トークンを所有できるようにします。
    • これにより、1つのERC721トークンが他のERC721トークンを所有することができます。
  • ERC998ERC20 Top-Down Composableは、ERC721トークンに追加の機能を持たせ、ERC20トークンを所有できるようにします。
    • これにより、1つのERC721トークンがERC20トークンを所有することができます。
  • ERC998ERC721 Bottom-Up Composableは、ERC721トークンに追加の機能を持たせ、他のERC721トークンにアタッチできるようにします。
    • これにより、ERC721トークンが他のERC721トークンに結びつくことができます。
  • ERC998ERC20 Bottom-Up Composableは、ERC20トークンに追加の機能を持たせ、ERC721トークンにアタッチできるようにします。
    • これにより、ERC20トークンがERC721トークンに結びつくことができます。

合成可能なトークンを使用することで、ERC721トークンとERC20トークンを所有によって接続したり、階層構造を作成したりすることができます。 この時、構造全体を単一の所有者アドレスで管理し、トランザクション1つで転送できます。

異なるコンポーザブル(Top-DownおよびBottom-Up)には、それぞれ利点と欠点があります(詳細は補足の章を確認してください)。 トークンは、1つ以上のコンポーザブルトークンであることが可能です。

このEIPに準拠する非代替トークンは、以下のインターフェースの1つ以上を実装する必要があります。

  • ERC998ERC721TopDown
  • ERC998ERC20TopDown
  • ERC998ERC721BottomUp
  • ERC998ERC20BottomUp

仕様

ERC721コントラクトは、ERC998ERC721TopDown、ERC998ERC20TopDown、ERC998ERC721BottomUpボトムアップの合成コントラクトと連携するために、ERC721インターフェースを実装する必要があります。 これは、これらの合成コントラクトがERC721トークンの標準的な機能を持っていることを確実にするためのルールです。

ERC20コントラクトの場合、ERC998ERC20BottomUpボトムアップ合成コントラクトは、ERC20インターフェースを実装する必要があります。 これにより、これらのコントラクトがERC20トークンの通常の機能を提供できることが保証されます。

さらに、ERC998インターフェースを利用する各コントラクトに対して、ERC165標準を適用する必要があります。 ERC165は、コントラクトが特定のインターフェースをサポートしているかどうかを判別するための規格です。 したがって、ERC998コンポーザブル機能を持つコントラクトが正しく他のコントラクトやユーザーとやり取りできることが確認できます。

ERC165については以下を参考にしてください。

ERC721コントラクトはERC721インターフェースを実装し、ERC20コントラクトはERC20インターフェースを実装し、ERC998コントラクトはERC165標準を適用して、相応しい機能を提供することが要求されます。

認証

ERC998ERC721TopDownとERC998ERC721BottomUpの合成可能なトークンに対して、ユーザーまたはコントラクトが特定のアクションを実行できるかどうかを確認するプロセスです。 どちらの場合でも、同じ方法で機能します。

「rootOwner(ルートオーナー)」は、合成可能なトークンとERC721トークンのツリーの最上位にある所有者アドレスを指します。

合成可能なトークン内での認証は、以下の手順で行われます。

  1. ルートオーナーを見つけます。
  2. ルートオーナーをmsg.sender(トランザクションを実行したユーザーまたはコントラクトのアドレス)と、getApproved(tokenId)の戻り値(指定したトークンIDに対して承認されているアドレス)と、isApprovedForAll(rootOwner, msg.sender)の戻り値(ルートオーナーとmsg.sender間の承認状況)と比較します。
  3. 一致する場合、認証が成功し、それ以外の場合認証が失敗し、コントラクトはエラーを投げます。

以下は認証コードの例です。

javascript
address rootOwner = address(rootOwnerOf(_tokenId));
require(
  rootOwner == msg.sender || 
  isApprovedForAll(rootOwner, msg.sender) ||
  getApproved(tokenId) == msg.sender
);

approve(address _approved, uint256 _tokenId)とgetApproved(uint256 _tokenId)は、ルートオーナーのために実装されたERC721の関数です。 これにより、合成可能なトークンツリー全体を新しいルートオーナーに移動する時に、子合成可能なトークン内で誰が承認されているかを気にせずに済みます。 なぜなら、以前の承認は以前のルートオーナーにしか利用できないためです。

認証はルートオーナーの確認と、トークンの承認状況を比較して、特定のアクションを実行するための権限を確認するプロセスです。 これにより、合成可能なトークンツリーが適切に操作されることが保証されます。

以下が実装例になります。

javascript
function approve(address _approved, uint256 _tokenId) external {
  address rootOwner = address(rootOwnerOf(_tokenId));	
  require(rootOwner == msg.sender || isApprovedForAll(rootOwner,msg.sender));

  rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId] = _approved;
  emit Approval(rootOwner, _approved, _tokenId);
}

function getApproved(uint256 _tokenId) public view returns (address)  {
  address rootOwner = address(rootOwnerOf(_tokenId));
  return rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId];
}

探索

合成可能なトークンの「ルートオーナー」は、rootOwnerOf(uint256 _tokenId)またはrootOwnerOfChild(address _childContract, uint256 _childTokenId)を呼び出すことで取得します。 これらの関数は、トップダウンとボトムアップの合成可能なトークンが、合成可能なトークンとERC721トークンのツリーをたどってルートオーナーを見つけるために使用されます。

ERC998ERC721のトップダウンとボトムアップの合成可能なトークンは、互換性があります。 トップダウンの合成可能なトークンがボトムアップの合成可能なトークンを所有することも、トップダウンの合成可能なトークンがボトムアップのトークンを所有するERC721トークンを所有することも可能です。 どの構成でも、合成可能なトークン上でrootOwnerOf(uint256 _tokenID)を呼び出すと、所有権ツリーの最上位にあるルートオーナーのアドレスが返されます。

rootOwnerOfのトラバーサル(探索)ロジックは、合成可能なトークンがボトムアップまたはトップダウンであるかどうかに関係なく、同じです。

rootOwnerOf(uint256 _tokenId)のロジックは以下のように説明できます。

  1. まず、トークンがボトムアップの合成可能なトークンであるかを確認し、かつ親トークンが存在する場合、親トークンのrootOwnerOfを呼び出します。
    • もし呼び出しが成功した場合、返されるアドレスはルートオーナーです。
    • 呼び出しが失敗した場合、親トークンのrootOwnerOfChildを呼び出します。
      • もし呼び出しが成功した場合、返されるアドレスはルートオーナーです。
      • 呼び出しが失敗した場合、トークンの所有者アドレスがルートオーナーです。
  2. もしトークンがボトムアップの合成可能なトークンでない場合、トークンのrootOwnerOfChildを呼び出します。
    • もし呼び出しが成功した場合、返されるアドレスはルートオーナーです。
    • 呼び出しが失敗した場合、トークンの所有者アドレスがルートオーナーです。

このロジックにより、どのトークンがボトムアップの合成可能なトークンであるか、またトップダウンの合成可能なトークンであるかに関係なく、正確にルートオーナーを特定できます。

トークンのrootOwnerOfChildを呼び出すためのロジックは、以下の通りです。

javascript
// Logic for calling rootOwnerOfChild for a tokenId
address tokenOwner = ownerOf(tokenId);
address childContract = address(this);
bytes32 rootOwner = ERC998ERC721(tokenOwner).rootOwnerOfChild(childContract, tokenId);
  1. まず、トークンの所有者をownerOf関数などを使って取得します。
  2. 自身のコントラクトアドレスを取得します。
  3. 取得した所有者とコントラクトアドレスを使用して、ERC998ERC721インターフェースを介してrootOwnerOfChildを呼び出します。

実際の呼び出しは、アセンブリ言語を使用して行われる必要があります。 これにより、呼び出しの成功または失敗を確認でき、staticcallオペコードを使用して、呼び出しによってコントラクトの状態が変更されないことを確認できます。

この方法により、合成可能なトークンやコントラクトが、トークンツリー内の親または子のトークンの所有者を正確に特定できます。

合成可能なトークンの移動

合成可能なトークンの移動に関連する関数は、すべて同じパラメータ形式を採用しています。 それは、「from : to : what」(送信元 : 送信先 : 対象)という形式です。

例えば、getChild(address _from, uint256 _tokenId, address _childContract, uint256 _childTokenId) という関数は、あるアドレスからトップダウンの合成可能なトークンに対してERC721トークンを転送します。 ここで、_fromパラメータが送信元(from)、_tokenIdパラメータが送信先(to)を表し、address _childContractやuint256 _childTokenIdパラメータが対象(what)を表します。

別の例として、safeTransferChild(uint256 _fromTokenId, address _to, address _childContract, uint256 _childTokenId)という関数があります。 この場合、_fromTokenIdパラメータが送信元(from)、_toパラメータが送信先(to)を示し、address _childContractやaddress _childTokenIdパラメータが対象(what)を示します。

この一貫したパラメータ形式により、合成可能なトークンの移動関連の関数が理解しやすくなり、役割を明確に把握できます。

トークンが所有するトークンの転送

ボトムアップとトップダウンの合成可能なコントラクトにおいて、transferFrom関数やsafeTransferFrom関数は、他のコントラクトによって所有されているトークンを直接転送しようとした場合にエラーを発生させる必要があります。

この制限は、これらの関数が明示的にトークンの所有者を指定せずにトークンの転送を試みると、トークンの所有権を確実に特定できないためです。

したがって、transferFrom関数やsafeTransferFrom関数は、トークンが特定のアドレスによって所有されている場合にのみ正常に動作するように設計されています。 つまり、トークンが他のトークンによって所有されている場合、これらの関数を直接呼び出すことはできず、エラーが発生します。

ERC-721 Top-Down Composable

ERC721トップダウンの合成可能なコントラクトは、ERC721トークンのコンテナとして機能します。 つまり、ERC721トークンを受け入れ、保持し、転送できるERC721トークン自体です。

ERC721トークンをERC721トップダウンの合成可能なコントラクトに転送する方法は2つあります。

  1. safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data)関数を使用する方法。

    • この関数では、_to引数にはトップダウンの合成可能なコントラクトのアドレスが指定されます。
    • また、bytes data引数には、ERC721トークンが転送されるトップダウンの合成可能なコントラクトのトークンIDの整数値が格納されます。
  2. ERC721トークンコントラクト内で、トップダウンの合成可能なコントラクトのためにapprove関数を呼び出す方法。

    • その後、合成可能なコントラクト内でgetChildを呼び出して転送を行います。

最初の方法は、safeTransferFrom関数を持つERC721コントラクトに適しており、2番目の方法はこの関数を持たないコントラクト(例:CryptoKittiesなど)に対して使用されます。

以下は、ERC721トークン3を特定のアドレスからトップダウンの合成可能なトークン6に転送する例です。

solidity
uint256 tokenId = 6;
bytes memory tokenIdBytes = new bytes(32);
assembly { mstore(add(tokenIdBytes, 32), tokenId) }
ERC721(contractAddress).safeTransferFrom(userAddress, composableAddress, 3, tokenIdBytes);
  1. uint256 tokenId = 6;
    • tokenIdを宣言し、転送したいERC721トークンのトークンIDを6に設定します。
  2. bytes memory tokenIdBytes = new bytes(32);
    • 長さ32のバイト配列tokenIdBytesを作成します。
    • このバイト配列は後でERC721トークンのトークンIDを格納するために使用されます。
  3. assembly { mstore(add(tokenIdBytes, 32), tokenId) }
    • アセンブリブロック内で、トークンIDをバイト配列に格納します。
    • 具体的には、tokenIdをバイト配列tokenIdBytesの32バイト目から始まる位置に格納します。
    • このアセンブリコードの目的は、整数値のトークンIDをバイト列に変換することです。
    • ERC721トークンのトークンIDは通常256ビットの整数ですが、バイト列に変換して転送する必要があります。
  4. ERC721(contractAddress).safeTransferFrom(userAddress, composableAddress, 3, tokenIdBytes);
    • ERC721インターフェースを持つコントラクト(contractAddressで指定されたERC721トークンコントラクト)に対して、safeTransferFrom関数を呼び出します。
    • これにより、ERC721トークンがuserAddressからcomposableAddress(トップダウンの合成可能なコントラクト)に転送されます。
    • 関数の引数として、転送元のアドレス (userAddress)、転送先のアドレス (composableAddress)、トークンID3、およびトークンIDをバイト配列に変換したtokenIdBytesが指定されます。

この手順により、ERC721トークンが指定されたトップダウンの合成可能なコントラクトに転送され、正確にトークンID6が転送されます。

ERC721トップダウンの合成可能なコントラクトを準拠させるためには、ERC998ERC721TopDownインターフェースを実装する必要があります。 また、ERC998ERC721TopDownEnumerableやERC998ERC20TopDownEnumerableインターフェースを実装することもできますが、これはオプションです。

javascript
pragma solidity ^0.4.24;

/// @title `ERC998ERC721` Top-Down Composable Non-Fungible Token
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md
///  Note: the ERC-165 identifier for this interface is 0xcde244d9
interface ERC998ERC721TopDown {

  /// @dev This emits when a token receives a child token.
  /// @param _from The prior owner of the token.
  /// @param _toTokenId The token that receives the child token.
  event ReceivedChild(
    address indexed _from, 
    uint256 indexed _toTokenId, 
    address indexed _childContract, 
    uint256 _childTokenId
  );
  
  /// @dev This emits when a child token is transferred from a token to an address.
  /// @param _fromTokenId The parent token that the child token is being transferred from.
  /// @param _to The new owner address of the child token.
  event TransferChild(
    uint256 indexed _fromTokenId, 
    address indexed _to, 
    address indexed _childContract, 
    uint256 _childTokenId
  );

  /// @notice Get the root owner of tokenId.
  /// @param _tokenId The token to query for a root owner address
  /// @return rootOwner The root owner at the top of tree of tokens and ERC-998 magic value.
  function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner);
  
  /// @notice Get the root owner of a child token.
  /// @param _childContract The contract address of the child token.
  /// @param _childTokenId The tokenId of the child.
  /// @return rootOwner The root owner at the top of tree of tokens and ERC-998 magic value.
  function rootOwnerOfChild(
    address _childContract, 
    uint256 _childTokenId
  ) 
    public 
    view
    returns (bytes32 rootOwner);
  
  /// @notice Get the parent tokenId of a child token.
  /// @param _childContract The contract address of the child token.
  /// @param _childTokenId The tokenId of the child.
  /// @return parentTokenOwner The parent address of the parent token and ERC-998 magic value
  /// @return parentTokenId The parent tokenId of _tokenId
  function ownerOfChild(
    address _childContract, 
    uint256 _childTokenId
  ) 
    external 
    view 
    returns (
      bytes32 parentTokenOwner, 
      uint256 parentTokenId
    );
  
  /// @notice A token receives a child token
  /// @param _operator The address that caused the transfer.
  /// @param _from The owner of the child token.
  /// @param _childTokenId The token that is being transferred to the parent.
  /// @param _data Up to the first 32 bytes contains an integer which is the receiving parent tokenId.  
  function onERC721Received(
    address _operator, 
    address _from, 
    uint256 _childTokenId, 
    bytes _data
  ) 
    external 
    returns(bytes4);
    
  /// @notice Transfer child token from top-down composable to address.
  /// @param _fromTokenId The owning token to transfer from.
  /// @param _to The address that receives the child token
  /// @param _childContract The ERC-721 contract of the child token.
  /// @param _childTokenId The tokenId of the token that is being transferred.
  function transferChild(
    uint256 _fromTokenId,
    address _to, 
    address _childContract, 
    uint256 _childTokenId
  ) 
    external;
  
  /// @notice Transfer child token from top-down composable to address.
  /// @param _fromTokenId The owning token to transfer from.
  /// @param _to The address that receives the child token
  /// @param _childContract The ERC-721 contract of the child token.
  /// @param _childTokenId The tokenId of the token that is being transferred.
  function safeTransferChild(
    uint256 _fromTokenId,
    address _to, 
    address _childContract, 
    uint256 _childTokenId
  ) 
    external;
  
  /// @notice Transfer child token from top-down composable to address.
  /// @param _fromTokenId The owning token to transfer from.
  /// @param _to The address that receives the child token
  /// @param _childContract The ERC-721 contract of the child token.
  /// @param _childTokenId The tokenId of the token that is being transferred.
  /// @param _data Additional data with no specified format
  function safeTransferChild(
    uint256 _fromTokenId,
    address _to, 
    address _childContract, 
    uint256 _childTokenId, 
    bytes _data
  ) 
    external;
  
  /// @notice Transfer bottom-up composable child token from top-down composable to other ERC-721 token.
  /// @param _fromTokenId The owning token to transfer from.
  /// @param _toContract The ERC-721 contract of the receiving token
  /// @param _toTokenId The receiving token  
  /// @param _childContract The bottom-up composable contract of the child token.
  /// @param _childTokenId The token that is being transferred.
  /// @param _data Additional data with no specified format
  function transferChildToParent(
    uint256 _fromTokenId, 
    address _toContract, 
    uint256 _toTokenId, 
    address _childContract, 
    uint256 _childTokenId, 
    bytes _data
  ) 
    external;
  
  /// @notice Get a child token from an ERC-721 contract.
  /// @param _from The address that owns the child token.
  /// @param _tokenId The token that becomes the parent owner
  /// @param _childContract The ERC-721 contract of the child token
  /// @param _childTokenId The tokenId of the child token
  function getChild(
    address _from, 
    uint256 _tokenId, 
    address _childContract, 
    uint256 _childTokenId
  ) 
    external;
}

rootOwnerOf

javascript
// Get the root owner of tokenId.
function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner);

概要

指定されたトークンIDのルートオーナーアドレスを取得する関数。

詳細

指定されたトークンIDのルートオーナーアドレスを特定するためにトークンの所有者を探索します。 rootOwnerの最初の4バイトにはERC998のマジックバリューである0xcd740db5が含まれており、残りの20バイトにはルートオーナーアドレスが格納されています。 マジックバリューは、この関数がコントラクト上で呼び出される場合、そのコントラクトがrootOwnerOf関数を持っているかどうか不明な場合に妥当な戻り値を受け取るために使用されます。 不明な場合、rootOwnerの戻り値の最初の4バイトと0xcd740db5を比較する必要があります。

0xcd740db5は以下のように計算されます。

javascript
0xcd740db5 = this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector ^ this.tokenOwnerOf.selector ^ this.ownerOfChild.selector;

引数

  • _tokenId
    • トークンIDをクエリするための値。

戻り値

  • rootOwner
    • ERC998のマジックバリュー(最初の4バイト)とルートオーナーアドレス(残りの20バイト)を含むバイト列です。

rootOwnerOfChild

javascript
// Get the root owner of a child token.
function rootOwnerOfChild(
  address _childContract, 
  uint256 _childTokenId
) 
  public 
  view 
  returns (bytes32 rootOwner);

概要

指定された子トークンのルートオーナーアドレスを取得する関数。

詳細

この関数は、指定された子トークンのルートオーナーアドレスを特定するためにトークンの所有者を探索します。 rootOwnerの最初の4バイトにはERC998のマジックバリューである0xcd740db5が含まれており、残りの20バイトにはルートオーナーアドレスが格納されています。 マジックバリューは、この関数がコントラクト上で呼び出される場合、そのコントラクトがrootOwnerOfChild関数を持っているかどうか不明な場合に妥当な戻り値を受け取るために使用されます。 不明な場合、rootOwnerの戻り値の最初の4バイトと0xcd740db5を比較する必要があります。

0xcd740db5は以下のように計算されます:

javascript
0xcd740db5 = this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector ^ this.tokenOwnerOf.selector ^ this.ownerOfChild.selector;

引数

  • _childContract
    • 子トークンのコントラクトアドレスを指定する値。
  • _childTokenId
    • 子トークンのトークンIDを指定する値。

戻り値

  • rootOwner
    • ERC998のマジックバリュー(最初の4バイト)とルートオーナーアドレス(残りの20バイト)を含むバイト列です。

ownerOfChild

javascript
// Get the parent tokenId of a child token and the owner address of the parent token.
function ownerOfChild(
  address _childContract, 
  uint256 _childTokenId
) 
  external 
  view 
  returns (
    address parentTokenOwner, 
    uint256 parentTokenId
  );

概要

子トークンの親トークンのトークンIDと親トークンの所有者アドレスを取得する関数。

詳細

指定された子トークンの親トークンのトークンIDと親トークンの所有者アドレスを取得します。 parentTokenOwnerの最初の4バイトにはERC998のマジックバリューである0xcd740db5が含まれており、残りの20バイトには親トークンの所有者アドレスが格納されています。 マジックバリューは、この関数がコントラクト上で呼び出される場合、そのコントラクトがownerOfChild関数を持っているかどうか不明な場合に妥当な戻り値を受け取るために使用されます。 不明な場合、parentTokenOwnerの戻り値の最初の4バイトと0xcd740db5を比較する必要があります。

0xcd740db5は以下のように計算されます。

javascript
0xcd740db5 = this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector ^ this.tokenOwnerOf.selector ^ this.ownerOfChild.selector;

引数

  • _childContract
    • 子トークンのコントラクトアドレスを指定する値。
  • _childTokenId
    • 子トークンのトークンIDを指定する値。

戻り値

  • parentTokenOwner
    • ERC998のマジックバリュー(最初の4バイト)と親トークンの所有者アドレス(残りの20バイト)を含むバイト列です。
  • parentTokenId
    • 親トークンのトークンIDです。

onERC721Received

javascript
// A token receives a child token
function onERC721Received(
  address _operator, 
  address _from, 
  uint256 _childTokenId, 
  bytes _data
) 
  external 
  returns(bytes4);

概要

トークンがERC721トークンが転送され、そのERC721トークンの親トークンのトークンIDが何であるかを通知する関数。

詳細

ERC721標準で定義されている関数。 この関数はERC721コントラクト内でsafeTransferFromが呼び出されたときに呼び出されます。 引数_dataには、1から32バイトまでの整数が含まれており、これはERC721トークンが転送される親トークンのトークンIDです。

onERC721Receivedの戻り値はマジックバリューである0x150b7a02で、これはbytes4(keccak256(abi.encodePacked("onERC721Received(address,address,uint256,bytes)")))と等しいです。

引数

  • _operator
    • トークン転送の発生源となったアドレス。
  • _from
    • 以前の子トークンの所有者のアドレス。
  • _childTokenId
    • 親トークンに転送される子トークンのトークンID。
  • _data
    • 最初の32バイトまでに親トークンのトークンIDが含まれています。

戻り値

  • bytes4
    • この関数が受け入れられたことを示すマジックバリュー0x150b7a02が返されます。

transferChild

javascript
// Transfer child token from top-down composable to address.
function transferChild(
  uint256 _fromTokenId,
  address _to, 
  address _childContract, 
  uint256 _childTokenId
) 
  external;

概要

msg.senderを認証し、トップダウンの合成可能なコントラクトから子トークンを別のアドレスに転送する関数。

詳細 この関数は、以下の呼び出しを含みます。

javascript
ERC721(_childContract).transferFrom(this, _to, _childTokenId);

この呼び出しにより、指定された_childTokenIdの子トークンが_childContractを使用してthis(合成可能なコントラクト自体)から_toアドレスに転送されます。 この関数は、トップダウン合成可能なコントラクトから子トークンを別のアドレスに転送します。

引数

  • _fromTokenId
    • 転送元となる所有トークンのトークンID。
  • _to
    • 子トークンを受け取るアドレス。
  • _childContract
    • 子トークンのERC721コントラクトのアドレス。
  • _childTokenId
    • 転送されるトークンのトークンID。

safeTransferChild

概要

トップダウン合成可能なコントラクトから子トークンを別のアドレスに転送する関数。

javascript
/// @notice Transfer child token from top-down composable to address.
/// @param _fromTokenId The owning token to transfer from.
/// @param _to The address that receives the child token
/// @param _childContract The ERC-721 contract of the child token.
/// @param _childTokenId The tokenId of the token that is being transferred.
function safeTransferChild(
  uint256 _fromTokenId,
  address _to, 
  address _childContract, 
  uint256 _childTokenId
) 
  external;

詳細

msg.senderの認証を行い、トップダウン合成可能なコントラクトから子トークンを別のアドレスに転送します。 内部で以下の呼び出しを行います。

javascript
ERC721(_childContract).safeTransferFrom(this, _to, _childTokenId);

これにより、指定された_childTokenIdの子トークンが_childContractを使用してthis(合成可能なコントラクト自体)から_toアドレスに転送されます。 この関数を使用することで、子トークンの所有権を転送することができます。

引数

  • _fromTokenId
    • 転送元となる所有トークンのトークンID。
  • _to
    • 子トークンを受け取るアドレス。
  • _childContract
    • 子トークンのERC721コントラクトのアドレス。
  • _childTokenId
    • 転送されるトークンのトークンID。

safeTransferChild

概要

トップダウン合成可能なコントラクトから子トークンを別のアドレスまたは別のトップダウン合成可能なコントラクトに転送する関数。

詳細

msg.senderの認証を行い、トップダウン合成可能なコントラクトから子トークンを別のアドレスまたは別のトップダウン合成可能なコントラクトに転送します。 転送先が別のトップダウン合成可能なコントラクトである場合、_dataパラメータに親トークンのトークンIDを指定します。 内部で以下の呼び出しを行います。

javascript
ERC721(_childContract).safeTransferFrom(this, _to, _childTokenId, _data);

これにより、指定された_childTokenIdの子トークンが_childContractを使用してthis(合成可能なコントラクト自体)から_toアドレスまたは別のトップダウン合成可能なコントラクトに転送されます。 この関数を使用することで、子トークンの所有権を転送することができます。

引数

  • _fromTokenId
    • 転送元となる所有トークンのトークンID。
  • _to
    • 子トークンを受け取るアドレスまたは別のトップダウン合成可能なコントラクトのアドレス。
  • _childContract
    • 子トークンのERC721コントラクトのアドレス。
  • _childTokenId
    • 転送されるトークンのトークンID。
  • _data
    • 規定された形式のない追加データ。
    • これを使用して転送先の親トークンIDを指定することができます。

transferChildToParent

概要

トップダウン合成可能なコントラクトから子トークン(ボトムアップ合成可能なコントラクトのトークン)を別のERC721トークンに転送する関数。

詳細

この関数は、msg.senderの認証を行い、トップダウン合成可能なコントラクトからボトムアップ合成可能なコントラクトのトークンを別のERC721トークンに転送します。 この関数は、子トークンがボトムアップ合成可能なトークンである場合にのみ使用できます。 これは、ボトムアップ合成のスタイルでトップダウン合成可能なコントラクトからERC721トークンにトークンを転送するために設計されています。

内部で以下の呼び出しを行います。

javascript
ERC998ERC721BottomUp(_childContract).transferToParent(address(this), _toContract, _toTokenId, _childTokenId, _data);

これにより、指定された_childTokenIdの子トークンが_childContractを使用してthis(合成可能なコントラクト自体)から_toContractに_toTokenIdのERC721トークンが転送されます。 この関数を使用することで、ボトムアップ合成可能なトークンをトップダウン合成可能なコントラクトから別のERC721トークンに転送できます。

引数

  • _fromTokenId
    • 転送元となる所有トークンのトークンID。
  • _toContract
    • 受け取るERC721トークンのコントラクトアドレス。
  • _toTokenId
    • 受け取るERC721トークンのトークンID。
  • _childContract
    • 子トークン(ボトムアップ合成可能なコントラクトのトークン)のERC721コントラクトのアドレス。
  • _childTokenId
    • 転送される子トークンのトークンID。
  • _data
    • 指定された形式のない追加データ。

getChild

概要

ERC721コントラクトがsafeTransferChild(uint256 _fromTokenId, address _to, address _childContract, uint256 _childTokenId, bytes _data)関数を持っていない場合に、ERC721トークンを転送する関数。

詳細

転送は以下の手順で行われます。

  1. ERC721トークンの所有者は、トークンがトップダウン合成可能なコントラクトに対してapproveまたはsetApprovalForAllを呼び出します。
  2. ERC721トークンの所有者は、ERC721トークンのトップダウン合成可能なコントラクトに対してgetChild関数を呼び出します。

getChild関数は、msg.senderがERC721トークンのERC721コントラクトでの所有者であるか、ERC721トークンのERC721コントラクトで承認されているか、またはERC721トークンのERC721コントラクトでオペレーターであることを認証する必要があります。

引数

  • _from
    • 子トークンの所有者のアドレス。
  • _tokenId
    • 親オーナーとなるトークン。
  • _childContract
    • 子トークンのERC721コントラクトのアドレス。
  • _childTokenId
    • 子トークンのトークンID。

ERC-721 Top-Down Composable Enumeration

トップダウンのコンポーザブル列挙のためのオプションのインターフェイスです。

javascript
///  @dev The ERC-165 identifier for this interface is 0xa344afe4
interface ERC998ERC721TopDownEnumerable {

  /// @notice Get the total number of child contracts with tokens that are owned by tokenId.
  /// @param _tokenId The parent token of child tokens in child contracts
  /// @return uint256 The total number of child contracts with tokens owned by tokenId.
  function totalChildContracts(uint256 _tokenId) external view returns(uint256);
  
  /// @notice Get child contract by tokenId and index
  /// @param _tokenId The parent token of child tokens in child contract
  /// @param _index The index position of the child contract
  /// @return childContract The contract found at the tokenId and index.
  function childContractByIndex(
    uint256 _tokenId, 
    uint256 _index
  ) 
    external 
    view 
    returns (address childContract);
  
  /// @notice Get the total number of child tokens owned by tokenId that exist in a child contract.
  /// @param _tokenId The parent token of child tokens
  /// @param _childContract The child contract containing the child tokens
  /// @return uint256 The total number of child tokens found in child contract that are owned by tokenId.
  function totalChildTokens(
    uint256 _tokenId, 
    address _childContract
  ) 
    external 
    view 
    returns(uint256);
  
  /// @notice Get child token owned by tokenId, in child contract, at index position
  /// @param _tokenId The parent token of the child token
  /// @param _childContract The child contract of the child token
  /// @param _index The index position of the child token.
  /// @return childTokenId The child tokenId for the parent token, child token and index
  function childTokenByIndex(
    uint256 _tokenId, 
    address _childContract, 
    uint256 _index
  )
    external 
    view 
    returns (uint256 childTokenId);
}

ERC-20 Top-Down Composable

ERC20トップダウン合成可能なコントラクトは、ERC721トークンの一種で、ERC20トークンを受け入れて保有し転送するためのコンテナのようなものです。

このコントラクトは、ERC20トークンを受け入れて保有し転送する機能を持つERC721トークンです。

ERC20トークンをERC20トップダウン合成可能なコントラクトに転送する方法は2つあります。

  1. ERC223規格をサポートするERC20コントラクトから、transfer(address _to, uint256 _value, bytes _data)関数を使用します。

    • ここで、_to引数はERC20TopDown合成可能なコントラクトのアドレスで、_value引数は転送するERC20トークンの数量を指定します。
    • bytes引数にはERC20TopDown合成可能なコントラクトが受け入れるトークンのトークンIDを表す整数値が含まれます。
  2. ERC20コントラクトで、ERC20TopDown合成可能なコントラクトのためにapproveを呼び出し、その後、ERC20TopDown合成可能なコントラクトからgetERC20(address _from, uint256 _tokenId, address _erc20Contract, uint256 _value)を呼び出します。

最初の方法は、ERC20コントラクトがERC223規格をサポートする場合に使用できます。 2番目の方法は、それをサポートしないERC20コントラクトに対して使用されます。

ERC20TopDown合成可能なコントラクトは、ERC20トークンとのやり取りを可能にするために実装されたインターフェースを持っています。

javascript
/// @title `ERC998ERC20` Top-Down Composable Non-Fungible Token
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md
///  Note: the ERC-165 identifier for this interface is 0x7294ffed
interface ERC998ERC20TopDown {

  /// @dev This emits when a token receives ERC-20 tokens.
  /// @param _from The prior owner of the token.
  /// @param _toTokenId The token that receives the ERC-20 tokens.
  /// @param _erc20Contract The ERC-20 contract.
  /// @param _value The number of ERC-20 tokens received.
  event ReceivedERC20(
    address indexed _from, 
    uint256 indexed _toTokenId, 
    address indexed _erc20Contract, 
    uint256 _value
  );
  
  /// @dev This emits when a token transfers ERC-20 tokens.
  /// @param _tokenId The token that owned the ERC-20 tokens.
  /// @param _to The address that receives the ERC-20 tokens.
  /// @param _erc20Contract The ERC-20 contract.
  /// @param _value The number of ERC-20 tokens transferred.
  event TransferERC20(
    uint256 indexed _fromTokenId, 
    address indexed _to, 
    address indexed _erc20Contract, 
    uint256 _value
  );

  /// @notice A token receives ERC-20 tokens
  /// @param _from The prior owner of the ERC-20 tokens
  /// @param _value The number of ERC-20 tokens received
  /// @param _data Up to the first 32 bytes contains an integer which is the receiving tokenId.  
  function tokenFallback(address _from, uint256 _value, bytes _data) external;
  
  /// @notice Look up the balance of ERC-20 tokens for a specific token and ERC-20 contract
  /// @param _tokenId The token that owns the ERC-20 tokens
  /// @param _erc20Contract The ERC-20 contract
  /// @return The number of ERC-20 tokens owned by a token from an ERC-20 contract
  function balanceOfERC20(
    uint256 _tokenId, 
    address _erc20Contract
  ) 
    external 
    view 
    returns(uint256);
  
  /// @notice Transfer ERC-20 tokens to address
  /// @param _tokenId The token to transfer from
  /// @param _value The address to send the ERC-20 tokens to
  /// @param _erc20Contract The ERC-20 contract
  /// @param _value The number of ERC-20 tokens to transfer
  function transferERC20(
    uint256 _tokenId, 
    address _to, 
    address _erc20Contract, 
    uint256 _value
  ) 
    external;
  
  /// @notice Transfer ERC-20 tokens to address or ERC-20 top-down composable
  /// @param _tokenId The token to transfer from
  /// @param _value The address to send the ERC-20 tokens to
  /// @param _erc223Contract The `ERC-223` token contract
  /// @param _value The number of ERC-20 tokens to transfer
  /// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to
  function transferERC223(
    uint256 _tokenId, 
    address _to, 
    address _erc223Contract, 
    uint256 _value, 
    bytes _data
  )
    external;
  
  /// @notice Get ERC-20 tokens from ERC-20 contract.
  /// @param _from The current owner address of the ERC-20 tokens that are being transferred.
  /// @param _tokenId The token to transfer the ERC-20 tokens to.
  /// @param _erc20Contract The ERC-20 token contract
  /// @param _value The number of ERC-20 tokens to transfer  
  function getERC20(
    address _from, 
    uint256 _tokenId, 
    address _erc20Contract, 
    uint256 _value
  ) 
    external;
}

tokenFallback

javascript
function tokenFallback(address _from, uint256 _value, bytes _data) external;

概要

ERC20TopDown合成可能なコントラクトがそのトークンのうちの1つがERC20トークンを受け取ったことを通知する関数。

詳細

この関数は、ERC223規格から派生したもので、ERC20標準の拡張です。 ERC20トークンが転送される時に、送信元コントラクトから受信コントラクトに呼び出されます。 どのトークンがERC20トークンを受け取ったかは、_dataパラメータで指定されます。 ERC-223規格の一環として、この関数はERC20トークンの受け取りを処理します。

引数

  • _from
    • ERC20トークンの前の所有者アドレス。
  • _value
    • 受け取ったERC20トークンの数量。
  • _data
    • 受信トークンのトークンIDを表す整数。

balanceOfERC20

javascript
function balanceOfERC20(uint256 _tokenId, address _erc20Contract) external view returns(uint256);

概要

特定のトークンとERC20コントラクトの所有するERC20トークンの残高を取得する関数。

詳細

指定されたERC20コントラクトから特定のトークンが所有するERC20トークンの残高を取得します。

引数

  • _tokenId
    • トークンID。
  • _erc20Contract
    • ERC20コントラクトのアドレス。

戻り値

  • ERC20トークンの数量が戻り値として返されます。

transferERC20

javascript
function transferERC20(uint256 _tokenId, address _to, address _erc20Contract, uint256 _value) external;

概要

トークンからアドレスへERC20トークンを転送するために使用されます。 この関数はERC20(_erc20Contract).transfer(_to, _value)を呼び出します。

詳細

この関数は、トークンからアドレスへERC20トークンを転送するために使用されます。 呼び出し元(msg.sender)は認証する必要があります。

引数

  • _tokenId
    • トークンID。
  • _to
    • ERC20トークンを転送するアドレス。
  • _erc20Contract
    • ERC20トークンのコントラクトアドレス。
  • _value
    • 転送するERC20トークンの数量。

transferERC223

javascript
function transferERC223(
  uint256 _tokenId, 
  address _to, 
  address _erc223Contract, 
  uint256 _value, 
  bytes _data
) 
external;

概要

ERC223から提供されるもので、ERC20トークンをトークンからアドレスまたは他のトークンに転送する関数。

詳細

この関数はERC223から提供されるもので、ERC20トークンをトークンからアドレスまたは他のトークンに転送するために使用されます。 _data引数には整数トークン値を入れて、転送対象を指定できます。 呼び出し元(msg.sender)は認証する必要があります。

引数

  • _tokenId
    • トークンのID。
  • _to
    • ERC20トークンを送信する先のアドレス。
  • _erc223Contract
    • ERC223トークンコントラクトのアドレス。
  • _value
    • 転送するERC20トークンの数量。
  • _data
    • 特定のフォーマットが指定されていない追加データです。
    • 通常はトークンIDの指定などに使用します。

getERC20

javascript
function getERC20(
  address _from, 
  uint256 _tokenId, 
  address _erc20Contract, 
  uint256 _value
) 
external;

概要

ERC20トークンをERC-20TopDown合成可能なコントラクトに転送する関数。

詳細

ERC20コントラクトにtransferERC223(uint256 _tokenId, address _to, address _erc223Contract, uint256 _value, bytes _data)関数が存在しない場合に使用されます。

この関数を使用する前に、ERC20コントラクトでERC20トップダウン合成可能なコントラクトのアドレスを転送するために承認する必要があります。

呼び出し元(msg.sender)は、_fromとERC20コントラクトで承認されたものと一致するか認証する必要があります。

引数

  • _from
    • ERC20トークンの現在の所有者のアドレス。
  • _tokenId
    • トークンのID。
  • _erc20Contract
    • ERC20トークンコントラクトのアドレス。
  • _value
    • 転送するERC20トークンの数量。

ERC-20 Top-Down Composable Enumeration

トップダウンのコンポーザブル列挙のためのオプションのインターフェイスです。

javascript
/// @dev The ERC-165 identifier for this interface is 0xc5fd96cd
interface ERC998ERC20TopDownEnumerable {
  
  /// @notice Get the number of ERC-20 contracts that token owns ERC-20 tokens from
  /// @param _tokenId The token that owns ERC-20 tokens.
  /// @return uint256 The number of ERC-20 contracts
  function totalERC20Contracts(uint256 _tokenId) external view returns(uint256);
  
  /// @notice Get an ERC-20 contract that token owns ERC-20 tokens from by index
  /// @param _tokenId The token that owns ERC-20 tokens.
  /// @param _index The index position of the ERC-20 contract.
  /// @return address The ERC-20 contract
  function erc20ContractByIndex(
    uint256 _tokenId, 
    uint256 _index
  ) 
    external 
    view 
    returns(address);
}

ERC-721 Bottom-Up Composable

ERC721BottomUp合成可能なコントラクトは、他のERC721トークンにアタッチされるERC721トークンです。 これらのコントラクトは他のERC721トークンに関連付けられることができます。

ERC721BottomUp合成可能なコントラクトは、特定のトークンが誰の所有であるかを示すトークンの所有者のアドレスと、そのトークンが親トークンである場合にその親トークンのトークンID(識別子)を保持します。 これにより、トークンの所有者や階層構造を管理し、トークン同士の関連性を確立できます。

javascript
/// @title `ERC998ERC721` Bottom-Up Composable Non-Fungible Token
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md
///  Note: the ERC-165 identifier for this interface is 0xa1b23002
interface ERC998ERC721BottomUp {

  /// @dev This emits when a token is transferred to an ERC-721 token
  /// @param _toContract The contract the token is transferred to
  /// @param _toTokenId The token the token is transferred to
  /// @param _tokenId The token that is transferred  
  event TransferToParent(
    address indexed _toContract, 
    uint256 indexed _toTokenId, 
    uint256 _tokenId
  );
  
  /// @dev This emits when a token is transferred from an ERC-721 token
  /// @param _fromContract The contract the token is transferred from
  /// @param _fromTokenId The token the token is transferred from
  /// @param _tokenId The token that is transferred  
  event TransferFromParent(
    address indexed _fromContract, 
    uint256 indexed _fromTokenId, 
    uint256 _tokenId
  );
  
  /// @notice Get the root owner of tokenId.
  /// @param _tokenId The token to query for a root owner address
  /// @return rootOwner The root owner at the top of tree of tokens and ERC-998 magic value.
  function rootOwnerOf(uint256 _tokenId) external view returns (bytes32 rootOwner);

  /// @notice Get the owner address and parent token (if there is one) of a token
  /// @param _tokenId The tokenId to query.
  /// @return tokenOwner The owner address of the token
  /// @return parentTokenId The parent owner of the token and ERC-998 magic value
  /// @return isParent True if parentTokenId is a valid parent tokenId and false if there is no parent tokenId
  function tokenOwnerOf(
    uint256 _tokenId
  )
    external 
    view
    returns (
      bytes32 tokenOwner, 
      uint256 parentTokenId, 
      bool isParent
    );
 
  /// @notice Transfer token from owner address to a token
  /// @param _from The owner address
  /// @param _toContract The ERC-721 contract of the receiving token
  /// @param _toToken The receiving token
  /// @param _data Additional data with no specified format
  function transferToParent(
    address _from, 
    address _toContract, 
    uint256 _toTokenId, 
    uint256 _tokenId, 
    bytes _data
  )
    external;
   
  /// @notice Transfer token from a token to an address
  /// @param _fromContract The address of the owning contract
  /// @param _fromTokenId The owning token
  /// @param _to The address the token is transferred to.
  /// @param _tokenId The token that is transferred
  /// @param _data Additional data with no specified format
  function transferFromParent(
    address _fromContract, 
    uint256 _fromTokenId, 
    address _to, 
    uint256 _tokenId, 
    bytes _data
  )
    external;
  
  /// @notice Transfer a token from a token to another token
  /// @param _fromContract The address of the owning contract
  /// @param _fromTokenId The owning token
  /// @param _toContract The ERC-721 contract of the receiving token
  /// @param _toToken The receiving token
  /// @param _tokenId The token that is transferred
  /// @param _data Additional data with no specified format
  function transferAsChild(
    address _fromContract, 
    uint256 _fromTokenId, 
    address _toContract, 
    uint256 _toTokenId, 
    uint256 _tokenId, 
    bytes _data
  )
    external;
}

rootOwnerOf

javascript
function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner);

概要

指定したトークンのルート所有者アドレスを取得する関数。

詳細

指定したトークンのルート所有者アドレスを見つけるまでトークン所有者を探索します。

ルート所有者のアドレスは、rootOwnerの最初の4バイトにERC998のマジック値0xcd740db5が含まれ、最後の20バイトにルート所有者のアドレスが含まれています。

このマジック値は、この関数がコントラクトに対して呼び出される時に、コントラクトがrootOwnerOf関数を持っているかどうか不明な場合に返されます。 このような呼び出しで正当な戻り値を受け取るために、マジック値が使用されます。

コントラクトがrootOwnerOf関数を持っているかどうか不明な場合、rootOwnerの戻り値の最初の4バイトを0xcd740db5と比較する必要があります。

0xcd740db5は次の式と等しいです。

javascript
this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector ^ 
this.tokenOwnerOf.selector ^ this.ownerOfChild.selector;

ルート所有者を取得するためのサンプルの戻り値の値は0xcd740db50000000000000000e5240103e1ff986a2c8ae6b6728ffe0d9a395c59です。

引数

  • _tokenId
    • トークンのルート所有者アドレスをクエリするためのトークンID。

戻り値

  • rootOwner
    • トークンとERC998のマジック値を含む、トークンおよびルート所有者のアドレス。

tokenOwnerOf

javascript
function tokenOwnerOf(uint256 _tokenId) external view returns (bytes32 tokenOwner, uint256 parentTokenId, bool isParent);

概要

トークンの所有者アドレスと親トークンID(あれば)を取得する関数。

詳細

isParentがtrueの場合、tokenOwnerは所有するERC721コントラクトのアドレスであり、parentTokenIdは有効な親トークンIDです。 isParentがfalseの場合、tokenOwnerはユーザーアドレスであり、parentTokenIdは有効な親トークンIDを含んでおらず、無視される必要があります。

tokenOwnerの最初の4バイトにはERC998のマジック値0xcd740db5が含まれており、最後の20バイトにはトークン所有者のアドレスが含まれています。

このマジック値は、この関数がコントラクトに対して呼び出される際に、コントラクトがtokenOwnerOf関数を持っているかどうか不明な場合に返されます。 このような呼び出しで正当な戻り値を受け取るために、マジック値が使用されます。

コントラクトがtokenOwnerOf関数を持っているかどうか不明な場合、tokenOwnerの戻り値の最初の4バイトを0xcd740db5と比較する必要があります。

引数

  • _tokenId
    • クエリするトークンのトークンID。

戻り値

  • tokenOwner
    • トークン所有者のアドレスとERC998のマジック値を含む。
  • parentTokenId
    • 親トークンのトークンID。
  • isParent
    • parentTokenIdが有効な親トークンIDである場合はtrue、それ以外の場合はfalse。

transferToParent

javascript
function transferToParent(address _from, address _toContract, uint256 _toTokenId, uint256 _tokenId, bytes _data) external;

概要

アドレスからトークンへのトークンの転送する関数。

概要 msg.senderは認証される必要があります。 この関数は、_toTokenが_toContract内に存在するかどうかをチェックし、存在しない場合は例外をスローする必要があります。

引数

  • _from
    • トークンの所有者アドレス。
  • _toContract
    • 受信トークンのERC721コントラクト。
  • _toTokenId
    • 受信トークンのトークンID。
  • _tokenId
    • 転送元トークンのトークンID。
  • _data
    • 指定されたフォーマットの追加データ。

transferFromParent

javascript
function transferFromParent(address _fromContract, uint256 _fromTokenId, address _to, uint256 _tokenId, bytes _data) external;

概要

トークンからアドレスへのトークン転送する関数。

詳細

msg.senderは認証される必要があります。

この関数は、_fromContractや_fromTokenIdが_tokenIdを所有していることをチェックし、所有していない場合はエラーを投げます。

引数

  • _fromContract
    • トークンを所有するコントラクトのアドレス。
  • _fromTokenId
    • 転送元のトークンのID。
  • _to
    • トークンを転送する先のアドレス。
  • _tokenId
    • 転送するトークンのID。
  • _data
    • 特定のフォーマットが指定されていない追加データ。

transferAsChild

javascript
function transferAsChild(address _fromContract, uint256 _fromTokenId, address _toContract, uint256 _toTokenId, uint256 _tokenId, bytes _data) external;

概要

トークンから別のトークンへのトークン転送を実行する関数。

詳細

msg.senderは認証される必要があります。

この関数は、_toContract内に_toTokenが存在することを確認し、存在しない場合はエラーを投げます。

また、この関数は_fromContractおよび_fromTokenIdが_tokenIdを所有していることをチェックし、所有していない場合もエラーを投げます。

引数

  • _fromContract
    • トークンを所有するコントラクトのアドレス。
  • _fromTokenId
    • 転送元のトークンのID。
  • _toContract
    • 転送先のERC-721トークンコントラクトのアドレス。
  • _toTokenId
    • 転送先のトークンのID。
  • _tokenId
    • 転送するトークンのID。
  • _data
    • 特定のフォーマットが指定されていない追加データです。

ERC-721 Bottom-Up Composable Enumeration

ボトムアップのコンポーザブル列挙のためのオプションのインターフェイスです。

javascript
/// @dev The ERC-165 identifier for this interface is 0x8318b539
interface ERC998ERC721BottomUpEnumerable {
  
  /// @notice Get the number of ERC-721 tokens owned by parent token.
  /// @param _parentContract The contract the parent ERC-721 token is from.
  /// @param _parentTokenId The parent tokenId that owns tokens
  //  @return uint256 The number of ERC-721 tokens owned by parent token.
  function totalChildTokens(
    address _parentContract, 
    uint256 _parentTokenId
  ) 
    external 
    view 
    returns (uint256);
  
  /// @notice Get a child token by index
  /// @param _parentContract The contract the parent ERC-721 token is from.
  /// @param _parentTokenId The parent tokenId that owns the token
  /// @param _index The index position of the child token
  /// @return uint256 The child tokenId owned by the parent token
  function childTokenByIndex(
    address _parentContract, 
    uint256 _parentTokenId, 
    uint256 _index
  ) 
    external 
    view 
    returns (uint256);
}

ERC-20 Bottom-Up Composable

ERC20ボトムアップ合成可能なコントラクトは、ERC20トークンの一種です。 このトークンは、自身をERC721トークンに取り付けることができるほか、通常のERC20トークンと同様にユーザーアドレスによって所有されることもできます。

ERC721トークンに取り付けられている場合、ERC20BottomUp合成可能なコントラクトは、そのトークンの所有者のアドレスと、親トークンのトークンID(存在する場合)を保持します。 また、このコントラクトはERC20やERC223インターフェースにいくつかの追加メソッドを追加し、親トークンの残高をクエリしたり、親トークンへのトークンの転送、親トークン間でのトークンの転送などが可能になります。

この機能を実装するためには、ユーザーアドレスの残高を追跡するための通常のマッピングに加えて、トークンの残高を追跡するための追加のマッピングを1つ追加する必要があります。

javascript
/// @dev This mapping tracks standard ERC20/`ERC-223` ownership, where an address owns
///  a particular amount of tokens.
mapping(address => uint) userBalances;

/// @dev This additional mapping tracks ERC-998 ownership, where an ERC-721 token owns
///  a particular amount of tokens. This tracks contractAddres => tokenId => balance
mapping(address => mapping(uint => uint)) nftBalances;

完全なインターフェイスは以下になります。

javascript
/// @title `ERC998ERC20` Bottom-Up Composable Fungible Token
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md
/// Note: The ERC-165 identifier for this interface is 0xffafa991
interface ERC998ERC20BottomUp {
  
  /// @dev This emits when a token is transferred to an ERC-721 token
  /// @param _toContract The contract the token is transferred to
  /// @param _toTokenId The token the token is transferred to
  /// @param _amount The amount of tokens transferred
  event TransferToParent(
    address indexed _toContract, 
    uint256 indexed _toTokenId, 
    uint256 _amount
  );

  /// @dev This emits when a token is transferred from an ERC-721 token
  /// @param _fromContract The contract the token is transferred from
  /// @param _fromTokenId The token the token is transferred from
  /// @param _amount The amount of tokens transferred
  event TransferFromParent(
    address indexed _fromContract, 
    uint256 indexed _fromTokenId, 
    uint256 _amount
  );

  /// @notice Get the balance of a non-fungible parent token
  /// @param _tokenContract The contract tracking the parent token
  /// @param _tokenId The ID of the parent token
  /// @return amount The balance of the token
  function balanceOfToken(
    address _tokenContract, 
    uint256 _tokenId
  )
    external
    view
    returns (uint256 amount);

  /// @notice Transfer tokens from owner address to a token
  /// @param _from The owner address
  /// @param _toContract The ERC-721 contract of the receiving token
  /// @param _toToken The receiving token
  /// @param _amount The amount of tokens to transfer
  function transferToParent(
    address _from, 
    address _toContract, 
    uint256 _toTokenId, 
    uint256 _amount
  )
    external;

  /// @notice Transfer token from a token to an address
  /// @param _fromContract The address of the owning contract
  /// @param _fromTokenId The owning token
  /// @param _to The address the token is transferred to
  /// @param _amount The amount of tokens to transfer
  function transferFromParent(
    address _fromContract, 
    uint256 _fromTokenId, 
    address _to, 
    uint256 _amount
  )
    external;

  /// @notice Transfer token from a token to an address, using `ERC-223` semantics
  /// @param _fromContract The address of the owning contract
  /// @param _fromTokenId The owning token
  /// @param _to The address the token is transferred to
  /// @param _amount The amount of tokens to transfer
  /// @param _data Additional data with no specified format, can be used to specify the sender tokenId
  function transferFromParentERC223(
    address _fromContract, 
    uint256 _fromTokenId, 
    address _to, 
    uint256 _amount, 
    bytes _data
  )
    external;

  /// @notice Transfer a token from a token to another token
  /// @param _fromContract The address of the owning contract
  /// @param _fromTokenId The owning token
  /// @param _toContract The ERC-721 contract of the receiving token
  /// @param _toToken The receiving token
  /// @param _amount The amount tokens to transfer
  function transferAsChild(
    address _fromContract, 
    uint256 _fromTokenId, 
    address _toContract, 
    uint256 _toTokenId, 
    uint256 _amount
   )
    external;
}

balanceOfToken

javascript
function balanceOfToken(address _tokenContract, uint256 _tokenId) external view returns (uint256 amount);

概要

非代替性親トークンの残高を返す関数。

詳細

この関数は、指定された親トークンの契約アドレスとトークンIDに対する残高を返します。 親トークンの所有権を持つERC721トークンによって所有されているトークンの残高をクエリするのに使用されます。

これは、標準のERC-20メソッドであるbalanceOfと似ていますが、親トークンのコントラクトアドレスと親トークンのIDを受け入れます。 このメソッドはbalanceOfと同様に動作しますが、ユーザーアドレスではなくERC721トークンによる所有権を確認します。

引数

  • _tokenContract
    • 親トークンを追跡しているコントラクトのアドレス。
  • _tokenId
    • 親トークンのID。

戻り値

  • amount
    • トークンの残高(非代替性親トークンの場合)。

transferToParent

javascript
function transferToParent(address _from, address _toContract, uint256 _toTokenId, uint256 _amount) external;

概要

ユーザーアドレスからERC721トークンにトークンの一部を転送する関数。

詳細

指定されたユーザーアドレスから指定されたERC-721トークンにトークンの一部を転送します 受信コントラクトがERC721を実装していることを確認する必要があります(ERC165のsupportsInterface関数を使用)。 また、受信トークンが実際に存在することを確認する必要があります。 これは、受信トークンのコントラクト上のownerOfを呼び出し、エラーを投げずにゼロアドレスを返さないことを確認することで行うべきです。 この関数は、トークンの転送が成功した場合(標準のERC20 Transferイベントに加えて)、TransferToParentイベントを発行する必要があります。 また、_fromアカウントの残高が転送に十分なトークンを持っていない場合はエラーを投げる必要があります。。

引数

  • _from
    • トークンを転送する所有者のアドレス。
  • _toContract
    • 受信トークンのERC721コントラクト。
  • _toTokenId
    • 受信トークンのID。
  • _amount
    • 転送するトークンの量。

transferFromParent

javascript
function transferFromParent(address _fromContract, uint256 _fromTokenId, address _to, uint256 _amount) external;

概要

ERC721トークンからアドレスにトークンの一部を転送する関数。

詳細

指定されたERC721トークンから指定されたアドレスにトークンの一部を転送します。 転送が成功した場合にTransferFromParentイベントを発行する必要があります(標準のERC20 Transferイベントに加えて)。 また、送信者ERC721トークンの残高が指定された_amount未満である場合エラーを投げる必要があります。 さらに、この関数は、msg.senderが送信者ERC721トークンの所有者であることを確認し、そうでない場合はエラーを投げる必要があります。

引数

  • _fromContract
    • トークンを所有するコントラクトのアドレス。
  • _fromTokenId
    • 送信元トークンのID。
  • _to
    • トークンを転送するアドレス。
  • _amount
    • 転送するトークンの量。

transferFromParentERC223

javascript
function transferFromParentERC223(address _fromContract, uint256 _fromTokenId, address _to, uint256 _amount, bytes _data) external;

概要

ERC721トークンからアドレスに、トークンの一部をERC-223のセマンティクスを使用して転送する関数。

詳細

指定されたERC721トークンから指定されたアドレスにトークンの一部をERC223のセマンティクスを使用して転送します。 transferFromParentと同じ要件を持っていますが、アドレスがコントラクトである場合、ERC223で指定されているように受信アドレスでtokenFallbackを呼び出す必要があります。

引数

  • _fromContract
    • トークンを所有するコントラクトのアドレス。
  • _fromTokenId
    • 送信元トークンのID。
  • _to
    • トークンを転送するアドレス。
  • _amount
    • 転送するトークンの量。
  • _data
    • 指定されたフォーマットの追加データ。
    • 送信者のトークンIDを指定するために使用できます。

transferAsChild

javascript
function transferAsChild(address _fromContract, uint256 _fromTokenId, address _toContract, uint256 _toTokenId, uint256 _amount) external;

概要

この関数は、ERC721トークンから、別のERC721トークンにトークンの一部を転送する関数。

詳細

標準のERC20コントラクトのTransferイベントに加えて、TransferFromParentおよびTransferToParentの両方のイベントを発行する必要があります。 送信者がERC721トークンの残高が指定された_amount未満の場合、この関数はエラーを投げます。 また、この関数は、msg.senderが送信者ERC721トークンの所有者であることを確認し、そうでない場合はエラーを投げます。 さらに、この関数は、受信者コントラクトがERC721を実装していることを確認する必要があります(ERC165のsupportsInterface関数を使用)。 受信者トークンが実際に存在することを確認するためにも、ownerOfを受信者トークンのコントラクトで呼び出し、エラーを投げずゼロアドレスを返さないことを確認する必要があります。

引数

  • _fromContract
    • トークンを所有するコントラクトのアドレス。
  • _fromTokenId
    • 送信元トークンのID。
  • _toContract
    • 受信トークンのERC721コントラクト。
  • _toTokenId
    • 受信トークンのID。
  • _amount
    • 転送するトークンの量。

メモ

このノートによれば、新しい実装は、トークンの転送が発生した場合に、送信者と受信者がアドレスであるかERC721トークンであるかに関係なく、標準のERC20コントラクトのTransferイベントを発行する必要があります。 つまり、トークン間の送金やトークンからアドレスへの送金に関わらず、トランザクションの詳細情報をイベントとして公開することが求められます。

また、この実装は、このインターフェースで指定された関数に加えて、ERC20やERC223のすべての関数を実装しなければならないとされています。 つまり、この新しいトークン標準に従う実装は、トークンの基本的な機能だけでなく、互換性のある他の関数も提供しなければなりません。

これにより、既存のトークンとのシームレスな連携や互換性が確保され、ユーザーと開発者が新しいトークンを円滑に採用できるようになります。

補足

トークン同士の組み合わせ(コンポーザブル)には、異なる使用ケースを処理するための2つの異なる種類が存在します。 通常のERC721トークンはトップダウンのコンポーザブルを所有することはできませんが、ボトムアップのコンポーザブルを所有することはできます。 逆に、ボトムアップのコンポーザブルは通常のERC721トークンを所有することはできませんが、トップダウンのコンポーザブルは通常のERC721トークンを所有できます。 異なるコンポーザブルを持つことで、異なるトークンの所有構造が可能になります。

どの種類のコンポーザブルを使用するか?

通常のERC721トークンを非代替トークンに転送したい場合は、トップダウンのコンポーザブルを使用します。

非代替トークンを通常のERC721トークンに転送したい場合は、ボトムアップのコンポーザブルを使用します。

明示的な転送パラメータ

すべてのERC998転送関数には、トークンの前の所有者と新しい所有者を指定する明示的なパラメータが含まれています。 fromとtoを明示的に指定する理由は、トークンが意図しない方法で転送される状況を避けるためです。

以下は、fromが転送関数に明示的に提供されなかった場合に何が起こり得るかの例です。

An exchange contract is an approved operator in a specific composable contract for user A, user B and user C. User A transfers token 1 to user B. At the same time the exchange contract transfers token 1 to user C (with the implicit intention to transfer from user A). User B gets token 1 for a minute before it gets incorrectly transferred to user C. The second transfer should have failed but it didn’t because no explicit from was provided to ensure that token 1 came from user A.

ある取引コントラクトは、特定のコンポーザブルコントラクトに対してユーザーA、ユーザーB、ユーザーCの承認されたオペレーターです。 ユーザーAがトークン1をユーザーBに転送します。同時に、取引コントラクトはトークン1をユーザーCに転送します(ユーザーAから転送する意図で行われます)。ユーザーBはトークン1を1分間所有した後、誤ってユーザーCに転送されます。第2の転送は失敗すべきでしたが、それは失敗しませんでした。なぜなら、トークン1がユーザーAから来たことを確実にするためのfromが明示的に提供されなかったためです。

後方互換性

コンポーザブルは、ERC721、ERC223、ERC20トークンと協力して機能するために設計されています。

一部の古いERC721コントラクトには、トークンを安全に転送するためのsafeTransferFrom関数が提供されていない場合があります。 しかし、それでもgetChild関数を使用することで、トークンをERC721トップダウンのコンポーザブルに転送できます。 これにより、古いコントラクトでも新しいトークン機能を利用できます。

また、ERC20コントラクトにERC223のtransfer(address _to, uint _value, bytes _data)関数が存在しない場合、getERC20関数を使用してERC20トークンをERC20TopDownのコンポーザブルに転送することができます。 これにより、ERC20トークンをコンポーザブルと連携させるための柔軟性が提供されます。

コンポーザブルは古いコントラクトや異なるトークンタイプとも連携できるように設計されており、トークンの組み合わせに対する柔軟性を提供します。

参考実装

以下を参考にしてください。

セキュリティ考慮事項

議論の必要があります。

引用

Matt Lockyer mattdlockyer@gmail.com, Nick Mudge nick@perfectabstractions.com, Jordan Schalm jordan.schalm@gmail.com, sebastian echeverry sebastian.echeverry@robotouniverse.com, Zainan Victor Zhou (@xinbenlv), "ERC-998: Composable Non-Fungible Token [DRAFT]," Ethereum Improvement Proposals, no. 998, July 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-998.

最後に

今回は「ERC721を拡張して、他のERC721やERC20トークンを所有する提案している規格であるERC998」についてまとめてきました! いかがだったでしょうか?

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!

他の媒体でも情報発信しているのでぜひ他も見ていってください!