/OkodukaiWallet

SampleAADapp (React.js + Hardhat + AA + TheGraph)

Primary LanguageTypeScriptMIT LicenseMIT

Okodukai Wallet

Okodukai Wallet (React.js + Hardhat + TheGraph)

Contract Info

No. コントラクト名 アドレス ネットワーク
1 FactoryManager 0xdd0412fDdD27bd54115E63E62f04318C4B4154F4 Mumbai
2 EntryPoint 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 Mumbai
3 DepositPaymaster 0x93fdd51462FB20fB631F8CA38c3DeB87583311Ea Mumbai

Deployed Subgraph

SubGraph

  • Sample Query (生成したFacotryコントラクトの情報を一括で取得するクエリ)
query MyQuery {
  factoryCreateds(orderBy: id, orderDirection: desc) {
    factoryId
    factoryAddress
  }
}

トランザクションの記録 (Mumbai Network)

No. トランザクションハッシュ 概要
1 0xe62537028fc2be51a7a0121926bf7ed024531577ce03f085135618fc1613ba26 FactoryManagerコントラクトのデプロイ
2 0xd16efa3e05c61dd0186434594f6c9004b7cc086ccc2cf6e12c3122fc985935e3 FactoryManagerコントラクトからSimpleAccountFacotoryコントラクトを生成したTx
3 0x0bf4ad5dd446d9b64e0e981506688b6d2159d1d1ad358317dad6380247f77c31 FactoryManagerコントラクト経由でEntryPointコントラクトに対してaddStakeしたTx
4 0x6b333e0b0587d3bfc7432f1cac0b163238cc9a0de27630d5fd7d263734c1b010 SimpleAccountコントラクトからネイティブトークンを送金したTx
5 0xaba33331fbd001e9a6f976c21c2abaf5f44ca20e6c6bc06a7a35b3cc183f596f SimpleAccountコントラクトからERC20トークンを送金したTx

なぜそれを開発しようと考えたのか

Web3のマスアダプションのため。

どんなプロダクトを目指して開発したか。

お小遣いをあげる感覚で、ownerからユーザーに暗号資産を渡してあげられる + 管理ができる アプリをイメージしていました。

このようにすることで子供でも暗号資産を使えるようにするイメージです。

実際のデモ

Youtube

工夫やユニークポイント

  • 全体像

  • コントラクトのコンセプト

AccountFactoryというコントラクトを追加して、ownerが複数のContractWalletを管理できるようにする構造を考えていました。ownerになった人がContractWalletに入金していくイメージです。

さらに、Factory生成時のタイミングでは Eventを発行させてThe Graphを使ってインデックス化させ検索・管理しやすいアプリにしたいと考えていました。

チームについて

Name Github Twitter
mashharuki Github Twitter
hakua Github Twitter
mameta Github Twitter

今後の課題や目標

  • 独自実装したFactoryContractAccountContractがちゃんと機能するようにバンドラーとエントリーポイントコントラクト・Paymasterコントラクトの仕組みをさらに調べること。

  • バンドラーとEntryPointコントラクトまでも自分たちで用意するのはとても大変(しかも独自実装が混ざるとさらに大変)だったので今回は除外したが、今後ハッカソンでこの辺りの機能拡張などが簡単にできる仕組みを考えると審査員受けしそうだと思った。

  • 今回学んだことを今後開発するプロダクトやハッカソンのアイディアに流用することが当面の目標である。

  • アプリのエンドユーザー側は、Metamaskインストールしていない状態になるので、セキュリティの担保のためには結局KYCが重要になる。なので、GoogleアカウントでもログインできるようなSSOの仕組みを導入した認証機能などを追加してあげる必要がある。

今回のブートキャンプで調べたこと・学んだこと

