[EIP5792] バッチトランザクションを実行するJSON-RPCメソッドの仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、ウォレットからバッチトランザクションを実行したり、実行ステータスを確認できるJSON-RPCメソッドを追加する仕組みを提案している**EIP5792**についてまとめていきます！

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

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

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

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

# 概要

この規格では新しいJSON-RPCメソッドを定義しています。
これにより、アプリケーションはMetamaskなどのウォレットにデータ更新のバッチ処理をリクエストし、それらのステータスを確認できます。

:::note info
ここでのデータ更新とは、ブロックチェーン上のデータの更新のことを指します。
**ERC20**トークンの`transfer`や**ERC721**トークンの`mint`、ETHの送金などです。
:::

アプリケーションは、ウォレット内に定義されている機能を使用してデータ更新のバッ処理リクエストを送ることができます。
さらに、新しいウォレットRPCが定義されても、アプリケーションはウォレットがどのような機能を持っているかを事前に確認できます。

# 動機

現在（2022年10月当時）、ウォレットからトランザクションを送り、そのステータスを確認するためには`eth_sendTransaction`や`eth_getTransactionReceipt`を使用する必要があります。
ただ、`eth_sendTransaction`などはノードがウォレットとして機能していた時の名残であり、新しいトランザクション形式に対応していません。

開発者目線では、複数のトランザクションをアトミックに1つのRPCコールにまとめて実行したいと考えています。
**ERC4337**において、PayMaster（ガス代を別のアドレスが負担する仕組み）機能を使用したいと考えても、`eth_sendTransaction`では利用することができません。

新しい`wallet_`RPCセットでは、ウォレットとアプリの役割を明確に分け、最小限の調整で新しい機能の追加を開発者が行えることを目標にしています。

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

https://qiita.com/cardene/items/8c97ca7c93c4557ebfc2

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58/HaPTTeXZeXRG3K5JfXesmKet9eKTbcFUixW6IaMvt4c

# 仕組み

この提案では、4つの新しいJSON-RPC メソッドが追加されました。
うち3つはブロックチェーン上のデータを更新するためのバッチ処理ようで、残りの1つはウォレットが特定の機能をサポートしているか確認できるものです。
新しいバッチ処理用のメソッドを使用することができ、もしウォレットが新しい3つのメソッドに対応していない場合は`eth_sendTransaction`と`eth_getTransactionReceipt`が実行します。

## wallet_sendCalls

複数のオンチェーン呼び出しを効率的に処理することができます。

以下の要件が求められます。

- 指定された順序でトランザクションを送信。
- リクエスト時に指定されたチェーンIDのチェーン上で全てのトランザクションを送信。
- ユーザーがリクエストを拒否した場合、必ずトランザクションの送信を止める。
- 1つでもトランザクションが失敗した場合、全てのトランザクションが失敗する。
- リクエスト内のチェーンIDが現在選択されているチェーンIDと異なる場合や、送信元アドレスが有効なアカウントではない場合リクエストを拒否する場合がある。
- バッチ内のトランザクションをシミュレートして失敗が想定される場合リクエストを拒否する場合がある。

### wallet_sendCallsのRPC仕様

```js
type SendCallsParams = {
  version: string;
  chainId: `0x${string}`; // Hex chain id
  from: `0x${string}`;
  calls: {
    to?: `0x${string}` | undefined;
    data?: `0x${string}` | undefined;
    value?: `0x${string}` | undefined; // Hex value
  }[];
  capabilities?: Record<string, any> | undefined;
};

type SendCallsResult = string;
```

- `from`
    - 送信元アドレス
- `chainId`
    - 実行したいブロックチェーンID
- `calls`
    - 各トランザクションの詳細が格納されている配列。
- `capabilities`
    - ウォレットがサポートする機能をアプリが指定します。
    - 例えば、**ERC4337**ウォレットにPayMasterサービスのURLを指定してもらい、そのサービスからPayMasterとデータを取得することができます。


### wallet_sendCallsパラメータ例

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

### wallet_sendCallsの戻り値の例

処理実行中に、ユーザーが処理のステータスを取得ができる識別子です。

```js
"0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
```

## wallet_getCallsStatus

`wallet_sendCalls`を使用して実行されたバッチトランザクションのステータスを返すためのJSON-RPCメソッドです。

複数トランザクションの場合、引数の`receipts`フィールドには各トランザクションのレシート（実行結果や詳細情報）が実行順に返されます。

単一のトランザクションの場合、1つのトランザクションレシートを返します。
`receipt`オブジェクトの`logs`は、`wallet_sendCalls`で送信されたトランザクションに関連するものだけ含みます。
他のユーザー操作のログなどは含まれません。


### wallet_getCallsStatusのRPC仕様

```js
type GetCallsParams = string;

type GetCallsResult = {
  status: 'PENDING' | 'CONFIRMED';
  receipts?: {
    logs: {
      address: `0x${string}`;
      data: `0x${string}`;
      topics: `0x${string}`[];
    }[];
    status: `0x${string}`; // Hex 1 or 0 for success or failure, respectively
    blockHash: `0x${string}`;
    blockNumber: `0x${string}`;
    gasUsed: `0x${string}`;
    transactionHash: `0x${string}`;
  }[];
};
```

### wallet_getCallsStatusのパラメータ例

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

### wallet_sendCallsの戻り値の例

`wallet_sendCalls`の戻り値同様、ステータスを確認ができる識別子です。

```js
[
  "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
]
```

## wallet_showCallsStatus

