はじめに

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

今回は、ERC4337に準拠したコントラクトアカウントが、UserOperationをbundlerに送付するための新しいJSON-RPC APIメソッドを提案しているERC7769についてまとめていきます!

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

他にも様々なEIPについてまとめています。

概要

ERC7769では、新しいJSON-RPC APIメソッドを定義してERC4337に準拠したコントラクトアカウントがUserOperationのmempoolノードやバンドラー(bundler)と通信できるようにする仕組みを提案しています。 これにより、従来のEthereumトランザクションと同じような機能が提供されます。 さらに、開発やテスト、デバッグをしやすくするために、デバッグ用のJSON-RPC APIメソッドも追加されています。

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

動機

ERC4337では、Ethereumの通常のトランザクションの代わりにUserOperationオブジェクトが使われます。 UserOperationには、ユーザーが実行したい処理内容がすべて含まれています。

しかし、既存のEthereumのJSON-RPC APIメソッドは、このUserOperationオブジェクトを扱うことを想定していません。 そのため、ERC4337のmempoolをスムーズに運用するには、ERC4337対応のすべての実装が共通で利用できる標準的なAPIが必要になります。 この標準化されたAPIがあれば、異なる実装でも互換性が確保され、開発者はどのERC4337環境でも同じAPIを使って操作できるようになります。

仕様

bundlerとは?

bundlerは、ERC4337のUserOperationを収集し、まとめてEthereumネットワークに送信する役割を持つノードです。 具体的には、複数のUserOperationをバンドルし、それをEntryPointコントラクトのhandleOps関数を使って一度に処理します。 この仕組みにより、従来のEthereumトランザクションとは異なる方法でトランザクションが管理されます。

eth_sendUserOperationとは?

eth_sendUserOperationは、UserOperationオブジェクトをUserOperationmempoolに送信するためのJSON-RPCメソッドです。 このメソッドは、送信されたUserOperationを検証し、適切なレスポンスを返します。

正常に処理された場合、UserOperationのハッシュ(userOpHash)を返します。 ただし、バリデーションに失敗した場合や、メモリプールに追加できない場合は、エラーメッセージを返す仕様になっています。

js
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_sendUserOperation",
  "params": [
    {
      sender, // address
      nonce, // uint256
      factory, // address
      factoryData, // bytes
      callData, // bytes
      callGasLimit, // uint256
      verificationGasLimit, // uint256
      preVerificationGas, // uint256
      maxFeePerGas, // uint256
      maxPriorityFeePerGas, // uint256
      paymaster, // address
      paymasterVerificationGasLimit, // uint256
      paymasterPostOpGasLimit, // uint256
      paymasterData, // bytes
      signature // bytes
    },
    entryPoint // address
  ]
}

パラメータの詳細

このメソッドには、UserOperationの各要素を含むオブジェクトと、処理を実行するEntryPointコントラクトのアドレスを渡します。

UserOperationには、送信者のアドレス(sender)、実行に必要なガスの情報(callGasLimit、verificationGasLimitなど)、署名(signature)、ペイマスター(paymaster)に関する情報などが含まれます。
entryPointは、このUserOperationを処理するEntryPointコントラクトのアドレスです。

これらのパラメータは、基本的にすべて16進数(hex)表記で指定する必要があります。 また、factoryとfactoryData、paymaster関連のパラメータは、それぞれセットで指定しなければなりません。

返り値とエラーコード

成功時のレスポンス

UserOperationが正常にメモリプールに追加された場合、そのハッシュ(userOpHash)が返されます。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x123456789012345678901234567890123456789012345678901234567890abcd"
}

失敗時のレスポンス

何らかの理由でUserOperationが受理されなかった場合、エラーメッセージとエラーコードが返されます。

  • 無効なUserOperation構造(コード:-32602)
    • UserOperationのフォーマットやフィールドが不正な場合に返されます。
  • ウォレット作成または検証の失敗(コード:-32500)
    • EntryPointコントラクトのsimulateValidationで検証に失敗した場合、このエラーが返ります。
json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "message": "AA21 didn't pay prefund",
    "code": -32500
  }
}
  • ペイマスターの検証失敗(コード:-32501)

    • ペイマスター(UserOperationの手数料を支払うコントラクト)のvalidatePaymasterUserOpで拒否された場合に返されます。
  • ERC7562のルール違反(コード:-32502)

    • UserOperationがERC7562のオペコードの検証ルールに違反している場合に返されます。

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

  • 有効期限切れ(コード:-32503)

    • ウォレットまたはペイマスターによって設定された有効期限が切れている場合に返されます。
    • このエラーが返された場合、validUntilやvalidAfterの情報が含まれます。
  • ペイマスターのスロットリングや禁止(コード:-32504)

    • ERC7562のレピュテーションルールに基づき、ペイマスターが制限されている場合に返されます。