特徴

  • UserOperation - ユーザーの代わりに送信されるトランザクションを記述する構造体です。混乱を避けるため、"transaction "という名前は付けられていない。 トランザクションと同様に、"sender", "to", "calldata", "maxFeePerGas", "maxPriorityFee", "signature", "nonce" を含む。 トランザクションとは異なり、以下のようないくつかのフィールドを含んでいます。 また、"nonce "と "signature "フィールドの使用方法は、プロトコルで定義されているわけではなく、各アカウントの実装によって定義されています。
  • Sender - ユーザーオペレーションを送信するアカウントコントラクト。
  • EntryPoint - UserOperationのバンドルを実行するためのシングルトンコントラクトです。バンドラー/クライアントは、サポートされるエントリーポイントをホワイトリストに登録します。
  • Bundler - 複数のUserOperationをバンドルし、EntryPoint.handleOps()トランザクションを作成するノード(ブロックビルダー)です。ネットワーク上のすべてのブロックビルダーがバンドラーであることを要求されるわけではないことに注意してください
  • アグリゲーター - アグリゲートされた署名を検証するために、アカウントによって信頼されるヘルパー契約です。バンドラー/クライアントは、サポートされるアグリゲーターをホワイトリスト化します。

AAのアーキテクチャ

アカウントアブストラクションを実現するためのコンポーネント

  1. EntryPoint contract (エントリーポイントコントラクト)
  2. Paymaster contract (コントラクト)
  3. UserOperation (ユーザーが実行したいトランザクション本体のデータ)
  4. Bundler (トランザクションを束ねてブロックチェーンに送信するためのAPIサーバー)
  5. Miner (マイナー)
  6. client library (アカウントアブストラクションの機能を呼び出すためのクライアントライブラリ)

エクステンション:ペイマスターズ

エントリーポイントのロジックを拡張し、他のユーザーのトランザクションをスポンサーすることができるペイマスターをサポートします。この機能は、アプリケーション開発者がユーザーの料金を補助したり、ユーザーがERC-20トークンで料金を支払うことを可能にしたり、その他多くのユースケースに使用することができます。

Paymasterは、dAppの利用者がトランザクション手数料を支払わずにdAppを使用できるようにするための機能です。Paymasterは、利用者の代わりにトランザクション手数料を支払うため、dAppの利用者はトランザクションを送信するときにETHを持っている必要がありません。

Paymasterは、dAppの所有者が管理するスマートコントラクトであり、dAppの利用者がPaymasterに対して任意の量のETHを送信し、Paymasterはトランザクション手数料を支払い、dAppに転送します。

ERC-2771は、Paymasterを実装するために必要なメソッドやイベントを定義しています。Paymasterを実装するには、ERC-2771で定義されたスマートコントラクトを作成し、必要なメソッドを実装する必要があります。

Paymasterの利点は、利用者がETHを持っていなくてもdAppを利用できるようになることです。これにより、ユーザーの利用率を向上させることができます。また、PaymasterはdAppの所有者が管理するため、手数料を自由に設定することができるため、dAppの収益性を向上させることもできます。

toEthSignedMessageHashメソッドについて

toEthSignedMessageHashは、Ethereumデジタル署名標準に従って、任意のメッセージに対して署名する前にハッシュ値を生成するために使用されるメソッドの1つです。

このメソッドは、web3.jsやSolidityなどのEthereum開発ツールキットで利用可能であり、以下のようなステップで使用することができます。

  1. メッセージをUTF-8形式でエンコードします。
  2. Ethereumの署名形式に必要なプレフィックス文字列 ("\x19Ethereum Signed Message:\n" + メッセージのバイト数)を追加します。
  3. 2で生成された文字列をハッシュ化します。
  4. ハッシュ値を16進数文字列に変換します。

Metamask Snapについて

Metamask Snapは、Metamaskブラウザ拡張機能に追加できる、分散型アプリケーション(dApps)のための拡張機能です。

Snapは、MetamaskのAPIを使用して、dAppsとのインタラクションを可能にすることができます。Snapは、dAppsがMetamaskのUIやAPIを直接利用することなく、dAppsがMetamaskと連携することを可能にすることができます。これにより、dAppsは、Metamask Snap APIを介してMetamaskのウォレット機能を利用することができます。

Metamask Snapを使用することで、ユーザーはMetamaskをアンロックすることなく、dAppsにアクセスすることができます。dAppsは、Metamask Snapを使用して、ユーザーにウォレットアドレスの確認やトランザクションの署名などの機能を提供することができます。

Metamask Snapは、Snap StoreというMetamaskのSnap拡張機能のマーケットプレイスで利用可能です。Snap Storeには、多数のSnapが提供されており、ユーザーは必要に応じてSnapを追加することができます。Snap Storeには、dAppの開発者が作成したSnapが含まれており、ユーザーはSnap Storeを通じてdAppsにアクセスすることができます。

