[ERC7085] NFT同士の関係性やNFTの属性を管理する仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、**ERC721**と**ERC1155**形式のNFTの関係性と属性を管理するための仕組みを提案している**ERC7085**についてまとめていきます！

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

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

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

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

# 概要

提案されている新しい標準は、**ERC1155**をさらに発展させたもので、NFT間に関係性を設定し、それぞれのNFTに対して定量的な属性を設定できるようにします。
これは、NFTを単独で扱うのではなく、複数のNFTが相互に関連し合いながら一つの大きなエコシステムを形成できるようにすることを目指しています。

### 主な特徴
- **関係性の定義**
    - NFT間にグラフ構造を構築することが可能になり、特定のNFTが他のNFTとどのように関係しているかを定義できます。
- **定量的属性の設定**
    - 各NFTに対して、重さ、価値、耐久度などの定量的な属性を設定できます。
    - これにより、NFTが単なるコレクタブルアイテムを超えた、より複雑な機能を持つことが可能になります。

### 利点と応用
このような拡張性は、ゲーム、仮想不動産、複雑なデジタルコレクションなど、さまざまなアプリケーションに応用可能です。
例えば、ゲーム内でプレイヤーがアイテムやキャラクター間に関係を構築し、それぞれに特定の属性を設定することができます。
また、メタバースの土地などでは、特定の土地の価値を周辺の土地との関係性によって決定することができるようになります。

:::note info
この提案は、NFTの使用方法を大幅に拡張し、より豊富なインタラクションと複雑なエコシステムの構築を可能にします。
現在の**ERC721**や**ERC1155**の枠組みを超えて、NFTの将来的な可能性を広げる重要な一歩と言えます。
:::

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

https://chaldene.net/erc721

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

https://chaldene.net/erc1155

# 動機

この提案は、現在のNFT（Non-Fungible Token、非代替トークン）の標準である**ERC721**および**ERC1155**の拡張を目指しています。
具体的には、NFT間の関係性を明確にし、それらの間に定量的属性を設定できる機能を加えることを提案しています。

### 背景と課題
現在のNFT標準（**ERC721**と**ERC1155**）では、トークン間の関係性を直接的に表現するメカニズムがありません。
NFTは、芸術作品、ゲーム内アイテム、コレクタブルなど、独自の属性やメタデータを持つことができますが、これらのトークン間で直接的な「**関係性**」や「**相互作用**」を定義する手段は標準化されていません。

この制限は、より複雑なNFTエコシステムを構築したいユーザーにとって大きな障壁となっています。
例えば、あるNFTが別のNFTを「**参照**」する、または一方のNFTがもう一方に対して「**影響**」を及ぼすといった関係を構築する場合、現行の標準ではこれらを効率的に管理する方法がありません。

### 提案の目的
この提案の主な目的は、NFT間での「**参照関係**」と「**定量的属性**」を標準化することです。
これにより、NFTの開発者やユーザーは、トークン間に複雑な関係を構築し、それぞれの関係に特定の属性（例えば、重要度、依存度、影響力の大きさなど）を割り当てることが可能になります。

### 潜在的な影響
この拡張により、NFTエコシステムはより複雑で豊かなインタラクションをサポートするようになります。
ゲーム、デジタルアート、教育、メタバースの土地など、さまざまな分野で新たな用途が開かれる可能性があります。
NFT間の関係性と属性を明確にすることで、新しい形の物語やコレクション、インタラクティブな体験が生み出されるかもしれません。

### 考慮すべき課題
- **複雑性の増加**
    - トークン間の関係性と属性を管理することは、システムの複雑性を増加させます。
    - 開発者は、これらの新しい要素を適切に扱うための追加のロジックを実装する必要があります。
- **ガスコストの増加**
    - イーサリアム上でのトランザクション（特にスマートコントラクトの実行）はガスコストがかかります。
    - 関係性と属性を追加することは、これらのコストを増加させる可能性があります。
- **規則の設計**
    - 新しい標準を効果的に使うためには、関係性と属性の定義、更新、削除を管理するための明確な規則が必要です。
    - これには、セキュリティやプライバシーの考慮も含まれます。

:::note info
この提案は、NFTエコシステムをさらに発展させ、新たな用途を生み出すための重要な一歩です。
しかし、これを実現するためには、技術的な複雑性、コスト、そして運用上の課題に対処する必要があります。
慎重な設計とコミュニティの協力によって、これらの課題を克服し、NFTの未来を形作ることができるでしょう。
:::

# 仕様

