# [EIP5792] ウォレットで複数オンチェーン処理をまとめて扱う仕組みを理解しよう！

> EIPC5792は、ウォレットとアプリケーション（DApp）がやり取りするための新しいJSON-RPCメソッド群を定義しています。

- URL: https://cardene.dev/articles/e958a08b4cbdfc1e98df
- 公開日: 2026-01-06
- 更新日: 2026-01-06
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

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

https://amzn.asia/d/gxvJ0Pw

今回は、複数のオンチェーン呼び出しをウォレットからまとめて送信し、その実行状態まで一貫して扱える仕組みを提案している**EIPC5792**についてまとめていきます！

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

https://eips.ethereum.org/EIPS/eip-5792

他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

## 概要

**EIPC5792**は、ウォレットとアプリケーション（DApp）がやり取りするための新しいJSON-RPCメソッド群を定義しています。
JSON-RPCとは、Ethereumで一般的に使われている、アプリとウォレットやノードが通信するためのリモート呼び出しの仕組みです。

この仕様で定義される主な機能は以下の2つです。

1つ目は、アプリがウォレットに対して「**複数のオンチェーン書き込み処理**」をまとめて依頼できることです。
ここでいうオンチェーン書き込みとは、トランザクションを送信してブロックチェーンの状態を変更する処理を指します。
**EIPC5792**では、これらの処理を1件ずつではなく、バッチ（まとめた単位）としてウォレットに渡すことができます。

2つ目は、そのバッチ処理が今どの状態にあるのかを確認する仕組みを提供することです。
トランザクションが送信されたのか、まだ待機中なのか、成功したのか失敗したのかといった進捗を、アプリ側が明確に追跡できます。

また、アプリは単に処理を投げるだけでなく、「**ウォレットが事前に表明している機能**」を前提として処理を依頼できます。
ここでいうウォレットの機能とは、例えば以下のようなものです。

* バッチトランザクションを1つのトランザクションとしてまとめて実行できるか
* 特定の新しいトランザクション形式に対応しているか
* ガス代を肩代わりする仕組み（後述するペイマスター）を使えるか

さらに、**EIPC5792**では、アプリがウォレットに対して「**どのような機能をサポートしているのか**」を問い合わせるための、新しい専用のウォレットRPCも定義しています。
これにより、アプリは実行前に「**このウォレットでは何ができるのか**」を安全に確認したうえで処理を組み立てられるようになります。

## 動機

#### 現在使われているトランザクション送信方法の問題点

現在、ウォレットからトランザクションを送信し、その結果を確認するために一般的に使われているJSON-RPCメソッドは以下の2つです。

| メソッド名                     | 役割                     |
| ------------------------- | ---------------------- |
| `eth_sendTransaction`|ウォレットにトランザクションの送信を依頼する |
| `eth_getTransactionReceipt` | 送信済みトランザクションの結果を取得する   |

しかし、これらの仕組みは現代の開発ニーズに合わなくなっています。

まず、`eth_sendTransaction`という名前自体が、歴史的な背景を引きずっています。
この名前は「**ノードがウォレットの役割も兼ねていた時代**」の名残であり、現在のようにウォレットとノードの責務が明確に分離された世界観とは一致していません。

また、機能面でも大きな制約があります。

#### バッチ処理に対応できない問題

現代のDApp開発では、複数のコントラクト呼び出しをまとめて実行したいケースが増えています。
特にスマートアカウント（コントラクトウォレット）では、複数の処理を1つのトランザクションとしてまとめ、すべて成功するか、すべて失敗するかを保証する「**アトミック実行**」が一般的です。

しかし、`eth_sendTransaction`は「**1回のRPCにつき1トランザクション**」しか扱えません。
そのため、以下のようなことができません。

* 複数の処理をまとめてウォレットに依頼する
* それらを1つのトランザクションとして実行する前提で設計する

結果として、アプリ側で無理な分割や複雑な状態管理が必要になります。

#### 新しいトランザクション形式を扱えない問題

Ethereumでは進化が続いており、新しいトランザクション形式も登場しています。
代表的な例が**ERC4337**で導入されたアカウント抽象化です。

**ERC4337**では、ペイマスター（Paymaster）という仕組みを使えます。
ペイマスターとは、ユーザー本人ではなく第三者がガス代を支払うことを可能にする仕組みです。
これにより、ユーザーはETHを直接持っていなくてもDAppを利用できるようになります。

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

https://zenn.dev/heku/books/88e65212339061/viewer/77d826

[\[ERC4337\] プロトコルの変更をせずにAAを実装する仕組みを理解しよう！](/articles/8c97ca7c93c4557ebfc2)