`wallet_sendCalls`で送信されたバッチトランザクションの情報を、ウォレットに表示するリクエストを送るJSON-RPCメソッドです。
このメソッドは何も返さず、単にウォレットに情報表示を要求するだけです。

### wallet_showCallsStatusのRPC仕様

```js
type ShowCallsParams = string; // Call bundle identifier returned by wallet_sendCalls
```

### wallet_showCallsStatusのパラメータ例

`wallet_sendCalls`実行時に返される識別子を受け取ります。

```js
[
  "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
]
```

## wallet_getCapabilities

アプリケーションがウォレットに対して、特定の機能がサポートされているか確認するリクエストを送るJSON-RPCメソッドです。
このメソッドは、アプリケーションと要求されたアドレス間の接続がユーザーによって承認されていない場合エラーを返します。
ウォレットの機能を確認する方法として、ウォレットに直接確認する方法以外にも以下の方法で確認ができます。

- ウォレットの機能情報を外部に保存されたデータから取得する。
- 現在のセッションに関連するうプロパティ情報に機能情報が保存されている。
- 標準化されたインターフェースを通じて機能情報を取得。

### wallet_getCapabilitiesのRPC仕様

```js
type GetCapabilitiesParams = [`0x${string}`]; // Wallet address

type GetCapabilitiesResult = Record<`0x${string}`, <Record<string, any>>; // Hex chain id
```


### wallet_getCapabilitiesのパラメータ例

```js
["0xd46e8dd67c5d32be8058bb8eb970870f07244567"]
```

### wallet_getCapabilitiesの戻り値の例

```json
{
  "0x2105": {
    "paymasterService": {
      "supported": true
    },
    "sessionKeys": {
      "supported": true
    }
  },
  "0x14A34": {
    "paymasterService": {
      "supported": true
    }
  }
}
```


## atomicBatch

ウォレットが複数のトランザクションを1つのトランザクションで実行できるかを返す機能です。
ウォレットが`atomicBatch`機能をサポートしている場合、`wallet_sendCalls`で送信された複数のトランザクションは必ず1つのトランザクションとして実行されます。

### atomicBatchの機能仕様

```js
type AtomicBatchCapability = {
  supported: true;
};
```

もし複数チェーンでトランザクションを送りたい場合は、チェーンごとに`atomicBatch`機能を含める必要があります。

### atomicBatchを含むwallet_getCapabilitiesの戻り値の例

```json
{
  "0x2105": {
    "atomicBatch": {
      "supported": true
    },
  },
  "0x14A34": {
    "atomicBatch": {
      "supported": true
    }
  }
}
```

# 補足

## 名前の選定

`eth_sendTransaction`は過去の機能の名残であるため、新しく`wallet_`というプレフィックスをつけることでより役割を明確にしました。
また、`wallet_sendCalls`は、EOAウォレットの場合複数のトランザクションを送信する可能性があり、**ERC4337**などのコントラクトウォレットの場合は複数のトランザクションを単一のトランザクションにまとめることがあり、どちらにも使用できる名前とのことで採用したそうです。

## トランザクション実行のAtomicity

複数のトランザクションを1つのトランザクションで実行（バッチトランザクション）するとき、それがアトミック（順番通りに全て一度に）実行されることが求められます。
これに対応する`atomicBatch`機能を導入することで、複数のトランザクションはそのまま実行させつつ、バッチトランザクションを実行するときのみアトミックに実行されるように選択できます。

## ガスリミット

最初、各トランザクションにはオプションとしてガスフィールドを含めていました。
しかし、**ERC4337**形式のコントラクトウォレットの場合はガスリミットを指定できず、単一のガス値を提案していました。
ただ、これはEOAウォレットに適しておらず、最終的にはガスフィールドを完全に削除しました。

# 互換性

新しいメソッドがウォレットでサポートされていないばあ、`eth_sendTransaction`を使用してトランザクションを順番に送ることができます。
また、ユーザーにはそのことを通知する必要があります。

# セキュリティ

以下の点に注意してトランザクションを実行する必要があります。

- バッチ内のトランザクション間で信頼できないトランザクションが追加される可能性があります。
- バッチ内のトランザクションは連続してないブロックに含まれたり、時間的な制約がありません。
- 一定時間内に実行されるという「**デッドライン**」や、特定の条件が時間内に満たされる必要がある「**タイムライン**」をコントラクトで設定して、予期しない遅延や失敗を防ぐことができます。
- ウォレットがバッチトランザクションをサポートしていることをアプリケーションに示さない限り、単一のトランザクhそんで送信されると仮定する必要があります。

## プライバシーの考慮

- ウォレットへのアクセスをアプリ側に許可したり、トランザクション送信の許可をユーザーに求めることで、ユーザーが細かく権限の管理を行うことができます。
- ウォレットが特定の機能をサポートしてないのか、ユーザーがアプリケーションとの接続を許可していないのかなど詳細なメッセージを返す。
- ユーザーが許可したアプリケーションにのみ機能を公開する。
- ウォレットの機能を過度にクエリしたり共有すると、ウォレットがどの機能を持っているか漏洩してしまうため避ける。

# 引用

Moody Salem (@moodysalem), Lukas Rosario (@lukasrosario), Wilson Cusack (@wilsoncusack), Dror Tirosh (@drortirosh), Jake Moxey (@jxom), Derek Rein (@arein), "EIP-5792: Wallet Call API [DRAFT]," Ethereum Improvement Proposals, no. 5792, October 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5792.

# 最後に

今回は「ウォレットからバッチトランザクションを実行したり、実行ステータスを確認できるJSON-RPCメソッドを追加する仕組みを提案している**EIP5792**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

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