### `setRelationship`

```javascript
function setRelationship(uint256 _originalID, uint256 _derivativeID, uint256 _attribute) external;
```

**概要**

元のNFTと派生NFTの間に参照関係を確立するう関数。

**詳細**

この関数は、元のNFTとそれに関連する派生NFTの間に、特定の属性を持つ参照関係を設定します。
関係の属性は定量化可能で、デフォルトでは`1`に設定されていますが、明示的に指定することも可能です。

**引数**

- `_originalID`
	- 元のNFTのID。
- `_derivativeID`
	- 元のNFTを参照する派生NFTのID。
- `_attribute`
	- この関係の定量的属性。
	- 指定されない場合、デフォルトで`1`になります。

### `setAttribute`

```javascript
function setAttribute(uint256 _id, string calldata _name, uint256 _value) external;
```

**概要**

NFTに定量的属性を設定する関数。

**詳細**

指定されたNFTに対して、名前と値で定義される属性を設定します。
この関数を使って、NFTに特定の特性や情報を付与することができます。

**引数**

- `_id`
	- 属性を設定するNFTのID。
- `_name`
	- 設定する属性の名前。
- `_value`
	- 属性の値。

### `getAttribute`

```javascript
function getAttribute(uint256 _id, string calldata _name) external view returns (bytes32);
```

**概要**

指定されたNFTの特定の属性の値を取得する関数。

**詳細**

指定されたNFTに関連付けられた特定の属性の値を取得します。
この関数により、NFTの属性情報を照会することが可能になります。

**引数**

- `_id`
	- 属性の値を取得したいNFTのID。
- `_name`
	- 取得したい属性の名前。

**戻り値**

- `bytes32`
	- 指定された属性の値。

### `getAttributeNames`

```javascript
function getAttributeNames(uint256 _id) external view returns (bytes32[] memory);
```

**概要**

NFTに関連付けられたすべての属性名を取得する関数。

**詳細**

指定されたNFTに設定されているすべての属性名を取得します。
これにより、NFTにどのような属性が存在するかの全体像を得ることができます。

**引数**

- `_id`
	- 属性名を取得したいNFTのID。

**戻り値**

- `bytes32[] memory`
	- NFTに関連付けられたすべての属性名の配列。

### `getRelationship`

```javascript
function getRelationship(uint256 _originalID, uint256 _derivativeID) external view returns (uint256);
```

**概要**

二つのNFT間の参照関係の値を取得する関数。

**詳細**

元のNFTと派生NFTの間の参照関係の値を取得します。
この関係は、`setRelationship`関数を使用して事前に設定されている必要があります。

**引数**

- `_originalID`
	- 元のNFTのID。
- `_derivativeID`
	- 元のNFTを参照する派生NFTのID。

**戻り値**

- `uint256`
	- 二つのNFT間の参照関係の値。

## 使用例

```ts
NFTGraph nftContract = NFTGraph(addressOfContract);

// Retrieve the value of an attribute named "Color" for NFT with ID 123
bytes32 colorValue = nftContract.getAttribute(123, "Color");

// Retrieve the names of all attributes associated with NFT with ID 456
bytes32[] memory attributeNames = nftContract.getAttributeNames(456);
```

上記のコードスニペットは、特定のNFTに関連する属性の値を取得したり、NFTに関連付けられている全ての属性名を取得する方法を示しています。
これらの操作は、NFTを扱うスマートコントラクトの一部として実装されることが想定されています。

### コードの説明

#### コントラクトのインスタンス化

```javascript
NFTGraph nftContract = NFTGraph(addressOfContract);
```

既存の`NFTGraph`コントラクトのアドレスを使用して、コントラクトのインスタンスを作成します。
ここでの`addressOfContract`は、`NFTGraph`コントラクトがデプロイされているブロックチェーン上のアドレスです。

#### 特定の属性の値の取得

```javascript
bytes32 colorValue = nftContract.getAttribute(123, "Color");
```

ここでは、IDが`123`のNFTに関連付けられている"Color"という名前の属性の値を取得しています。
この関数は`bytes32`型のデータを戻り値として返します。
これは、属性の値がこの形式でエンコードされていることを意味します。
実際の色の値（例えば、"赤"、"青"など）は、この`bytes32`型のデータからデコードする必要があります。

#### 関連付けられた全属性名の取得

```javascript
bytes32[] memory attributeNames = nftContract.getAttributeNames(456);
```