しかし、`eth_sendTransaction`には以下のような制約があります。

* ペイマスターの指定方法がない
* 新しいトランザクション構造を表現できない
* 将来の拡張を前提とした設計になっていない

つまり、新しい仕組みを使いたくても、RPCレベルで表現できないという問題があります。

#### ウォレットとアプリの責務を明確に分けたいという要求

**EIPC5792**が目指している大きな方向性の1つは、「ウォレットとアプリの役割をきれいに分離すること」です。

* アプリは「**どのような処理をしたいか**」を宣言する
* ウォレットは「**その処理をどう実行するか**」を判断する

この分離ができていないと、アプリはウォレットの内部実装や制約を過度に意識する必要が出てしまいます。

#### 将来の拡張を安全に行うための仕組み

もう1つの重要な動機は、将来の機能追加を安全かつ段階的に行えるようにすることです。

**EIPC5792**では、ウォレットが対応している機能を事前に問い合わせる仕組みを用意しています。
これにより、以下のようなことが可能になります。

* アプリが未対応機能を誤って使ってしまうのを防ぐ
* 新機能を段階的に追加しても、既存アプリとの互換性を保てる
* ウォレットとアプリ間での事前調整を最小限に抑えられる

結果として、エコシステム全体がより安全に、かつ柔軟に進化できるようになります。

## 仕様

**EIPC5792**では、新しく4つのJSON-RPCメソッドが定義されています。

このうち3つは「**オンチェーン書き込み処理をバッチで扱うためのRPC**」、残り1つは「**ウォレットが対応している機能（ケイパビリティ）を問い合わせるためのRPC**」です。

アプリケーションは、これら3つのバッチ系RPCを優先的に使い、もしウォレットが未対応であれば、従来の `eth_sendTransaction` や `eth_getTransactionReceipt` にフォールバックする想定になっています。
つまり、後方互換性を保ちながら段階的に移行できる設計です。

### `wallet_sendCalls`

#### 基本的な役割

`wallet_sendCalls` は、アプリがウォレットに対して「**複数のオンチェーン呼び出しをまとめて送信してほしい**」と依頼するためのRPCです。

従来のように1トランザクションずつ送るのではなく、以下の情報を1回のRPCでまとめて渡します。

* どのアドレスから送信するか
* どのチェーンで実行するか
* 実行したい複数の呼び出し内容
* アトミックに実行する必要があるかどうか
* ウォレット固有の追加機能に関する指定

#### チェーンIDとアドレスの指定方法

`from` と `chainId` は **EIP155** に基づく形式で指定されます。

* 16進数表記
* `0x` プレフィックス付き
* `chainId` に先頭ゼロは不可

これはEthereumで一般的に使われているチェーン識別方法と同じです。

#### `calls` フィールドの構造

`calls` は、実行したい処理を配列で指定します。
各要素は以下のような単純な構造です。

| フィールド        | 説明             |
| ------------ | -------------- |
| `to`           | 呼び出し先アドレス      |
| `data`         | コントラクト呼び出しデータ  |
| `value`        | 送金するETH量（16進数） |
| `capabilities` | この呼び出し専用の追加指定  |

これにより、ETH送金とコントラクト呼び出しを混在させたバッチも作れます。

#### `capabilities` の考え方

##### `capabilities` とは何か

`capabilities` は、アプリとウォレットが「**追加機能について合意するための仕組み**」です。

例えば**ERC4337**のようなアカウント抽象化ウォレットでは、ペイマスターという仕組みを使ってガス代を第三者が支払えます。
その場合、アプリは「**どのペイマスターを使うか**」という情報をウォレットに伝える必要があります。

その指定を行う場所が `capabilities` です。

##### グローバル指定と呼び出し単位指定

`capabilities` は2か所に書けます。

* リクエスト全体に適用されるもの
* 個々の call にだけ適用されるもの

これにより、「**基本は共通設定だが、この呼び出しだけ特別な条件を付けたい**」といった柔軟な指定が可能になります。

##### `optional` フラグの意味

各 `capability` には `optional` フラグを付けられます。

| optional    | 意味              |
| ----------- | --------------- |
| `true`        | 非対応でも処理を続行してよい  |
| `false` / 未指定 | 非対応ならウォレットは必ず拒否 |

この仕組みによって、アプリは「**使えたら便利だが必須ではない機能**」を安全に指定できます。

#### `atomicRequired` の意味と挙動

##### アトミック実行とは

