11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[ERC4337] プロトコルの変更をせずにAAを実装する仕組みを理解しよう!

Last updated at Posted at 2023-08-20

はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

今回は、プロトコルの変更を回避して、アカウント抽象化を実装する提案であるERC4337についてまとめていきます!

以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。

また、AA(AccountAbstraction)やERC4337についてのわかりやすい記事は、この記事の後半でまとめています。
この記事はあくまでERC4337の提案をまとめたものと認識してください。

ERC4337は現在Draft段階です。
そのため今後内容が変更される可能性があるのでご注意ください。

以下のより仕組みを噛み砕いてわかりやすく説明しています。

ERC4337について以下のスライドで簡単にまとめました。

ERC4337を含むAccount Abstraction関連の規格については以下の記事にまとめています。

概要

ERC4337は、Ethereumのコンセンサス層(プロトコルの最下層)を一切変更せずにアカウント抽象化(Account Abstraction)を実現するための仕組みを提案しています。
従来のトランザクション構造を変更せず、UserOperation という構造体を導入します。

ユーザーは UserOperation を専用の mempool に送信し、bundler と呼ばれる特別な役割を持つノードがそれらを集約して、handleOps 関数を呼び出すトランザクションを作成します。
このトランザクションは通常通りブロックに含まれて処理されるため、既存のEthereumの仕組みと矛盾せずに機能します。

ざっくりとEthereumのレイヤー構造について紹介します。

Ethereumのネットワークレイヤー
これは、Ethereumの基盤となるネットワークです。
ノードが情報をやり取りし、トランザクションをブロックにまとめて確定させる層です。
このレイヤーは提案に変更を加えることなく維持されます。

コンセンサスレイヤー
ここでは、ネットワーク全体での合意が形成されます。
提案されているアカウント抽象化は、このレイヤーに変更を加えずに実現されます。
新しいプロトコル変更やトランザクションタイプの導入はありません。

アプリケーションレイヤー
ここに提案されているアカウント抽象化のアイデアが詳細に位置付けられます。
ユーザーオペレーションという新しい概念が導入され、これによってスマートコントラクトウォレット内で実行される操作が表現されます。
ユーザーはこれらの操作を作成し、専用のmempoolに送信します。

動機

詳細は以下のリンクを参照してください。

提案されている方法は、アカウント抽象化を実現する際に、新しいアプローチを採用しています。
具体的な目標とその方法について詳しく説明します。

Account Abstractionの目標

ERC4337は、ユーザーがEOA(Externally Owned Account)に依存せず、コントラクトアカウントのみで動作する環境を目指しています。
具体的には、ユーザーがスマートコントラクトウォレットを通じて任意の検証ロジックを持つアカウントをメインのアカウントとして使用できるようにすることを目指しています。
これにより、ユーザーはEOA(外部所有者アカウント)を持つ必要がなくなり、スマートコントラクトウォレットを使用して以下のような複雑な操作を実行できるようになります。

  • 任意の署名方式(例:量子耐性署名)
  • マルチシグやソーシャルリカバリー
  • 任意トークン(例: ERC20)でのガス支払い
  • ウォレットアップグレードやカスタムロジックの導入

分散化

ERC4337は、全てのやり取りを公開されているmempool上で完結させます。
ユーザーは特定のノードやIPを知る必要がなく、任意の bundler が UserOperation を取り扱えるようになっています。
これにより、中央集権的なリレーやゲートウェイを排除し、より分散化された処理が可能になります。

Ethereumプロトコルへの変更を回避

コンセンサス層の変更には年単位での対応必要になり、非常に時間がかかります。
ERC4337はコンセンサス層に依存しないため、実装や導入の障壁が低く、実用化のスピードを加速できるという利点があります。

幅広いユースケースへの対応

ERC4337は、以下のようなユースケースもサポートできます。

  • プライバシー保護型アプリケーション
  • アトミックな複数操作(複数の操作を1つのトランザクションでまとめて実行)
  • 開発者によるガス代の肩代わり(sponsored transaction)
  • ERC20トークンでのガス支払い
  • クロスチェーンに対応したガス支払いモデル

バリデーション(署名検証)・ガス支払い・実行の3要素をそれぞれ抽象化することで、これまで制限されていたトランザクション設計の自由度を大幅に拡張できます。

仕様

定義

UserOperation

ユーザーが実行したい操作に関するデータをまとめている構造体です。
Ethereumの通常のトランザクションと似ていますが、通常のtransactionと差別化するために「UserOperation」と定義されています。
以下のような項目があります(詳細は後述)。

  • to
  • calldata
  • maxFeePerGas
  • maxPriorityFeePerGas
  • nonce
  • signature

署名(signature)の検証方法はEthereumプロトコルでは定義されておらず、各コントラクトアカウント側が独自に実装します。

Sender

UserOperationを送信するコントラクトアカウントです。
EOA(Externally Owned Account)ではなく、コードを持ったコントラクトアカウントです。

EntryPoint

全てのUserOperationを受け付けて処理する中央的なコントラクトです。
bundlerはこのコントラクトの handleOps() 関数を通じて、複数のUserOperationをまとめて実行します。
ネットワーク上で1つだけ存在するコントラクトです。
bundlerは対応するEntryPointを事前にホワイトリスト登録する必要があります。

Bundler

UserOperationを集め、EntryPointへのトランザクションを構築してブロックに含める役割を持つノードです。
以下の方法で動作可能です。

  • 自身がブロックビルダーとして直接動作
  • mev-boostEIP7732 に準拠した提案者・ビルダー分離(PBS)インフラと連携
  • ERC-7796eth_sendRawTransactionConditional RPCを活用(利用可能な場合)

Paymaster

ユーザーに代わってガス代を支払うスマートコントラクトです。
ユーザー自身がETHを持っていない場合でも、PaymasterがETHでガス代を肩代わりできます。

Factory

新しいコントラクトアカウント(sender)をまだ持っていない場合に、そのコントラクトをデプロイするコントラクトです。
CREATE2 などを用いて、事前にアドレスを決定することが可能です。

Aggregator

複数のUserOperationの署名検証を1つにまとめて検証するためのコントラクトです。
別名「authorizer contract(認証者コントラクト)」とも呼ばれ、仕様はERC7766で定義されています。

Canonical UserOperation mempool

ERC7562に準拠した、許可不要・分散型のP2Pネットワーク上のメモリプールです。
全ての bundler はここで UserOperation を共有して有効性を検証します。

Alternative UserOperation mempool

ERC7562とは異なるルールで有効性を判断する、独自のP2Pメモリプールです。
最適化や特定用途向けに使用されます。

Deposit

