はじめに
初めまして。
CryptoGamesというブロックチェーンゲーム企業でエンジニアをしている cardene(かるでね) です!
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。
代表的なゲームはクリプトスペルズというブロックチェーンゲームです。
今回は、NFTにEthereumアカウントかのような振る舞いをさせ、NFTやトークンを保有させることができるトークンバウンドアカウントの実装を提案しているERC6551についてまとめていきます!
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
ERC4337は現在Review段階です。
そのため今後内容が変更される可能性があるのでご注意ください。
概要
非代替トークン(NFT)という特別な種類のデジタルアセットを考えてみましょう。
NFTは、個々のアイテムやアートなどをオンラインで一意に識別するために使用されます。
しかし、これまでNFTは他のアセットやアプリケーションと関わるのが難しかったです。
この提案では、NFTにEthereumアカウントを関連付ける方法を提案しています。
これにより、まるでNFT自身が個別のユーザーのように行動できるようになります。
NFTは資産を所有し、アプリケーションとやり取りすることができますが、既存のスマートコントラクトや仕組みを大幅に変更する必要はありません。
例えば、あるロールプレイングゲームの中のキャラクターを考えてみましょう。
このキャラクターは、遊んでいるうちにさまざまなアイテムや特性を手に入れることがあります。
この提案に従うと、そのキャラクター自体がEthereumアカウントを持つことができ、そのアカウントを通じてアイテムを所有したり、ゲーム内の操作を実行したりできるようになります。
この提案は、すべてのNFTをEthereumユーザーと同じように扱うことを目指しています。
これにより、NFTがさまざまな複雑な現実世界のアセットを表現するための新しい方法が提供されます。
動機
ERC721という規格によって、非代替トークン(NFT)の利用が増えました。
これにより、育成可能な猫や生成されたアート、取引所での流動性ポジションなど、さまざまな面白い使い方が生まれました。
しかしながら、NFTはこれまで自分で動くエージェントのように振る舞ったり、他のオンチェーンのアセットと結びついたりすることが難しかったです。
そのため、現実世界の様々な非代替資産をNFTとして表現するのは難しい状況でした。
例えば、以下のようなケースです。
- 行動に基づいて成長するロールプレイングゲームのキャラクター。
- 代替可能と非代替の部品で構成される自動車。
- 複数の代替可能な資産から成る投資ポートフォリオ。
- エスタブリッシュメントへのアクセスを提供し、過去の相互作用を記録するパンチパスメンバーシップカード。
パンチパスメンバーシップカード
特定の場所やサービスへのアクセスを提供し、その利用履歴を記録するためのカードのことを指します。
例えば、ジムやカフェ、映画館などのエスタブリッシュメント(施設や場所)では、顧客に対して定期的な利用の際にカードを押すことで、サービスを利用できるようになることがあります。
これが「パンチパスメンバーシップカード」です。
カードには通常、一定回数の利用や購入を行うごとに「パンチ」(スタンプや印)が押され、一定数のパンチを集めることで特典や割引が提供されることがあります。
また、カードには過去の利用履歴も記録されるため、顧客がどれくらい利用しているかやどのサービスを利用したかが把握できます。
このようなカードは、ロイヤルティプログラムや顧客のエンゲージメントを促進するために使用されることがあります。
そこで、この提案は全てのNFTに、まるでEthereumのユーザーと同じような権限を持たせることを狙っています。
これには、資産を管理する能力、任意の操作を行う能力、複数の独立したアカウントを制御する能力、そして複数のブロックチェーンでアカウントを使う能力が含まれます。
これにより、現実世界の複雑な資産も、共通の方法でNFTとして表現することができ、既存のEthereumの所有モデルに似たやり方で扱えるようになります。
この目標は、各NFTにユニークで確定的なスマートコントラクトアカウントアドレスを割り当てる「シングルトンレジストリ」というものを定義することで達成されます。
各アカウントは1つのNFTに永久的に紐づけられ、そのNFTを持っている人がアカウントを制御します。
この提案のアプローチは、既存のNFTスマートコントラクトに対して変更を要求しません。
そして、オンチェーンのプロトコルからオフチェーンのインデクサーまで、ほぼ全ての既存のEthereumアカウントをサポートする既存の仕組みと互換性があります。
トークンバウンドアカウントは、全ての既存のオンチェーン資産規格と適合し、将来的な新しい資産規格にも対応できるように拡張可能です。
全てのNFTにEthereumアカウントの完全な機能を提供するこの提案により、既存のNFTだけでなく将来のNFTにも多くの新たな可能性が開かれることとなります。
仕様
この提案に概要を述べると、主に以下の2つの要素から成り立っています。
- トークンバウンドアカウントのためのシングルトンレジストリ。
- トークンバウンドアカウントの実装に共通のインタフェース。
以下の図は、NFT、NFTの保持者、トークンバウンドアカウント、およびレジストリの関係を示しています。
引用: https://eips.ethereum.org/EIPS/eip-6551
この提案では、トークンバウンドアカウントという仕組みを導入しています。
このアカウントはNFTに関連付けられ、NFTがアセットを所有したりアプリケーションとやり取りしたりするためのものです。
また、それらのアカウントの管理や実装方法に関する共通のインタフェースも提供されています。
この提案によって、NFTがより多くの機能や可能性を持つことができるようになります。
レジストリ
この「レジストリ」というのは、1つの場所に、トークンバウンドアカウント(トークンとアカウントを関連付けるアドレス)に関する情報を集約するシステムです。
このシステムには、以下の2つの大きな機能があります。
-
createAccount
トークンバウンドアカウントを作成するための機能です。
具体的には、実装アドレス(どのアカウントを使って動かすかを示すアドレス)を指定することで、そのNFTに関連付けられたアカウントを作成します。 -
account
トークンバウンドアカウントのアドレスを計算するための機能です。
ここでも実装アドレスが使われて、それを基にトークンバウンドアカウントのアドレスを計算します。
このレジストリがトークンバウンドアカウントを作成・管理する際には、ERC1167という規格の最小プロキシという技術を使用します。
最小プロキシには、以下のデータが含まれています。
ERC-1167 Header (10 bytes)
<implementation (address)> (20 bytes)
ERC-1167 Footer (15 bytes)
<salt (uint256)> (32 bytes)
<chainId (uint256)> (32 bytes)
<tokenContract (address)> (32 bytes)
<tokenId (uint256)> (32 bytes)
このデータが含まれた最小プロキシが、それぞれのトークンバウンドアカウントとして展開されます。
つまり、NFTごとに専用のアカウントが作られ、必要な情報が保持された形で動作することが可能となります。
これにより、トークンバウンドアカウントがNFTと連携し、トークンに関する情報を効果的に管理できるようになります。
具体例を考えてみましょう。以下の条件を持つトークンバウンドアカウントを考えてみます。
- アドレス
0xbebebebebebebebebebebebebebebebebebebebe
- ソルト
0
- チェーンID
1
- トークンコントラクトアドレス
0xcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf
- トークンID
123
Salt
ソルト(Salt
)は、セキュリティやランダム性を向上させるために使用されるランダムな値です。
簡単に言うと、パスワードを保護するために塩を加えるのと同じようなアイディアです。
例えば、同じパスワードを持つ複数のユーザーがいる場合、ハッカーがユーザーパスワードを解析すると、その結果は全て同じになります。
しかし、Salt
を導入することで、同じパスワードでも異なるソルトが加えられるため、ハッカーが解析するのが難しくなります。
このトークンバウンドアカウントのコンテキストでは、Salt
はアカウントアドレスをよりランダム化し、予測不可能にするために使用されます。
Salt
を組み合わせることで、同じ条件でも異なるアカウントアドレスが生成され、セキュリティが向上します。
このトークンバウンドアカウントの最小プロキシの展開されるバイトコードは、以下のようになります。
363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf000000000000000000000000000000000000000000000000000000000000007b
これが、トークンバウンドアカウントの最小プロキシとしての展開されたバイトコードです。
このトークンバウンドアカウントの最小プロキシは、IERC6551Accountインタフェースを実装したコントラクトに処理を委任します。
レジストリコントラクトは、権限なしで変更不可能で、オーナーを持ちません。
レジストリは、アドレスTBDにNick's Factory(0x4e59b44847b379578588920cA78FbF26c0B4956C
)を使用して、ソルト0x6551655165516551655165516551655165516551655165516551655165516551
を使って展開されます。
また、レジストリはcreate2
オペコードを使用して、各アカウントアドレスを決定論的に展開します。
各トークンバウンドアカウントのアドレスは、実装アドレス、トークンコントラクトアドレス、トークンID、EIP155チェーンID、およびソルトなどの要素の組み合わせによって確定します。
create2
イーサリアムのスマートコントラクト展開の方法の1つで、アドレスを決定論的に計算することができる特徴的なオペコードです。
通常、スマートコントラクトを展開する際には、トランザクションがマイニングされ、その結果として新しいアドレスが割り当てられます。
しかし、create2を使うと、あらかじめ特定の条件に基づいてアドレスを計算し、そのアドレスにコントラクトを展開することができます。
create2は以下のような手順で使います。
- 展開したいスマートコントラクトのバイトコードと必要なパラメータを用意します。
- バイトコード、イニシャルソルト(任意のランダムな値)、および計算に使用するハッシュ関数を組み合わせて、展開されるコントラクトのアドレスを計算します。
- 計算されたアドレスにスマートコントラクトを展開するトランザクションを送信します。
この手法により、同じ条件であれば必ず同じアドレスが生成されるため、アドレスの衝突や競合を避けることができます。
また、create2は事前にアドレスを計算できるため、トランザクションのマイニングを待つ必要がなく、効率的なコントラクト展開が可能となります。
簡単に言えば、create2は特定の条件に基づいてアドレスを計算し、そのアドレスにスマートコントラクトを展開する方法です。
これにより、コントラクトのアドレス予測性や展開の効率性が向上します。
レジストリは以下のインターフェイスを実装します。
interface IERC6551Registry {
event AccountCreated(
address account,
address indexed implementation,
uint256 chainId,
address indexed tokenContract,
uint256 indexed tokenId,
uint256 salt
);
function createAccount(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 seed,
bytes calldata initData
) external returns (address);
function account(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
) external view returns (address);
}
AccountCreated
概要
`アカウントが正常に作成されたときに発行されるイベント。
パラメータ
-
account
- アカウントのアドレス。
-
implementation
- 実装アドレス。
-
chainId
- チェーンID。
-
tokenContract
- トークンコントラクトのアドレス。
-
tokenId
- トークンID。
-
salt
- ソルト(ランダムな値)。
createAccount
概要
非代替トークン(NFT)に対してトークンバウンドアカウントを作成する関数。
詳細
- アカウントが既に作成されている場合、
create2
を呼び出さずにアカウントアドレスを返します。 -
initData
が空でなく、まだアカウントが作成されていない場合、作成後に提供されたinitData
を使用してaccount
を呼び出します。 - アカウントが作成されると、
AccountCreated
イベントが発生します。
引数
-
implementation
- 実装アドレス。
-
chainId
- チェーンID。
-
tokenContract
- トークンコントラクトのアドレス。
-
tokenId
- トークンID。
-
seed
- シード(ランダムな値)。
-
initData
- 初期データ。
戻り値
- アカウントのアドレス
account
概要
NFTに対して計算されたトークンバウンドアカウントのアドレスを返す関数。
詳細
- 計算されたアカウントアドレスを返します。
- 計算には、実装アドレス、チェーンID、トークンコントラクトのアドレス、トークンID、ソルトが使用されます。
引数
-
implementation
- 実装アドレス。
-
chainId
- チェーンID。
-
tokenContract
- トークンコントラクトのアドレス。
-
tokenId
- トークンID。
-
salt
- ソルト(ランダムな値)。
戻り値
- 計算されたアカウントのアドレス。
アカウントインターフェース
ERC165インタフェース検出の実装
トークンバウンドアカウントの実装は、ERC165インタフェース検出を実装する必要があります。
ERC1271 シグネチャ検証の実装
トークンバウンドアカウントの実装は、ERC1271シグネチャ検証を実装する必要があります。
以下のインタフェースの実装
トークンバウンドアカウントの実装は、指定されたインタフェースを実装する必要があります。
interface IERC6551Account {
receive() external payable;
function token()
external
view
returns (
uint256 chainId,
address tokenContract,
uint256 tokenId
);
function state() external view returns (uint256);
function isValidSigner(address signer, bytes calldata context)
external
view
returns (bytes4 magicValue);
}
receive
概要
アカウントがEtherを受け取るための関数。
詳細
- アカウントはこの関数を実装することで、他のアドレスから送信されたEtherを受け取ることができます。
- Etherの受け取りに関する条件を制御するために任意のロジックを実装することも可能です。
token
概要
アカウントが所有する非代替トークンの識別子を返す関数。
アカウントが所有するトークンの詳細情報を返します。
戻り値
-
uint256
- EIP-155チェーンID。
-
address
- トークンコントラクトのアドレス。
-
uint256
- トークンのID。
state
概要
アカウントの状態が変更されるたびに変更される値を返す関数。
詳細
- アカウントの現在の状態を示す値を取得できます。
- アカウントの状態が変更されるたびに、この値が変更されるようになっています。
戻り値
-
uint256
- アカウントの状態を示す値。
isValidSigner
概要
特定の署名者がアカウントの代理として承認されているかどうかを示すマジックバリューを返す関数。
詳細
- 特定の署名者がアカウントの代理として有効かどうかを確認できます。
- 有効な場合、特定のマジックバリュー(
0x523e3260
)が返されます。 - デフォルトでは、アカウントがバインドされている非代替トークンの保持者は有効な署名者とみなされますが、アカウントは追加の承認ロジックを実装して、保持者以外のアカウントに署名権限を付与したり、無効にしたりすることも可能です。
引数
-
signer
- 署名者のアドレス。
-
context
- 署名者が有効かどうかを判断するための追加データ。
戻り値
署名者が有効な場合はマジックバリュー(0x523e3260
)を返します。
実行インタフェース
トークンバウンドアカウントは、有効な署名者がアカウントの代理として任意の操作を実行できるようにする「実行インタフェース」を実装する必要があります。
この実行インタフェースのサポートは、アカウントがERC165インタフェース検出を使用して示す必要があります。
トークンバウンドアカウントは、以下のような実行インタフェースをサポートすることができます。
/// @dev the ERC-165 identifier for this interface is `0x74420f4c`
interface IERC6551Executable {
/**
* @dev Executes a low-level operation if the caller is a valid signer on the account
*
* Reverts and bubbles up error if operation fails
*
* @param to The target address of the operation
* @param value The Ether value to be sent to the target
* @param data The encoded operation calldata
* @param operation A value indicating the type of operation to perform
*
* Accounts implementing this interface MUST accept the following operation parameter values:
* - 0 = CALL
* - 1 = DELEGATECALL
* - 2 = CREATE
* - 3 = CREATE2
*
* Accounts implementing this interface MAY support additional operations or restrict a signer's
* ability to execute certain operations
*
* @return The result of the operation
*/
function execute(
address to,
uint256 value,
bytes calldata data,
uint256 operation
) external payable returns (bytes memory);
}
execute
概要
有効な署名者の場合、呼び出し元がアカウント上で低レベルの操作を実行します。
詳細
- アカウントの有効な署名者である場合に、低レベルの操作を実行します。
- 操作が失敗した場合、リバートしてエラーを伝えます。
引数:
-
to
- 操作の対象アドレス。
-
value
- 対象に送信するEtherの値。
-
data
- エンコードされた操作のcalldata。
-
operation
- 実行する操作の種類を示す値。
パラメータ:
このインタフェースを実装するアカウントは、次の操作パラメータの値を受け入れる必要があります。
0 = CALL
1 = DELEGATECALL
2 = CREATE
3 = CREATE2
また、アカウントは追加の操作をサポートするか、署名者の特定の操作の実行能力を制限することがあります。
戻り値
操作の結果を返します。
低レベルの操作を実行する
例えば、あなたがあるトークンバウンドアカウントの有効な署名者であり、そのアカウントにはEtherが保管されているとします。
このアカウントは、ERC20トークンを別のアドレスに送信する操作を実行したいと考えているとします。
この場合、execute
関数を使用して、低レベルの操作を実行することができます。
以下はその具体的な手順です。
-
to
にトークンの送信先のアドレスを指定します。 -
value
に送信するEtherの値を指定します。
この場合、Etherは必要ありませんので、0
を指定します。 -
data
トークンの送信操作を指定するデータをエンコードします。
このデータはERC20トークンの送信関数とその引数です。 -
operation
トークン送信操作を示す値を指定します。
ERC20トークン送信は一般的にCALL
操作になります(operation = 0
)。
アカウントがこの操作を受け入れている場合、execute
関数を呼び出すことで、有効な署名者がトークンを送信できるようになります。
アカウントはトークン送信操作の詳細なデータを受け取り、それに基づいてトークンの送信を行います。
操作が成功すると、トークン送信の結果が返されます。
このように、execute
関数はアカウントの有効な署名者によって指定された操作を実行するための方法を提供します。
戻り値
トークン送信操作が成功したかどうかや、必要な情報が含まれています。
具体的な戻り値の内容は使用するトークンの実装や操作によって異なりますが、一般的にはトランザクションのハッシュや成功のフラグなどが含まれることがあります。
補足
シングルレジストリ
理論的背景(Rationale)
この提案では、シングルトンレジストリという概念が導入されています。
これは、どのチェーンでも既知のアドレスに対して許可なく展開できる、単一で共通のレジストリを定義することを意味します。
複数の異なるレジストリコントラクトを作成するのではなく、1つのレジストリを使うアプローチを取ることで、以下の重要な特性が実現されます。
カウンターファクトアルな(予測可能な)アカウント
トークンバウンドアカウントは、create2
オペコードを使用して作成されます。
このため、アカウントは実際に作成される前に、予測可能な状態で存在できます。
つまり、コントラクトが作成される前に、アカウントはアセットを受け取ることができます。
シングルトンアカウントレジストリが存在することで、全てのトークンバウンドアカウントアドレスに共通のアドレッシングスキームが確立されます。
信頼性のある展開
シングルトンのオーナー不在のレジストリは、アカウントの所有者が信頼できるのはアカウントの実装のみであることを確保します。
これは、アカウントの実装が特定のアクションを実行するためのコードとロジックを提供するものです。
アカウントの実装は、アカウント内の操作や資産の管理を担当します。
例えば、トークンの保持者があるNFT(非代替可能トークン)を所有しているとします。
このNFTは特定のトークンバウンドアカウントに関連付けられており、そのアカウント内には資産が保管されています。
シングルトンのオーナー不在のレジストリにより、このアカウントの実装は信頼されるものとされ、そのアカウント内のすべての操作はこの実装によって制御されることが保証されます。
トークンの保持者は、信頼されたアカウント実装を使用して、アカウント内の資産や操作を安全に管理できます。
アカウント実装はカウンターファクトアル(予測可能な)な状態で存在することができるため、アセットを保管するためのアカウントが実際に作成される前にも資産の受け渡しや操作が可能です。
これにより、トークンの保持者は安全かつ確実に資産にアクセスし、トークンバウンドアカウント内の機能を利用することができます。
クロスチェーンの互換性
既知のアドレスを持つシングルトンレジストリにより、各トークンバウンドアカウントは複数のチェーン上で存在できるようになります。
createAccount
のパラメータにchainId
を追加することで、同じアドレスにトークンバウンドアカウントのコントラクトを任意のチェーン上で展開できます。
アカウントの実装は、異なるチェーン上のNFTが別のチェーン上のトークンバウンドアカウントを制御するクロスチェーンのアカウント実行をサポートできます。
単一のエントリーポイント
アカウントアドレスとAccountCreated
イベントをクエリするための単一のエントリーポイントとは、アプリケーション内でアカウントのアドレスや作成イベントを簡単に取得できる方法のことです。
このエントリーポイントが提供されることで、トークンバウンドアカウントに関連する情報を取得するプロセスが効率的になります。
具体的には、この提案をサポートするアプリケーションでは、アカウントのアドレスや作成イベントを検索・表示するために複雑なインデックスタスクを行う必要がありません。
単一のエントリーポイントを使用することで、アプリケーションは簡単な方法で特定のNFTに関連するアカウントのアドレスや作成イベントを取得できるようになります。
これにより、ユーザーは迅速にアカウントの情報を取得し、管理することができます。
複雑なクエリやデータベース操作を行う必要がないため、アプリケーションの使い勝手が向上し、ユーザーエクスペリエンスが向上します。
結果として、トークンバウンドアカウントに関連する情報の取得と管理が簡素化され、効率的に行えるようになります。
実装の多様性
シングルトンレジストリの存在は、異なる種類のアカウント実装が共通の方法でアドレスを生成・管理できるようにします。
このことにより、開発者はさまざまなアカウント機能やモデルを、クライアントアプリケーション内で容易にサポートできる環境を提供します。
例えば、アカウント固有の機能(委任など)や代替的なアカウントモデル(一時的なアカウントなど)を考えてみましょう。
これらの機能やモデルは、アカウントの特性に合わせて異なる方法で実装されることがあります。
しかし、シングルトンレジストリが共通のアドレッシングスキームを提供することで、異なる実装でも同じようなアドレスを生成し、アカウントを一元的に管理できるようになります。
これにより、開発者は柔軟性を持ってアカウントの機能や特性を設計・実装できます。
例えば、特定のアカウントが委任機能を持つ場合でも、それを同じアドレススキームで運用することができます。
同様に、アカウントが一時的なものであっても、共通のアドレススキームで管理できるため、アプリケーション内での処理が簡略化されます。
要するに、シングルトンレジストリは、異なるアカウント実装が共通の基盤を共有することを可能にし、開発者にアカウントの設計と実装に対する自由度を提供します。
これにより、多様なアカウント機能やモデルを簡単に統合・サポートできる環境を創出します。
レジストリ vs ファクトリ
Factoryではなく、Registryという用語が採用された理由は、特定のアカウントの作成(アカウントごとに一度だけ行われる)よりも、アカウントアドレスの検索クエリ(頻繁に行われる)に焦点を当てるためです。
つまり、この提案ではアカウントの作成よりも、既存のアカウントアドレスを照会することがより重要な役割を果たすということを示しています。
可変的な実行インターフェース
この提案では、異なるアカウントが互換性を持つために特定の実行インターフェースを実装する必要ありません。
代わりに、少なくとも1つの実行インターフェースをERC165インターフェース検出を通じてサポートすることで、アカウントはこの提案と互換性を持つことができます。
つまり、アカウント開発者は自身のアカウントに適切な実行インターフェースを選択する自由があります。
これにより、既存の多様な実行インターフェースをサポートするだけでなく、将来的に標準化される可能性のある新しいインターフェースとも連携可能です。
このアプローチにより、アカウント開発者は自身のニーズやアプリケーションの要件に合わせて柔軟な実行インターフェースを選択できるため、より多くの開発者がこの提案を活用できるようになります。
また、将来的な変更や新しい標準にも対応しやすく、システム全体の進化に柔軟に適応することができます。
アカウントの曖昧さ
この提案は、NFTが複数のトークンバウンドアカウントを持つことができるように設計されています。
しかし、開発の過程で、各NFTに対して1つのトークンバウンドアカウントを割り当てる代替案も考えられました。
この代替案では、各トークンバウンドアカウントアドレスが一意の識別子となりました。
ただし、代替案にはいくつかの検討すべき点があります。
まず第1に、スマートコントラクトの性質上、1つのNFTごとに1つのトークンバウンドアカウントを強制することはできません。
複数のトークンバウンドアカウントを1つのNFTに関連付けたい場合、別のレジストリコントラクトを展開することで実現できます。
第2に、各NFTを1つのトークンバウンドアカウントに制限することは、特定の静的で信頼性のあるアカウント実装を提供する必要があります。
しかしこのアプローチは、トークンバウンドアカウントの機能に制約を課す可能性があります。
提案が可能にする多くのユースケースや、異なるアカウント実装が非代替性トークンエコシステムにもたらす可能性を考慮すると、この段階で制約を含む標準的な実装を定義することは早計です。
最後に、この提案はNFTにチェーン上で活動する機能を与えようとしています。
現実の場面では、チェーン上で活動するエージェントは通常、複数のアカウントを使用します。
例えば、日常的な活動には「ホット」アカウントを使用し、価値のあるものを保管するために「コールド」アカウントを使用する個人がいます。
チェーン上のエージェントが複数のアカウントを使用するのが一般的な場合、同じようにNFTも複数のアカウントを利用できるようにするのが理にかなっています。
プロキシの実装
既存のシステムで、ERC1167ミニマルプロキシはよく使われており、一般的なスマートコントラクトのパターンです。
この提案では、各トークンバウンドアカウントを、特別なERC1167プロキシを使って作成します。
このプロキシ実装は、ソルト(ランダムな値)、チェーンID、トークンコントラクトのアドレス、そしてトークンIDを、ABIエンコード(特定のデータ形式に変換する方法)された定数データとして、コントラクトのバイトコードに追加します。
このアプローチによって、トークンバウンドアカウントの実装は簡単にこれらのデータをクエリできるようになります。
同時に、これらのデータは常に一定のままであることが保証されます。
この方法は、既存のインフラストラクチャとの互換性を最大限に確保しつつ、スマートコントラクト開発者には、カスタムのトークンバウンドアカウント実装を作成する際に十分な柔軟性を提供するために採用されました。
つまり、異なるトークンバウンドアカウントの実装を簡単に作成でき、その実装が他のシステムと適切に連携することができるということです。
EIP155サポート
この提案では、EIP155チェーンIDを使用して、各NFTをそのコントラクトアドレスとトークンIDとともに識別します。
トークン識別子は単一のEthereumチェーン上でグローバルにユニークですが、複数のEthereumチェーン間ではユニークであるとは限りません。
後方互換性
この提案は、既存の非代替性トークンコントラクトと最大限に後方互換性を持つことを目指しています。
そのため、ERC721標準を拡張することはありません。
さらに、この提案では、アカウントの作成前にERC165インターフェースのチェックをレジストリに要求しません。
これにより、ERC721標準より前に存在した非代替性トークンコントラクト(例: CryptoKitties)や、ERC721インターフェースの一部のみを実装しているトークンコントラクト(例: ENS NameWrapper names)との互換性が最大限に保たれます。
また、この提案で説明されているシステムは、セミ代替性トークンや代替性トークンにも使用できますが、これらのユースケースは提案の範囲外です。
スマートコントラクトの作者は、自身のアカウント実装でERC721のインターフェース検出を強制的に行うことを選択できます。
参考実装
アカウント実装例
引用: https://eips.ethereum.org/EIPS/eip-6551
レジストリ実装
引用: https://eips.ethereum.org/EIPS/eip-6551
セキュリティの考慮事項
詐欺防止
トークンバウンドアカウントの信頼性のない売買を可能にするために、分散型マーケットプレイスは、悪意のあるアカウント所有者による詐欺行為に対する保護策を実装する必要があります。
次の潜在的な詐欺行為を考えてみましょう:
- AliceはERC721トークンXを所有しており、トークンバウンドアカウントYを所有しています。
-
AliceはアカウントYに
10
ETHを預け入れます。 -
Bobは分散型マーケットプレイスを介して、トークンXを
11
ETHで購入するオファーを出し、トークンバウンドアカウントに保管されている10
ETHもトークンと一緒に受け取ると仮定します。 -
Aliceはトークンバウンドアカウントから
10
ETHを引き出し、すぐにBobのオファーを受け入れます。 - BobはトークンXを受け取りますが、アカウントYは空です。
悪意のあるアカウント所有者による詐欺行為を軽減するために、分散型マーケットプレイスは、このような詐欺行為に対する保護策をマーケットプレイスのレベルで実装するべきです。
このEIPを実装するコントラクトは、詐欺行為に対する一定の保護策も実装することができます。
以下は、考慮すべきいくつかの詐欺対策の戦略です。
1. マーケットプレイスのオーダーに現在のアカウントの状態を添付する
トークンバウンドアカウントの状態を、分散型マーケットプレイスでのオーダーに含めます。
オーダーが配置された後、アカウントの状態が変更された場合、オファーは無効とみなされます。
これにより、オーダーがアカウントの状態を確認して、詐欺行為を防ぐことができます。
2. オーダーに資産コミットメントのリストを添付する
オーダーに、トークンバウンドアカウントに保管されている資産が達成されたときに残る予定の資産コミットメントのリストを追加します。
オーダーが配置された後、オーダーの成立時にコミットされた資産のうち、アカウントから削除されたものがある場合、オファーは無効とされます。
この対策により、オーダーがコミットされた資産の安全性を確保し、詐欺を防ぐことができます。
3. 外部スマートコントラクトを介してオーダーを提出する
オーダーを分散型マーケットプレイスに提出する前に、オーダー署名を検証する外部スマートコントラクトを介してオーダーを処理します。
この外部スマートコントラクトは、アカウントの状態の確認やコミットされた資産の検証を行います。
オーダーが安全かつ正当であることを確認した後、マーケットプレイスにオーダーを提出します。
4. アカウント実装にロッキングメカニズムを実装する
トークンバウンドアカウント実装に、資産が盗まれるのを防ぐためのロッキングメカニズムを導入します。
これにより、悪意のある所有者がアカウントから資産を取り出すことを防ぎ、安全性を高めます。
これらのセキュリティ対策は、詐欺行為を防ぐために重要です。
ただし、詐欺対策はこの提案の範囲外であり、アプリケーションやマーケットプレイスが独自に実装する必要があります。
所有サイクル
所有サイクルとは、あるトークンバウンドアカウントが別のトークンバウンドアカウントを所有し、同時にその所有されているアカウントも最初のアカウントを所有するような状態を指します。
これにより、所有権の循環が発生し、資産にアクセスできなくなるリスクが生じます。
具体的な例を考えてみましょう。
ケース
- ERC721トークンが自身のトークンバウンドアカウントに転送される。
- AliceがERC721トークンXを所有し、トークンバウンドアカウントYも所有しているとします。
- AliceがERC721トークンXをトークンバウンドアカウントYに転送するトランザクションを実行します。
- しかし、トークンバウンドアカウントYはERC721トークンXの転送トランザクションを実行することができないため、トークンXとトークンバウンドアカウントY内のすべての資産がアクセスできなくなります。
所有サイクルの可能性
1つのトークンバウンドアカウントが別のトークンバウンドアカウントを所有し、同時にその所有されているアカウントも最初のアカウントを所有する場合、所有サイクルが発生します。
このような所有サイクルは、複数のトークンバウンドアカウントが相互に所有し合う場合にも発生する可能性があります。
所有サイクルの影響
所有サイクルが存在する場合、アカウント内に保管されているすべての資産はアクセスできなくなります。
なぜなら、所有サイクルにより資産の転送トランザクションがブロックされるからです。
この結果、資産が取り出せず、資産が永久にロックされてしまいます。
所有サイクルの制御
所有サイクルは深さが1
以上のグラフで発生する可能性があり、防止するためには膨大な計算が必要です。
この提案の範囲外であり、アプリケーションクライアントやアカウント実装が所有サイクルのリスクを制限するための措置を講じることが推奨されます。
つまり、所有サイクルには資産のアクセス制限が関わっており、それを避けるために対策を講じる必要があります。
引用
Jayden Windle (@jaydenwindle), Benny Giang bg@futureprimitive.xyz, Steve Jang, Druzy Downs (@druzydowns), Raymond Huynh (@huynhr), Alanah Lam alanah@futureprimitive.xyz, Wilkins Chung (@wwhchung) wilkins@manifold.xyz, Paul Sullivan (@sullivph) paul.sullivan@manifold.xyz, Auryn Macmillan (@auryn-macmillan), Jan-Felix Schwarz (@jfschwarz), Anton Bukov (@k06a), Mikhail Melnik (@ZumZoom), Josh Weintraub (@jhweintraub) josh@revest.finance, Rob Montgomery (@RobAnon) rob@revest.finance, "ERC-6551: Non-fungible Token Bound Accounts [DRAFT]," Ethereum Improvement Proposals, no. 6551, February 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6551.
考察
NFTにスマートコントラクトアカウントを与えることで、NFTやトークンの所有などまるでウォレットのような振る舞いができると注目を集めているERC6551ですが、後半のセキュリティの章でも述べられているようにまだまだ課題点が残っています。
新しい技術だからこそまだ課題はありつつも、活用の幅は広い規格だと思っています。
また、まだERC自体がレビュー段階のため、ファイナルになるまでは様子を見ながら実装するのが安全だと考えます。
以下の記事などを参考にしながら色々実装してみても面白そうです!
最後に
今回は「NFTにEthereumアカウントかのような振る舞いをさせ、NFTやトークンを保有させることができるトークンバウンドアカウントの実装を提案しているERC6551」についてまとめてきました!
いかがだったでしょうか?
質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!