はじめに

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

今回は、1つのERC1155トークンに複数のアセットを持たせ、利用される環境ごとに最適なデータを出力できる仕組みを提案しているERC7603についてまとめていきます!

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

他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。

概要

Multi-Asset Token standardは、ERC1155と互換性のあるトークン標準で、1つのトークンコレクションに対して「複数のアセット(出力)」を持てるようにする仕組みです。 ここで言うアセットは、画像・PDF・3Dモデル・設定情報などの「表示や利用に使うファイル一式」を指します。

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

この標準の中心にある考え方が「コンテキスト依存のデータ出力」です。 これは、同じトークンであっても、アクセスしてきた環境(どのアプリ・どの用途で開いているか)に応じて、最適な形式のアセットを返すという意味です。

例えば以下のように、利用シーンごとに出力を切り替えます。

  • 電子書籍リーダーで開く場合はPDFを表示します。
  • マーケットプレイスで開く場合はPNGやSVG(画像)を表示します。
  • ゲーム内から参照する場合は3Dモデルを取得します。
  • IoTハブ(ネットワーク機器などをまとめて管理する中枢)から参照する場合は、アドレス情報や仕様情報を含むアセットを取得します。

このように、トークンが「どこから開かれたか」という前提(コンテキスト)に合わせて、出すべきデータを切り替えるのがポイントです。

さらに、トークンコレクションは複数のアセットを持てます。 アセットは「優先順位」を付けて並べられ、必要に応じて上位から採用できます。 重要なのは、これらのアセットが以下のように扱われる点です。

  • アセット同士はmime-type(ファイル形式を表す種類情報。例: image/png や application/pdf)が一致している必要はありません。
  • tokenURI(トークンのメタデータの参照先URLやIPFS等の参照)も一致している必要はありません。
  • アセット同士が依存関係を持つ必要もありません(あるアセットが別アセットを前提にする必要はない、という意味です)。
  • アセットは単体で独立した存在というより、「名前空間付きの tokenURI」として扱うべきものです。 つまり「同じコレクションの中で用途別に別のURIを持てるようにする仕組み」と捉えるのが近いです。

動機

ERC1155互換トークンはEthereumエコシステムで広く使われ、用途も多様化しています。 その状況で、1つのトークンコレクションに複数アセットを持たせる標準が整うと、ユーティリティ(使い道)・扱いやすさ・将来への適応力が上がります。

この標準がERC1155を改善する点は、以下の3つです。

改善点 意味
クロスメタバース互換性 複数のゲームやエンジン、環境で同じトークン資産を使いやすくすることです。
マルチメディア出力 同じ内容を、PDFや音声など複数形式で提供できるようにすることです。
メディア冗長化 同じメタデータや参照情報を複数の保存基盤に置き、落ちにくくすることです。

クロスメタバース互換性

ここで言う「クロスメタバース互換性」は、「クロスエンジン互換性」とも呼ばれる内容です。 つまり、ゲームAで使えるアイテムがゲームBでは使えない、といった問題を緩和する方向性です。

具体例として、同じ「コスメアイテム(見た目変更アイテム)」でも、ゲームごとに必要なデータ形式や実装方式が違うため、ゲームA向けのアセットをゲームBにそのまま持って行けないことがあります。

この標準では、同じトークンコレクションに用途別アセットを複数持たせられるため、たとえば次のように整理できます。

  • アセット1 ゲームA向けのコスメアイテムデータ(ゲームAが理解できる形式の一式)。
  • アセット2 ゲームB向けのコスメアイテムデータ(ゲームBが理解できる形式の一式)。
  • アセット3 一般的な閲覧用途向けのアセット。

これはカタログ、マーケットプレイス、ポートフォリオトラッカー(保有NFTやトークン資産を一覧表示するサービス)などで表示するためのもので、見た目の表現、スタイル付きサムネイル、アニメーションデモやトレーラーなどを含む想定です。

こうしておくと、ゲーム開発者は「ユーザーが持っているトークンから、必要なアセットを直接引く」設計がしやすくなります。 従来のように「このゲームではこのアイテムはこういうデータで…」とアプリ側にハードコードするよりも、トークン側にデータの取り出し口を揃えるイメージです。

その結果として、複数の実装や環境に対応しやすくなり、同じトークンの活躍場所を増やせます。

マルチメディア出力

電子書籍トークンを例にすると、同じ内容でも「読む」と「聴く」では適切な形式が違います。

  • 電子書籍リーダーで読み込むならPDF表示が適しています。
  • オーディオブックアプリで読み込むならMP3(音声)形式が適しています。

