ERC-7303:トークンによるトークンの流通制御
はじめに
この記事はERC-7303:Token-Controlled Token Circulation (TCTC)について説明し、特に、どのようなケースに応用できるかについて述べます。
ERC-721やERC-1155などのトークンの流通には、発行(mint/issue)、譲渡(transfer)、および焼却(burn)の3つの主要なトランザクションがあります。これらのトランザクションを実行するには、アプリケーションに応じてさまざまな条件を満たす必要があります。例えば、認定された店舗だけがトークンを発行でき、特定の代理店だけが譲渡が許されるなどです。ERC-7303は、このようなトランザクションの権限を制御するために、(別の)制御トークン(control token)を使用します。制御トークンとは、ERC-721やERC-1155などのトークンであり、取引参加者によってこれらの指定されたトークンを所有している場合にのみ、ターゲットとなるトークンを発行、譲渡、または焼却可能となるように制御するものです。これらの制御トークンは、アプリケーションによって、社員証や会員証など、様々なトークンが指定されます。そして、それらの制御トークン自体も同じくERC-7303によって再帰的に流通を制御することができます。
従来、トランザクションのアクセス制御を実現するには、ERC-5982:Role-based Access Controlなどを使用して指定されたインターフェースを通じて、アドレスに対して権限を付与します。しかし、これを実現するオフチェーン管理ツールなどの開発が必要となります。しかし、そのような役割をトークンとして表現することで、MetaMaskやetherscanなどの汎用的なシステムで、制御トークンを発行することで、権限を付与できるようになります。
また、ERC-7303によって権限をトークンとして発行することで、ユーザにとってもウォレット上で自分の権限が可視化され、管理がしやすくなります。例えば、以下はMyTokenの発行権限をMiner Certificateとして発行した例です。MetaMaskなどのウォレット上で自分の持つ権限が確認できるようになります。
ユースケース
誰がどの関数を実行できるかを制御する「アクセス制御」は、DAOやDeFiを含むスマートコントラクトの文脈では非常に重要です。したがって、TCTCのユースケースは広く、以下に示すものに限定されませんが、いくつかの典型的な例を示します。
ケース1: 発行許可
これは最も単純なケースです。企業がMyTokenを顧客に配布したい状況を考えてみましょう。MyTokenは任意のトークンですが、ここでは、コンテンツ配信サーバーでコンテンツを視聴するためのチケットであると仮定します。企業にいくつかの支店がある場合、本社はこれらの支店に発行権限を付与したいかもしれません。これは、各支店に制御トークンの形でMinter Certificateを発行することで実現できます。支店はその後、この場合はMyTokenというトークンを、自分たちのウェブサイトを通じて顧客にそれを発行します。
ケース2: 譲渡許可
次に、譲渡権限の使用例を考えてみましょう。前のケースと同様に、ある企業が顧客にMyTokenを配布したいと考えている状況を考えます。ただし、このシナリオでは、生成されるトークンの数は本社によって制御される必要があります。本社は支店にトークン生成権限を付与したくない場合も考えられます。代わりに、譲渡権限がこれらの支店に付与されます。
このように、ビジネスモデルに応じて、トークンの流通を柔軟に制御することができます。ちなみに、誰にも転送権限が付与されていない場合、このトークンは転送不可能なトークン(Soulbound token)となります。
ケース3: アドレス証明書
多くのアプリケーションでは、ターゲットトークンを生成(minting)または転送する際に受信者のアドレスに誤りがないように、アドレスの確認が必要です。このような状況で有用なのがアドレス証明書または保有者証明書です。これは、ターゲットトークンの取引を行う前にユーザーに対して発行される、アドレス確認の証拠となります。通常、この証明書は、身元確認プロセスの後に政府機関や特定の企業によって発行される場合があります。
このアドレス証明書は、トークンの生成や転送取引が実行される際に、受信者によって必要とされます。これにより、誤配送を防ぐことができます。
ERC-7303の使い方
ERC-7303に掲載されているリファレンス実装は、以下の通りで非常に短いコードです。これを継承することで、TCTCによるアクセス制御が簡単に実現できます。
使用例
使用方法は非常に簡単です。定義したい各ロールに対して、そのロールを付与、取り消し、またはアカウントがそのロールを持っているかどうかを確認するために使用される新しいロール識別子を作成します。各ロールについて、ERC-7303は、そのロールでトランザクションを実行する参加者が所有する必要がある制御トークンの契約IDを保持しており、_grantRoleByERC721()又は_granRoleByERC1155()によって契約IDが追加されます。これらが複数回呼び出されると、指定された契約IDのうちの少なくとも1つのトークンを持っている必要があります。
以下は、ERC-721トークンまたはERC-1155トークンで発行者(minter)および焼却者(burner)のロールを定義する簡単な例です。これらの制御トークンを指定することで、そのロールを持っているアカウントが新しいトークンを作成したり、既存のトークンを焼却したりすることができます:
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "./ERC7303.sol";
contract MyToken is ERC721, ERC7303 {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
constructor() ERC721("MyToken", "MTK") {
// Specifies the deployed contractId of ERC721 control token.
_grantRoleByERC721(MINTER_ROLE, 0x...);
_grantRoleByERC721(BURNER_ROLE, 0x...);
// Specifies the deployed contractId and typeId of ERC1155 control token.
_grantRoleByERC1155(MINTER_ROLE, 0x..., ...);
_grantRoleByERC1155(BURNER_ROLE, 0x..., ...);
}
function safeMint(address to, uint256 tokenId, string memory uri)
public onlyHasToken(MINTER_ROLE, msg.sender)
{
_safeMint(to, tokenId);
}
function burn(uint256 tokenId)
public onlyHasToken(BURNER_ROLE, msg.sender)
{
_burn(tokenId);
}
}
ロールの付与と取り消し
上記の例では、_grantRoleByERCXXX
という内部関数を使用しています。この関数は、プログラムコードで直接的にロールを指定しています。ただし、実際のユーザーアカウントにminterやburnerのロールを付与することは、この契約の生成とは独立のタイミングで自由に行えます。たとえば、ユーザーがminterのロールを取得するには、指定された制御トークン発行者から必要な制御トークンを取得する必要があります。上記のユースケースでは、minterのロールはMinter Certと呼ばれるトークンがMyTokenの発行者によって指定されています。同様に、発行者がMinter Certを焼却することで、minterのロールを取り消すことができます。
デプロイされたコード
以下に、実際にPolygon zkEVMとGoerl testnet上にデプロイしたコード例を示します。
Code on Polygon zkEVM
発行権限と焼却権限として既にデプロイされている制御トークンを指定したMyTokenのコントラクトの例です。
発行権限と焼却権限をERC-1155で実現した上記のコントラクトから参照される制御トークンのコードは以下にあります。こちらはERC-7303を使用しておらず、通常のOwnableでアクセス制御しています。
Code on Goerli testnet.
以下はGoerliネットワーク上にデプロイしたものです。参照するERC7303.solは古いバージョンのもので、関数名などが現行と若干異なりますので、ご注意ください。そのうち更新します。
おわりに
ERC-7303は現在、ERC-7303はDraftのステータスです。Standardになるには、多くのプロジェクトでの活用実績が必要だと思います。是非、ご活用いただければ幸いです。また、修正要望がありましたら、是非、EIPのディスカッションに参加いただくか、あるいは私に連絡いただければ幸いです。
Discussion