Metamask Snapは、分散型アプリケーションとのインタラクションを簡素化し、ユーザーにとってよりシームレスなdAppエクスペリエンスを提供することを目的としています。 → AA系のアプリだとMetamaskはインストールしていない前提なのでそうすること。

デプロイしたコントラクト

No. コントラクト名 アドレス ネットワーク
1 SimpleAccountFactory 0xA0B912d2797602863ce04F370b36330d80e76832 Mumbai
2 EntryPoint 0x607cAAF3fF8bB0469F1e9b1e3214008C0B1D05C6 Mumbai
3 SimpleAccount 生成したコントラクトウォレット Mumbai

本リポジトリ内の動かし方

  • インストール
cd 4337-sample && npm i
  • コンパイル
npm run compile
  • テスト
npm run test
  • EntryPointコントラクトのデプロイ & Verify
npm run entryPoint:deploy:mumbai

package.jsonファイルにデプロイしたコントラクトアドレスを埋め込むこと!!

npm run entryPoint:verify:mumbai
  • SimpleAccountFactoryコントラクトのデプロイ & Verify
npm run simpleAccountFactory:deploy:mumbai 

package.jsonファイルにデプロイしたコントラクトアドレスを埋め込むこと!!

npm run simpleAccountFactory:verify:mumbai

QuickStartをやった記録

yarn
yarn run init
  • コントラクトウォレット生成
yarn run simpleAccount address
  • 実行結果
SimpleAccount address: 0xAcF13ddE0328fC1D971b14b46601f72EfCde988a
✨  Done in 9.51s.
  • ネイティブトークンの送金
yarn run simpleAccount transfer --to 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072 --amount 0.05
  • 実行結果
Signed UserOperation: {
  sender: '0xAcF13ddE0328fC1D971b14b46601f72EfCde988a',
  nonce: '0x0',
  initCode: '0x9406cc6185a346906296840746125a0e449764545fbfb9cf000000000000000000000000e6d171e50dc760f74e1e5c78f3f4e1e2df72cb5e0000000000000000000000000000000000000000000000000000000000000000',
  callData: '0xb61d27f600000000000000000000000051908f598a5e0d8f1a3babfa6df76f9704dad07200000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
  callGasLimit: '0x814c',
  verificationGasLimit: '0x583f4',
  preVerificationGas: '0xaf9c',
  maxFeePerGas: '0x670cebc0',
  maxPriorityFeePerGas: '0x670ceba0',
  paymasterAndData: '0x',
  signature: '0xf41970f318f4c9425ac22689fcf8d361fff35cc8134cbb69a2b65de733a6657131982677a02d9f89a6dce63bf272be036e33b78d02bfb8404a878b8ebe6d5f881b'
}
UserOpHash: 0xed45d1eb9ecd6457172aeed91e4cd6b13ef907eedd7a27661cabbf1d1a603634
Waiting for transaction...
Transaction hash: 0xcc122aca392ae5096f9404d868f4e4336f2f81f8933bdead8e1ca25909a9c09e
✨  Done in 25.13s.
  • ERC20のトークン(LINKトークン)を送信する
yarn run simpleAccount erc20Transfer --token 0x326C977E6efc84E512bB9C30f76E30c160eD06FB --to 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072 --amount 0.1
  • 実行結果
Transferring 0.1 LINK...
Signed UserOperation: {
  sender: '0xAcF13ddE0328fC1D971b14b46601f72EfCde988a',
  nonce: '0x1',
  initCode: '0x',
  callData: '0xb61d27f6000000000000000000000000326c977e6efc84e512bb9c30f76e30c160ed06fb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000051908f598a5e0d8f1a3babfa6df76f9704dad072000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000',
  callGasLimit: '0x9922',
  verificationGasLimit: '0xed10',
  preVerificationGas: '0xaeb8',
  maxFeePerGas: '0x6507a5de',
  maxPriorityFeePerGas: '0x6507a5c0',
  paymasterAndData: '0x',
  signature: '0xb0a132bd539cfadcc4e05b2f6753f88ad6d53fa0a7e2379d642cc5295b98e2ee107d7a45bf76766cd7f1ca9dc98a8c34f3ae4182a2808e36617d0ef85456aedb1c'
}
UserOpHash: 0x1a4b3f80da3e0c6fd52dd4c1657e14f837eaf4a32f1bf13a98cf7365be7b2144
Waiting for transaction...
Transaction hash: 0x93117543b9382f20f58c27811887dfebac7833080f89132682ad509f0e220cdb
✨  Done in 21.65s.
  • ERC20を承認するコマンド