Sender または Paymaster が EntryPoint コントラクトにあらかじめ預ける ETH(またはL2のネイティブ通貨)です。
UserOperationの実行時にガス代として差し引かれ、bundlerへの報酬支払いに使われます。

UserOperation

UserOperation は、ユーザーがコントラクトアカウント(CA)を通じて操作を実行するための構造体です。
Ethereumのプロトコル(コンセンサス層)を変更せずにAccount Abstractionを実現するため、従来のトランザクション形式ではなく独自の構造体を使って処理されます。

この構造体は、専用の UserOperation mempool に送信され、bundlerによって集約・処理されます。

UserOperationの各フィールドの説明

フィールド名 説明
sender address このUserOperationを実行するコントラクトアカウントのアドレス。
nonce uint256 リプレイ攻撃を防ぐための番号。アカウントごとにインクリメントされる。
factory address 新しいアカウントを作成するためのファクトリーコントラクトのアドレス。
EIP7702の場合は 0x7702、既存アカウントであれば address(0)
factoryData bytes factory に渡す初期化データ、またはEIP7702の初期化情報。使用しない場合は空配列。
callData bytes sender に渡す実行データ。処理の本体部分。
callGasLimit uint256 上記 callData を実行するために割り当てるガス量。
verificationGasLimit uint256 validateUserOp の検証処理に割り当てるガス量。
preVerificationGas uint256 Bundlerのために確保される追加ガス(パッキング処理やmempool対応のため)。
maxFeePerGas uint256 EIP1559における max_fee_per_gas に相当。
トランザクション全体で許容される最大手数料。
maxPriorityFeePerGas uint256 EIP1559における max_priority_fee_per_gas に相当。
Bundlerへの優先手数料。
paymaster address ガス代を肩代わりするPaymasterコントラクトのアドレス。自己負担の場合は空アドレス(0x0)。
paymasterVerificationGasLimit uint256 paymaster の検証コード実行に使うガス上限(指定されている場合のみ)。
paymasterPostOpGasLimit uint256 paymaster の後処理(postOp)で使われるガス上限(指定されている場合のみ)。
paymasterData bytes paymaster に渡す追加データ(署名などを含む場合も)。
signature bytes sender 内の認証ロジックに渡される署名データ。署名の構造や検証方法はSCAごとに自由に定義可能。

EIP1559については以下の記事を参考にしてください。

セキュリティに関する注意点

リプレイ攻撃(同じ操作を他のチェーンやEntryPointで再実行されるリスク)を防ぐために、署名は必ず chainidEntryPoint アドレスに依存するように設計されている必要があります。

EIP7702との関係

EIP7702の「authorization tuple」は、UserOperationの構造体とは別で提供される値です。
これは外部的に UserOperation に追加で付与される認可情報で、署名以外の柔軟な認証手法を補完します。

EIP7702については以下の記事を参考にしてください。

EntryPointインターフェース

ERC4337における中心的なコントラクトが EntryPoint です。
このコントラクトは、複数の UserOperation を一括で処理する役割を担います。
オンチェーンでの効率性とガスコスト削減のため、UserOperation は圧縮された形式(PackedUserOperation)としてEntryPointに渡されます。

PackedUserOperationの構造

フィールド名 説明
sender address 実行元となるコントラクトアカウントのアドレス。
nonce uint256 リプレイ攻撃防止のためのカウンター。
initCode bytes 新規アカウントを作成する時のファクトリーアドレスと初期化データを結合したもの。
EIP7702にも対応。
callData bytes 実際にアカウントで実行される処理内容。
accountGasLimits bytes32 verificationGasLimit(16バイト)と callGasLimit(16バイト)を結合したもの。
preVerificationGas uint256 bundlerが検証やパッキングに使うガスを補うための追加ガス。
gasFees bytes32 maxPriorityFeePerGas(16バイト)と maxFeePerGas(16バイト)を結合したもの。
paymasterAndData bytes paymaster関連のアドレス・検証データ・postOp用データを結合したもの(使わない場合は空)。
signature bytes senderが定義する検証ロジックに渡される署名データ。

このように、複数の UserOperation をあらかじめ効率的にパックすることで、処理時のガス効率を高め、EntryPoint内での一括処理が可能になります。

handleOps

function handleOps(PackedUserOperation[] calldata ops, address payable beneficiary);

ERC4337のコアとなる関数。

  • ops
    • PackedUserOperationの配列(複数のUserOperation)
  • beneficiary
    • UserOperationをブロックに含めたBundlerやExecutor(BundlerのようにUserOperationの実行を行うアドレス)に報酬として支払われるアドレス

処理の流れは以下です。

  1. EntryPointコントラクtpが ops を順番に検証し、必要に応じてウォレットやPaymasterを初期化
  2. 検証に成功した UserOperationcallData を実行
  3. 実行が完了したら使われたガス量に応じて手数料を beneficiary に支払う

この構造により、1つのトランザクション内で複数の UserOperation を安全かつ効率的に処理できます。

コントラクトアカウントのインターフェース

IAccount

interface IAccount {
  function validateUserOp(
    PackedUserOperation calldata userOp,
    bytes32 userOpHash,
    uint256 missingAccountFunds
  ) external returns (uint256 validationData);
}

EntryPointコントラクトによって呼び出され、UserOperation を検証して必要なガス代を支払う関数。

引数

引数名 説明
userOp PackedUserOperation calldata 署名を含む実行対象のUserOperation構造体。
userOpHash bytes32 userOp(署名を除く)、EntryPointコントラクトのアドレス、chainId を含めたハッシュ。
missingAccountFunds uint256 EntryPointコントラクトに預ける最低額。
senderのdepositが不足している場合は0は指定できません。
  • msg.sender が信頼されたEntryPointコントラクトであることを確認する必要があります。
  • 署名検証に失敗した場合、SIG_VALIDATION_FAILED (1) を返しrevertしないことが推奨されます。ただし、署名以外のエラーの場合は必ずrevertする必要があります。
  • ガス見積もりのため、SIG_VALIDATION_FAILED を返す場合でも、途中で処理を打ち切らず最後まで関数の処理を行うべきです。
  • EntryPointコントラクトに対して missingAccountFundsdepositTo 関数 経由で支払う必要があります。必要以上に多く預けた場合は後で withdrawTo 関数を使って引き出すことが可能です。

戻り値

戻り値は以下の3つの値をビット圧縮形式で返す必要があります。

変数名 説明
aggregator/authorizer 署名検証の状態を表します。
0 = 有効な署名
1 = 無効な署名
それ以外 = Aggregator(署名検証委任先)のアドレス
validUntil UserOperation の有効期限(最大6バイトのUNIXタイムスタンプ)。
0は無期限です。
validAfter UserOperation の有効開始時刻(6バイトのUNIXタイムスタンプ)。
この時刻より前は無効とみなされます。

