[ERC1633] ERC721形式のNFTを共有所有する仕組みを理解しよう！

# はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

https://amzn.asia/d/gxvJ0Pw

今回は、ERC721形式のNFTを共有所有する仕組みを提案している**ERC1633**についてまとめていきます！

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

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

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

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

# 概要

**ERC1633**は、**ERC20**トークン標準を拡張し、**ERC721**の共有所有権を表現する新しいトークンであるRe-Fungible Token（RFT）を提案しています。
**ERC1633**では、既存の**ERC20**との互換性を最大限保ちながら、**ERC165**インターフェース検出を活用することで、RFTがNFTの一部所有権を示していることをコントラクトレベルで確認できるように設計されています。

:::note info
**ERC165**については以下の記事を参考にしてください。
:::

https://qiita.com/cardene/items/541184977a5cba51b759

# 動機

共有所有は、多くの業界で実際に行われておりニーズは日々増しています。
特に、**ERC721**で表現されるデジタル資産や現実世界の資産において、複数人による所有が求められるケースが増えています。

例えば、ARTBLX Inc.は、アート作品（物理・デジタル・概念的）を複数人で共同所有するためのプロトコルを開発しています。
このプロセスによって生成される**ERC20**トークンは、関連付けられたNFTの価値を表し、価格発見や所有者間での流動性の確保、ローンやステーブルコインといった金融商品への活用も可能になります。

また、RFTとしてのインターフェースを提案することで、別のDappsが識別できるようになります。
例えば、ウォレットがRFTに対応して元となるNFTのメタデータを表示したり、NFTマーケットプレイスがそのRFTの取引所を案内したりすることが可能です。
どこで**ERC20**が使われている場合でも、それがNFTの共有所有を示すかどうかを知ることは有用です。

# 仕様

## 他Dappsに必要な機能

Re-Fungible Token（RFT）標準は、以下の2点を他Dappsに実装されていることを前提として設計されています。

- RFTが通常の**ERC20**トークンではなく、NFTの共有所有権を表すトークンであることを識別できること。
- 特定のNFTが、RFTによって所有されているか判別できること。

## RFTからの確認方法

```js
pragma solidity ^0.4.20;

/// @dev Note: the ERC-165 identifier for this interface is 0x5755c3f2.
interface RFT /* is ERC20, ERC165 */ {

  function parentToken() external view returns(address _parentToken);
  function parentTokenId() external view returns(uint256 _parentTokenId);

}
```

RFTコントラクトには、以下の2つの読み取り関数が実装されます。

- `parentToken()`
    - 対象となるNFTコントラクトのアドレス（`address`型）を返す。
- `parentTokenId()`
    - 対象となるNFTのトークンID（`uint256`型）を返す。

これにより、RFTがどのNFTを表しているかを特定できます。  
また、**ERC721**準拠であることの確認、NFTの所有者がRFTであることの確認も可能です。

### オンチェーンでの確認例

```js:ConfirmRFT.sol
pragma solidity ^0.4.20;

import './RFT.sol';
import './ERC721.sol';

contract ConfirmRFT {

  function confirmRFT(address _RFT) external view returns(bool) {
    address _NFT = RFT(_RFT).parentToken(); // returns address of NFT contract
    uint256 _tokenId = RFT(_RFT).parentTokenId(); // returns id of ID of NFT

    return
      NFT(_NFT).supportsInterface(0x80ac58cd) && // confirm it is ERC-721
      NFT(_NFT).ownerOf(_tokenId) == _RFT; // confirm the owner of the NFT is the RFT contract address
  }

}
```

### オフチェーン（JavaScript + web3.js）での確認例