アトミック実行とは、「**すべて成功するかすべて失敗するか**」のどちらかになる実行方式です。
途中まで成功して一部だけ失敗する、という状態は発生しません。

##### `atomicRequired` が `true` の場合

アプリが `atomicRequired: true` を指定した場合、ウォレットには厳しい要件が課されます。

* すべての呼び出しをアトミックに実行しなければならない
* 呼び出しの途中に、他のトランザクションや呼び出しが割り込んではならない
* ウォレットがアトミック実行に未対応の場合、対応可能であれば事前にアップグレードしなければならない

この条件を満たせない場合、ウォレットは処理を拒否する必要があります。

##### `atomicRequired` が `false` の場合

`atomicRequired: false` の場合は、ウォレットに裁量が与えられます。

* 単純に順番に実行してもよい
* 対応していればアトミック実行してもよい
* アトミック対応へアップグレードしてから実行してもよい

この指定は「**アトミック実行が必須ではない**」ことを意味します。

#### ウォレットが守るべき基本ルール

`wallet_sendCalls` を受け取ったウォレットは、以下のルールを必ず守る必要があります。

| 項目             | 内容                    |
| -------------- | --------------------- |
| 実行順序           | calls に指定された順序を守る     |
| チェーン           | 指定された `chainId` のみで実行   |
| 送信元            | `from` が指定されていれば必ずそれを使用 |
| `from` 未指定時      | ユーザーに選択させる機会を提供       |
| ユーザー拒否         | 拒否されたら一切送信しない         |
| 未対応 `capability` | `optional` でなければ必ず拒否    |

また、ウォレットは「**すべての呼び出しが確定するまで待つ**」ような挙動をしてはいけません。

#### IDの取り扱いルール

`wallet_sendCalls` では、バッチを識別するための `id` が使われます。

* 文字列型
* 最大4096バイト
* アプリ側で指定しても、ウォレットが生成してもよい

アプリが指定する場合、以下を満たす必要があります。

* 同一アプリ・同一送信者内で一意であること
* アプリはドメイン単位で識別される想定

ウォレットは重複したIDを含むリクエストを必ず拒否します。

#### `wallet_sendCalls` RPC定義

```ts
type Capability = {
  [key: string]: unknown;
  optional?: boolean;
}

type SendCallsParams = {
  version: string;
  id?: string;
  from?: `0x${string}`;
  chainId: `0x${string}`;
  atomicRequired: boolean;
  calls: {
    to?: `0x${string}`;
    data?: `0x${string}`;
    value?: `0x${string}`;
    capabilities?: Record<string, Capability>;
  }[];
  capabilities?: Record<string, Capability>;
};

type SendCallsResult = {
  id: string;
  capabilities?: Record<string, any>;
};
```

#### wallet_sendCalls の使用例

##### リクエスト例

```json
[
  {
    "version": "2.0.0",
    "from": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
    "chainId": "0x01",
    "atomicRequired": true,
    "calls": [
      {
        "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
        "value": "0x9184e72a",
        "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
      },
      {
        "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
        "value": "0x182183",
        "data": "0xfbadbaf01"
      }
    ],
    "capabilities": {
      "paymasterService": {
        "url": "https://...",
        "optional": true
      }
    }
  }
]
```

ここではペイマスター指定が `optional: true` なので、非対応ウォレットでも処理は続行されます。

##### レスポンス例

```json
{
  "id": "0x00000000000000000000000000000000000000000000000000000000000000000e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
```

#### `wallet_getCallsStatus` の概要

##### 基本的な役割

`wallet_getCallsStatus` は、`wallet_sendCalls` で送信したバッチが「**今どうなっているか**」を確認するためのRPCです。

返されるレシート情報は、`eth_getTransactionReceipt` のサブセットになっています。

##### `atomic` フィールドと `receipts` の関係

このRPCでは、ウォレットがどのように実行したかを `atomic` フィールドで明示します。

| atomic | receipts の内容             |
| ------ | ------------------------ |
| `true`   | 1件またはまとめられたレシート          |
| `false`  | 実行されたすべてのトランザクションのレシート配列 |

順序は必ずオンチェーンに含まれた順です。

##### `logs` の制限

`receipts.logs` には、以下のみが含まれます。

* `wallet_sendCalls` によって作られた処理に直接関係するログ

例えば**ERC4337**のバンドラーが複数のユーザー操作をまとめた場合でも、他ユーザーのログは含まれてはいけません。
また、ウォレットのアップグレード処理など、間接的なログも除外されます。

### `wallet_getCallsStatus`

#### 基本的な役割

