Multi Tenant Account from ETHIndia
この記事は Ethereum Advent Calendar 2023 における20日目の記事です。
はじめに
こんにちは、クリプトやってます、kai(@dikxs118)です。
12/8-12/10でETHIndiaというEthereumのハッカソンに出てきたので、そこで出したものについて書いていこうと思います!
ハッカソンなどは疲れるのであまりでないのですが、今回はお誘いいただいたので出ることにしました。
ちなみに3人で行ったのですが、色々あり各々がSOLOで参加するというストロングスタイルでやることになりました。いやあ疲れた。
結果としては私のプロダクトは諸々のPool Prizeと、
Scrollから Best Prize をいただきました〜!🏆
プロジェクトの詳細やコードなどの実態は下記にあります。
死ぬほどタイポしているのとコードは適当ですが、ハッカソンということでそこはご愛嬌です。(2徹した)
一緒に行った2人もそれぞれ SafeとScrollで Best Prize を獲り、全勝ということでみんなハッピーに終われたのが何よりよかったですw
今回の話ですが、前提となる知識があるので、下記の順序で解説していきます。(抜粋)
- Multi Tenant Accountの概要
- デモ
- ERC7546について
- Multi Tenant Accountの詳細
- デモ解説
Multi Tenant Account 概要
さて、まずは Multi Tenant Account について概要を説明していきます。
Multi Tenant Account は ERC7546: Upgradeable Clone(後述) に基づき実装された、既存はもちろん、後発で開発されたContract Accountの実装をデプロイなしで自由に取り込めるようになる最強のUpgradeabilityをもったContract Accountです。
要は好きなContarct Accountの実装を自由に取り込んで何にでも対応するContract Accountを作成することもできるし、仕様変更の可能性があるContract Accountを取り込んだとしても、いつでも柔軟に実装の変更対応できる可用性、保守性の高いContract Accountを作ることができるのが Multi Tenant Account です。
デモ
Multi Tenant Accountの一例として、
Multi Tenant Accountが、 LightAccountImpl(AA) と SafeImpl(Multisig)
の実装を取り込むことで、MultisigやAAとして機能していることを証明します。(ETHIndiaの動画尺の関係で早めに操作しているので、わからなかったら止めてください🙏)
手順としては下記です。
- AAとして機能: handleOpsを呼び出し、entrypointを経由して、Multi Tenant AccountがLightAccountImplを呼び出すことでEOAに送金する
- Multisigとして機能: execTransactionを呼び出し、Multi Tenant AccountがSafeImplを呼び出すことでEOAに送金する
もしこのデモを見てもチンプンカンプンだとしても(というより補足ないし多分よくわからないですよね)以降で詳しく解説した後、再度デモ解説をするのでご安心ください。
ERC7546
Multi Tenant Account 動作する基盤となる ERC7546 について詳細を見ていきます。
これから書く内容は私の噛み砕いた理解なので、正確にキャッチアップしたい方は公式ドキュメントを見てください。
ERC7546は @shogochiai, @kai_hiroi によって2023/11月に proposal された新たなデザインパターンです。私も近くにいるので、これについては少しわかっていて、将来性を強く感じたので、これを使ってプロダクトを作ることにしました。
Upgradeable Clone が何かということを一言で表すならば、 Beacon Proxy と Diamond Standard を組み合わせたデザインパターンで、Beacon Proxyのように、紐づくProxyの一括のアップグレードが可能で、 Beacon の代わりに Dictionary というリゾルバで管理し、Diamond Standardのように関数毎の登録が可能で has many impls として振る舞います。
上記の図から全体の処理の流れをまとめると、
- Dictionaryに関数を登録
- setImplementation というメソッドで登録したい関数のセレクター(bytes4)と、その実装のアドレスをマッピングして登録します
- ERC7546Proxyに呼びたいメソッドをcall
- ユーザーがProxyに呼び出したいメソッドをcallします
- Dicitonaryから関数を持っている実装コントラクトのアドレスを取得
- Proxyは fallback し、 getImplementation というメソッドを呼び、呼び出された関数の実装コントラクトアドレスを取得します
- そのアドレスにdelegatecall
- 実装コントラクトアドレスにdelegatecallすることで機能します
となります。
管理と振る舞いという点で分割して切り離してみてみるとわかりやすいです。
振る舞い
点線で囲っている部分はまさに Diamond Standard のように振る舞います。
つまり、複数の実装を同時に参照し、呼び出された関数に応じて適切な実装をdelegatecallすることができます。
管理
管理という表現が適切かは分かりませんが、点線で囲っている部分が Beacon Proxy のようにManageします。Beaconでは、設定されている固定の実装のアドレスを返し、Proxyがそのアドレスにdelegatecallしますが、Dictionaryでは、関数のセレクターと実装のアドレスをマッピングを永続化することで、都度返却するアドレスを解決しています。
Multi Tenant Account 詳細
さて、ERC7546について理解した上で、Multi Tenant Accountを利用するメリットとモチベーションについて解説していきます!
一つずつ見ていきましょう。
自在なアップグレード
先述した通り、 Multi Tenant Account では、自由自在に他のContract Accountの実装を取り込むことができ、デプロイなしで適応できます。これにより、仕様の変更にもシームレスに対応できます。
例えば、ERC4337(AA)が発表されたとき、AA対応しようと思ったら既存のAccount(Proxy)は捨てるしかなかったですよね?
また、せっかくAA対応してもERC4337の仕様が今後ドラスティックに変わっていく可能性は高いでしょう。辛いですね。マイグレーションは非常にセンシティブで、疲れるのでできるだけやりたくない作業です。
Multi Tenant Accountの機能の一つとして、AAの実装を取り込んでおけば、そのようなマイグレーション作業は不要になります。
一括アップグレード
Multi Tenant AccountのBaseになるERC7546にはDictionaryというリゾルバが存在していましたね。
そのため、紐づくProxyがいくつあったとしても新しい Contract Accountの実装を取り込み直せば、
全てのProxyが一括でアップデートできます。
関数レベルのアップグレード
例えば、完全にマルチシグとして振る舞おうと思えば、SafeImplに存在するpublicとexternalの関数を全て登録すれば良いですが、関数の一部だけ更新したり登録したりしたい場面もあると思います。
ここがBeacon Proxyとは異なるところで、Multi Tenant Account(ERC7546)では、関数単位のアップグレードが可能なため、コントラクト全体ではなく、特定の関数のみアップデートすることが可能です。
つまり、元のコントラクトに影響を与えることなく影響範囲を局所化することができます。
低コストデプロイ
Proxyについてですが、SolidityではなくHuffという低級言語で書かれているため、デプロイのコストがSolidityで書くのに比べて6割ほど削減されています。(すごい!)
デモ解説
それでは、最後に再度デモを見てみましょう!
まずはやりたいことをおさらいします。
やりたいことはただのProxyが、 LightAccountImpl(AA) と SafeImpl(Multisig)
の実装を取り込むことで、MultisigやAAとして機能していることを証明します。
手順としては下記です。
- AAとして機能: handleOpsを呼び出し、entrypointを経由して、Multi Tenant AccountがLightAccountImplを呼び出すことでEOAに送金する
- Multisigとして機能: execTransactionを呼び出し、Multi Tenant AccountがSafeImplを呼び出すことでEOAに送金する
デモ動画で、やっていることを詳細に順序立てて噛み砕きます。
- ERC7546Proxyをデプロイ(ガス代節約のためデモ動画ではすでにデプロイ済みのアドレスを利用してます) 0:02~
- Proxyにガス代を送金 0:30~
- ただのProxyなので、AAのsendUserOperationできるわけないよね、を明示 1:00~
- sendUserOperation(コントラクトレベルでは handleOps)を呼び出すために必要な関数をDictionaryに登録 1:24~
- LightAccountのexternal, publicの関数を全て登録すれば、完全なLightAccountとして機能しますが、今回はデモなのでhandleOpsを行うための最小限のメソッドを登録してます
- LightAccountのInitializeを行う 2:00~
- これによりLightAccountの初期状態がProxyのStorageに書き込まれ、LightAccountとして機能するようになります
- sendUserOperationを行う 2:18~
- 動画では割愛されていましたが、きちんとentrypointを経由してMulti Tenant Accountが呼ばれています
- Multisigとして機能しないことを明示 3:20~
- execTransactionを呼び出すために必要な関数をDictionaryに登録 3:30~
- Safeのexternal, publicの関数を全て登録すれば、完全なSafeとして機能しますが、今回はデモなのでexecTransactionを行うための最小限のメソッドを登録してます
- safeのsetupを呼び出しInitializeを行う
- execTransactionを行う
SpecialThanks
これを形にするまでに、様々なアドバイスやアイデアをいただいたのでここに記載させていただきます!
ありがとうございました🏆🏆🏆
@0xHaku(DeFiGeek, DeFiの神)
@kai_hiroi(Ecdysis, ERC7546の神)
@shogochiai(Ecdysis, クリプトの神)
@m0t0k1ch1(SIVIRA, AAの神)
Discussion