IAccountExecute

拡張インターフェースです。

interface IAccountExecute {
  function executeUserOp(
    PackedUserOperation calldata userOp,
    bytes32 userOpHash
  ) external;
}

実行したい処理のデータである callData の実行を行う関数。
callData の実行処理は通常EntryPointコントラクトから呼び出していましたが、この関数を呼び出すように拡張しています。
これにより、複雑な制御の追加など実行ロジックをカスタムすることができるようになります。

Semi-abstracted Nonce Support

Ethereumにおける従来のトランザクションでは、単一の nonce によってリプレイ防止とトランザクション順序の制御を行います。
しかしこの仕組みでは、コントラクトアカウントが独自の順序管理や操作を行う柔軟性が制限されます。

ERC4337では、この制約を取り払いながらも安全性と一意性を保つために「Semi-abstracted Nonce」という新しい仕組みを導入しています。

nonce構造の変更

ERC4337では UserOperation.nonceuint256)を以下のように分割して扱います。

ビット 意味 長さ
上位 192 ビット key(分類キー) 192ビット
下位 64 ビット sequence(順序番号) 64ビット

この形式により、1つのアカウントで複数の異なる操作カテゴリ(key)を持ち、それぞれに独立した順序管理(sequence)を持たせることが可能になります。

ノンスの取得方法と検証

EntryPointコントラクトには以下のインターフェースが用意されています。

function getNonce(address sender, uint192 key) external view returns (uint256 nonce);

指定したsenderのアカウントとkeyに対応する現在のsequence番号を取得する関数。

Bundlerは UserOperation を検証する時、この関数を使用して nonce が正しいか事前に確認する必要があります。

nonceの仕様

  • sequenceは常に昇順である必要があります。
  • 同一のkeyに対して、2つ以上の UserOperation を同時に処理することはできません。
  • 新しいkeyは任意の値で導入でき、その時点で sequence = 0 から開始します。

これにより、異なるカテゴリの操作を並列に進行させるといった高度なアカウント制御が可能になります。

利用例

