[ERC7578] RWAに使用できる物理資産情報をトークンに紐づける仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、物理資産に裏付けられたトークンに、物理資産に関連する様々な情報を紐付ける仕組みを提案している**ERC7578**についてまとめていきます！

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

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

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

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

# 概要

この規格は**ERC721**を拡張して、物理的な資産を裏付けとするNFTの保有者が、その資産の真正性を確認して物理的資産と交換できる情報を取得できる仕組みを提案しています。
これにより、以下の情報をNFTに紐づけて透明性を保ちます。

- 物理的資産の所有者や管理者などの関係者の情報。
- NFT保有者と物理的資産の所有者との法的関係についての情報。
- 資産の詳細情報（シリアル番号、製造元、検証証明書）。

:::note info
NFTと物理的資産の交換条件や所有者の権利や義務についての法的情報を含んでいます。
:::

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

https://chaldene.net/erc721

# 動機

これまでのNFTはデジタル資産の所有権を示していました。
しかし、今後は実世界の資産をトークン化する傾向が予測され（RWAなど）、NFTを物理的資産に使用することが急速に拡大することが見込まれます。
物理的資産のトークン化を行う時、物理的資産に関する情報の欠如や所有者や管理者の透明性が不足していると、NF保有者や興味を持っているユーザーに不安をもたせてしまいます。
この提案では、Mintされたトークンに以下の情報を含めることで物理的資産の真正性の確認や交換を容易にします。

1. **[トークン発行者] NFTを発行する個人または法人の識別情報: [名前], [ユニーク識別番号], [ネットワーク識別子]**
    - トークン発行者は物理的資産とデジタル資産を結びつける存在です。
    - これにより、NFT保有者は発行者の信頼性を評価するためのデューデリジェンスを実施できます。
    - また、発行者の責任が明確になり、NFTのmintプロセスの標準化と改善が期待されます。

2. **[物理的資産の法的所有者] 物理的資産の法的所有者の識別情報: [名前], [ユニーク識別番号], [ネットワークID]**
    - NFT発行者が物理的資産の法的所有者であるとは限らないため、NFT保有者が法的所有者を確認できることが重要です。
    - これにより、法的なカウンターパーティリスクを評価できます。

3. **[保管場所] 物理的資産の保管場所の識別情報: [物理アドレスまたは管轄区域]**
    - 特定の物理的資産は特定の保管条件を必要とします。
    - 保管場所の情報を開示することで、NFT保有者は保管リスクを評価できます。

4. **[法的契約] 法的関係の種類: [リンク先文書のIPFSリンク]**
    - NFT保有者と物理的資産の法的所有者との関係を定義する法的文書へのリンクを提供します。
    - これにより、NFT保有者は法的リスクと執行の可能性を評価できます。

5. **[適用法] 適用される法と管轄区域: [管轄区域]**
    - 法的契約の一部として、適用法と管轄区域を明示します。
    - これにより、管轄リスクを即座に評価できます。

6. **[宣言された価値] 基礎となる資産の価値: [価値]**
    - NFTと物理的資産の価値を定義します。
    - これにより、保険などの補助的なサービスを利用する際に役立ちます。
    - 宣言された価値は必ずしも市場価値を反映しません。

これにより以下の効果が期待できます。
- **信頼性の向上**
    - 物理的資産の真正性と関係者の透明性が確保されるため、NFT市場全体の信頼性が向上します。
- **トレーサビリティの確保**
    - 物理的資産の履歴や所有権の変遷が明確になるため、資産の追跡が容易になります。
- **法的保護の強化**
    - NFT保有者と物理的資産の所有者間の法的関係が明確化されることで、取引の安全性が向上します。

# 仕様

## インターフェース