`wallet_getCallsStatus` は、`wallet_sendCalls` で送信した「**呼び出しバッチ**」が現在どの状態にあるのかを取得するためのRPCです。

このRPCは単にトランザクションレシートを返すだけではなく、「**まだ実行中なのか**」、「**成功したのか**」、「**失敗したのか**」といったオフチェーンの状態も含めた要約情報を返します。

そのため、従来の `eth_getTransactionReceipt` よりも、アプリ側での状態管理がしやすくなっています。

#### `wallet_getCallsStatus` RPC仕様

##### パラメータ構造

```ts
type GetCallsParams = [string];
```

引数は1つだけで、`wallet_sendCalls` のレスポンスで返されたバッチ識別子（id）を指定します。
このIDは64バイトの値を16進数文字列として表現したものです。

##### レスポンス構造

```ts
type GetCallsResult = {
  version: string;
  id: `0x${string}`;
  chainId: `0x${string}`;
  status: number;
  atomic: boolean;
  receipts?: {
    logs: {
      address: `0x${string}`;
      data: `0x${string}`;
      topics: `0x${string}`[];
    }[];
    status: `0x${string}`;
    blockHash: `0x${string}`;
    blockNumber: `0x${string}`;
    gasUsed: `0x${string}`;
    transactionHash: `0x${string}`;
  }[];
  capabilities?: Record<string, any>;
};
```

それぞれのフィールドの意味を整理します。

| フィールド        | 説明                       |
| ------------ | ------------------------ |
| `version`      | RPCレスポンスのバージョン           |
| `id`           | 呼び出しバッチの識別子              |
| `chainId`      | 実行されたチェーンID              |
| `status`       | バッチ全体の状態を表すステータスコード      |
| `atomic`       | 実際にアトミック実行されたかどうか        |
| `receipts`     | オンチェーンに含まれたトランザクションのレシート |
| `capabilities` | ウォレットが付加した能力関連の追加情報      |

`receipts` は、`eth_getTransactionReceipt` の完全なコピーではなく、
必要な情報だけに絞ったサブセットである点が重要です。

#### `status` フィールドの考え方

#### status フィールドの目的

`status` は、呼び出しバッチ全体の「**今の状態**」を一目で分かるようにするための数値です。

これはオンチェーン情報だけでは判断できない、「**ウォレット内部でどう扱われているか**」という文脈を補う役割を持っています。

#### ステータスコードの分類

ステータスコードは大きく以下のカテゴリに分かれます。

| カテゴリ | 意味            |
| ---- | ------------- |
| 1xx  | 処理中（未確定）      |
| 2xx  | 成功してオンチェーンに確定 |
| 4xx  | オフチェーンでの失敗    |
| 5xx  | チェーンルールに基づく失敗 |

#### 定義されているステータスコード

| コード | 説明                             |
| --- | ------------------------------ |
| `100` | ウォレットは受信したが、まだオンチェーン実行が完了していない |
| `200` | すべて成功し、オンチェーンに確定した             |
| `400` | オンチェーンに含まれず、ウォレットは再試行しない       |
| `500` | 完全にリバートされ、ガス関連以外の変更は残らなかった     |
| `600` | 部分的にリバートされ、一部の変更がオンチェーンに残った    |

より詳細なコードは、将来的に別のERCで定義される想定です。

#### `wallet_getCallsStatus` の使用例

##### リクエスト例

```json
[
  "0x00000000000000000000000000000000000000000000000000000000000000000e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
]
```

##### レスポンス例

```json
{
  "version": "2.0.0",
  "chainId": "0x01",
  "id": "0x00000000000000000000000000000000000000000000000000000000000000000e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
  "status": 200,
  "atomic": true,
  "receipts": [
    {
      "logs": [
        {
          "address": "0xa922b54716264130634d6ff183747a8ead91a40b",
          "topics": [
            "0x5a2a90727cc9d000dd060b1132a5c977c9702bb3a52afe360c9c22f0e9451a68"
          ],
          "data": "0xabcd"
        }
      ],
      "status": "0x1",
      "blockHash": "0xf19bbafd9fd0124ec110b848e8de4ab4f62bf60c189524e54213285e7f540d4a",
      "blockNumber": "0xabcd",
      "gasUsed": "0xdef",
      "transactionHash": "0x9b7bb827c2e5e3c1a0a44dc53e573aa0b3af3bd1f9f5ed03071b100bb039eaff"
    }
  ]
}
```

この例では、バッチはアトミックに実行され、すべて成功していることが分かります。

### `wallet_showCallsStatus`

#### 基本的な役割

