はじめに

初めまして。 CryptoGamesというブロックチェーンゲーム企業でエンジニアをしている cardene(かるでね) です! スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

代表的なゲームはクリプトスペルズというブロックチェーンゲームです。

今回は、ERC4337で提案されているPaymasterサービスと通信するための標準化されたAPIを提案し、アプリ側が指定したPaymasterサービスとウォレットを経由して通信する仕組みを提案しているERC7677についてまとめていきます!

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

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

概要

EIP5792は、アプリがウォレットと通信して高度な機能を利用できるようにする提案です。 この提案では、アプリがERC4337ウォレットに特定のPaymasterサービスと通信するようにリクエストできる機能を定義しています。 この機能をサポートするために、Paymasterサービスの標準化されたAPIも定義しています。

Paymasterは、コントラクトウォレットからのトランザクション手数料を肩代わりしてくれるアドレスです。 これにより、ユーザーはガス代の負担をせずにトランザクションを実行できます。 このPaymasterの機能を提供するサービスとウォレットが通信するためのAPIについて提案しています。

EIP5792ではバッチトランザクションをはじめとする、複数の新しいJSON-RPCメソッドを定義しています。 より詳しくは以下の記事を参考にしてください。

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

動機

現在、アプリ開発者はがユーザーのトランザクションのガス代をPaymasterに負担させたいと思っている時、アプリがウォレットに特定のPaymasterサービスと通信するよう指示する方法がありません。 また、ウォレットがPaymasterサービスとどのように通信するかの標準も存在しません。 そのため、アプリがウォレットに特定のPaymasterサービスと通信するように指示する方法と、ウォレットがPaymasterサービスと通信するための標準を両方必要としています。

仕様

新しいEIP5792ウォレットの機能が1つ定義されています。 また、Paymasterサービスの標準インターフェースについても定義しています。

Paysmaster Webサービスインターフェース

pm_getPaymasterStubData

このメソッドは、Paymasterが負担するガス代の見積もり値を返します。 以下のデータを受け取って実行します。

  • 署名されていないユーザー操作 (unsigned user operation)
  • エントリーポイントアドレス (entrypoint address)
  • チェーンID (chain id)
  • コンテキストオブジェクト (context object)

Paymasterサービスプロバイダーは、アプリ開発者に対してウォレットと通信する時にどのような情報が必要かのフィールドを定義することができます。

このメソッドは、提供されたEntoryPointのバージョンに適用される場合、Paymaster固有のガス値を返すことがあります。 例えば、エントリーポイントv0.7が提供された場合、このメソッドはpaymasterVerificationGasLimitとpaymasterPostOpGasLimitの値を返すことがあります。 ウォレットは、ガス見積もりのためにUserOperationをバンドラーへ送信されたガス値を使用する必要があります。

EntoryPointは署名の検証などを行うコントラクトのことです。 UserOperationは実行するトランザクションの内容のことです。 Bundler(バンドラー)は複数のコントラクトウォレットからのトランザクションをまとめて実行してくれるEOAアドレスのことです。 より詳しくは以下の記事を参考にしてください。