この行は、IDが`456`のNFTに関連付けられている全ての属性名を取得します。
戻り値は`bytes32[]`型で、これは属性名の配列を表します。
各属性名は`bytes32`型でエンコードされており、具体的な文字列名にデコードする必要があります。

### 重要性

これらの関数と方法を仕様に含めることで、開発者やユーザーがNFTに関連する属性を標準化された方法で読み取ることができるようになります。
これは、NFTエコシステム内でより複雑な関係や属性を管理し、利用するための基盤を提供します。
例えば、ゲームやデジタルアートのコレクション、仮想不動産など、さまざまなアプリケーションでこの機能を活用することが可能になります。
また、これによりNFTの柔軟性と拡張性が大幅に向上し、新たなユースケースの開発を促進することができます。

# 補足

このEIP（Ethereum Improvement Proposal、イーサリアム改善提案）を開発する過程で、設計上のいくつかの重要な決定がなされました。
これらの決定は、新しいNFT標準の有用性と実用性を高めるために重要です。

### NFT間の関係性のグラフ複雑性の制限

- **決定内容**
    - 二つのNFT間に一つの参照関係のみを許可することで、関係性のグラフの複雑性を制限しました。
- **意図**
    - この制限により、NFT間の関係性グラフが管理可能な範囲内に保たれ、過度に複雑化することなく、利用者が理解しやすい構造になります。
    - 複雑性が制限されることで、グラフの可視化や分析が容易になり、エンドユーザーや開発者がNFT間の関係をより簡単に把握できるようになります。

### 属性設定のガスコスト最小化

- **決定内容**
    - 一度に一つの属性のみを設定できるようにすることで、属性を設定する時のガスコストを最小限に抑えました。
- **意図**
    - イーサリアム上でのトランザクションはガスコストが発生します。
    - 複数の属性を同時に設定すると、それに伴うコストが増大し、特に多くのNFTを管理する場合に負担が大きくなります。
    - この設計決定により、NFTの属性を設定する時のコストを低減し、より多くのユーザーが新しい機能を利用できるようになります。

### グラフ理論からのインスピレーション

- **背景**
    - 他のブロックチェーン言語や標準で類似の機能が現在存在しない中、このEIPは数学の一分野であるグラフ理論からインスピレーションを得ています。
- **意図**
    - グラフ理論は、オブジェクト間の関係を研究する学問であり、この理論を基にNFT間の関係性を構築することで、NFTエコシステムをより複雑かつ豊かなものにすることが可能です。
    - NFT間に関係性を設定し、それらの関係に定量的な属性を付与する能力を加えることで、NFTの標準をさらに有用で多用途にわたって活用できるものにすることを目指しています。

これらの設計決定は、新しいNFT標準をより実用的で、NFTクリエーターやユーザーにとって有益なものにするための基盤を築きます。
同時に、これらの機能を実装することにより、NFTの使用可能性を大幅に広げ、新たなアプリケーションやユースケースの開発を促進することが期待されます。

# 互換性

このEIP（Ethereum Improvement Proposal、イーサリアム改善提案）は、既存の**ERC721**および**ERC1155**コントラクトおよびトークンと完全に後方互換性を持つように設計されています。
これは、この提案が採用された場合でも、既存のNFTコントラクトやトークンは以前と同じように機能し続けることを意味します。
新しい機能である`setRelationship`および`setAttribute`関数は、このEIPを明示的に実装するコントラクトにのみ利用可能になります。

### 後方互換性の重要性

- **互換性の維持**
    - 既存の**ERC721**や**ERC1155**ベースのNFTは、このEIPを実装していなくても、影響を受けることなく引き続き機能します。
    - これにより、既に大量に存在するNFTや、それを使用するアプリケーションがこのEIPによって不利になることはありません。
- **スムーズな移行**
    - 新しい機能を実装したい開発者は、自分のコントラクトをこのEIPに従ってアップデートすることで、新しい`setRelationship`および`setAttribute`機能を利用できるようになります。
    - これにより、開発者は既存のシステムを完全に置き換えることなく、新しい機能を導入することができます。

### 新機能の実装

- **`setRelationship`関数**
    - この関数を使用することで、二つのNFT間に参照関係を設定できます。
    - この機能は、NFT間の新しい形の相互作用を可能にし、より複雑なエコシステムを構築するための基盤を提供します。
- **`setAttribute`関数**
    - NFTに特定の属性を設定するために使用されます。
    - この関数により、NFTに追加の情報や特性を付与することが可能になり、NFTの用途をさらに広げることができます。

# 実装

## ERC165インターフェイスサポート