`wallet_showCallsStatus` は、指定した呼び出しバッチについて、ウォレットのUI上でユーザーに情報を表示させるためのRPCです。
このRPCはデータを返しません。

#### 挙動の特徴

* 正常なIDが指定された場合、ウォレットはUI表示を行う
* IDが不明、または処理できない場合はRPCエラーを返す

アプリ側は、このRPCを使って「**詳細を見る**」ボタンのような導線を提供できます。

#### RPC仕様と使用例

```ts
type ShowCallsParams = string;
```

```json
["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"]
```

### `wallet_getCapabilities`

#### 基本的な役割

`wallet_getCapabilities` は、ウォレットがどのような機能をサポートしているかを、事前の個別交渉なしに取得するためのRPCです。

例として、以下のような能力を問い合わせられます。

* バッチトランザクション対応
* ペイマスター対応（**ERC4337**）
* セッションキー対応

#### 認可に関するルール

アプリがまだユーザーから接続許可を得ていない場合、
ウォレットは**4100 Unauthorized**エラーを返すべきとされています。

#### `capabilities` の取得方法と優先順位

`capabilities` は、以下のような方法で取得・共有される可能性があります。

* `wallet_getCapabilities` による直接取得
* **CAIP25**に準拠したセッション情報
* **EIP6963**の `rdns` 識別子から導出されるURL

:::message
**CAIP25**については以下の記事を参考にしてください。
:::

[\[ACP25\] リクエスト失敗を即時通知するAppErrorの仕組みを理解しよう！](/articles/86dbeeea0e0ac179db98)

ただし、これらの情報に食い違いがある場合、ライブRPCのレスポンスが常に正として扱われます。

#### `wallet_getCapabilities` RPC仕様

##### パラメータ構造

```ts
type GetCapabilitiesParams = [
  `0x${string}`,
  [`0x${string}`]?
];
```

* 第1引数はウォレットアドレス
* 第2引数は問い合わせたいチェーンIDの配列（省略可能）

##### レスポンス構造

```ts
type GetCapabilitiesResult =
  Record<`0x${string}`, Record<string, any>>;
```

`capabilities` はチェーンごとに分かれて返されます。

#### チェーンIDごとのルール

| ルール       | 説明            |
| --------- | ------------- |
| `0x0`       | 全チェーン共通の能力    |
| 個別chainId | チェーン固有の能力     |
| 非対応チェーン   | エラーにせず、単に含めない |

#### `wallet_getCapabilities` の使用例

##### リクエスト例

```json
[
  "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
  ["0x2105", "0x14A34"]
]
```

##### レスポンス例

```json
{
  "0x0": {
    "flow-control": {
      "supported": true
    }
  },
  "0x2105": {
    "paymasterService": {
      "supported": true
    },
    "sessionKeys": {
      "supported": true
    }
  },
  "0x14A34": {
    "auxiliaryFunds": {
      "supported": true
    }
  }
}
```

このように、チェーンごとに対応状況が異なることが明確に分かります。

### atomic Capability

#### atomic Capabilityの役割

atomic Capabilityは、`wallet_sendCalls` を通じて要求されたバッチトランザクションを、ウォレットがどのように実行できるかを表すための能力定義です。

ここでいう「`atomic`」とは、単に複数の呼び出しをまとめることではなく、以下の2つを同時に満たすことを意味します。

* すべて成功するか、すべて失敗するかのどちらかになること（原子性）
* 途中に他のトランザクションや呼び出しが割り込まないこと（連続性）

この `capability` は、`wallet_getCapabilities` を通じてアプリが事前に確認できます。

#### atomic Capabilityのステータス定義

atomic Capabilityには `status` という1つのプロパティだけがあり、その値によってウォレットの対応状況が表されます。

| status      | 意味                              |
| ----------- | ------------------------------- |
| `supported`   | バッチをアトミックかつ連続的に実行できる |
| `ready`   | ユーザー承認があれば `supported` にアップグレード可能 |
| `unsupported` | アトミック実行も連続実行も保証しない         |

##### `supported` の意味

`status: supported` の場合、そのチェーンにおいてウォレットは以下を保証します。

* すべての呼び出しを1つの論理単位として実行する
* 途中で失敗した場合、オンチェーンに意味のある変更を残さない
* 他のトランザクションが途中に挟まることがない

この状態では、`atomicRequired: true` なリクエストも問題なく処理できます。

##### `ready` の意味

`status: ready` は、「**今は未対応だがユーザーが承認すれば対応可能**」という状態です。

例えば以下のようなケースを想定しています。

* ウォレットの内部ロジックをアップグレードする必要がある
* スマートアカウントへの移行が必要
* セキュリティ上、明示的なユーザー確認が必要