json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "message": "paymaster stake too low",
    "data": {
      "paymaster": "0x123456789012345678901234567890123456790",
      "minimumStake": "0xde0b6b3a7640000",
      "minimumUnstakeDelay": "0x15180"
    },
    "code": -32504
  }
}
  • ウォレットの署名検証失敗(コード:-32507)

    • ウォレットの署名チェックが失敗した場合に返されます。
  • ペイマスターの残高不足(コード:-32508)

    • ペイマスターの残高が不足している場合に返されます。
    • このエラーが発生した場合、他のUserOperationの影響で資金が足りなくなっている可能性があります。

eth_estimateUserOperationGasとは?

eth_estimateUserOperationGasは、UserOperationのガス使用量を見積もるためのJSON-RPCメソッドです。 このメソッドでは、UserOperationの実行に必要なガスの上限を計算します。 特に、ガス制限(gas limit)や手数料(gas price)を指定しなくても、必要な値を自動的に見積もってくれます。

署名フィールドはウォレット側で無視されるため、ガス見積もり時にユーザーの承認を必要としません。 ただし、適切なデータ構造を維持するため、署名フィールドには適当な長さのダミー値(例えば、適切な長さの空の署名データ)を入れる必要があります。

このメソッドでは、オプションとして「State Override Set」を受け取ることができ、これを使うとガス見積もり時に一時的にブロックチェーンの状態を変更できます。 これは、eth_callメソッドのオーバーライド機能と同じ仕組みです。

返り値として、以下のガス値が返されます。

  • preVerificationGas
    • UserOperationの実行前のオーバーヘッドガス量。
  • verificationGasLimit
    • UserOperationの検証に必要なガス量。
  • paymasterVerificationGasLimit
    • ペイマスターの検証に必要なガス量(ペイマスターが指定されている場合のみ)。
  • callGasLimit
    • アカウント内での処理に必要なガス量。

postOpGasLimitは正確に見積もることができないため、ペイマスターがこの値を指定してチェーン上で適切に処理する必要があります。

エラーが発生した場合は、eth_sendUserOperationと同じエラーコードが返されます。 また、コントラクトアカウントの内部処理がrevertした場合や、ペイマスターのpostOp処理がrevertした場合にもエラーになります。

eth_getUserOperationByHashとは?

eth_getUserOperationByHashは、eth_sendUserOperationのレスポンスとして返されたuserOpHashを使って、対応するUserOperationの情報を取得するためのメソッドです。

このメソッドにuserOpHashを渡すと、以下のような動作をします。

  • そのUserOperationがすでにブロックに含まれている場合、entryPointのアドレス、blockNumber、blockHash、transactionHashを含む完全なUserOperationデータを返します。
  • まだブロックに含まれておらず、bundlerのmempoolにある場合、完全なUserOperationデータが返される可能性がありますが、blockNumber、blockHash、transactionHashの値はnullになります。
  • それ以外(例えば、メモリプールから削除された場合)は、nullを返します。

eth_getUserOperationReceiptとは?

eth_getUserOperationReceiptは、userOpHashをもとにUserOperationの実行結果(レシート)を取得するメソッドです。

UserOperationがまだブロックに含まれていない場合はnullが返ります。

含まれている場合は、以下の情報が取得できます。

  • userOpHash
    • UserOperationのハッシュ。
  • entryPoint
    • 処理が行われたEntryPointコントラクトアドレス。
  • sender
    • UserOperationを送信したアドレス。
  • nonce
    • UserOperationの実行に使われたnonce。
  • paymaster
    • 使用されたペイマスター(存在しない場合は空)。
  • actualGasCost
    • 実際に消費されたガスコスト(アカウントまたはペイマスターが負担)。
  • actualGasUsed
    • UserOperation全体で使用されたガス量(事前検証、作成、検証、実行を含む)。
  • success
    • 実行が成功したかどうか(trueなら成功、falseなら失敗)。
  • reason
    • UserOperationがrevertした場合のエラーメッセージ(バイト配列)。
  • logs
    • このUserOperationによって生成されたログ(同じバンドル内の他のUserOperationのログは含まれない)。
  • receipt
    • トランザクションのレシート(これはバンドル全体のものであり、単体のUserOperation専用ではない)。