NFT関係性強化コントラクトは、**ERC165**標準インターフェイスを実装しており、インターフェイス検出を可能にします。
これにより、スマートコントラクトやアプリケーションは、あるコントラクトがこの提案で定義された関数をサポートしているかどうかを事前に確認することができます。
これは、コントラクト間の互換性を確認し、期待通りに機能することを保証するために重要です。

- **参照実装の提供**
    - 提案には、関係性と属性の機能を実装するための参照Solidityインターフェイスとコントラクトが含まれています。
    - これは開発者が自身のプロジェクトにこの新しい標準を統合する際の出発点となります。
- **ERC165によるインターフェイス検出**
    - **ERC165**は、スマートコントラクトが特定のインターフェイスをサポートしているかどうかを動的に検出するための標準です。
    - この機能を使用することで、あるコントラクトがNFT関係性強化機能を持っているかどうかを確認できます。
    - これは、特に複数のコントラクトが相互作用する複雑なシステムにおいて、エラーを防ぎ、安全な操作を保証するのに役立ちます。
- **スマートコントラクトとアプリケーションの相互作用の強化**
    - この検出機能により、開発者はコントラクトが必要とするインターフェイスをサポートしているかどうかを確認し、サポートされていない場合は代替の処理を行うことができます。
    - これにより、アプリケーションはより堅牢になり、ユーザーエクスペリエンスが向上します。

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

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

##  INFTGraphインターフェース

```ts
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC165/IERC165.sol";  // Import IERC165 for interface detection

interface INFTGraph is IERC165 {
    // setRelationship: Establishes relationships between NFTs.
    function setRelationship(uint256 _originalID, uint256 _derivativeID, uint256 _attribute) external;
    // setAttribute: Sets quantifiable attributes for NFTs.
    function setAttribute(uint256 _id, string calldata _name, uint256 _value) external;
    // getRelationship: Retrieves relationship values between NFTs.
    function getRelationship(uint256 _originalID, uint256 _derivativeID) external view returns (uint256);
    // getAttribute: Retrieves the value of specific attributes associated with NFTs.
    function getAttribute(uint256 _id, string calldata _name) external view returns (bytes32);
    // getAttributeNames: Retrieves all attribute names associated with an NFT.
    function getAttributeNames(uint256 _id) external view returns (bytes32[] memory);
}
```

INFTGraph インターフェースは、関係や属性を設定する関数、属性情報や関係値を取得する関数を定義しています。

## NFTGraphコントラクト

```ts
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/introspection/ERC165.sol";  // Import ERC165 for interface detection

import "./INFTGraph.sol";  // Import INFTGraph interface

contract NFTGraph is INFTGraph{
    mapping(uint256 => mapping(uint256 => uint256)) public relationship;
    mapping(uint256 => mapping(bytes32 => bytes32)) public attributes;

    // Implement the setRelationship and setAttribute functions as described in the EIP specification.


    // Implement the supportsInterface function for ERC-165.
    function supportsInterface(bytes4 interfaceID) public view override returns (bool) {
        return interfaceID == type(INFTGraph).interfaceId || super.supportsInterface(interfaceID);
    }

    // Additional implementation details...
    function getRelationship(uint256 _originalID, uint256 _derivativeID) external view returns (uint256) {
        return relationship[_originalID][_derivativeID];
    }

    function getAttribute(uint256 _id, string calldata _name) external view returns (bytes32) {
        return bytes32(attributes[_id][_name]);
    }

    function getAttributeNames(uint256 _id) external view returns (bytes32[] memory) {
        bytes32[] memory names = new bytes32[](attributes[_id].length);
        for (uint256 i = 0; i < attributes[_id].length; i++) {
            names[i] = bytes32(attributes[_id][i]);
        }
        return names;
    }

    function setRelationship(uint256 originalNFT, uint256 derivativeNFT, uint256 relationshipValue) public {
        require(originalNFT != derivativeNFT, "Original and derivative NFTs must be different");
        relationship[originalNFT][derivativeNFT] = relationshipValue;
    }
    
    function setAttribute(uint256 nft, bytes32 attributeName, bytes32 attributeValue) public {
        attributes[nft][attributeName] = attributeValue;
    }

}
```

`NFTGraph`コントラクトは、`INFTGraph`インターフェイスで指定された関数を実装し、関係性と属性のためのストレージを提供します。
これは、NFT間の関係性を確立し、それぞれのNFTに属性を割り当てることを可能にするための機能です。
このようにして、NFTのエコシステム内でより豊かな相互作用と関係性のネットワークを構築することができます。