この場合、`atomicRequired: true` が指定されると、ウォレットはアップグレードを提案し、
ユーザーが拒否した場合はエラーになります。

##### `unsupported` の意味

`status: unsupported` の場合、そのウォレットは以下を意味します。

* バッチをアトミックに実行できない
* 連続実行の保証もしない
* アップグレードの提案もしない

この状態で `atomicRequired: true` が指定された場合、ウォレットは必ずリクエストを拒否します。

#### チェーンごとの `capability` と省略時の意味

atomic Capabilityはチェーンごとに独立して定義されます。
つまり、あるチェーンでは `supported` でも、別のチェーンでは `unsupported` ということがあり得ます。

また、特定のチェーンに対してatomic Capabilityがそもそも存在しない場合、そのチェーンではバッチ処理自体をサポートしていないと解釈されます。

ただし、他の `capability` によって明示的に上書きされている場合は、その定義が優先されます
（そのような `capability` は**EIPC5792**のスコープ外です）。

#### atomic Capabilityの型定義

```ts
type AtomicCapability = {
  status: 'supported' | 'ready' | 'unsupported';
};
```

#### atomic Capabilityを含む `wallet_getCapabilities` の例

```json
{
  "0x2105": {
    "atomic": {
      "status": "supported"
    }
  },
  "0x14A34": {
    "atomic": {
      "status": "unsupported"
    }
  }
}
```

この例では、`0x2105` チェーンではアトミック実行が可能ですが、
`0x14A34` チェーンでは不可能であることが分かります。

### エラーコード

**EIPC5792**では、定義されているRPCメソッド専用のエラーコードがいくつか追加されています。
これらはJSON-RPC 2.0の仕様に従い、`code` と `message` を持つエラーオブジェクトとして返されます。

#### エラーコード一覧と意味

以下は、定義されているすべてのエラーコードです。

| コード    | メッセージ  | 説明   | 関連RPC   |
| ------ | ----- | --- | ------ |
| `-32602` | Invalid params | パラメータ形式が不正。0xプレフィックス不足やスキーマ不一致など | `sendCalls` / `getCallsStatus` / `showCallsStatus` / `getCapabilities` |
| `4001`   | User Rejected Request                | ユーザーがバッチ送信を拒否した   | `wallet_sendCalls`    |
| `4100`   | Unauthorized  | 指定アドレスが未接続、またはウォレットに存在しない  | `wallet_sendCalls` / `wallet_getCapabilities`  |
| `5700`   | Unsupported non-optional capability  | optional でない capability が未対応     | `wallet_sendCalls`     |
| `5710`   | Unsupported chain id  | 指定されたチェーンIDに非対応                  | `wallet_sendCalls`   |
| `5720`   | Duplicate ID                         | 同じIDのバッチがすでに存在する | `wallet_sendCalls`      |
| `5730`   | Unknown bundle id | 指定されたバッチIDが存在しない        | `wallet_getCallsStatus` / wallet_showCallsStatus   |
| `5740`   | Bundle too large | バッチサイズが大きすぎる | `wallet_sendCalls `    |
| `5750`   | Atomic-ready wallet rejected upgrade | atomic 対応アップグレードをユーザーが拒否 | `wallet_sendCalls`   |
| `5760`   | Atomicity not supported  | アトミック実行必須だがウォレットが未対応   | `wallet_sendCalls `         |

#### atomic Capabilityとエラーコードの関係

atomic Capabilityとエラーコードは密接に関係しています。

例えば以下のような流れになります。

* `atomicRequired` が `true`
* walletのatomic statusが `ready`
* ユーザーがアップグレードを拒否

この場合、ウォレットは `5750` を返します。
一方で、`status` が `unsupported` の場合は、即座に `5760` が返されます。

## 補足

#### 命名について

まず、`eth_sendTransaction` を拡張して新機能を載せる案が検討されていますが、見送られています。
理由は、`eth_sendTransaction` という名前自体が「**ノードがトランザクション署名をしていた時代**」の名残で、いまの「**ウォレットが署名してアプリが依頼する**」という役割分担に合っていないからです。
そこで、用途が分かりやすい `wallet_` 名前空間のRPCへ移行する方針になっています。

次に、名前の候補として `wallet_sendTransaction`、`wallet_sendCalls` などが検討されています。最終的に `wallet_sendCalls` が選ばれた理由は、ウォレットの種類によって「**呼び出し**」と「**トランザクション**」の対応関係が変わるからです。

