# [ERC998] NFTが他のNFTやERC20トークンを所有する仕組みを理解しよう！

> この規格は、ERC721トークンが他のERC721トークンとERC20トークンを所有できるようにするものです。また、ERC20やERC223トークンがERC721トークンによって所有できるようにもします。

- URL: https://cardene.dev/articles/9198e509a1ea99985dd0
- 公開日: 2023-10-31
- 更新日: 2023-10-31
- トピック: Blockchain
- タグ: ethereum, solidity, ブロックチェーン, nft

## はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

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

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

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

:::message alert
**998**は現在（2023年10月28日）では「**Draft**」段階です。
:::

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

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

## 概要

この規格は、**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**トークンにアタッチできるトークン。

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

https://chaldene.net/erc20

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

https://chaldene.net/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**コンポーザブル機能を持つコントラクトが正しく他のコントラクトやユーザーとやり取りできることが確認できます。

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

[\[ERC165\] コントラクトが特定のインターフェースをサポートしているか確認する仕組みを理解しよう！](/articles/541184977a5cba51b759)

:::message
**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**の関数です。
これにより、合成可能なトークンツリー全体を新しいルートオーナーに移動する時に、子合成可能なトークン内で誰が承認されているかを気にせずに済みます。
なぜなら、以前の承認は以前のルートオーナーにしか利用できないためです。

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

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

```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);
```

:::message
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`)、トークンID`3`、およびトークンIDをバイト配列に変換した`tokenIdBytes`が指定されます。

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

**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**トークンをコンポーザブルと連携させるための柔軟性が提供されます。

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

## 参考実装

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

https://github.com/mattlockyer/composables-998

## セキュリティ考慮事項

議論の必要があります。

## 引用

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などからお気軽に質問してください！

[Twitter @cardene777](https://twitter.com/cardene777)

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

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58


https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4