### 参照インターフェイスとコントラクトの利用

開発者はこの参照インターフェイスとコントラクトを使用して、NFT関係性強化機能を自分たちのプロジェクトに統合することができます。
`INFTGraph`インターフェイスは、コントラクトとの対話を行うための明確で標準化された方法を提供します。
これにより、一貫性が促進され、統合の容易さが向上します。

### 機能の概要

- **関係性の設定**
    - 開発者は`setRelationship`関数を使用して、2つのNFT間に特定の関係性を確立することができます。
    - これにより、例えば、一つのNFTが別のNFTに影響を与えるといった複雑な関係性をモデル化できます。
- **属性の割り当て**
    - `setAttribute`関数を通じて、個々のNFTに対して定量的な属性（例えば、色、サイズ、レア度）を割り当てることが可能になります。
- **属性と関係性の読み取り**
    - `getAttribute`や`getRelationship`などの関数により、登録された属性や関係性の情報を取得することができます。

### 利点

- **柔軟性**
    - 関係性と属性をNFTに統合することで、より動的で対話的なエコシステムを構築することが可能になります。
- **一貫性と標準化**
    - インターフェイスを通じた標準化された方法でコントラクトと対話することにより、異なるプロジェクトやアプリケーション間での互換性と統合が容易になります。
- **開発の効率化**
    - 参照インターフェイスとコントラクトを出発点として使用することで、開発プロセスが加速し、NFT関係性強化機能の統合がスムーズに行えるようになります。

この参照インターフェイスとコントラクトを活用することで、NFTの持つ可能性をさらに拡張し、ユーザーに対してよりリッチなエクスペリエンスを提供するための基盤を築くことができます。

# セキュリティ

この提案を実装する時に、スマートコントラクトの開発者は以下のセキュリティ側面を考慮する必要があります。
これらの検討事項は、コントラクトとNFTエコシステムの整合性を保護するために不可欠です。

### 関係性の検証

- **問題**
    - `setRelationship`関数を利用するコントラクトは、確立されている関係性が有効で、関連する当事者によって承認されていることを保証する必要があります。
    - 無許可や悪意のある関係性が確立されると、意図しない結果を招く可能性があります。
- **解決策**
    - 関係性を設定する前に、当事者の承認を検証するプロセスを実装します。
    - また、関係性の合理性や有効性を確認するロジックを導入することも有効です。

### 属性の検証

- **問題**
    - `setAttribute`関数を実装するコントラクトは、属性を慎重に検証して、悪意のあるまたは有害な値を防ぐ必要があります。
    - 無効または未検証の属性は、NFTエコシステムの機能性を乱す可能性があります。
- **解決策**
    - 属性値を設定する時に、その形式や範囲が適切であることを確認するための厳格な検証プロセスを実装します。

### アクセス制御

- **問題**
    - 関係性や属性を変更する重要な関数を呼び出すことができるユーザーを制限するために、適切なアクセス制御メカニズムを実装する必要があります。
    - 無許可のアクセスは、悪用や不正利用につながる可能性があります。
- **解決策**
    - ロールベースのアクセス制御や、関数を呼び出す権限を持つアドレスのホワイトリストを導入することで、認証されたユーザーのみが特定の関数を実行できるようにします。

### 再入性保護

- **問題**
    - 関係性や属性を変更する関数に再入性保護メカニズムを追加することを検討してください。
    - 再入性攻撃を利用されると、コントラクトの挙動を不正に操作される恐れがあります。
- **解決策**
    - 例えば、[reentrancy guard](https://docs.openzeppelin.com/contracts/2.x/api/utils#ReentrancyGuard)パターンを使用して、関数が完了するまで同一の関数の再呼び出しを防ぐなど、再入性攻撃に対する保護メカニズムを実装します。

:::note info
リエントランシー攻撃については以下の記事を参考にしてください。
:::

https://chaldene.net/solidity-reentrancy-attack

これらのセキュリティ検討事項に対処することで、開発者は自身のコントラクトのセキュリティを強化し、NFTエコシステム全体の整合性を保護することができます。
これは、ユーザーの信頼を獲得し、広範な採用に向けた重要なステップです。

# 引用

Guang (@xg1990), "ERC-7085: NFT Relationship Enhancement [DRAFT]," Ethereum Improvement Proposals, no. 7085, May 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7085.

# 最後に

今回は「**ERC721**と**ERC1155**形式のNFTの関係性と属性を管理するための仕組みを提案している**ERC7085**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下の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