| 候補名 | 採用しなかった理由|
| ----------- | ------------- |
| `wallet_sendTransaction`  | EOA（外部所有アカウント）のウォレットでは、`wallet_send*` が結果として複数トランザクションを送る可能性があり、「**Transaction**」という単数が誤解を招く               |
| `wallet_sendTransactions` | **ERC4337**のような実装では、複数の `call` が結果として「**1つのトランザクション（または同等のオンチェーン単位）**」にまとまることがあり、「**Transactions**」という複数形が誤解を招く |
| `wallet_sendCalls` | 複数の `call` を受け取り、ウォレット実装に応じて「**1つにも複数にもなり得る**」ことを名前が自然に表現できるため採用 |

ここでいうEOAは、秘密鍵で直接署名する通常のアカウントを指します。
スマートアカウントはコントラクトで制御されるアカウントで、**ERC4337**が代表例です。

#### 呼び出しのアトミック実行

`wallet_sendCalls` は `calls` 配列を受け取りますが、「**必ず1つのトランザクションとして実行しなければならない**」とはしていません。
これは意図的です。

理由は、ウォレットの種類で実行方法が変わるからです。
EOAウォレットは複数 `call` を1トランザクションにまとめられる場合もあれば、複数トランザクションとして送る実装もあり得ます。
そこで、ウォレット側は「**自分がどの程度の保証でバッチを実行できるか**」を表明し、アプリ側は「**最低限必要な保証**」を要求できるようにしています。

このために、atomic capability（特別な `capability`）が仕様の中心要素として組み込まれています。
曖昧さを減らすために、アトミック要件と実際の扱いは次の2点で明示されます。

* `wallet_sendCalls` の `atomicRequired`（アプリが要求する最低要件）
* `wallet_getCallsStatus` の `atomic`（ウォレットが実際にどう実行したか）

当初は「**複数 `call` は必ずアトミックに実行する**」を提案していましたが、意見の分かれる設計になりやすく、ウォレット実装の自由度を潰す方向になりやすいという判断で撤回されています。
代わりに atomic capabilityを定義することで、EOAウォレットが複数 `call` を受け付けられる余地を残しつつ、開発者が「**アトミック実行される場合だけバッチ送信したい**」という選択もできるようにしています。

理解しやすいように、アプリとウォレットの関係を図にします。

```mermaid
sequenceDiagram
  participant App as アプリ
  participant Wallet as ウォレット
  participant Chain as チェーン

  App->>Wallet: wallet_getCapabilities（atomic statusを確認）
  Wallet-->>App: supported / ready / unsupported

  App->>Wallet: wallet_sendCalls（atomicRequired=true/false）
  alt atomicRequired=true かつ supported
    Wallet->>Chain: まとめて実行（アトミック・連続）
  else atomicRequired=true かつ ready
    Wallet->>Wallet: アップグレード提案（ユーザー承認待ち）
    alt 承認
      Wallet->>Chain: まとめて実行（アトミック・連続）
    else 拒否
      Wallet-->>App: エラー（アップグレード拒否）
    end
  else atomicRequired=true かつ unsupported
    Wallet-->>App: エラー（アトミック非対応）
  else atomicRequired=false
    Wallet->>Chain: 1つまたは複数に分けて実行（裁量）
  end

  App->>Wallet: wallet_getCallsStatus（結果確認）
  Wallet-->>App: status / atomic / receipts
```

### callごとのガスリミット（Call Gas Limit）

当初は、`calls` の各要素に `gas` フィールド（call単位のガス上限）を持たせる案がありました。しかし、これは誤解を生むとして撤回されています。

理由は、ウォレット実装によって「call単位のガス指定」が成立しないケースがあるからです。
特に **ERC4337** では、callごとではなく「**ユーザーオペレーション全体**」に対してガス制限が決まります。
つまり、`call` 単位でガス上限を指定できません。

そこで次に、「**全 `call` に共通で適用される単一の `gas` 値**」を提案しました。
これは **ERC4337**には合いますが、EOAウォレットには合いません。
EOAが複数 `call` を複数トランザクションで送る場合、トランザクションごとにガス制御の考え方が変わり、単一の `gas` 値が現実の実装に合わなくなります。

結果として、ユースケースを横断して一貫した意味を持たせるのが難しいため、ガス指定そのものを仕様から削除しています。

## 互換性

新しい `wallet_` RPC群をサポートしないウォレットは、これらのメソッドが呼ばれたときにエラーを返すべきです。

アプリ側のフォールバックは2つの方針が許容されています。