eth_supportedEntryPointsとは?

eth_supportedEntryPointsは、クライアントがサポートするEntryPointコントラクトのアドレスを取得するメソッドです。

このメソッドを呼び出すと、配列としてサポートされているEntryPointコントラクトのアドレスを返します。 配列の最初の要素が、クライアントが推奨するEntryPointコントラクトのアドレスになります。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_supportedEntryPoints",
  "params": []
}

例えば、上記のリクエストに対するレスポンスは以下のようになります。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0xcd01C8aa8995A59eB7B2627E69b40e0524B5ecf8",
    "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6"
  ]
}

この場合、0xcd01C8aa8995A59eB7B2627E69b40e0524B5ecf8が推奨EntryPointとなります。

eth_chainIdとは?

eth_chainIdは、現在のネットワークのEIP155で提案されているChain IDを取得するメソッドです。

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

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_chainId",
  "params": []
}

例えば、Ethereumメインネットの場合、上記のリクエストに対してのレスポンスは以下のようになります。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}

この0x1は、EthereumメインネットのChain IDを示しています。 その他のチェーンでは異なる値が返されます。 例えば、Polygonなら0x89、Goerliテストネットなら0x5などになります。

debug Namespaceとは?

debug 名前空間に含まれるJSON-RPCメソッドは、ERC4337のバンドラー(bundler)の動作をテストしやすくするためのデバッグ用APIです。 これらのメソッドは、テスト環境でのみ使用できるようになっており、本番環境では使用できないようにするべきです。 主に、互換性テストやデバッグ作業の効率化を目的として用意されています。

debug_bundler_clearStateとは?

このメソッドは、バンドラーのメモリプールや、ペイマスター・アカウント・ファクトリーのレピュテーションデータをリセットします。 テスト中に状態をリセットしたいときに便利です。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "debug_bundler_clearState",
  "params": []
}

上記のリクエストを送ると、成功時には"ok"が返ります。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "ok"
}

debug_bundler_dumpMempoolとは?

現在バンドラーのメモリプールにあるUserOperationを一覧取得するメソッドです。
EntryPointコントラクトのアドレスを指定すると、そのEntryPointコントラクトで管理されているUserOperationのリストが返されます。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "debug_bundler_dumpMempool",
  "params": ["0x1306b01bC3e4AD202612D3843387e94737673F53"]
}

例えば、上記のリクエストに対して以下のようなレスポンスが返ってきます。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
        sender, // address
        nonce, // uint256
        factory, // address
        factoryData, // bytes
        callData, // bytes
        callGasLimit, // uint256
        verificationGasLimit, // uint256
        preVerificationGas, // uint256
        maxFeePerGas, // uint256
        maxPriorityFeePerGas, // uint256
        signature // bytes
    }
  ]
}

mempoolに入っているUserOperationの内容を確認することで、バンドラーが正しく動作しているかを検証できます。

debug_bundler_sendBundleNowとは?

通常、バンドラーは一定のルールに従ってUserOperationをバンドルし、Ethereumネットワークに送信します。 しかし、このメソッドを使うと、現在メモリプールにあるUserOperationを即座にバンドルして送信することができます。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "debug_bundler_sendBundleNow",
  "params": []
}

上記のリクエストのレスポンスとして、送信されたバンドルのトランザクションハッシュが返されます。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xdead9e43632ac70c46b4003434058b18db0ad809617bd29f3448d46ca9085576"
}

debug_bundler_setBundlingModeとは?

バンドラーの動作モードを変更するメソッドです。

  • auto に設定すると、バンドラーは通常のルールに従って自動でバンドルを処理します。
  • manual に設定すると、明示的にdebug_bundler_sendBundleNowを呼び出さない限り、バンドラーはUserOperationをバンドルしません。
json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "debug_bundler_setBundlingMode",
  "params": ["manual"]
}

例えば、上記の以下のリクエストでモードを手動(manual)に切り替えられ、以下のレスポンスが取得できます。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "ok"
}

成功すると、"ok"が返ります。

debug_bundler_setReputationとは?

特定のアドレスのレピュテーションデータを設定するメソッドです。

  • opsSeen
    • そのアドレスのUserOperationがメモリプールに追加された回数。
  • opsIncluded
    • そのアドレスのUserOperationがブロックチェーンに記録された回数。

これを使うと、テスト中に特定のアドレスのレピュテーションを操作し、異なる状況をシミュレーションできます。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "debug_bundler_setReputation",
  "params": [
    [
      {
        "address": "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6",
        "opsSeen": "0x14",
        "opsIncluded": "0x0D"
      }
    ],
    "0x1306b01bC3e4AD202612D3843387e94737673F53"
  ]
}