単純な順序制御(シーケンシャル nonce

従来通りの動作をさせたい場合、以下のように制限すれば良いです。

require(userOp.nonce < type(uint64).max);

これは、key = 0 固定、かつsequenceのみを使う単純なモデルです。

管理用操作と通常操作の分離

コントラクトアカウントに「管理操作(ADMIN)」と「通常操作(NORMAL)」の2種類の操作チャネルを持たせたい場合、keyを使い分けることができます。

bytes4 sig = bytes4(userOp.callData[0 : 4]);
uint key = userOp.nonce >> 64;

if (sig == ADMIN_METHODSIG) {
    require(key == ADMIN_KEY, "wrong nonce-key for admin operation");
} else {
    require(key == 0, "wrong nonce-key for normal operation");
}

このようにすることで、管理処理と通常処理を別々の順序で並列実行可能になり柔軟な操作制御が実現できます。

EntryPoint コントラクトの必須機能

ERC4337のEntryPointコントラクトの中心的な機能は、handleOps() 関数です。
この関数は複数の UserOperation をまとめて処理するもので、トランザクションの検証から実行、ガス代の支払いまでを行います。

具体的な処理は以下のようになっています。

bundle-seq.png

handleOps の呼び出し

function handleOps(PackedUserOperation[] calldata ops, address payable beneficiary)
  • ops
    • 処理対象の UserOperation の配列。
  • beneficiary
    • 処理後にガス代を受け取るアドレス(通常はBundler)。

検証ループ(Verification Loop)

UserOperation に対して以下の処理が行われます。

  1. コントラクトアカウントの作成(initCode が指定されている場合)

    • senderがまだデプロイされていない場合、UserOperation.initCode を使って新しくデプロイされます。
    • Factoryコントラクトが 0x7702 の場合は、EIP7702に従ってEOAと認証情報の整合性を検証します。
    • initCode が空でsenderが存在しない、またはデプロイ先が一致しない場合は処理は失敗します。
  2. ガス代の最大見積もり

    • validationGasLimitcallGasLimit と現在の maxFeePerGas から、最大支払い額を計算します。
  3. 必要なデポジット金額の計算と確認

    • すでにEntryPointコントラクトにdepositされているか、不足分を missingAccountFunds として計算します。
  4. validateUserOp() の呼び出し

    • senderに対して署名検証、nonce検証、必要に応じてデポジット支払いが行われます。
    • 署名が無効であれば SIG_VALIDATION_FAILED を返されますが、署名以外のエラーであればrevertします。
    • この段階で失敗した UserOperation は、以降の実行対象から除外されます(必要に応じて handleOps 全体を revert させることも可能)。
  5. デポジットの妥当性確認

    • 全ての必要ガスコストをカバーできるだけのdepositがあるかを最終確認します。

実行ループ(Execution Loop)

検証に成功した UserOperation に対して以下の処理が行われます。

  1. callData の実行

    • callData をそのまま呼び出すか、先頭の関数シグネチャが executeUserOp()(IAccountExecute インターフェース)の場合は、コントラクトアカウント内の以下の関数を実行します。
    executeUserOp(userOp, userOpHash)
    
  2. 未使用ガスに対するペナルティ

    • callGasLimitpaymasterPostOpGasLimit に対して 未使用ガスが40,000以上残っている場合、10%のペナルティが差し引かれます。
    • これは意図的に大量のガスを予約し、他のトランザクションをブロックする行為を防止するためです。
    • ブロックに含められるガスの量には上限があるため、余計なガス代をかけることで他のトランザクションを含めるスペースが小さくなり、他のUserOperationの処理の実行が遅くなる可能性があるためです。
  3. 残りのガス代の返金処理

    • pre-charged(前払い)されたガスと実際に使用されたガスとの差額をsenderに返金します。
  4. beneficiary への報酬支払い

    • UserOperation から徴収された手数料(gasUsed × effectiveGasPrice)を、まとめて beneficiary (通常はBundler) に送金します。

Bundler側での推奨処理

Bundlerは UserOperation を受け入れる前に、EntryPoint.handleOps() をローカルでシミュレーション実行することが推奨されています。
これにより以下の事前確認ができます。

  • 署名が正しいか
  • depositされているガス代が十分か
  • ガス代がきちんと支払われるか

失敗する UserOperation は mempool に追加せず、他のノードにも伝播させてはいけないです。

ERC4337におけるJSON-RPC API

ERC4337では、ユーザーが従来のトランザクションではなく UserOperation を送信するために、Ethereumノードが提供する標準のJSON-RPC APIに新たなエンドポイントを追加する必要があります。
これにより、Bundlerが UserOperation を受け取り、P2Pメモリプールで中継・共有できるようになります。

主なJSON-RPCメソッド

ERC4337では以下の2つのメソッドが特に重要です。

これらのRPCインターフェースは ERC7769 にて定義されています。
この仕様に準拠することで、ウォレット・アプリケーション・Bundlerが共通のプロトコルでやりとりできるようになります。

ERC7769については以下の記事を参考にしてください。

eth_sendUserOperation

UserOperation オブジェクトをbundlerノードに送信するためのメソッド。
送信されたUserOperationは、bundlerのローカルメモリプールに追加され、他のノードにも中継されます。

eth_getUserOperationReceipt

指定した UserOperation の実行結果(receipt)を取得するためのメソッド。
実行済みであれば以下のような情報が返されます。

  • 実行されたブロックの番号やハッシュ
  • EntryPointコントラクトによる処理結果
  • 成功/失敗の状態
  • 使用されたガス量

EIP712署名への対応

ERC4337では、UserOperation に対する署名検証を柔軟にするために、EIP712形式の署名をサポートしています。
EIP712は、構造化されたデータに対して署名を行うための規格です。

EIP712については以下の記事を参考にしてください。

userOpHash の生成方法

UserOperation を識別・署名対象とするハッシュ(userOpHash)は、EIP712に準拠した以下の2つのステップで構成されます。

ドメイン情報のハッシュ(TYPE_HASH

まず、署名対象とするドメインを定義するため、以下の内容をハッシュ化します。

bytes32 constant TYPE_HASH =
    keccak256(
        "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
    );

このドメイン情報には以下の内容が含まれます。

項目 説明
name EIP712メッセージの名前
version バージョン情報
chainId チェーンID(例:Ethereum Mainnetは1)
verifyingContract 検証を行うコントラクト(EntryPointコントラクトのアドレス)

このハッシュは署名ドメインの一意性を保証するために使用されます。

PackedUserOperationの型情報のハッシュ

UserOperationの本体に相当するデータ構造を次のように定義し、その型情報をハッシュ化します。

bytes32 constant PACKED_USEROP_TYPEHASH =
    keccak256(
        "PackedUserOperation(address sender,uint256 nonce,bytes initCode,bytes callData,bytes32 accountGasLimits,uint256 preVerificationGas,bytes32 gasFees,bytes paymasterAndData)"
    );

この構造体には、トランザクションの詳細(送信元、コール内容、ガス制限など)を含めたデータが含まれており、この型に基づいて署名対象のハッシュが生成されます。

EIP7702署名認可への対応

ERC4337は、EIP7702に対応することで、EOA(Externally Owned Account)からコントラクトアカウントへの柔軟な移行や、特別な認可処理を可能にしています。

認可(CAに特定の操作の実行を許可)の情報をまとめたデータ構造(タプル)には以下のデータが含まれます。

  • 認可を行うEOAアドレス
  • 認可対象のコントラクトアカウント
  • その認可に署名した署名データ(EOAによる署名)

このタプルをUserOperationに追加することで、EOAがあらかじめコントラクトアカウントに「この操作は正当だ」と認可しておく仕組みです。

eth_sendUserOperation における eip7702Auth パラメータ

EIP7702を有効にしているネットワークでは、eth_sendUserOperation RPCメソッドで eip7702Auth という追加のパラメータが使用可能です。

  • eip7702Auth は、EIP7702に準拠した認可タプルである必要があります。
  • sender(ユーザーのEOA)によって署名されている必要があります。

Bundlerは、全ての UserOperation に含まれる eip7702AuthauthorizationList に集約し、SET_CODE_TX_TYPE という特殊なトランザクションタイプでバンドルを実行します。

initCode による特殊な処理(EIP7702対応)

UserOperationinitCode が特別な形式の場合、以下のような処理が行われます。

通常の initCode 処理

  • 先頭20バイト
    • Factoryコントラクトアドレス
  • それ以降
    • そのFactoryコントラクトに渡す初期化用 calldata

EntryPointコントラクトはFactoryコントラクトを呼び出してCAを生成します。

EIP7702対応時の特別な処理

  • initCode0x7702EIP7702フラグ)で始まる
  • 以降がゼロ埋めされている

この UserOperation はFactoryコントラクト経由ではなく、EOAによる委任(delegate) という形でコントラクトアカウントが作成されたとみなされます

この場合、ハッシュ計算(userOpHash)の際、通常 initCode の先頭にあるはずのFactoryコントラクトアドレスを、EXTCODECOPY 命令を使って取得した「デリゲート先コントラクトアドレス」に置き換えます。

さらに、initCode の残りの部分(20バイトより後)は、コントラクトアカウント自身の初期化処理を呼び出すために使われます。

この設計により、Factoryコントラクトを用いずに、EOAが自分で認可したアカウントを直接初期化できる柔軟性を提供します。

なお、initCode を空にすることも可能です。
その場合、EntryPointコントラクトはEIP7702のデリゲート情報をハッシュに含めず、既存のアカウントに対して実行されることになります。

認可処理のガスコスト

EIP7702における認可処理の実行には、1件あたり PER_EMPTY_ACCOUNT_COST = 25000 gas の消費が必要とされます。
このガス消費はEntryPointコントラクト外で行われるため検知できるません。
そのため、ユーザーは preVerificationGas にその分を上乗せして指定する必要があります。

Paymasterによるスポンサー機能の拡張

ERC4337では、EntryPoint の処理フローにPaymasterが追加されることで、アプリ開発者がユーザーのガス代を肩代わりしたりユーザーがERC20トークンで手数料を支払えるような多様なユースケースを実現できます。
以下のフローで処理が実行されます。

bundle-seq-pm.png

Paymaster付き UserOperation の処理フロー

UserOperationpaymasterAndData フィールドが空でない場合、EntryPoint.handleOps() は以下の拡張された処理を行います。

検証フェーズ(Validation)

  1. アカウント側の validateUserOp を呼び出す

    • このとき missingAccountFunds = 0 として呼ばれる(=Accountのdepositは使用しない)。
  2. Paymasterの預け金を確認

    • PaymasterがEntryPointコントラクトに十分な量のdeposit(支払い用のETH)をしているか確認。
  3. Paymaster による検証

    • validatePaymasterUserOp(userOp, userOpHash, maxCost) を呼び出す。
    • 戻り値の context が空でなければ、この情報は後続の postOp 呼び出しに使用される。
    • validationData は署名検証や有効期限などに使われる。

context は、Paymasterの validatePaymasterUserOp() 関数が返す任意のバイト列データです。
この context は検証フェーズで生成され、実行フェーズの postOp() 関数にそのまま渡されます。

この仕組みは、検証時にPaymaster側で一時的に計算・取得した値を保持しておき、後続の処理で再利用したい場合に使います。
例えば以下のようなケースがあります。

  • トークン支払い型Paymasterで、ユーザーが支払うべき手数料のトークン残高やレートを取得
  • アカウントに紐づく状態やアクセス情報を一時保存

これにより、Paymasterはガスを節約しつつ必要な情報を postOp() に渡して、成功・失敗に応じたロジック(課金・リファンドなど)を実装できます。

validationData は、validatePaymasterUserOp() または validateUserOp() の戻り値として返される uint256 型のビットデータです。

| フィールド | 内容 |
| 下位 20bit| シグネチャ検証の結果(例: 1 = 署名無効) |
| 中位 64bit| validUntil:UserOperationが有効な期限 |
| 上位 64bit| validAfter:UserOperationが有効になる開始時刻 |

このデータにより、UserOperationが特定の期間内のみ有効であることを表現でき、署名の有効性や期限制御が可能になります。

実行フェーズ(Execution)

  1. アカウントの executeUserOp を実行(または callData 呼び出し)

  2. Paymaster に対して postOp を呼び出す(必要な場合)

    • validatePaymasterUserOp の戻り値 context が空でなければ、以下の形式で呼ばれる。
    postOp(mode, context, actualGasCost, actualUserOpFeePerGas)
    
    • mode は以下のいずれかの値です。
      • opSucceeded
        • UserOperation が成功した場合
      • opReverted
        • UserOperation が失敗した場合(この場合でもガス代はPaymasterが支払う)

Paymaster のガス代支払いの仕組み

Paymasterは、ユーザーの代わりにEntryPointコントラクトへのdepositからガス代を支払うことになります。
このdepositは、Paymaster自身が EntryPoint.depositTo() を使って事前に預けておく必要があります。

Stake(ステーク)による信頼確保

悪意のあるPaymasterによる以下のようなDoS攻撃を仕掛けるのを防ぐために、Paymasterにはステーク(ロックされたETH)を保持させることが推奨されています。

  • 検証は通るが実行時に意図的に revert する
  • ガスを大量に消費してBundlerや他のUserOperationに影響を与える

このような攻撃を防ぐために、addStake() で一定期間引き出せないETHを預けさせ、問題行動が検知された場合に「一定期間トランザクション受付を停止する」などの制御が可能になります。

ステーク管理用関数(EntryPoint)

function addStake(uint32 _unstakeDelaySec) external payable;
function unlockStake() external;
function withdrawStake(address payable withdrawAddress) external;
  • addStake
    • ETHをステークとしてロック。
    • unstakeDelaySec (アンロック可能になるまでの待機時間)を指定。
  • unlockStake
    • アンロック要求(一定時間待つ必要あり)
  • withdrawStake
    • アンロック後にステークを引き出す

デポジット管理(EntryPoint)

function balanceOf(address account) public view returns (uint256);
function depositTo(address account) public payable;
receive() external payable;
function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external;
  • depositTo
    • ETHを指定アカウントのdepositとして追加
  • withdrawTo
    • depositからETHを出金
  • balanceOf
    • 現在のdeposit残高を確認

Paymasterだけでなく、Account側も同じインターフェースを使ってdepositを管理できます。

Bundlerの動作:UserOperation受信後の処理フロー

ERC4337において、Bundlerは UserOperation を受け取ってEntryPointコントラクトに送る役割を担います。
この流れはEthereumのトランザクション実行までのフローと似ていますが、複数段階のバリデーションやmempool制御が含まれており、より高度な処理が求められます。

以下がBundlerの動作と各種検証ロジックのフローです。

bundle-build-full-seq.png

Bundlerの基本処理ステップ

  1. UserOperationの受信

    • クライアントが eth_sendUserOperation をRPCで呼び出し、UserOperation をBundlerに送信します。
  2. 1回目の検証(受信時)

    • Bundlerは UserOpをmempool に追加する前に、初期検証を行います。
    • この検証に失敗した UserOperation をmempoolは破棄されエラーが返されます。
  3. 2回目の検証(バンドル構築中)

    • mempool内の UserOperation を使ってバンドルを作成する時各 UserOperation を再検証します。
    • 有効なものはバンドルに追加し、失敗したものはスキップされます。
  4. 3回目の検証(ブロック提出直前)

    • 最終的に構築されたバンドル全体に対して、再度検証を行います。
    • 無効な UserOperation が含まれていた場合、それらを除外して評判スコアを更新します(詳細はERC7562参照)。

ERC7562については以下の記事を参考にしてください。

初期検証

Bundlerが UserOperation をmempoolに追加する前に実行する基本チェック項目は以下のとおりです。

チェック項目 内容
アカウント存在の整合性 senderが既存のコントラクトか、initCode が空でないこと(どちらか一方のみ)。
initCode の整形 先頭20バイトがFactoryコントラクトアドレスまたは 0x7702 フラグであること。
Factoryステーク状態の記録 Factoryコントラクトがグローバルステートを読む場合、ステークが必要。
検証ガス制限 verificationGasLimitpaymasterVerificationGasLimitMAX_VERIFICATION_GAS (=500000) 未満であること。
preVerificationGas シリアライズ済み calldata の gas + PRE_VERIFICATION_OVERHEAD_GAS (=50000) をカバーすること。
Paymasterの妥当性 paymasterAndData が空でない場合:
① コントラクトコードが存在する。② 十分なdepositがある。③ banされていないこと。
callGasLimit CALL 処理(valueあり)の最小ガスを上回っていること。
fee設定の下限 maxFeePerGas および maxPriorityFeePerGas が、basefeeおよびBundler設定値以上であること
senderの一意性 同じsender+ nonceUserOperation がすでに存在しないこと(ある場合は、より高いfeeでの置き換えのみ許可)
※ステーク済みのsenderのみ例外的に複数 UserOperation を許可。

バンドル構築とブロック提出のイメージ

  1. AliceとBobが UserOperation を送信 → Bundler RPCに到達
  2. Bundlerはそれらをmempoolに追加(検証済みのもののみ)
  3. 次のブロックに向けたバンドル作成時、再度検証を実施
  4. 検証成功した UserOperation だけを含むバンドルが EntryPoint.handleOps() を通じて実行される

UserOperationのSimulation

ERC4337では、UserOperationの検証を事前にするために、simulationという新しい仕組みを導入しています。
これは、Ethereumの通常のトランザクション検証とは異なり、EVMのステートや他のトランザクションによって結果が変わる可能性がある UserOperation に対応するために必要です。

なぜシミュレーションが必要か

Ethereumの通常のトランザクションでは、以下のような静的検証が可能です。

  • ecrecover による署名者(EOA)の検証
  • nonce が現在のEOAの nonce と一致するか
  • アカウント残高が十分か
  • gasLimit が基本コストを超えているか
  • chainId が現在のチェーンと一致するか

これらは他のトランザクションに依存せず、EVMのステートに影響されないため単純な検証が可能です。

一方、UserOperation は以下のような状態依存の検証が必要です。

  • account.validateUserOp の呼び出し
    • UserOperation を検証する関数。
  • paymaster.validatePaymasterUserOp の呼び出し
    • Paymasterがガス代の支払いを検証
  • initCode によるコントラクトアカウントのデプロイ
  • 時間制限(validAfter, validUntil)の確認
    • validAfter
      • UserOperation が有効になる開始時刻(Unix timestamp)
    • validUntil
      • UserOperation が期限切れになる時刻

このような状態依存の処理を事前に安全に確認するには、EVM上でその UserOperation を仮実行して結果を確認する必要があるため、simulationが導入されています。

シミュレーション仕様

実行方法

Bundlerは handleOps() に対象の UserOperation だけを含めて読み取り呼び出しを行います。
これはチェーンには書き込まれず、オフチェーンでの検証に使われます。

シミュレーション範囲

検証フェーズのみを実行対象とし、実行フェーズ(callData の実行)は対象外です。
シミュレーションの早期終了のために、UserOperation の配列の2つ目に「常に失敗する UserOperation」をバンドルに含めることも許可されています。

実行内容

シミュレーションでは以下の検証を行います。

  1. initCode があればsenderアカウントを作成
  2. account.validateUserOp() を呼び出し
  3. paymaster.validatePaymasterUserOp()(Paymasterが指定されている場合)
  4. validateUserOp または validatePaymasterUserOp の戻り値に validAfter / validUntil が含まれていれば、現在のブロック時間がその範囲内であることを確認

注意点

  • シミュレーションが失敗した場合、その UserOperation は無効とされ、mempoolから破棄されます。
  • UserOperation がすぐに期限切れになるような状態(validUntil が近すぎる)である場合も、Bundlerはその UserOperation を破棄する必要があります。
  • Bundlerは、validAfter / validUntil を確認するためにEVMのトレース(trace)を用いて戻り値をデコードする必要があります。

DoS防止と制約

BundlerがこのシミュレーションによりDoS攻撃を受けないよう、以下のような制限が必要です。

  • validateUserOp()validatePaymasterUserOp() 内で使用可能なopcodeやストレージ操作に制限を設ける
  • これらのルールはERC7562で定義されており、シミュレーションが安全に完了するように設計されています

preVerificationGas の推定

ERC4337における preVerificationGas は、UserOperation をブロックチェーン上で実行する前に必要となるガス量を示すデータです。
この値は UserOperation のデータ送信や検証に関わる固定的なガスコストを表し、Bundlerが適切なコスト回収を行えるように設計されています。

preVerificationGas とは

preVerificationGas は、UserOperation の実行する処理データである calldata の送付・固定化されているデータ・静的なメモリ確保にかかるガス量です。
実際の処理(callData の実行)とは別に、EntryPointコントラクトの検証処理以前に最低限かかるガスをカバーするためのものです。
Bundlerが UserOperation を安全に処理するためには、この値を正確に見積もる必要があります。
この値が適切に見積もられていないと、Bundlerが事前コストを回収できなくなるなど、トランザクションが 途中で失敗する可能性があります。

推定値の要素

仕様では準的な計算式は定められていませんが、以下のコストを含める必要があります。

ベースのトランザクションコスト

Ethereumのトランザクションには最低限 21000 gas が必要。
バンドル内のUserOperation数に応じて分割される。
例:バンドルに3つのUserOperation → 21000 / 3 = 7000 gas

calldata のガスコスト(EIP2028準拠)

calldataに含まれる各バイトは、値に応じて以下のガスがかかる。

  • 0バイト
    • 4 gas
  • 非0バイト
    • 16 gas

EIP2028については以下の記事を参考にしてください。

EntryPointコントラクトの静的コードの処理

validateUserOp の呼び出しに伴う、EntryPointコントラクト内部の静的なコード実行のためのガス。

固定長フィールドのメモリ読み込みコスト

UserOperation 構造体に含まれる固定フィールド(例:nonce, sender等)をEVMメモリに読み込む際の静的メモリ使用量。

Paymasterの validatePaymasterUserOp のcontextによるメモリコスト(任意)

Paymasterが context を返す場合、そのメモリ領域の読み取り・展開・保持に関わるガス消費。

innerHandleOp() の外部呼び出しガス

  • EntryPointコントラクトの内部処理の主要部分である innerHandleOp() 関数の呼び出しコスト。
  • この値はバンドル内の位置によって変動する。

EIP7702の認可コスト(任意)

  • EIP7702の認可が含まれている場合、追加で 25000 gas が想定される。

EIP7623による calldata ガスの底上げ(floor price)

  • tx.gasUsed の新しい計算式に基づき、以下を推定値として用いる。
    • シミュレーション中の検証処理(アカウント作成、validateUserOp、paymaster)のガス使用合計
    • 実行ガス+postOpガスの合計の10%

Bundlerに求められる対応

preVerificationGas には将来のメモリ拡張やバンドル内での位置の変動に備えて余裕を持たせる必要があります。
preVerificationGas を必要最小限に見積もった場合、以下のようなリスクがあります。

  • EntryPointコントラクト内で validateUserOp() を実行中にガス不足により失敗
  • ガス不足により UserOperation全体が失敗し、バンドルされた他の操作にも影響が出る
  • Bundlerが肩代わりしたガス代の一部を回収できなくなる

Alternative Mempools(代替メモプール)について

ERC4337では、UserOperation の検証におけるセキュリティを保つために非常に厳格なシミュレーションルールが定められています。
これにより、不正なPaymasterによるDoS攻撃やネットワークの不安定化を防止できます。
しかし、一部のPaymasterに対しては、安全性が保証されていることが確認できるにもかかわらず、標準ルールでは許可されないケースも存在します。

このような状況に対応するために導入された概念がAlternative Mempools(代替メモプール)です。

なぜ代替メモプールが必要か

通常のメモプールでは、ERC7562に定義されたopcodeの制限やストレージアクセス制限などが厳格に適用されます。
これは未知または悪意あるPaymasterがシミュレーション時に不正な動作をしないようにするための措置です。
しかし、この制約に縛られずにPaymasterを使用したいという場合があります。

代替メモプールとは何か

通常のメモプールとは異なる独自の検証ルールを適用するためのメカニズムです。
特定の制限(例:opcodeの使用やガス上限)を緩和するために用いられ、一部のBundlerだけで共有されるP2Pネットワーク内の独立したメモプールとして動作します。
これにより、特定の条件を満たすPaymasterのみ使用可能など、より柔軟な検証ルールが適用可能になります。

なぜ単純なホワイトリストでは不十分か

単に「このPaymasterは安全だからOK」としてBundlerが個別に対応すると、その UserOperation は一部のバンドラーにしか伝播せず断片化される可能性があります。
ユーザーから見て予測できない挙動になり、ネットワーク全体の一貫性が損なわれます。

解決策としての「代替メモプール」

全ての参加バンドラーが特定の緩和ルールを採用する明確な意思表示を行い、それに基づいた共通の検証・伝播ルールを持つ別メモプールを形成します。
これにより、緩和ルールに準拠した UserOperation が、確実かつ広範に伝播・処理されることが保証されます。

ERC7562

代替メモプールの定義とルールは ERC7562に規定されています。
どのようなルールを緩和するか、どうやってBundler間で伝播するか、どうやってreputationを扱うかといった詳細も記載されています。

ERC7562については以下の記事を参考にしてください。

Bundling

Bundlingとは、Bundlerが複数の UserOperation を集めて、1つのトランザクションとしてブロックチェーンに送信するプロセスです。

バンドリング中の必須処理

Bundlerはバンドルを作成する時に、以下の条件を満たす必要があります。

  • 他の UserOperation のsenderアドレスにアクセスする UserOperation は除外する
  • 他の UserOperation のバリデーション中にFactoryコントラクトにより生成されたアドレスにアクセスする UserOperation も除外する
  • 同じPaymasterを使う複数の UserOperation に対しては、Paymasterの残高を追跡して全ての手数料を支払えるだけの残高があるか確認する

トランザクション生成後に行うべき検証

Bundlerはバンドルをブロックに含める前に、以下の処理が推奨されます。

  • debug_traceCall を使って最大限のガスでシミュレーションを実行し、handleOps 全体が正しく動作するか検証する。
    (opcodeやストレージアクセスの制限に違反していないかも確認)
  • もしシミュレーションが失敗た場合は、最後にEntryPointコントラクトから CALL されたコントラクトアカウントを特定してそれが原因であるとみなす
    ※ Bundlerは発行された FailedOp イベントだけを根拠に原因を決める蹴ることは避ける必要があります。

不正な UserOperation の処理

不正が見つかった UserOperation はバンドルとMempoolの両方から削除する必要があります。
エラーの原因がFactoryコントラクトまたはPaymasterの場合、以下の処理を行います。

  • 送信者(sender)がステークしていない場合
    • その Factoryコントラクト / Paymaster を「ban(利用禁止)」にする
  • 送信者がステークしている場合
    • Factoryコントラクト / Paymasterはbanせず、そのsenderをbanする
  • debug_traceCall が成功するまで上記の修正を繰り返す

セキュリティ上の注意点

ステークされたエンティティ(sender、factoryコントラクト、paymaster)は、他の UserOperation と一時的にデータを共有する場合があります。
そのため、個別の UserOperation と同様に、handleOps 全体のバリデーションにも同じ制約(opcode禁止・ストレージアクセス制限)を適用する必要があります。
これをしないと、攻撃者が禁止されたopcodeを使ってon-chain実行中かどうかを判別し、FailedOp イベントを意図的に引き起こす可能性があります。

ブロックにバンドルを含める際の注意点

ブロック内の先行トランザクションが原因で、UserOperation が失敗することを避ける必要があります。
そのために以下の対策が推奨されます。

  • EIP2930のアクセスリストを活用して競合を防ぐ。
  • もしくはバンドルをブロックの最初のトランザクションとして配置する。

EIP2930については以下の記事を参考にしてください。

エラーコードの扱い

EntryPointコントラクトでは、UserOperation の検証中にエラーが発生した場合、revert を行う必要があります。
ただし、そのエラーがどのエンティティ(sender、factoryコントラクト、paymaster)によって発生したかを、Bundlerが正確に判断できるようにすることが重要です。

エラー原因の特定方法

失敗の原因となったエンティティ(sender、factoryコントラクト、paymaster)は、以下のようにして特定されます。
EntryPointコントラクトによる処理中に発生したrevertの直前に呼び出された(CALLされた)コントラクトをコールトレースで特定。

コールトレースとは、EVM上で実行された処理の呼び出し履歴を追跡・記録する仕組みです。
EVM上の処理では、コントラクトから別のコントラクトへ CALL 命令を使って関数を呼び出すことがあります。
コールトレースを取得すると、「どのコントラクトがどの順番でどの関数を呼び出したか」が詳細にわかります。
コールトレースは debug_traceCall などのRPCを通じて取得できます。

  • その最後に呼び出されたエンティティ(sender、factoryコントラクト、paymaster)がエラーの原因とみなされます。

使用されるエラー形式

EntryPointコントラクトは、シミュレーション中や本番時に以下の明示的なエラー型のみを使って`revertする必要があります。

  • SignatureValidationFailed()
  • FailedOp()
  • FailedOpWithRevert()

これにより、バンドラーはcall-tracingと組み合わせてエラーの原因を正確に判断できます。

エラーコードの命名規則(AA##)

各エラーには識別用のイベントコード(AA##)が付与されており、接頭辞でエラーの種別を分類しています。

イベントコード 発生箇所 意味
AA1x sender作成時 Factoryでのアカウント作成エラー
AA2x sender検証時 validateUserOpの実行エラー
AA3x paymaster検証時 validatePaymasterUserOpの実行エラー

このように、イベントコードはエラーの種類を迅速に特定するために用いられます。

補足

DoS対策としてのバリデーションと実行の分離

Account Abstractionをコントラクトアカウントだけで実現しようとすると、「ガス代を本当に支払うか確認するには実行してみるしかない」という問題があります。
このような仕組みでは、攻撃者が実行の最後で revert をする悪意ある UserOperation を大量に送ることで、バンドラーやブロックビルダーに過剰な負荷を与えるDoS攻撃が可能になってしまいます。

この問題を防ぐため、ERC4337では「バリデーション(署名検証と支払い能力の確認)」と「実行」を完全に分離します。
アカウントには validateUserOp 関数を実装させ、事前に署名の検証や支払い確認のみを行い、成功した場合にのみ callData による実行を行うようにします。

バリデーションの制限による安全性確保

Bundlerが誤って無効な UserOperation を含めてしまうことを防ぐため、バリデーションで使用できるストレージやオペコードに制限を設けます。
以下のようなケースに備えています。

  • 個別ではバリデーションに成功しても、ブロックに含める段階で失敗するようなUserOperation
  • 他の UserOperation と干渉してバリデーションに失敗するような UserOperation

これらを防ぐために、バンドラーはERC7562に定義された検証ルールに従って UserOperation を受け入れる必要があります。

レピュテーション(評判)による制御とスロットリング

PaymasterやFactoryコントラクトのようなグローバルエンティティは複数の UserOperation に共有されるため、1つの不正が複数の UserOperation を無効にしてしまう可能性があります。
これを防ぐため、以下の仕組みが用意されています。

  • 一定の割合で UserOperation を無効化したエンティティは、スロットリング(処理速度の低下)または一時的なbun(mempoolから除外)の対象となります。
  • ステーク(ETHの預け入れ)を要求することで、攻撃コストを高くしてSybil攻撃(偽装エンティティの大量作成)を防止します。
  • ステークは没収(スラッシュ)されず、所定の unstakeDelay を過ぎれば引き出せます。

Paymasterによるガス抽象化

Paymasterは、トランザクションの送信者以外がガス代を負担する仕組みを実現するためのコントラクトです。
例えば、ERC20トークンでガス代を支払う「トークンペイマスター」は、あらかじめ最大の支払い見込み額を請求し、最終的な実行後に余剰分をユーザーに返金することができます。

コントラクトアカウントの初回作成

コントラクトアカウントの作成には、Factoryコントラクトを利用します。
これは CREATE2(オペコード 0xF5)を使ってアドレスを決定的に生成することで、未作成のアカウントであっても事前にそのアドレスを予測可能とします。

  • initCode フィールドには、20バイトのFactoryコントラクトアドレスとそれに渡す初期化用の calldata が含まれます。
  • initCode の結果、指定したsenderアドレスにコントラクトが存在しない場合や、存在していた場合に正しくないコントラクトだった場合、UserOperation は中止されます。
  • Factoryコントラクトはストレージにアクセスする場合、DoS対策の観点からステークが必要になります。

この仕組みにより、ユーザーは事前にコントラクトを作成しなくても、安全にアドレスを用意し、そこに資金を送ることができます。
これによりEOAと同様に、即座に受け取り可能なアカウントという体験を実現します。

互換性

ERC4337はEthereumのコンセンサスレイヤー(ブロック生成やトランザクションの確定に関わる根幹部分)を変更しないため、Ethereum全体としての互換性の問題はありません。

しかし、ERC4337以前に作成されたコントラクトアカウントとは互換性がありません。
これは、ERC4337において重要な validateUserOp 関数が、それらのアカウントには実装されていないためです。

一方で、既存のアカウントに「信頼できるUserOperation送信者を許可する」ような仕組み(例:許可されたコントローラによるアクセス制御)がある場合には、そのアカウントの代わりにERC4337対応のアカウントを作成し、元のアカウントの検証ロジックをラップした上で、その新しいアカウントを「信頼できる送信者」として設定することで互換性を保つことができます。

参考実装

セキュリティ

ERC4337では、EntryPointコントラクトが全てのトランザクション処理の中心に位置するため、徹底した監査と形式的検証が必要です。
この設計により、個々のコントラクトアカウントが担う責任は減少し、検証すべき範囲も限定的になりますが、その代わりにEntryPointコントラクトが単一障害点となるため、高い堅牢性が求められます。

EntryPointの安全性

EntryPointコントラクトの検証において、以下の2点が特に重要です。

  • 不正な呼び出しの防止
    EntryPointは、事前に validateUserOp が成功した送信者アカウントに対してのみ callData を実行する必要があります。

  • 不正な手数料支払いの防止
    validateUserOp が成功した場合に限り、EntryPointはそのUserOperationに指定された callData を実行する必要があります。

各種コントラクトの安全な呼び出し元検証

EntryPointコントラクトをセキュリティモデルでは、以下のように各コントラクトが呼び出し元を限定する必要があります。

  • Factoryコントラクト
    createAccount() 関数は必ず entryPoint.senderCreator() アドレスからのみ呼び出されるように確認する必要があります。

  • Paymasterコントラクト
    validatePaymasterUserOp()postOp() 関数は、EntryPointからのみ呼び出されるようにする必要があります。

  • Aggregatorコントラクト
    validateSignatures() 関数も、EntryPointコントラクトからの呼び出しのみを受け入れるよう制御する必要があります。

  • EIP7702に基づくアカウント
    初期化関数は entryPoint.senderCreator() からのみ呼び出されるようにし、同じコードが複数回実行されないようアカウント側で制御する必要があります。

なお、EntryPointコントラクト自体はEIP7702アカウントの初期化状態を判別できないため、Walletアプリケーション側で initCode を繰り返し送信しないようにするべきです。

コントラクトアカウントにおける追加の注意点

  • ストレージレイアウトの衝突回避
    多くのアカウントはアップグレード可能な設計になっており、異なる実装間でのストレージの衝突を防ぐ必要があります。そのためには、ERC7201で定義されている「ダイヤモンドストレージ」パターンのような手法を用いることが推奨されます。

ERC7201については以下の記事を参考にしてください。

  • 一時ストレージ(Transient Storage)の扱い
    EIP1153による一時的なストレージを使う場合、ERC4337では異なる送信者の複数の UserOperation が1つのトランザクションに含まれる可能性があるため、アクセス制御や機密情報に関わる場合は手動でクリーンアップ処理を行う必要があります。

引用

Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn), Alex Forshtat (@forshtat), Kristof Gazso (@kristofgazso), Tjaden Hess (@tjade273), "ERC-4337: Account Abstraction Using Alt Mempool [DRAFT]," Ethereum Improvement Proposals, no. 4337, September 2021. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-4337.

引用

Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn), Alex Forshtat (@forshtat), Kristof Gazso (@kristofgazso), Tjaden Hess (@tjade273), "ERC-4337: Account Abstraction Using Alt Mempool [DRAFT]," Ethereum Improvement Proposals, no. 4337, September 2021. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-4337.

AA・ECR4337関連記事

この章では、AA(AccountAbstraction)やERC4337についてのわかりやすい記事をまとめていきます。
ぜひこの章の記事を参考にしてください。

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

Account Abstraction勉強会

AA(Account Abstraction)の先にある、コントラクトウォレット中心の世界

AA(Account Abstraction)における署名集約技術の活用

【動画で学ぶブロックチェーン】【Ethereum】Account Abstraction— 中城元臣氏

ERC4337からAccountAbstractionを理解する

Account Abstraction(アカウント抽象化)とは?

ERC 4337: account abstraction without Ethereum protocol changes

Account Abstractionの誤解と真実

ERC-4337 Documentation

ERC-4337 Progress report

【EIP-4337】UserOperationをBundlerに投げてからTransactionが発行されるまで

Ethereum wallets today and tomorrow — EIP-3074 vs. ERC-4337

Account Abstractionの威力:RaisePay Walletの技術的概要

Why we need wide adoption of social recovery wallets

最後に

今回は「プロトコルの変更を回避して、アカウント抽象化を実装する提案であるERC4337」についてまとめてきました!
いかがだったでしょうか?

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!

Twitter @cardene777

11
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?