| アプリの対応 | 内容   |
| ------------- | -------- |
| 直列送信にフォールバック  | `wallet_sendCalls` が使えない場合、同じcall群を `eth_sendTransaction` で1件ずつ送る（バッチの意図を直列に分解して実行する） |
| 非対応としてユーザーに通知 | 「**このウォレットは未対応なので処理できなかった**」とユーザーに示し、リクエストを処理しない        |

どちらを選ぶかはアプリ次第で、必ずフォールバックしなければならないとは書かれていません。

## セキュリティ

#### アプリは「1トランザクションで送られる」と決めつけてはいけない

`atomicRequired` や `atomic` の値に関係なく、アプリ開発者は「**必ず1つのトランザクションにまとまる**」と仮定してはいけません。
例として、reorg（再編成）に強いL2が `sendBundle` のような機能を提供するケースが挙げられています。

reorg（再編成）は、ブロックチェーンで一時的に採用されたブロックが後から別のブロックに置き換わる現象です。
これに強い設計のL2では、オンチェーンの載せ方が独特になる可能性があり、アプリ側の「**1トランザクション前提**」は壊れやすいという意味です。

#### バッチ識別子（id）は推測できないようにする

ウォレットは、`wallet_sendCalls` のレスポンスで返すバッチ識別子（id）が予測可能にならないようにしなければなりません。
予測可能だと、悪意あるアプリが「**他ユーザーの取引状況**」を推測する足がかりを得る可能性があるからです。

ここで重要なのは、idが「**順番に増える**」、「**特定パターンを持つ**」といった性質を避けることです。

#### `wallet_getCallsStatus` の `capabilities` で機密情報を漏らさない

`wallet_getCallsStatus` のレスポンスには `capabilities` が含まれ得ますが、ウォレットはここでセンシティブな情報を漏らしてはいけません。
例えば、内部のリトライ戦略やユーザー固有の状態が推測できるような情報を返すと、攻撃や追跡に繋がる可能性があります。

#### プライバシー考慮

ここで言っていることの中心は、「**`capability` の問い合わせが、ユーザーやウォレット実装の特定につながらないようにする**」という点です。

##### 段階的な許可と同意を守る

近年のUXでは、最初から全部許可を求めるのではなく、必要になったタイミングで少しずつ許可や同意を求めるやり方が一般的です。
これはユーザー体験を良くするだけでなく、ユーザーの匿名性の維持にも役立ちます。

その一方で、機能発見（feature discovery）を強くすると、「**このウォレットは何ができるか**」を過剰に露出させる動機が働きやすくなります。
そこで、`capability` の仕組みを使い、「**機能が未対応なのか、許可がないだけなのか**」を設計上わざと分かりにくくしています。
これにより、問い合わせ結果だけからユーザーの状態や許可状況を推測しにくくします。

##### 過剰な `capability` 公開はフィンガープリントにつながる

ウォレットは、信頼できない相手に対して `capabilities` をむやみに公開しないことが推奨されています。
理由は、`capabilities` の組み合わせがウォレット実装やクライアント（ユーザーエージェント）固有の特徴になりやすく、「**フィンガープリント**」に使われる可能性があるからです。

フィンガープリントとは、ブラウザや拡張機能の挙動・対応機能などの特徴を組み合わせて、個人や同一クライアント集団を確率的に特定する手法です。
Webには他にも匿名性を崩す手段が存在するため、`capability` の過剰公開はそれらと組み合わさって、個人の特定や「**特定ウォレットユーザー全体**」の特定につながり得ます。

同様に、アプリ側が `capabilities` を過剰に問い合わせたり、第三者も含めて `capability` を過剰共有させるような設計は、避けるべきアンチパターンとされています。

## 引用

Moody Salem (@moodysalem), Lukas Rosario (@lukasrosario), Wilson Cusack (@wilsoncusack), Dror Tirosh (@drortirosh), Jake Moxey (@jxom), Derek Rein (@arein), Alex Forshtat (@forshtat), Sam Wilson (@SamWilsn) <sam@binarycake.ca>, Borislav Itskov (@Oxbobby), Joao Tavares (@cryptotavares), Adam Fuller (@azf20), Philip Liao (@phil-ociraptor), bumblefudge (@bumblefudge), "EIP-5792: Wallet Call API," Ethereum Improvement Proposals, no. 5792, October 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5792.

## 最後に

今回は「複数のオンチェーン呼び出しをウォレットからまとめて送信し、その実行状態まで一貫して扱える仕組みを提案している**EIPC5792**」についてまとめてきました！
いかがだったでしょうか？

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

[Twitter @cardene777](https://twitter.com/cardene777)

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

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4