例えば、上記のリクエストで特定のアドレスのレピュテーションを設定でき、以下のレスポンスが取得できます。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "ok"
}

成功すると、"ok"が返ります。

debug_bundler_dumpReputationとは?

現在バンドラーが記録しているすべてのアドレスのレピュテーションデータを取得するメソッドです。

  • opsSeen
    • UserOperationがメモリプールに追加された回数。
  • opsIncluded
    • UserOperationがブロックチェーンに記録された回数。
  • status
    • アドレスの現在の状態(ok / throttled / banned)。
json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "debug_bundler_dumpReputation",
  "params": ["0x1306b01bC3e4AD202612D3843387e94737673F53"]
}

例えば、上記のリクエストに対して以下のようなレスポンスが返ります。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    { "address": "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6",
      "opsSeen": "0x14",
      "opsIncluded": "0x13",
      "status": "ok"
    }
  ]
}

アドレスのステータスがthrottledやbannedになっている場合は、ペイマスターやアカウントがレート制限を受けている可能性があるため調整が必要になります。

debug_bundler_addUserOpsとは?

通常、UserOperationはバンドラーによる検証を経てmempoolに追加されます。 しかし、このメソッドを使うと、UserOperationの検証をスキップして強制的にmempoolへ追加できます。

テストの時に、特定の状態をシミュレーションするために使われることが多いです。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "debug_bundler_addUserOps",
  "params": [
    [
      { sender: "0xa...", ... },
      { sender: "0xb...", ... }
    ]
  ]
}

例えば、以下のリクエストで複数のUserOperationを追加でき、以下のレスポンスを取得できます。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "ok"
}

成功すると、"ok"が返ります。

補足

bundlerは、デバッグ機能を一式提供することが求められており、この機能を使用することでERC7769の仕様に準拠しているかを確認できます。

互換性

ERC7769では、互換性の課題を引き起こさない新しいJSON-RPC APIとして定義しています。

セキュリティ

UserOperation mempoolのDoS攻撃対策

ERC4337ノードを運用することは計算コストが高く、特に公開されている本番環境のノードはDoS攻撃の標的になりやすくなります。

この問題への対策として、ERC7562の検証ルールが導入されています。 ERC7562では、各参加者のレピュテーション(信用度)をトラッキングする仕組みが定義されており、不正なUserOperationがmempoolに追加されるのを防ぐことができます。

例えば、スパム的なUserOperationを大量に送信してノードを負荷で圧迫しようとする攻撃があった場合、ERC7562の仕組みによって悪意のあるアドレスを特定して制限やブロックを行うことが可能になります。

そのため、本番環境でERC4337ノードを運用する場合は、必ずERC7562のルールを実装することが推奨されています。 このルールを導入することで、攻撃のリスクを最小限に抑えて安定した運用ができるようになります。

本番環境でのデバッグAPIの無効化

ERC4337のデバッグAPIは、あくまで開発やテスト用の機能であり、本番環境では絶対に公開すべきではないです。

デバッグAPIには、mempoolを直接操作したり、バンドラーの動作を強制的に変更したりする機能が含まれています。 そのため、万が一これが本番環境で開放されてしまうと、攻撃者がノードの動作を意図的に操作してシステム全体の安定性を損なう可能性があります。

具体的なリスクとしては、以下のようなものが考えられます。

  • debug_bundler_clearState を悪用して、mempoolを強制的にクリアし、全てのUserOperationを削除する。
  • debug_bundler_sendBundleNow を連続で呼び出し、意図しないタイミングでバンドルを送信させる。
  • debug_bundler_setReputation を操作し、特定のアカウントを不当にブロックまたは優遇する。

そのため、本番環境ではデフォルトでデバッグAPIを無効化することが必須です。 もし何らかの理由でデバッグAPIを有効化する必要がある場合でも、システムに警告を表示してAPIが誤って公開されないように十分な対策を取るべきです。

引用

Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn), Alex Forshtat (@forshtat), "ERC-7769: JSON-RPC API for ERC-4337 [DRAFT]," Ethereum Improvement Proposals, no. 7769, August 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7769.

最後に

今回は「ERC4337に準拠したコントラクトアカウントが、UserOperationをbundlerに送付するための新しいJSON-RPC APIメソッドを提案しているERC7769」についてまとめてきました! いかがだったでしょうか?

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

他の媒体でも情報発信しているのでぜひ他も見ていってください!