yarn run simpleAccount erc20Approve --token 0x326C977E6efc84E512bB9C30f76E30c160eD06FB --spender 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072 --amount 0.5
  • 実行結果
Approving 0.5 LINK...
Signed UserOperation: {
  sender: '0xAcF13ddE0328fC1D971b14b46601f72EfCde988a',
  nonce: '0x2',
  initCode: '0x',
  callData: '0xb61d27f6000000000000000000000000326c977e6efc84e512bb9c30f76e30c160ed06fb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000051908f598a5e0d8f1a3babfa6df76f9704dad07200000000000000000000000000000000000000000000000006f05b59d3b2000000000000000000000000000000000000000000000000000000000000',
  callGasLimit: '0xc623',
  verificationGasLimit: '0xed10',
  preVerificationGas: '0xaeb8',
  maxFeePerGas: '0x6507a5e2',
  maxPriorityFeePerGas: '0x6507a5c0',
  paymasterAndData: '0x',
  signature: '0xdeeef33f860c1224d0cbc9e72f9c9a942c4da9661b80ae9f9b5268790e51a0382511f8c8e2543bd02a04eaf4ac2846d2ebeb0999607782be4e36ab0f2e2c539d1b'
}
UserOpHash: 0xa9c59076dc2aed6216a8eafaa335a40898a5b470a82b2ff873b42834d57e29d6
Waiting for transaction...
Transaction hash: 0xc96ac903a0130cfbd2fdc0a9dd9553cb040b909d981a9465dc2120d1ab03d6a2
✨  Done in 21.18s.
  • 一気に複数のERC20トークンした場合
yarn run simpleAccount batchErc20Transfer --token 0x326C977E6efc84E512bB9C30f76E30c160eD06FB --to 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072,0x1295BDc0C102EB105dC0198fdC193588fe66A1e4 --amount 0.01
  • 実行結果

Paymasterを使った場合のコマンド(Developerプランに変更する必要あり)

基本的にPaymasterで使用したいトークンの設定を追加で行い、--withPaymasterをつけることで実行できる。

  • ネイティブトークンの送金
yarn run simpleAccount transfer --to 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072 --amount 0.05 --withPaymaster

AAを使ったアプリの開発で詰まったポイント

  • 独自の実装を混ぜ込むのが難しい。(Solidityレベルでは可能だが、フロント、APIを合わせると話は別。ちょっと変えただけでも上手く動かない。)
  • Bundlerの開発が大変(上述の独自実装を組み込むことに加えてそれを考慮した設計にしないといけない。)
  • userOpのデータを作るのは、userOp.jsなどのSDKを使わないとめちゃくちゃめんどくさい。
  • 上記点を上手く組み合わせるだけでもかなり大変。

学習すべき順番

  • まず、QuickStartをやる。
  • コントラクトのソースとバンドラーのAPIの内容を理解する。
  • 標準仕様で、userOp.jsを使ってReact.jsなどで開発したフロントエンドリクエストを投げられる様にすること。
  • まずは上記でフロントから動かすこと。(普通にライブラリを使ってリクエストを投げる。)
  • 独自実装を組み込んだFactoryContract及びAccountContractのデプロイ方法と適用方法を調べること。

Paymasterで使えるトークンについて

現在はStackupが指定したトークンのみしか使えない(USDC、テストネットだと特定のトークンStackup 6 Decimal Test Token)

参考文献

  1. EIP-4337
  2. FireWallet - Github
  3. Account-Abstruction
  4. NPM AccountAbstruction
  5. jiffyScan
  6. 【GitHub】jiffyScan
  7. 【StackUp Docs】AA introduction
  8. AAsnap
  9. stackup-bundler Sample source
  10. 【npm】Userop.js
  11. MetaMask/snaps-monorepo
  12. extend-the-functionality-of-metamask
  13. Template Snap monorepo
  14. Create a gas estimation snap
  15. Hardhatの使い方メモ(4) テスト - イベントのテスト方法
  16. ERC4337に関するメモ
  17. 発表資料
  18. tailwindcomponents