この標準では、1つのコレクションにPDFやMP3など複数のアセットを持たせ、どのソフトウェアがロードしたかに応じて適切な形式を選べるようにします。

メディア冗長化

現実には、ベストプラクティスを踏まずに急いで発行されたトークンも多く、メタデータが脆弱な場所に置かれていることがあります。 典型例は以下のようなケースです。

  • メタデータがどこかのサーバーに集中して置かれていて、そのサーバーが落ちると参照できなくなる
  • IPFSを使っていても、特定のIPFSゲートウェイ(IPFSの内容をHTTPで取得するための中継サービス)のURLがハードコードされていて、そのゲートウェイが落ちると取得できなくなる

この標準では、同じメタデータと参照画像の組み合わせを「別アセット」として複数の保存基盤に配置できます。 例として、以下のように分散させます。

  • Arweave上に同じメタデータ+画像を置いたアセット
  • Sia上に同じメタデータ+画像を置いたアセット
  • IPFS上に同じメタデータ+画像を置いたアセット

こうしておくと、どれか1つのプロトコルやゲートウェイに問題が起きても、別アセットにフォールバック(代替先へ切り替え)できる可能性が上がります。 結果として、メタデータと参照情報の耐障害性が大きく高まります。 全てが同時に落ちる確率は相対的に低くなるためです。

仕様

ERC7603は、ERC1155と組み合わせて使うことを前提とした「コンテキスト依存マルチアセットトークン」のインターフェースです。 1つのトークンに対して複数のアセットを紐づけ、それらを優先順位付きで管理・取得できる点が特徴です。

IERC7603.sol
/// @title ERC-7603 Context-Dependent Multi-Asset Tokens, ERC-1155 Execution
/// @dev See https://eips.ethereum.org/EIPS/erc-7603

pragma solidity ^0.8.23;

interface IERC7603 /* is ERC165 */ {
    /**
     * @notice Used to notify listeners that an asset object is initialised at `assetId`.
     * @param assetId ID of the asset that was initialised
     */
    event AssetSet(uint64 assetId);

    /**
     * @notice Used to notify listeners that an asset object at `assetId` is added to token's asset
     *  array.
     * @param tokenId An ID of the token that received a new asset
     * @param assetId ID of the asset that has been added to the token's assets array
     * @param replacesId ID of the asset that would be replaced
     */
    event AssetAddedToToken(
        uint256[] tokenId,
        uint64 indexed assetId,
        uint64 indexed replacesId
    );

    /**
     * @notice Used to notify listeners that token's priority array is reordered.
     * @param tokenId ID of the token that had the asset priority array updated
     */
    event AssetPrioritySet(uint256 indexed tokenId);

    /**
     * @notice Sets a new priority array for a given token.
     * @dev The priority array is a non-sequential list of `uint16`s, where the lowest value is considered highest
     *  priority.
     * @dev Value `0` of a priority is a special case equivalent to uninitialised.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     *  - The length of `priorities` must be equal the length of the assets array.
     * @dev Emits a {AssetPrioritySet} event.
     * @param tokenId ID of the token to set the priorities for
     * @param priorities An array of priorities of assets. The succession of items in the priorities array
     *  matches that of the succession of items in the array
     */
    function setPriority(uint256 tokenId, uint64[] calldata priorities)
        external;

    /**
     * @notice Used to retrieve IDs of assets of given token.
     * @dev Asset data is stored by reference, in order to access the data corresponding to the ID, call
     *  `getAssetMetadata(tokenId, assetId)`.
     * @dev You can safely get 10k
     * @param tokenId ID of the token to retrieve the IDs of the assets
     * @return uint64[] An array of the asset IDs of the given token
     */
    function getAssets(uint256 tokenId)
        external
        view
        returns (uint64[] memory);

    /**
     * @notice Used to retrieve the priorities of the assets of a given token.
     * @dev Asset priorities are a non-sequential array of uint16 values with an array size equal to asset
     *  priorites.
     * @param tokenId ID of the token for which to retrieve the priorities of the assets
     * @return uint16[] An array of priorities of the assets of the given token
     */
    function getAssetPriorities(uint256 tokenId)
        external
        view
        returns (uint64[] memory);

    /**
     * @notice Used to fetch the asset metadata of the specified token's asset with the given index.
     * @dev Can be overridden to implement enumerate, fallback or other custom logic.
     * @param tokenId ID of the token from which to retrieve the asset metadata
     * @param assetId Asset Id, must be in the assets array
     * @return string The metadata of the asset belonging to the specified index in the token's assets array
     */
    function getAssetMetadata(uint256 tokenId, uint64 assetId)
        external
        view
        returns (string memory);
}