```js
pragma solidity ^0.8.21;

/**
 * @notice Struct encapsulating fields required to by the ERC-7578 standard to represent the physical asset
 * @param tokenIssuer The network or entity minting the token
 * @param assetHolder The legal owner of the physical asset
 * @param storedLocation The physical storage location
 * @param terms Link to IPFS contract, agreement or terms
 * @param jurisdiction The legal justification set out in the terms
 * @param declaredValue The declared value at time of token minting
 */
struct Properties {
    string tokenIssuer;
    string assetHolder;
    string storedLocation;
    string terms;
    string jurisdiction;
    Amount declaredValue;
}

/**
 * @notice Struct encapsulating fields describing the declared value of the physical asset
 * @param currency The currency of the amount
 * @param value The value of the amount
 */
struct Amount {
    string currency;
    uint256 value;
}

/**
 * @notice Required interface of an ERC-7578 compliant contract
 */
interface IERC7578 {
    /**
     * @notice Emitted when the properties of the `tokenId` token are set
     *
     * @param tokenId The ID of the token
     * @param properties The properties of the token
     */
    event PropertiesSet(uint256 indexed tokenId, Properties properties);

    /**
     * @notice Emitted when the properties of the `tokenId` token are removed
     *
     * @param tokenId The ID of the token
     */
    event PropertiesRemoved(uint256 indexed tokenId);

    /**
     * @notice Retrieves all the properties of the `tokenId` token
     * @dev Does NOT revert if token doesn't exist
     * @param tokenId The token ID of the minted token
     */
    function getProperties(uint256 tokenId) external view returns (Properties memory properties);

    /**
     * @notice Sets the properties of the `tokenId` token
     *
     * IMPORTANT: Properties required to be set when minting a token
     *
     * @param tokenId The ID of the token
     * @param properties The properties of the token
     */
    function setProperties(uint256 tokenId, Properties calldata properties) external;
}
```

### `Properties`

```js
struct Properties {
    string tokenIssuer;
    string assetHolder;
    string storedLocation;
    string terms;
    string jurisdiction;
    Amount declaredValue;
}
```

物理的資産を表現する構造体。

- `tokenIssuer`
    - トークンを発行するネットワークまたはエンティティ。
- `assetHolder`
    - 物理的資産の法的所有者。
- `storedLocation`
    - 物理的資産の保管場所。
- `terms`
    - IPFSへのリンク、契約書、利用規約。
- `jurisdiction`
    - 利用規約に基づく法的管轄。
- `declaredValue`
    - トークン発行時の宣言された価値。
    - `Amount` 構造体を使用。

### `Amount`

```js
struct Amount {
    string currency;
    uint256 value;
}
```

物理的資産の価値を表現する構造体。

- `currency`
    - 金額の通貨。
- `value`
    - 金額の値。


### `PropertiesSet`

```js
event PropertiesSet(uint256 indexed tokenId, Properties properties);
```

トークンのプロパティが設定されたときに発行されるイベント。

- `tokenId`
    - トークンのID。
- `properties`
    - トークンのプロパティ。

### `PropertiesRemoved`

```js
event PropertiesRemoved(uint256 indexed tokenId);
```

トークンのプロパティが削除されたときに発行されるイベント。

- `tokenId`
    - トークンのID。

  このイベントは、トークンのプロパティが削除されたときに発行されます。


### `getProperties`

```js
function getProperties(uint256 tokenId) external view returns (Properties memory properties);
```

指定された `tokenId` のトークンのプロパティを取得する関数。
一意の`properties`情報を返します。

- `tokenId`
    - ミントされたトークンのID。
- 戻り値
    - トークンのプロパティ。

### `setProperties`

```js
function setProperties(uint256 tokenId, Properties calldata properties) external;
```

指定された `tokenId` のトークンのプロパティを設定する関数。

- `tokenId`
    - トークンのID。
- `properties`
    - トークンのプロパティ。

## その他

- `Properties`はトークンをミントする前に設定する必要があります。
- `tokenId`は0より大きい必要があります。
- `terms`パラメータは、IPFSに保存されているドキュメントへのリンクである必要があります。
- トークンに`properties`が設定されていない時、ミントは失敗（`revert`）します。
- トークンに有効な`properties`がある時、トークンの`burn`時には`properties`を削除する必要があります。

