💡

Account Abstractionを、具体的な処理を追ってしっかりと理解してみましょう。

2022/12/11に公開約9,500字

こんにちは、CryptoGamesのユウキです。

本日は、こちらの「Starkup」を利用して、Account Abstractionを実際に行い、その仕組みを見ていこうと思います。
https://docs.stackup.sh/

なお、ちょっと長いですが、こちらのヴィタリックの記事はとても参考になると思います。
https://medium.com/infinitism/erc-4337-account-abstraction-without-ethereum-protocol-changes-d75c9d94dc4a

0 大まかな内容を理解する

内容に入る前に、仕組みを簡単に見てみましょう。

1. 問題の所在

まず、アカウントには次の2つがあります。

  1. EOA(いわゆるウォレットアドレス)
  2. コントラクトアカウント

コントラクトアカウントには秘密鍵がありません。

また、コードが書かれているので、色々な処理を書くことができます。

一方、EOAには秘密鍵があります。

そして、プロトコル上、トランザクションの起点は必ずこのEOAである必要があります。

これをプロトコルを変えずに解決しようとしているのが、今回紹介するERC4337です。

2. 主な仕組み

まずは、下の図を見てください。今は詳細な理解は不要です。

見ると、「Bundler」がトランザクションを起こしています。
トランザクションの起点はEOAなので、「Bundler」はEOAです。

そして、下のように、「UserOperation」を、「Bundler」に渡しています。

「UserOperation」の中にはコントラクトアカウントや実行するために必要なデータが入っています。

そして、下のように、「UserOperation」を、「Bundle transaction」にまとめてもらっています。

つまり、EOAである「Bundler」さんがtransactionの起点となることで、私たちはコントラクトアドレスから実行することができます。

これがERC4337の主な仕組みです。

1 全体像を把握する

まずは、全体像を把握しましょう。

結局下のことが行われていますので、一つ一つ見ていきましょう。


https://medium.com/infinitism/erc-4337-account-abstraction-without-ethereum-protocol-changes-d75c9d94dc4a

1) UserOperationを作る

まずは、userが「UserOperation」を作っています。

「UserOperation」とは、

  • 送り主は?
  • 何を実行したい?
  • ガスの上限は?

のような要素をまとめてパッケージにしたイメージです。

2) Bundlerに送られる

続いて、この「UserOperation」が「Bundler」に送られます。

「Bundler」とはその名の通り(bundle:束ねる)、「UserOperation」を束ねてくれます。

3) BundlerがUserOperationをトランザクションにまとめる

そして、 「Bundler」がその名の通り(bundle:束ねる)の、「UserOperation」を束ねることをしてくれます。

4) トランザクションがBlockに入る

最後に、3で作ったトランザクションが他のトランザクションと同じように、Blockに入っています。

ちなみに、この部分をもう少し詳しく見てみましょう。

「Bundler」は「EntryPoint」というコントラクトに「UserOperation」をまとめたものを送っています。


https://medium.com/infinitism/erc-4337-account-abstraction-without-ethereum-protocol-changes-d75c9d94dc4a

「EntryPoint」は大まかに、それぞれの「ウォレットコントラクト」を①検証し、それぞれのバックの中に入っている②処理を実行します。

ちなみに、それらができるのは、「UserOperation」の中にパッケージとして、「ウォレット」や「署名」などが入っているためです。


https://docs.stackup.sh/docs/introduction/erc-4337-overview

5) 全体像をもう一度見てみよう

これらを踏まえるとこのような流れになっています。

「UserOperation」というパッケージが「Bundler」でまとめられ、「EntryPoint」というコントラクトに送られます。

「EntryPoint」はそれぞれの「ウォレットコントラクト」に対して、①検証と②処理の実行を行います。


https://docs.stackup.sh/docs/introduction/erc-4337-overview

これで全体像としては、以上です。

わからなくなった時は、また全体像を見てみると、イメージがつきやすいかと思います。

2 実際にやってみよう

では、「Starkup」を使って実際にやってみましょう。
https://docs.stackup.sh/

処理の流れについては、こちらもご参照ください。
https://docs.stackup.sh/docs/guides/quickstart

では、やっていきます。

1) コードを準備する

まずは、git cloneを行います。

git clone https://github.com/stackup-wallet/erc-4337-examples.git

下のようになりました。

続いて、yarn installで依存関係を入れていきます。

yarn install

下のようになりました。

最後に、yarn run initで、configファイルを初期化します。

yarn run init

下のように、「config.json」ができました。

全体像で出てきた、「Bundler」や「EntryPoint」がいますね。

2) BundlerのURLを取得する

下の、「Starkup」のページに行きます。
https://app.stackup.sh

