alchemyでAA(account abstraction)を実装する
AAとは
account abstractionとは、Ethereum上でEOAアドレスとスマートコントラクトを抽象化して新しいアカウントを目指した概念といえます。
Ethereumのアプリでは、従来のweb2のアプリと比べてUXを下げる課題がありました。例えばユーザーがEVM上のゲームアプリを使う場合、現状であればまずメタマスクなどでアドレス(EOAアドレス)を作る必要があります。ただゲームアプリの新規ユーザーはまず最初に「面白そうだし、とりあえずプレイしてみよう」というようなユーザーが多く、最初の段階でEOAアドレスを用意する必要といった面倒なステップがあると初期の離脱率が高くなってしまいます。そのようEthereum特有のUXの低さを改善するための構想がAA(account abstraction)です。AAでは、ユーザーがEOAアドレスではなくスマートコントラクトを使ってトランザクションを実行するような形をとっています。
また、ユーザーの生成したトランザクションの検証には従来secp256k1というアルゴリズムを用いていました。しかしAAでは、ユーザーがカスタムできるため、他のアルゴリズムを取り入れることができるようになりました。
AAの規格: ERC4337
上記のaccount abstractionの構想を実際にコードの規格に落とし込んだのがERC4337です。
実行の流れとしては以下です。
- ユーザーがUserOperationを作る
- ユーザーがUserOperationに署名する
- Bundler(EOAアドレス)がUserOperationを実行できるかシミュレーションする
- 3で問題なければBundlerがEntryPoint(スマートコントラクト)にUserOperationを送る
- EntryPointで検証を行い、UserOperationをスマートアカウントに送る
- スマートアカウントで検証を行い、問題なければUserOperationを実行する
UserOperation
UserOperationとはユーザーがEVM上でどのようなステートの変更を行いたいかを記述したデータ構造です。例えばNFTの購入する関数のデータをUserOperationとしてバンドラーに送ります。
Bundler
AAで登場する唯一のEOAアドレスです。UserOperationをまとめてentrypointに送るために存在します。
EntryPoint
UserOperationが送られている受付部分の役割を果たします。ここでUserOperationで指定されているターゲット先のスマートアカウント(コントラクト)にUserOperationを投げます。
スマートアカウント
ユーザーごとに持っているコントラクトです。ユーザーはスマートアカウントを生成することで自分のEOAアドレスのようにスマートアカウント(コントラクト)を使うことができます。これにより、ユーザーはメタマスクなどでEOAアドレスを作らなくてもブロックチェーンのサービスを楽しむことができます。
サービスによってスマートアカウントの生成に若干の違いがあります。
Simple Account
Simple Acccountとはイーサリアム財団がオープンソースで提供しているスマートアカウントの種類です。筆者がいくつかのAA関係のドキュメントを読むとこちらのSimple Accountについて言及されていることがありました。
このSimple AccountではEntryPointの情報を保持し、実際にUserOperationのcalldataを実行する関数が用意されています。Simple Accountではexecute関数もしくはexecuteBatch関数でUserOperationのcaldataをcallしています。
AAの実装2パターン
AAを実装する方法としては大まかに2つあります。上記のERC4337の規格をもとに自分でsolidityファイルを作り、そのsolidityファイルのインスタンスから関数を叩くフルスクラッチに近い方法。
もう1つはAAのSDKを提供しているライブラリを使う方法です。現状SDKを提供しているサービスは多く、AlchemyやPimlico、Thirdweb、Biconomy、ZerodevやEtherspotなどがあります。
※Stackupというサービスがあったのですが、サービス終了しました。
SDKを使う場合に開発者(システム)が行うこと
・UserOperationの作成
・Smart accountの作成
・paymasterにお金を持たせる
など
EVMレベルでのUserOperationの検証などはsolidityファイルを提供している提供元のsolファイルが行います。そのため、SDKを使う場合は実行したいアクションをまとめたUserOperationとユーザーごとにもつSmart accountの作成を行えばOKです。
また余談ですがUserOperationの検証ロジックなどはSDKの提供元に依存することになるため、できれば監査(audit)に通ったSolidityファイルを提供しているところのライブラリやSDKを使うのが無難です。
alchemyが提供するAAのSDKとは
今回はalchemyのSDKを使って、typescript上でAAを実装します。
さらに、AlchemyのSDKを使うやり方でも筆者がドキュメントを読んだところ3つの方法がありました。
・Account-kit/reactを使う
・lightaccountを使う
・ERC6900と組み合わせる
違いとしては、フロントで実装するか、バックエンドで実装するか、またスマートアカウントはどのように生成するかの違いになります。ざっくり説明すると、Account-kit/reactがフロント用、licghtaccountとERC6900と組み合わせる方法がバックエンド向けです。そのほかにlightaccountとERC6900を使う場合の違いはユーザーごとに持つスマートアカウントをどのように作るかを定義したsolファイルが違うという点です。
それぞれ詳しく説明します。
【共通】UserOperationをpostする
alchemyのSDKでUserOperationをpostする際には、SDKの関数を叩けばOKです。lightaccountを使うかERC6900のMSCAをスマートアカウントとして使うかによってuseroperationをpostする際の関数自体は違いますが、関数の処理でやっていることは同じでUserOperationというstructを投げているだけです。
方法1: Account-kit/reactを使う
こちらはreact上でAAを実装する時に使います。今回はnode.jsで動かしたいので詳しい説明は割愛します。
方法2: lightaccountとAA-SDKライブラリを使う
lightaccountはAlchemyが先述したSimple Accountを改良したものです。上記のlightaccountを使うことでスマートアカウントをブロックチェーン上に生成できます。
BaseLightAccount.solがスマートアカウントのメイン部分になります。上記のコードを見れば分かる通り、execute関数で_call関数が実行され、UserOperationのcalldataはこの_call関数によって実行されます。
そして、上記のBaseLightAccout.solを継承したLightAccount.solがスマートアカウントの生成やアカウントの権利者の管理を行なっています。
上記のLightAccount.solのinitialize関数やtransferOwnership関数でブロックチェーンのストレージ上でownerのアドレスを格納したり、書き換えたりしたりしています。
見てきたようにlightaccountを使えばユーザーごとのスマートアカウントを作成し、UserOperationを実行できるようになります。
方法3: ERC6900のMSCAとAA-SDKライブラリを使う
以下はcreateModularAccountAlchemyClient関数を呼び出しているファイルです。
以下がcreateModularAccountAlchemyClient関数が定義されている部分です。
実際にはcreateMultiOwnerModularAccount関数を実行しています。createMultiOwnerModularAccount関数では、複数のオーナーが1つのコントラクトの所有者であるモジュラーアカウントというスマートコントラクトを生成します。そしてそのスマートコントラクトを、AAでのスマートアカウントとして利用しています。
createAlchemySmartAccountClient関数が最後returnされていますが、以下がcreateAlchemySmartAccountClient関数です。
lightaccountとは?
SimpleAccountにAlchemyが改良を加えたものです。
ERC6900とは?
ERC6900とは、account abstruction(ERC4337)におけるスマートアカウントの問題点・課題を解決するために作られた企画です。
以下のEIP6900に動機や具体的な関数のインターフェースが書かれています。
現在スマートアカウントを作る場合、スマートアカウントがどのような機能・関数を持つかは作り手に依存しています。例えば今回はAlchemyを使っているのでAlchemyのスマートアカウント関連のsolidityファイルでどのような機能をスマートアカウントが持つのか決まっています。ですが、他のサービスのSDKでスマートアカウントを作る場合、スマートアカウントの機能はそのサービスのsolidityファイルに依存します。当然ですよね。例えばあるサービスによっては、特殊な署名を採用したスマートアカウントを生成するなどが考えられます。
そうなると、ユーザーはサービスごとにスマートアカウントを作る必要が出てきます。スマートコントラクトAのA'機能を使うためだけにスマートコントラクトAを使い、スマートコントラクトBのB'機能を使うためだけにスマートコントラクトBを使うといった具合です。これってユーザからしたら、どのサービスを使うにしてもユーザーが持っておけば良いスマートアカウントは1つだけの方が管理が楽ですよね。
そこで、スマートアカウントが持つ機能をwordpressなどのプラグインのように簡単に設定したりプラグインを外したりできるようにしたものがモジュラーアカウント(モジューラースマートアカウント)です。そしてERC6900では、モジュール(プラグイン機能)とそのモジュールをプラグインとして扱うスマートアカウントの間の規格を標準化することで、モジュールを開発する人はスマートアカウントの仕様を考える必要がないようにしています。モジュールを開発する側はそのスマートアカウントはERC6900を準拠している前提で開発を行います。
このような背景を踏まえて現在AlchemyではAAを実装する際に、スマートアカウントにはlightAccountとモジュラースマートアカウントの2通りの実装があるようです。
ERC6900とAA-SDKで実装してみる
では、いよいよ本題に入ります。Alchemyが提供しているSDKを使ってAAを実装します。
以下に既に動作確認済みのコードを貼ります。
※Node.jsとTypeScriptのバックエンド環境で動かすことを想定
今回は以下のsolidityコントラクトを用意して、xのストレージを更新するchange関数をuserOperationで投げます。
以下で上記のAAを実装したコードの解説をします。
createModularAccountAlchemyClient関数
createModularAccountAlchemyClient関数は、与えられた設定データでモジュラーアカウントクライアントを作成します。このクライアントを通じてuserOperationをエントリーポイントに投げます。
encodeFunctionData関数
AAではuserOperationをエントリーポイントに送信しますが、その際に実行したい関数情報をバイトコードに変換して送る必要があります。これは最終的にスマートアカウントに実装されているcall関数が実行されるからです。
以下はイーサリアム財団のSimpleアカウントの_call関数の実装例です。バイトデータが実行されているのがわかります。
まとめ
今回AlchemyのAA関連のSDKをまとめましたが、スマートアカウント(スマートコントラクトアカウント)のカスタマイズ性が高いことが理解できました。一方でユーザーが"あのアプリとこのアプリで自分はスマートコントラクトアカウントを持っている"と考える必要が出てきやすそうで、ERC6900などの規格があるにしてもユーザーが1つのスマートアカウントだけ持っていれば良いという状態はなかなか作りにくそうな気がしました。
今後どのようなAAサービスが出てくるのか楽しみです。
Discussion