# 補足

- `tokenId`は`0`より大きい必要があるため、ミント前に`properties`を確認できます。
- `properties`がミント前に設定され、`burn`後に削除されるかをチェックするために`_update`関数は上書きされます。
- `terms`パラメータは、IPFSに格納されているドキュメントへのHTTPリンクです。
    - ドキュメントが不変であり、NFT保有者が検証できることを保証するために設定します。
- 宣言されたデータの正確性はトークン発行者の責任であると考えているため、プロパティではコントラクトレベルの検証は行われません。
    - これにより、トークン発行者とトークン自体に対する信頼が構築されます。


# 互換性

この規格は**ERC721**と互換性があります。

# 実装

```js
pragma solidity ^0.8.21;

import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { IERC7578, Properties, Amount } from "./interfaces/IERC7578.sol";

/**
 * @title ERC7578
 * @author DESAT
 * @notice Implementation of the ERC-7578: Physical Asset Redemption standard
 **/
contract ERC7578 is ERC721, IERC7578 {
    /**
     * @notice Thrown when the properties of a token are not initialized
     */
    error PropertiesUninitialized();

    /**
     * @notice Retrieves the properties of the `tokenId` token
     */
    mapping(uint256 tokenId => Properties) private _properties;

    /**
     * @notice Initializes the [ERC-721](/EIPS/eip-721) dependency contract by setting a `name` and a `symbol` to the token collection
     */
    constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {}

    /**
     * @inheritdoc IERC7578
     */
    function setProperties(uint256 tokenId, Properties calldata properties) public {
        _properties[tokenId] = Properties({
            tokenIssuer: properties.tokenIssuer,
            assetHolder: properties.assetHolder,
            storedLocation: properties.storedLocation,
            terms: properties.terms,
            jurisdiction: properties.jurisdiction,
            declaredValue: Amount({
                currency: properties.declaredValue.currency,
                value: properties.declaredValue.value
            })
        });

        emit PropertiesSet(tokenId, _properties[tokenId]);
    }

    /**
     * @inheritdoc IERC7578
     */
    function getProperties(uint256 tokenId) public view override returns (Properties memory properties) {
        properties = _properties[tokenId];
    }

    /**
     * @notice Removes the properties of the `tokenId` token
     * @param tokenId The ID of the token from which to remove the properties
     */
    function _removeProperties(uint256 tokenId) internal {
        delete _properties[tokenId];
        emit PropertiesRemoved(tokenId);
    }

    /**
     * @notice Override of the {_update} function to remove the properties of the `tokenId` token or
     * to check if they are set before minting
     * @param tokenId The ID of the token being minted or burned
     */
    function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
        address from = _ownerOf(tokenId);
        if (to == address(0)) {
            _removeProperties(tokenId);
        } else if (from == address(0)) {
            if (bytes(_properties[tokenId].tokenIssuer).length == 0) revert PropertiesUninitialized();
        }

        return super._update(to, tokenId, auth);
    }
}
```

# セキュリティ

トークンのプロパティの真正性を保証するために、`setProperties`メソッドは信頼されEOAアカウントかコントラクトによってのみ呼び出されるべきです。
この信頼されたエンティティは、プロパティがトークンに紐づく物理資産を正確に反映していることを検証する必要があります。

# 引用

Lee Vidor (@V1d0r) <vidor@emergentx.org>, David Tan <david@emergentx.org>, Lee Smith <lee@emergentx.org>, Gabriel Stoica (@gabrielstoica) <gabriel@emergentx.org>, "ERC-7578: Physical Asset Redemption [DRAFT]," Ethereum Improvement Proposals, no. 7578, August 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7578.

# 最後に

今回は「物理資産に裏付けられたトークンに、物理資産に関連する様々な情報を紐付ける仕組みを提案している**ERC7578**」についてまとめてきました！
いかがだったでしょうか？

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