また、このメソッドは、スポンサーオブジェクト(`nameフィールドとオプションのiconフィールド)を返すことがあります。 nameフィールドはトランザクションをスポンサーしてくれる主体の名前で、iconフィールドは画像へのURIです。 ウォレット開発者は、ユーザーにスポンサー情報を表示することを選ぶことができます。 iconのURIは[RFC-2397](https://www.ietf.org/rfc/rfc2397.txt)で定義されているデータURIの必要があり、画像は最低96x96pxの解像度を持つ正方形であるべきです。 画像フォーマットは、レンダリングが容易なPNG、WebP、SVGなどが推奨されます。 SVG画像はJavaScriptを実行できるため、ウォレットは信頼されていないJavaScriptの実行を防ぐために`タグを使用してSVG画像をレンダリングする必要があります。

場合によっては、Paymaterデータが署名を含まない場合など、pm_getPaymasterStubDataの呼び出しだけで必要な情報が取得できる場合があります。 この場合、RPC呼び出しでisFinal: trueを返すことにより、2番目のRPC呼び出しであるpm_getPaymasterDataをスキップすることができます。

Paymaster Web Serviceは、pm_getPaymasterStubDataの実行中に受け取ったUserOperationに対して検証を行い、スポンサーしなのであればこの段階でリクエストを拒否するべきです。

具体例
  1. ガス見積もりのためのスタブ値の取得

    • アプリがウォレットに署名されていないユーザー操作とともにこのメソッドを呼び出す。
    • Paymasterサービスが適用可能なガス値を返す。
    • ウォレットがこれらの値を使用してガス見積もりを行う。
  2. スポンサー情報の表示

    • メソッドがスポンサーの名前とアイコンURIを含むオブジェクトを返す。
    • ウォレットがこれらの情報をユーザーに表示する。
  3. 署名が不要な場合

    • isFinal: trueを返すことで、追加のRPC呼び出しをスキップする。
pm_getPaymasterStubDataRPC仕様

ユーザーは、他のすべてのフィールドが入力された後に署名するため、UserOperationパラメータには署名が含まれません。

入力パラメータ

入力パラメータは以下の4つです。

  • userOp
    • sender
      • 送信者のアドレス。
    • nonce
      • ユニークなトランザクションID。
    • initCode
      • 初期化コード。
    • callData
      • 呼び出しデータ。
    • callGasLimit
      • 呼び出しのガスリミット。
    • verificationGasLimit
      • 検証のガスリミット。
    • preVerificationGas
      • 事前検証のガス量。
    • maxFeePerGas
      • ガスあたりの最大料金。
    • maxPriorityFeePerGas
      • ガスあたりの優先料金。
  • EntryPoint (エントリーポイント)
    • エントリーポイントアドレス。
  • Chain ID (チェーンID)
    • チェーンのID。
  • Context (コンテキスト)
    • サービスプロバイダーが指定する追加情報(例: policyIdなど)。
出力結果

出力結果は、PaymasterサービスがどのEntryPointバージョンを使用しているかに依存します。

  • 共通フィールド
    • sponsor
      • スポンサー情報(名前とオプションでアイコンURI)。
  • EntryPoint v0.6の場合
    • paymasterAndData
      • Paymasterとデータ。
  • EntryPoint v0.7の場合
    • paymaster
      • Paymasterアドレス。
    • paymasterData
      • Paymasterデータ。
    • paymasterVerificationGasLimit
      • Paymaster検証ガスリミット(オプション)。
    • paymasterPostOpGasLimit
      • Paymasterポストオペレーションガスリミット(オプション)。
    • isFinal
      • 追加のRPC呼び出しが不要であることを示すフラグ(オプション)。
例
入力例
json
[
  {
    "sender": "0x...",
    "nonce": "0x...",
    "initCode": "0x",
    "callData": "0x...",
    "callGasLimit": "0x...",
    "verificationGasLimit": "0x...",
    "preVerificationGas": "0x...",
    "maxFeePerGas": "0x...",
    "maxPriorityFeePerGas": "0x..."
  },
  "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
  "0x2105",
  {
    "policyId": "962b252c-a726-4a37-8d86-333ce0a07299"
  }
]
出力例
  • EntryPoint v0.6の場合
json
{
  "sponsor": {
    "name": "My App",
    "icon": "https://..."
  },
  "paymasterAndData": "0x..."
}
  • EntryPoint v0.7の場合
json
{
  "sponsor": {
    "name": "My App",
    "icon": "https://..."
  },
  "paymaster": "0x...",
  "paymasterData": "0x..."
}
  • EntryPoint v0.7でペイマスターガス見積もりも含む場合
json
{
  "sponsor": {
    "name": "My App",
    "icon": "https://..."
  },
  "paymaster": "0x...",
  "paymasterData": "0x...",
  "paymasterVerificationGasLimit": "0x...",
  "paymasterPostOpGasLimit": "0x..."
}
  • 追加のRPC呼び出しが不要であることを示す場合
json
{
  "sponsor": {
    "name": "My App",
    "icon": "https://..."
  },
  "paymaster": "0x...",
  "paymasterData": "0x...",
  "isFinal": true
}

pm_getPaymasterData

署名されたUserOperationにおいてPaymaster関連フィールドに使用される値を返すJSON-RPCメソッドで、実際のトランザクション送信時に使用されます。 このメソッドは、署名されていないUserOperation、EntryPointアドレス、チェーンID、コンテキストオブジェクトを受け取ります。

pm_getPaymasterData RPC 仕様
入力パラメータ

入力パラメータは以下の4つです。

  • userOp (ユーザー操作)
    • sender
      • 送信者のアドレス。
    • nonce
      • ユニークなトランザクションID。
    • initCode
      • 初期化コード。
    • callData
      • 呼び出しデータ。
    • callGasLimit
      • 呼び出しのガスリミット。
    • verificationGasLimit
      • 検証のガスリミット。
    • preVerificationGas
      • 事前検証のガス量。
    • maxFeePerGas
      • ガスあたりの最大料金。
    • maxPriorityFeePerGas
      • ガスあたりの優先料金。
  • EntryPoint (エントリーポイント)
    • エントリーポイントアドレス。
  • Chain ID (チェーンID)
    • チェーンのID。
  • Context (コンテキスト)
    • サービスプロバイダーが指定する追加情報(例: policyIdなど)。
出力結果

出力結果は、PaymasterサービスがどのEntryPointバージョンを使用しているかに依存します。

  • EntryPoint v0.6の場合
    • paymasterAndData
      • ペイマスターとデータ。
  • EntryPoint v0.7の場合
    • paymaster
      • ペイマスターアドレス。
    • paymasterData
      • ペイマスターデータ。
例
入力例
json
[
  {
    "sender": "0x...",
    "nonce": "0x...",
    "initCode": "0x",
    "callData": "0x...",
    "callGasLimit": "0x...",
    "verificationGasLimit": "0x...",
    "preVerificationGas": "0x...",
    "maxFeePerGas": "0x...",
    "maxPriorityFeePerGas": "0x..."
  },
  "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
  "0x2105",
  {
    "policyId": "962b252c-a726-4a37-8d86-333ce0a07299"
  }
]
出力例
  • EntryPoint v0.6の場合
json
{
  "paymasterAndData": "0x..."
}
  • EntryPoint v0.7の場合
json
{
  "paymaster": "0x...",
  "paymasterData": "0x..."
}

paymasterService

このpaymasterServiceはアプリとウォレットの両方で実装されます。

App実装

PaymasterService Capability

paymasterService機能は、アプリとウォレットの両方に実装されます。この機能を利用して、アプリはウォレットに対して特定のペイマスターサービスURLを提供し、ウォレットはそのURLに対してRPCコールを行います。

アプリの実装

アプリは、ウォレットにPaymasterサービスのURLを渡す必要があります。 これを実現するために、アプリはpaymasterService機能を使ってEIP5792で提案されているwallet_sendCalls呼び出しの一部としてウォレットに情報を送ります。

wallet_sendCalls Paymaster Capability 仕様
  • PaymasterCapabilityParams
    • url
      • ペイマスターサービスのURL。
    • context
      • サービスプロバイダーが指定する追加情報。
js
type PaymasterCapabilityParams = {
  url: string;
  context: Record<string, any>;
}
wallet_sendCalls パラメータの例

以下は、wallet_sendCalls呼び出しの例です。

json
[
  {
    "version": "1.0",
    "chainId": "0x01",
    "from": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
    "calls": [
      {
        "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
        "value": "0x9184e72a",
        "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
      },
      {
        "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
        "value": "0x182183",
        "data": "0xfbadbaf01"
      }
    ],
    "capabilities": {
      "paymasterService": {
        "url": "https://...",
        "context": {
          "policyId": "962b252c-a726-4a37-8d86-333ce0a07299"
        }
      }
    }
  }
]
  • version
    • APIのバージョン。
  • chainId
    • チェーンのID。
  • from
    • トランザクションの送信元アドレス。
  • calls
    • 複数のトランザクションの詳細情報。
    • 各トランザクションは以下のフィールドを持ちます。
      • to
        • トランザクションの宛先アドレス。
      • value
        • トランザクションの値(wei単位)。
      • data
        • トランザクションのデータ。
  • capabilities
    • 使用する機能を指定。
    • ここではpaymasterService機能を指定します。
  • url
    • ペイマスターサービスのURL。
  • context
    • サービスプロバイダーが指定する追加情報(例:policyId)。
ウォレットの動作

ウォレットは、アプリから受け取ったPaymasterサービスのURLに対して、上記のPaymasterRPCcallを行います。 具体的には、ウォレットはpm_getPaymasterStubDataやpm_getPaymasterDataの呼び出しを指定されたURLに対して実行して必要なデータを取得します。

Wallet実装

この仕様に準拠するために、Appスポンサー付きのトランザクションを活用したコントラクトウォレットは以下の要件を満たす必要があります。

  • ウォレットはアプリに対してPaymasterウェブサービスと通信できることを示す必要があります。
    • これは、EIP-5792 wallet_getCapabilitiesコールのレスポンスとして判別できます。
  • ウォレットは、アプリが指定したPaymasterサービスの値を使用する必要があります。
    • これは、EIP-5792 wallet_sendCallsコールのcapabilitiesフィールドで指定されたPaymasterサービスの値を使用することを意味します。
    • ただし、ウォレットがユーザーにPaymasterを選ばせる場合などの例外もあります。
    • Paymasterサービスが最終的に使用されない場合があるため、アプリケーションは提供されたPaymasterが必ずしもトランザクション手数料を支払うことを前提としてはいけません。
wallet_getCapabilitiesレスポンス仕様

ウォレットはwallet_getCapabilitiesコールに対して以下の形式でレスポンスします。

typescript
type PaymasterServiceCapability = {
  supported: boolean;
}
wallet_getCapabilities応答例
json
{
  "0x2105": {
    "paymasterService": {
      "supported": true
    }
  },
  "0x14A34": {
    "paymasterService": {
      "supported": true
    }
  }
}
フロー図

0.png

  1. アプリからのリクエスト

    • アプリは、wallet_sendCallsコールを使用してウォレットに対してトランザクションを送信します。
    • このコールには、PaymasterサービスのURLとコンテキスト情報が含まれています。
  2. ウォレットの応答

    • ウォレットは、指定されたPaymasterサービスのURLに対してpm_getPaymasterStubDataやpm_getPaymasterDataのRPCコールを行い必要なデータを取得します。
  3. トランザクションの処理

    • ウォレットは取得したデータを使用してトランザクションを処理し、必要に応じてユーザーにスポンサー情報を表示します。

補足

ガス見積もり

現在、Paymasterサービスはpm_sponsorUserOperationメソッドを使用して、UserOperationに関連するフィールドとガス代の見積もり値を返します。 しかし、各Paymasterサービスプロバイダーはガス見積もりの方法が異なるため、見積もり値が一定でなく見積もりが不十分なこともあります。

そこで、ガス見積もりをウォレットに任せる方が良いと考えています。 ウォレットはUserOperationがどのようにバンドラーに渡されるかについての詳細情報を持っているため、より正確な見積もりが可能です。 そして、ウォレットはその見積もりに基づいてPaymasterサービスにガススポンサーシップを依頼します。

pm_getPaymasterStubDataメソッド

pm_getPaymasterStubDataメソッドはPaymaster固有のガス見積もりを返すことができ、これによりバンドラーが見積もりを不足するリスクを軽減します。

チェーンIDパラメータ

現在、Paymasterサービスプロバイダーは通常チェーンごとにURLを渡し、マルチチェーン対応はしていません。 しかし、ウォレットがどのチェーンのリクエストであるかをPaymasterサービスと通信できるようにするために、チェーンIDパラメータが必要です。

  • オプション1
    • 現在の標準を正式化し、PaymasterサービスURLがチェーンごとに1対1対応するようにする。
  • オプション2
    • Paymasterサービスリクエストの一部としてチェーンIDパラメータを要求する。

オプション2はより柔軟であり、マルチチェーンURLを提供するサービスプロバイダーにも対応できます。

ガスの見積もりの課題

ガス見積もりに柔軟性を持たせることで、Paymasterサービスが信頼性の高いガス見積もりを生成するために以下の点に注意が必要です。

preVerificationGas

preVerificationGasの値はUserOperationのサイズやゼロバイトと非ゼロバイトの比率に大きく影響されます。 これにより、pm_getPaymasterStubDataが返す値が最終的なpm_getPaymasterDataで必要なpreVerificationGasよりも低く見積もられる場合があります。 この場合、バンドラーはeth_sendUserOperationのの送信中に不十分なpreVerificationGasエラーを返します。

このシナリオを避けるために、Paymasterサービスは以下の条件を満たすガス見積もり値を返す必要があります。

  • 最終データと同じ長さであること。
  • ゼロバイト(0x00)の量が最終データよりも少ないか等しいこと。

一貫したコードパス

UserOperationに使用されるデータと同じの長さの無意味なデータの繰り返し(例:0x01)で構成された値(スタブデータ)は、preVerificationGasの計算においては有効ですがシミュレーションに失敗する可能性があります。

また、有効なスタブデータであってもガス見積もりが不十分になることがあります。 これは、スタブデータがvalidatePaymasterUserOpやpostOp関数で異なるコードパスをシミュレートする場合に起こります。 例えば、シミュレーション中にコードが早期に終了した場合、予想よりも少ないガス見積もりが返され、その結果バンドラーに提出されたUserOperationがガス不足エラーを引き起こす可能性があります。

そのため、Paymasterサービスは実行されるUserOperationと同じコードパスを実行するシミュレーション結果をもたらすスタブデータを返す必要があります。

セキュリティ

Paymasterサービスプロバイダーがアプリ開発者に提供するURLには、通常APIキーが含まれています。 アプリ開発者はこれらのAPIキーをウォレットに渡したくない場合があります。 これを解決するために、以下の方法を推奨します。

  • アプリ開発者は、Paymasterサービスへの呼び出しを仲介するアプリのバックエンドURLを渡します。
  • アプリのバックエンドがPaymasterサービスへのリクエストを処理し、必要なAPIキーを使用してペイマスターサービスにリクエストを送信します。

フロー図

1.png

  1. アプリからバックエンドへのリクエスト

    • アプリはウォレットにペイマスターサービスのURLではなく、バックエンドのURLを渡します。
    • ウォレットは、このバックエンドURLに対してPaymasterサービスリクエスト(例:pm_getPaymasterStubDataやpm_getPaymasterData)を送信します。
  2. バックエンドの処理

    • アプリのバックエンドは、受信したリクエストを元にPaymasterサービスに対して実際のAPIリクエストを行います。
    • この時に必要なAPIキーを付与します。
    • バックエンドはPaymasterサービスからのレスポンスを受け取り、必要に応じて追加のシミュレーションや検証を行います。
  3. バックエンドからウォレットへの応答

    • バックエンドは、Paymasterサービスのレスポンスを適切な形式でウォレットに返します。

詳細フロー

  1. アプリの設定

    • アプリはウォレットに次のようなURLを提供します。
    text
    https://app-backend.example.com/proxy-paymaster
  2. ウォレットのリクエスト

    • ウォレットはpm_getPaymasterStubDataを次のようにバックエンドに送信します。
    json
    {
      "sender": "0x...",
      "nonce": "0x...",
      "initCode": "0x...",
      "callData": "0x...",
      "callGasLimit": "0x...",
      "verificationGasLimit": "0x...",
      "preVerificationGas": "0x...",
      "maxFeePerGas": "0x...",
      "maxPriorityFeePerGas": "0x...",
      "entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
      "chainId": "0x2105",
      "context": {
        "policyId": "962b252c-a726-4a37-8d86-333ce0a07299"
      }
    }
  3. バックエンドの処理とペイマスターサービスへのリクエスト

    • バックエンドはリクエストを受け取り、APIキーを付与してPaymasterサービスに次のようなリクエストを送信します。
    json
    {
      "sender": "0x...",
      "nonce": "0x...",
      "initCode": "0x...",
      "callData": "0x...",
      "callGasLimit": "0x...",
      "verificationGasLimit": "0x...",
      "preVerificationGas": "0x...",
      "maxFeePerGas": "0x...",
      "maxPriorityFeePerGas": "0x...",
      "entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
      "chainId": "0x2105",
      "context": {
        "policyId": "962b252c-a726-4a37-8d86-333ce0a07299"
      }
    }
  4. Paymasterサービスからのレスポンスとバックエンドの処理

    • Paymasterサービスからのレスポンスを受け取り、必要に応じて追加の検証を行います。
  5. バックエンドからウォレットへのレスポンス

    • 最終的なレスポンスをウォレットに返します。

引用

Lukas Rosario (@lukasrosario), Dror Tirosh (@drortirosh), Wilson Cusack (@wilsoncusack), Kristof Gazso (@kristofgazso), Hazim Jumali (@hazim-j), "ERC-7677: Paymaster Web Service Capability [DRAFT]," Ethereum Improvement Proposals, no. 7677, April 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7677.

最後に

今回は「ERC4337で提案されているPaymasterサービスと通信するための標準化されたAPIを提案し、アプリ側が指定したPaymasterサービスとウォレットを経由して通信する仕組みを提案しているERC7677」についてまとめてきました! いかがだったでしょうか?

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

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