---
title: "L2とシグネチャアグリゲーターのためのERC-4337ガス見積もり"
description: "Alchemy社のERC-4337 Bundlerが、シグネチャアグリゲーターやOptimism、Arbitrumを含むレイヤー2トランザクションなど、エントリーポイントコントラクトで計測されないガス代のガス見積もりをどのように処理するかを解説します。"
---

# L2とシグネチャアグリゲーターのためのERC-4337ガス見積もり

[ERC-4337 Gas Estimation](https://www.alchemy.com/blog/erc-4337-gas-estimation)では、ERC-4337におけるガスの仕組みと、私たちのガス見積もり手法について解説した。パート2の[Dummy Signatures and Gas Token Transfers](https://www.alchemy.com/blog/dummy-signatures-and-gas-token-transfers)では、ガス見積もりが常に単純とは限らず、エッジケースを考慮する必要があることを確認した。本記事では、私たちが遭遇したエッジケースをさらにいくつか取り上げる。

## L2の問題

[Ethereum Layer 2 rollup](https://ethereum.org/en/layer-2/)の定義の一部にはこうある。「layer 1にセキュリティ、データ可用性、分散性を担わせ、layer 2にスケーリングを担わせる」。

これを実現するため、L2は多数のトランザクションを1つのバッチに「ロールアップ」し、それをlayer 1ブロックチェーンに投稿する。このトランザクションコストは無料ではない。L2はL1チェーンに大量のデータのバッチを投稿する際に発生するcalldataコストを支払う必要があるためだ。

L2はこのL1 calldataコストをユーザーに課金する方法を必要とする。rollupフレームワークはそれぞれ異なる方法でこれを実現している。

この記事では、最大の2つのEVM rollupであるArbitrumとOptimismに焦点を当てる。

### ArbitrumはL1ガス手数料をカバーするコストをどう計算するか？

Arbitrumでは、[L1ガスコストをカバーするL2ガス料金は次の式で計算される](https://developer.arbitrum.io/arbos/l1-pricing)。ここでデータのサイズはBrotli圧縮後のバイト単位のサイズである。

<CalloutBlock>

💡 L1コスト\(L1C\) = 1バイトあたりのL1価格\(L1P\) \* 投稿するデータのバイトサイズ\(L1S\)

</CalloutBlock>

<CalloutBlock>

💡 ガス\(G\) = L1コスト\(L1C\) / L2ガス価格\(P\)

</CalloutBlock>

このガスはトランザクションの実行が始まる前に課金され、トランザクションの`gasLimit`にカウントされる。したがって、[トランザクションのガス見積もり](https://developer.arbitrum.io/devs-how-tos/how-to-estimate-gas)の際にこれを考慮する必要がある。

### OptimismはL1ガス手数料をカバーするコストをどう計算するか？

Optimismでは、L1ガスコストは同様の方法で計算される。圧縮後のバイトサイズにL1手数料を乗じる。Arbitrumのようにこの値をL2ガスに変換する代わりに、OptimismはSenderのアカウントから必要なETHを直接差し引く。

Senderはガス見積もりの際にこの値を考慮する必要はないが、自分の支出に上限を設定することはできない。Optimismはこの手数料が急騰しないよう配慮している。

## L2上のbundlerはどうやってL1手数料を課金できるか？

いずれの場合も、L2でバンドルトランザクションを送信するbundlerはL1手数料を課される。bundlerは、L2ガスを増やすことでバンドルされたuser operationにこの手数料を課金する方法を必要とする。

**L2ガスへの実質的な影響は次のように求められる:**

`L2\_gas = L1\_gas \* L1\_fee / L2\_fee`

`verificationGasLimit`と`callGasLimit`はentry pointによって計測されるため、bundlerがこの追加ガスの課金に使うことはできない。bundlerは他の方法に頼る必要がある。

### 試み1: priority feeを引き上げる

より高い`maxPriorityFeePerGas`を要求すれば、bundlerはこの失われた手数料を取り戻せる可能性がある。

**計算は次のようになる:**

1. L1手数料を見積もり、L2ガスに変換する
   a. サイズ1のuser operationからなるバンドルを想定し、通常は特別なコントラクトコールとして公開されているネットワーク提供のメソッドを使ってガスを見積もる。
   b. この手数料をL2ガス値に変換するには、L1ベース料金とL2ベース料金の両方を仮定する必要がある。
1. `verificationGasLimit`を見積もる
   a. このガス上限は厳格なシミュレーションルールの下にあるため、`callGasLimit`とは異なり、見積もり値が実際の値に非常に近くなる可能性が高い。
1. `maxPriorityFeePerGasBuffer` = `L1\_fee / verificationGasLimit‍` を設定する
1. そのバッファを必要なpriority feeに加算する

これは機能する可能性はあるが、UXがひどく悪い。

自身を守るために、bundlerはcall gasの使用量を0と仮定し、verification gasのみが構成要素であるかのようにユーザーへ課金しなければならない。すると、ユーザーはバッファのpriority fee成分に実際に使われたcall gasを乗じた分だけ過剰に支払うことになる。これはユーザーにとって好ましくない。

### 試み2: preVerificationGasを操作する

そうなると、`preVerificationGas`だけが操作可能な唯一の妥当なフィールドとして残る。これは上記で示した「`preVerificationGas`はentry pointが計測できないガス使用量を捕捉するために使われるガスフィールドである」という定義にもよく合致する。これはentry pointが計測しないガスであるため、このフィールドを使ってユーザーに課金できると期待できる。

**計算は次のようになる:**

1. L1手数料を見積もり、L2ガスに変換して、`preVerificationGas`として設定する。
   a. サイズ1のuser operationからなるバンドルを想定し、通常は特別なコントラクトコールとして公開されているネットワーク提供のメソッドを使ってガスを見積もる。
   b. この手数料をL2ガス値に変換するには、L1ベース料金とL2ベース料金の両方を仮定する必要がある。
1. L2の非計測ガスを計算し、上記で計算した値に加算する。
   a. これは、通常の`preVerificationGas`計算に関するPreVerificationGas計算のセクションで説明したのと同じ方法である。
1. `eth\_sendUserOperation`の際にも\(1\)と\(2\)を実行し、「事前チェック」段階の一部として、十分に高い`preVerificationGas`を持たないoperationは拒否する。
1. バンドリングの際、送信直前に再度\(1\)と\(2\)を実行する。
   a. 十分に高い`preVerificationGas`を持たないoperationは拒否する。

**この仕組みは機能するが、非常に大きなUXの問題がある。**

\(2\)の際、bundlerはガス計算を行うためにL1とL2のベース料金の値を仮定する必要がある。ベース料金は動的であるため、ガス見積もりのステップと送信/バンドリングのステップの間に`L1\_fee / L2\_fee`の比率が上昇すると、より高い`preVerificationGas`が必要になり、低い比率で計算されたuser operationは拒否されてしまう。

これを改善するためにユーザーができる最善のことは、見積もりからバンドリングまでの間に比率が上昇すると仮定し、`preVerificationGas`に余裕を持たせて、成功の可能性を高めることである。

`preVerificationGas`は常に全額課金される（つまりlimitフィールドではない）ため、ユーザーは価格がどうなろうとこのオーバーヘッド分を支払わざるを得ない。ユーザーは、過剰に支払うか、operationが拒否されるかの二択を迫られることになる。

<CalloutBlock>

💡 Rundlerは上記の`preVerificationGas`計算を実装している。これらのL2のユーザーには、`eth\_estimateUserOperationGas`から返される`preVerificationGas`に25%のバッファを追加し、operationが拒否される可能性を減らすことを推奨する。

</CalloutBlock>

### Optimismのエッジケース

Optimismのベース料金は非常に低く、しばしば100 wei\(そう、wei\)を大きく下回る。L1ガス手数料を課金するために必要な`preVerificationGas`はL2ガス料金と反比例の関係にあるため、`preVerificationGas`は極めて高く\(数百万に\)なる必要がある。

Optimismのpriority feeはしばしばベース料金より桁違いに高い。そのため、ユーザーはネットワークのpriority feeに比例したpriority feeを送信しないよう、_非常に_注意する必要がある。entry pointは`preVerificationGas \(very high\) \* priority fee \(normal\) `分の支払いを要求するため、それは大幅な過剰支払いにつながる。この理由から、RundlerはOptimism上でのバンドリングを促すため、priority feeをベース料金の一定割合の静的値とするよう要求する。

## Signature aggregatorの問題

[Signature aggregation](https://www.alchemy.com/overviews/account-abstraction-aggregate-signatures)は、以下の理由から[ERC-4337](https://eips.ethereum.org/EIPS/eip-4337#using-signature-aggregators)の機能として頻繁に話題になる。

1. 署名の圧縮によりL2上のcalldataコストを削減し、大幅な節約につながる。
1. バンドル内のoperation群全体で、集約された検証チェックのガスコストを償却できる。
   a. この検証チェックはBLS署名のようにシンプルなものから、集約された[ZK](https://www.alchemy.com/blog/zero-knowledge-rollups)証明のように複雑なものまでありうる。

現行バージョンのentry pointでは、[signature aggregatorのvalidate関数への呼び出し](https://github.com/eth-infinitism/account-abstraction/blob/d1333cf58c17d5cc84e830f3fc3a69081c777979/contracts/core/EntryPoint.sol#L137)は計測されていない。つまり、bundlerは上記のL2の問題と同様に、集約されたuser operationにこのガスを課金する手段を見つける必要がある。

L2の問題と同様、これを行う唯一の妥当な方法は`preVerificationGas`を増やすことである。

**その方法の1つは以下の通り:**

1. bundlerは`target`のバンドルサイズを仮定する
1. bundlerは見積もりの前に、受け取ったすべてのoperationに対して`validateUserOp`を呼び出し、使用されている場合はaggregatorのアドレスを抽出する。
1. 集約されたuser operationを受け取った際、bundlerはそのターゲットバンドルサイズにおいて、signature aggregatorが1つのoperationあたりに使用するガス量を見積もる必要がある。
   a. 1つの方法は、受け取ったuser operationを`target`回複製してバンドルにし、`aggregator.validateSignatures‍`に対して`eth\_estimateGas`を使うことである。
   b. もう1つの方法は、あらかじめガス測定値\(静的な値とoperationごとの動的な値\)を登録したsignature aggregatorのホワイトリストを維持することである。
1. 見積もったガスを上記の`preVerificationGas`計算に加算し、その値を返す。

**このアプローチにはいくつかの問題がある:**

1. bundlerは`target`のバンドルサイズを仮定することでリスクを負っている。以下のいずれかとなる:
   a. bundlerはターゲットとするoperation数を実際にバンドルできるまで待機し、レイテンシによってUXを損なう
   b. bundlerはターゲットより少ない数でバンドルし、コストを自ら負担する
1. ユーザーは、どれだけ速く取り込まれたいかに応じて多く払ったり少なく払ったりする「入札」ができない。
   a. limitベースのアプローチでは、ユーザーが迅速なマイニングを確実にしたい場合、コストを過大に見積もることができる。結果的にコストが低ければ、過剰請求されることはない。しかしこのアプローチでは、`preVerificationGas`が静的であるため、ユーザーは常に自分の入札額全額を支払わなければならない。
1. P2Pネットワークにおいて、bundlerごとに異なるターゲットバンドルサイズを持つ場合がある。
   a. つまり、見積もりに使われたbundlerは、mempool内の別のbundlerが必要とする`preVerificationGas`を過大または過小に見積もる可能性がある。
   b. こうしたオフチェーンの仮定は、mempoolの相互運用性を損なう。

Rundlerは、こうした複雑さのため現時点ではsignature aggregatorのサポートを持っていない。上記の方法をサポートし、小さな\(1から始める\)バンドルサイズを仮定するようになる可能性は高く、その場合signature aggregatorの利用は非常に高コストになるだろう。

## Entry pointの変更案

上記の問題はいずれも、user operationによる重要なガス使用量をentry pointコントラクトが計測していないことに起因する。この使用量の課金に`preVerificationGas`を頼ることには、ユーザーがUOをオンチェーンに素早く取り込ませる可能性を高めるために、実際の使用量より多く支払わなければならないという重大なUX上の問題がある。

これらの問題への解決策として、entry pointコントラクトを修正し、この追加のガス使用量を計測してlimitベースのガスフィールドに反映させることが考えられる。

### L2-L1 calldataガス計測

<CalloutBlock>

💡 これは難しい問題に対する解決策の非常に大まかな概要である。コミュニティからのアイデアをぜひ聞きたい！

</CalloutBlock>

L1 calldataのガスコストはオンチェーンで計測でき、user operationにはオーバーヘッドではなく実際にかかったコストのみを課金できる。limitベースのフィールドを使用すべきだ。

考えられる解決策の1つは、新しいフィールド`daCallDataGasLimit`\(daはData Availabilityの略、仮称\)を導入することである。このフィールドはL2\+版のentry pointにネイティブなものとなり、トランザクションのcalldataが別のシステムに投稿され、それゆえ別の方法で課金しなければならないチェーンで使用される。

**entry pointのロジックは次のようになる:**

1. デプロイ時に、entry pointは単一の関数`measureUserOperationDaGas\(UserOperation userOp\)`を持つヘルパーコントラクト`daGasMeter`と紐付けられる
   a. この関数はuser operationを受け取り、使用されたDAガス量を正確に計測する。
   b. 例えば、Arbitrumではこのメーターは、メモリ上のuser operationを使って[`gasEstimateL1Component`](https://github.com/OffchainLabs/nitro-contracts/blob/2ba206505edd15ad1e177392c454e89479959ca5/src/node-interface/NodeInterface.sol#L112)を呼び出すことで、使用されたL1ガス量を_ほぼ_正確に求められる\(複数のoperationを含むバンドルによる追加の圧縮までは考慮できない\)。
1. entry pointは検証前に各user operationに対してこの関数を呼び出し、このガスを`daCalldataGasLimit `に反映させることができる。
1. リバートを避けるため、使用されたDAガスが`daCalldataGasLimit`より大きい場合にリバートする代わりに、entry pointはその上限でキャップし、その上限を超えるガスについてはbundlerが負担することになる。これはオフチェーンでbundlerが、バンドリング前にバンドルされる各operationのlimitフィールドに十分なバッファがあることを確認することで対応できる。

**このアプローチには、かなり重大な、いくつかの欠点がある:**

1. チェーンごとにentry pointコントラクトのアドレスが異なることになる。
1. 計測中のガス消費。
   a. L2\+では実行ガスコストが通常データガスコストよりはるかに小さいため、これはあまり大きな問題ではない。

## Signature aggregatorのガス計測

entry pointコントラクトを修正し、[`aggregator.validateSignatures`](https://github.com/eth-infinitism/account-abstraction/blob/5e78f8635552e3c55fbced072056a77c8f1e8b75/contracts/core/EntryPoint.sol#L137)呼び出し中に使用されたガス量を計測し、それを集約されたuser operationの数で割り、`verificationGasLimit`から差し引くことでガス使用量を均等に反映させることができる。

ホワイトリストに登録された各signature aggregatorには、静的な値（集約署名を検証するための基本コスト）と、動的な値（集約されるoperation1件あたりのコスト増分）を紐付けるべきである。例えば、BLS signature aggregatorであれば、静的な値として1回限りの[署名検証](https://github.com/eth-infinitism/account-abstraction/blob/5e78f8635552e3c55fbced072056a77c8f1e8b75/contracts/samples/bls/BLSSignatureAggregator.sol#L70)コストを、動的な値として[operationごとのハッシュ演算](https://github.com/eth-infinitism/account-abstraction/blob/5e78f8635552e3c55fbced072056a77c8f1e8b75/contracts/samples/bls/BLSSignatureAggregator.sol#L68)コストを設定できる。bundlerは`eth\_estimateUserOperationGas`の際、静的な値をターゲットバンドルサイズで割った値に動的な値を加えた分だけ`verificationGasLimit`を増やすことができる。

#### ターゲットバンドルサイズはどう設定するか？

考えられる解決策の1つは、`eth\_estimateUserOperationGas`の引数に、ユーザーが取り込まれたい最小のバンドルサイズ\(つまり最大のガス\)に対応する`minimumBundleSize`を追加することである。より多く支払ってもよいユーザーは、最小バンドルサイズを小さくすることで取り込みまでの時間を短縮できる。

bundlerは、そのUOの最小バンドルサイズ以上の大きさのバンドルにのみそのUOを含めるようにするロジックを必要とする。この最小バンドルサイズは、シミュレーション中にアカウント検証ステップで使用されたガス量を追跡し、それを`verificationGasLimit `から差し引き、残りから最小サイズを計算することで求められる。bundlerはこの値をmempool内でUOと一緒に保存し、バンドル構築の際のヒントとして利用できる。

<CalloutBlock>

💡 このアプローチの欠点の1つは、signature aggregatorによるガス使用量がoperationごとに均一であると仮定している点である。そうでない場合、計測をaggregatorコントラクト側に押し出し、その検証関数から返させることもできる。bundlerは、こうした不均一な値をオフチェーンで計算する方法も必要とすることになる。

</CalloutBlock>

### これはsignature aggregatorの開発者にとって何を意味するか？

Signature aggregatorは、オフチェーン署名を計算するメソッドと、静的/動的なガスコスト成分をbundlerに提供することで、直接ホワイトリストに登録される必要が生じる可能性が高い。特にユーザーが自分のoperationを速めるために「入札」できないことを考えると、コールドスタート問題は困難なものになるだろう。

### これはアカウントクライアントの開発者にとって何を意味するか？

上記で説明したL2ごとの違いを理解しておくことが重要である。特にガス手数料を見積もる際には注意が必要だ。Optimismでは、ネットワーク提供のpriority feeを使うと、このpriority feeがL1コストにも適用されるようになるため、user operationに対して大幅な過剰支払いをしてしまう可能性がある。Alchemyのbundlerエンドポイントを使用している場合は、手数料見積もりのヒントについて[ドキュメント](https://www.alchemy.com/docs/reference/bundler-faqs#how-do-we-determine-fee-values-to-give-your-userop-the-best-chance-of-landing-on-chain)を参照してほしい。

🦀

## 続けて読む

ERC-4337のガス見積もりを深く掘り下げるこのシリーズの次の記事では、user operationの手数料見積もりプロセスを詳しく解説する。パート1または2を見逃した方は、ERC-4337のガス見積もり、ダミー値、そしてトークン転送の問題がどのように機能するかを学べる。

- [How ERC-4337 Gas Estimation Works](https://www.alchemy.com/blog/erc-4337-gas-estimation)
- [Dummy Signatures and Gas Token Transfers](https://www.alchemy.com/blog/dummy-signatures-and-gas-token-transfers)
- [User Operation Fee Estimation](https://www.alchemy.com/blog/user-operation-fee-estimation)