```javascript
async function confirmRFT(web3) {

  const ERC721ABI = [...] // abi for ERC721
  const RFTABI = [...] // abi for RFT
  const RFTAddress = '0x0123456789abcdef0123456789abcdef' // address for the deployed RFT

  const RFTContract = new web3.eth.Contract(RFTABI, RFTAddress) // deployed RFT contract instance
  const ERC721Address = await RFTcontract.methods.parentToken().call() // returns address of NFT contract
  const ERC721TokenId = await RFTcontract.methods.parentTokenId().call() // returns id of ID of NFT

  const ERC721Contract = new web3.eth.Contract(ERC721ABI, ERC721Address) // deployed ERC721 (as reported by RFT)
  const isERC721 = await ERC721Contract.methods.supportsInterface('0x80ac58cd').call() // confirm it is ERC-721
  const ownerOfAddress = await ERC721Contract.methods.ownerOf(ERC721TokenId).call() // get the owner of the NFT

  return ERC721Response.toLowerCase() === RFTAddress.toLowerCase() // confirm the owner of the NFT is the RFT contract
}
```

## NFTからの確認方法

特定のNFTトークンの所有者がRFTであるかを判断するには、**ERC165**インターフェース検出機能を活用します。

- `supportsInterface(bytes4 interfaceID)` が `0x01ffc9a7`（ERC-165）および `0x5755c3f2`（RFT）に対して `true` を返す必要があります。
- `0x5755c3f2` は `parentToken()` と `parentTokenId()` のセレクタをXORした結果で、RFT専用のインターフェースIDです。

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

### オンチェーンでの確認例

```js
pragma solidity ^0.4.20;

import './RFT.sol';
import './ERC721.sol';

contract ConfirmRFT {

  function confirmRFT(address _NFT, uint256 _tokenId) external view returns(bool) {
    address _RFT = ERC721(_NFT).ownerOf(_tokenId); // get the owner of the NFT

    return
      RFT(_RFT).supportsInterface(0x01ffc9a7) && // confirm it supports ERC-165
      RFT(_RFT).supportsInterface(0x5755c3f2) // confirm it is RFT
  }

}
```

### オフチェーン（JavaScript + web3.js）での確認例

```javascript
async function confirmRFT(web3) {

  const ERC721ABI = [...] // abi for ERC721
  const RFTABI = [...] // abi for RFT
  const ERC721Address = '0x0123456789abcdef0123456789abcdef' // address for the deployed NFT
  const ERC721TokenId = '7' // token Id of the NFT

  const ERC721Contract = new web3.eth.Contract(ERC721ABI, ERC721Address) // deployed ERC721
  const RFTAddress = await ERC721Contract.methods.ownerOf(ERC721TokenId).call() // owner address of the NFT


  const RFTContract = new web3.eth.Contract(RFTABI, RFTAddress) // deployed RFT contract instance
  const isERC165 = await RFTContract.methods.supportsInterface('0x01ffc9a7').call() // confirm it is ERC-165
  return isERC165 && await RFTContract.methods.supportsInterface('0x5755c3f2').call() // confirm it is RFT

}
```

# 補足

**ERC1633**の設計における基本方針は、できる限り多くのユースケースに対応できるように柔軟性を最大化することです。
そのため、既存の**ERC20**トークン標準との完全な後方互換性を維持しつつ、すでにデプロイ済みまたは将来的に登場する**ERC721**ともシームレスに連携できる設計になっています。

Re-Fungible Token（RFT）を用いる各プロジェクトは、RFTの発行（`mint`）、`burn`、ガバナンスの仕組みなどを自らのニーズに応じて自由に構築することが可能です。

# 互換性

RFT標準は、**ERC20**トークン標準と完全に互換性があります。

# 引用

Billy Rennekamp (@okwme), Dan Long <dan@artblx.com>, Kiryl Yermakou <kiryl@artblx.com>, Nate van der Ende <nate@artblx.com>, "ERC-1633: Re-Fungible Token Standard (RFT) [DRAFT]," Ethereum Improvement Proposals, no. 1633, November 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1633.

# 最後に

今回は「ERC721形式のNFTを共有所有する仕組みを提案している**ERC1633**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

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