イベント

AssetSet
javascript
event AssetSet(uint64 assetId);

アセットが新しく初期化されたときに発行されるイベント。 新しいアセット定義がコントラクト内に作成されたことを外部に通知します。 マーケットプレイスやインデクサは、このイベントを監視することで、どのアセットIDが新しく利用可能になったかを把握できます。

パラメータ

  • assetId
    • 初期化されたアセットのID。
AssetAddedToToken
javascript
event AssetAddedToToken(
    uint256[] tokenId,
    uint64 indexed assetId,
    uint64 indexed replacesId
);

トークンに新しいアセットが追加されたときに発行されるイベント。 特定のトークンに対してアセットが紐づけられたことを示します。 replacesIdを指定することで、既存アセットを置き換える形での更新も表現できます。 これにより、アセットの差し替え履歴や更新フローをイベントレベルで追跡できます。

パラメータ

  • tokenId
    • 新しいアセットが追加されたトークンIDの配列。
  • assetId
    • 追加されたアセットのID。
  • replacesId
    • 置き換え対象となる既存アセットのID。
    • 置き換えがない場合は0などの値が使われます。
AssetPrioritySet
javascript
event AssetPrioritySet(uint256 indexed tokenId);

トークンに紐づくアセットの優先順位が変更されたときに発行されるイベント。 アセットの並び順や優先度が更新されたことを示します。 表示ロジックや取得ロジックを持つ外部アプリケーションは、このイベントを契機にキャッシュを更新できます。

パラメータ

  • tokenId
    • 優先順位が更新されたトークンのID。

関数

setPriority
javascript
function setPriority(uint256 tokenId, uint64[] calldata priorities)
    external;

指定したトークンに対して、アセットの優先順位配列を設定する関数。 アセットの優先順位は数値で管理され、数値が小さいほど優先度が高くなります。 この配列は連番である必要はなく、純粋に相対的な優先度を表します。 値が0の場合は「未初期化」と同等の扱いになる特別なケースです。 アセット配列の長さと優先順位配列の長さは一致している必要があります。

引数

  • tokenId
    • 優先順位を設定する対象のトークンID。
  • priorities
    • 各アセットに対応する優先順位の配列。
getAssets
javascript
function getAssets(uint256 tokenId)
    external
    view
    returns (uint64[] memory);

指定したトークンに紐づくアセットIDの一覧を取得する関数。 この関数はアセットのIDのみを返します。 実際のメタデータやURIを取得するには、返されたIDを使って別の関数を呼び出します。 アセット情報は参照型として管理されており、IDをキーにしてアクセスする設計です。

引数

  • tokenId
    • アセット一覧を取得したいトークンID。

戻り値

  • uint64[]
    • トークンに紐づくアセットIDの配列。
getAssetPriorities
javascript
function getAssetPriorities(uint256 tokenId)
    external
    view
    returns (uint64[] memory);

指定したトークンに紐づくアセットの優先順位一覧を取得する関数。 返される配列は、アセット配列と同じ順序で並んでいます。 これにより、どのアセットが最優先で使われるべきかをクライアント側で判断できます。

引数

  • tokenId
    • 優先順位を取得したいトークンID。

戻り値

  • uint64[]
    • 各アセットに対応する優先順位の配列。
getAssetMetadata
javascript
function getAssetMetadata(uint256 tokenId, uint64 assetId)
    external
    view
    returns (string memory);

指定したトークンとアセットIDに対応するメタデータを取得する関数。 返される文字列は、一般的にはURI(IPFS、Arweave、HTTPSなど)やメタデータJSONへの参照です。 この関数はオーバーライド可能で、列挙ロジックやフォールバック処理などを実装できます。 コンテキスト依存の出力を実現するための中核となる関数です。

引数

  • tokenId
    • メタデータを取得する対象のトークンID。
  • assetId
    • トークンに紐づいているアセットID。

戻り値

  • string
    • 指定されたアセットのメタデータ文字列。

互換性

ERC1155と互換性があります。

引用

Haru (@haruu8), "ERC-7603: ERC-1155 Multi-Asset extension [DRAFT]," Ethereum Improvement Proposals, no. 7603, January 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7603.

最後に

今回は「1つのERC1155トークンに複数のアセットを持たせ、利用される環境ごとに最適なデータを出力できる仕組みを提案しているERC7603」についてまとめてきました! いかがだったでしょうか?

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

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