下のように、「Create instance」を行います。

これで、BundlerのURLができます。

ただ、このままでは利用ができません。
使用するためには「Settings」からプランへの登録が必要です。

登録すると、利用ができるようになるので、ここから、URLをコピー。

下の、「bundlerURL」に貼り付けます。

ここで、全体像の復習ですが、UserOperationを送る先のBundlerのURLを設定していましたね。

追記

このまま処理を進めていくと、下のようなエラーに遭遇するようになりました。

確認すると、「EntryPoint」のコントラクトアドレスが変更されたようです。

https://github.com/stackup-wallet/erc-4337-examples

そのため、「config.json」の「entryPoint」も変更を行ってください。

Default value is set to 0x78d4f01f56b982a3B03C4E127A5D3aFa8EBee686.

3) 「ウォレット」アドレスを取得する

では、Userとなる、「ウォレット」アドレスを取得していきます。

この段階では、まだコントラクトアドレスではありません。

こちらを実行します。

yarn run simpleAccount:address

下のように、アドレスが取得できました。

Polygonscan(mumbai)で見てみると、下のように「Address」となっています。

処理を行うために、外からこのアドレスに0.2maticを送りました。

4) 処理を実行する(transfer)

では、準備ができたので、処理を行なっていきましょう。

yarn run simpleAccount:transfer <受け取り先アドレス> <amount>

今回は、0.1 maticを別のアドレスに送ろうとしています。

下のようになりました。何やら、「UserOperation」ができていそうですね。

Polygonscan(mumbai)を見ると、コントラクトができていることがわかります。

作っているのは、下の「SimpleAccountFactory」になります。

5) transferの処理を具体的に見ていこう

まずは、下のように、アカウントの取得の際に、「SimpleAccountFactory」が引数に入っていることが確認できます。

また、余談ですが、コマンドに渡していた引数もこのように確認できました。

①UserOperationの作成について

そして、下のように、「UserOperation」が作られ、その中の「initCode」を見ると、「SimpleAccountFactory」のアドレスが確認できます。

ちなみに、全体像の復習ですが、この「UserOperation」が作られたのですね。

②initCodeとは

ちなみに、こちらの「initCode」はウォレットを作るためのコードです。

ウォレットが作られていない場合、「EntryPoint」を通じて、このコードからウォレットを作ってくれます。

https://medium.com/infinitism/erc-4337-account-abstraction-without-ethereum-protocol-changes-d75c9d94dc4a

これにより、「EntryPoint」が「initCode」をの内容を実行し、そのコードに従って、「simpleAccountFactory」がコントラクトを作成しました。

③UserOperationのBundlerへの送付

下のように、「sendUserOpToBundler」で、「Bundler」に「UserOperation」に送っています。

そして、「Bundler」が「UserOperation」をまとめて、「EntryPoint」に送るのは「Bundler」の仕事なので、これでこちらの処理としては完了しています。

流れとしては、このようになっていましたね。

6) EntryPointとウォレットコントラクトのやりとりを見てみる

①概要について

下のように、①検証と②処理の実行が行われていましたね。

そして、実はガスの支払いと使わなかったガスの返金も行われています。

②検証部分について

そして、検証の部分を具体的に見ると、「ウォレットコントラクト」から「EntryPoint」にガスが支払われています。

それが、Polygonscan(mumbai)ではここにあたります。

確かにガスが支払われていますね。

②実行部分について

では、実行部分を見てみましょう。

下のように、「EntryPoint」が「calldata」と共に「ウォレットコントラクト」を呼び出しています。

ここで、「calldata」とは、下のように、「UserOperation」に含まれる、実行のために必要なデータです。

実際に、Polygonscan(mumbai)で見てみると、下のように、「ウォレットコントラクト」から「受け取りアドレス」に対して、指定した0.1Maticがtransfer(送付)されていました。

最後に、下のように、使われなかったガスが「EntryPoint」から「ウォレットコントラクト」に返還されます。

実際に、Polygonscan(mumbai)で見てみると、、、あれ?なんか違うアドレスにガスが返還されていますね。

ここの部分、なぜこうなっているのか(正しい処理なのか)わからなかったので、ここは分かり次第更新していきたいと思います。

7) transferを実行する(ウォレットがすでにできている状態)

では、最後にもう一度新しく、0.2Maricの送付の処理を行ってみましょう。

ここで、前回と異なるのは、「initCode」です。

こちらは、すでに「ウォレットコントラクト」が作成されているため、中身は特にないようですね。

処理はこのように実施されました。

今回は以上です。

かなり長かったですが、最後までお読みいただき、ありがとうございました。

Discussion

ログインするとコメントできます