---
title: "適用於 L2 與簽章聚合器的 ERC-4337 gas 估算"
description: "了解 Alchemy 的 ERC-4337 Bundler 如何處理未由 entry point 合約計量的 gas 費用估算，包括簽章聚合器與第 2 層交易（Optimism、Arbitrum）。"
---

# 適用於 L2 與簽章聚合器的 ERC-4337 gas 估算

在[ERC-4337 Gas Estimation](https://www.alchemy.com/blog/erc-4337-gas-estimation)中，我們討論了 gas 在 ERC-4337 中的運作方式，以及我們的 gas 估算方法。在第二部分[Dummy Signatures and Gas Token Transfers](https://www.alchemy.com/blog/dummy-signatures-and-gas-token-transfers)中，我們發現估算 gas 並不總是直截了當，還需要考慮各種邊緣案例。本文將深入探討我們遇到的其他幾種邊緣案例。

## L2 問題

[Ethereum Layer 2 rollup](https://ethereum.org/en/layer-2/) 的部分定義是：「讓 layer 1 負責安全性、資料可用性與去中心化，而 layer 2 負責擴展性。」

為達成這個目標，L2 會將許多交易「捲入」單一批次，再將其發布到 layer 1 區塊鏈上。這個交易成本並非免費，因為 L2 需要為發布大批資料到 L1 鏈上所產生的 calldata 成本付費。

L2 需要一種方式向使用者收取這些產生的 L1 calldata 成本。不同的 rollup 框架以不同方式達成這個目標。

本文聚焦於兩個最大的 EVM rollup：Arbitrum 與 Optimism。

### Arbitrum 如何計算涵蓋 L1 gas 費用的成本？

在 Arbitrum 上，用來涵蓋 [L1 gas 成本的 L2 gas 費用計算方式](https://developer.arbitrum.io/arbos/l1-pricing)如下，其中資料大小是指經過 Brotli 壓縮後的位元組大小：

<CalloutBlock>

💡 L1 成本 \(L1C\) = 每位元組資料的 L1 價格 \(L1P\) \* 需發布資料的位元組大小 \(L1S\)

</CalloutBlock>

<CalloutBlock>

💡 Gas \(G\) = L1 成本 \(L1C\) / L2 Gas 價格 \(P\)

</CalloutBlock>

這部分的 gas 是在交易開始執行前收取的，會計入交易的 `gasLimit`。因此，必須在[交易 gas 估算](https://developer.arbitrum.io/devs-how-tos/how-to-estimate-gas)時納入考量。

### Optimism 如何計算涵蓋 L1 gas 費用的成本？

在 Optimism 上，L1 gas 成本的計算方式類似，將壓縮後的位元組大小乘以 L1 費用。不同於 Arbitrum 將此值轉換為 L2 gas，Optimism 是直接從發送者帳戶中扣除所需的 ETH。

發送者在估算 gas 時不需要考慮這個值，但也無法設定其支出的上限。Optimism 會確保此費用不會突然飆升。

## L2 上的 bundler 如何為 L1 費用收費？

在兩種情況下，於 L2 上提交 bundle 交易的 bundler 都會被收取 L1 費用。bundler 需要有方法向被打包的使用者操作收取這筆費用，方式是提高 L2 gas。

**對 L2 gas 的實際影響可透過以下方式判斷：**

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

`verificationGasLimit` 和 `callGasLimit` 是由 entry point 計量的，因此 bundler 無法用它們來收取這筆額外的 gas 費用。bundler 需要仰賴其他方法。

### 嘗試一：設定較高的 priority fee

要求較高的 `maxPriorityFeePerGas` 或許能讓 bundler 彌補這些損失的費用。

**計算方式如下：**

1. 估算 L1 費用並將其轉換為 L2 gas
   a. 假設 bundle 大小為 1 個使用者操作，並使用網路提供的方法（通常以特殊合約呼叫的形式提供）來估算 gas。
   b. 這需要同時假設 L1 base fee 與 L2 base fee，才能將費用轉換為 L2 gas 值。
1. 估算 `verificationGasLimit`
   a. 由於這個 gas limit 受到嚴格的模擬規則限制，估算值很可能非常接近實際值，不像 `callGasLimit` 那樣。
1. 設定 `maxPriorityFeePerGasBuffer` = `L1\_fee / verificationGasLimit‍`
1. 將此緩衝值加到所需的 priority fee 上

這樣做是可行的，但使用者體驗很差。

為了自保，bundler 必須假設使用的 call gas 為 0，並將驗證 gas 視為唯一的收費成分向使用者收費。使用者因此會多付出緩衝 priority fee 成分乘上實際使用的 call gas 的金額。這對使用者來說並不划算。

### 嘗試二：調整 preVerificationGas

這樣一來，`preVerificationGas` 就成了唯一合理可調整的欄位。這確實符合前面所提供的定義，即 `preVerificationGas` 是「用來記錄 entry point 無法計量之 gas 用量的欄位」。既然這是 entry point 無法計量的 gas，我們應該可以用這個欄位來向使用者收費。

**計算方式如下：**

1. 估算 L1 費用並將其轉換為 L2 gas，設為 `preVerificationGas`。
   a. 假設 bundle 大小為 1 個使用者操作，並使用網路提供的方法（通常以特殊合約呼叫的形式提供）來估算 gas。
   b. 這需要我們同時假設 L1 base fee 與 L2 base fee，才能將費用轉換為 L2 gas 值。
1. 計算 L2 未計量的 gas，並將其加到上面計算出的值中。
   a. 這與我們在 PreVerificationGas 計算章節中描述的一般 `preVerificationGas` 計算方法相同。
1. 在 `eth\_sendUserOperation` 期間，也要執行步驟 \(1\) 與 \(2\)，並在「pre-check」階段拒絕任何 `preVerificationGas` 不夠高的操作。
1. 在打包過程中，於提交前再次執行步驟 \(1\) 與 \(2\)。
   a. 拒絕任何 `preVerificationGas` 不夠高的操作。

**雖然這個機制可行，但存在一個非常嚴重的使用者體驗問題。**

在步驟 \(2\) 中，bundler 必須假設 L1 與 L2 的 base fee 值來進行 gas 計算。由於 base fee 是動態變動的，如果在 gas 估算步驟與提交/打包步驟之間，`L1\_fee / L2\_fee` 的比率上升，就需要更高的 `preVerificationGas`，而以較低比率計算出的使用者操作就會被拒絕。

使用者為改善這種情況所能做的最好的方式，就是假設在估算與打包之間比率會上升，並在其 `preVerificationGas` 上預留一些餘裕以提高成功機率。

由於 `preVerificationGas` 一定會全額收取（即它不是一個上限欄位），使用者無論價格如何變化都必須為這個餘裕付費。使用者只能在可能多付費用或操作被拒絕之間做選擇。

<CalloutBlock>

💡 Rundler 實作了上述的 `preVerificationGas` 計算方式。我們建議這些 L2 的使用者在 `eth\_estimateUserOperationGas` 回傳的 `preVerificationGas` 上額外增加 25% 的緩衝，以提高操作不被拒絕的機率。

</CalloutBlock>

### Optimism 的邊緣案例

Optimism 的 base fee 非常低，通常遠低於 100 wei（沒錯，就是 wei）。用來收取 L1 gas 費用的 `preVerificationGas` 與 L2 gas 費用成反比，因此需要 `preVerificationGas` 非常高（達到數百萬）。

Optimism 的 priority fee 通常比其 base fee 高出好幾個數量級。因此，使用者必須*非常*小心，不要提交與網路 priority fee 成比例的 priority fee，因為 entry point 要求對 `preVerificationGas \(very high\) \* priority fee \(normal\) ` 付費，這會導致大幅超付。因此，Rundler 要求 priority fee 必須是 base fee 的固定百分比，以鼓勵在 Optimism 上進行打包。

## 簽章聚合器問題

[簽章聚合](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. 將聚合驗證檢查的 gas 成本分攤到一個 bundle 中的多個操作上。
   a. 這個驗證檢查可以像 BLS 簽章那樣簡單，也可以像聚合的 [ZK](https://www.alchemy.com/blog/zero-knowledge-rollups) 證明那樣複雜。

在目前版本的 entry point 中，[對簽章聚合器驗證函式的呼叫](https://github.com/eth-infinitism/account-abstraction/blob/d1333cf58c17d5cc84e830f3fc3a69081c777979/contracts/core/EntryPoint.sol#L137)是未被計量的。這表示 bundler 必須找到方法向聚合的使用者操作收取這部分的 gas 費用，與上述的 L2 問題類似。

和 L2 問題一樣，唯一合理的做法就是提高 `preVerificationGas`。

**其中一種做法是：**

1. bundler 假設一個 `target` 的 bundle 大小
1. bundler 在估算前，對收到的每個操作呼叫 `validateUserOp`，以取出使用的聚合器位址（若有使用）。
1. 當收到一個聚合的使用者操作時，bundler 需要估算在該目標 bundle 大小下，每個操作由簽章聚合器所使用的 gas 量。
   a. 一種方法是將收到的使用者操作複製 `target` 次，組成一個 bundle，再對 `aggregator.validateSignatures‍` 使用 `eth\_estimateGas`
   b. 另一種方法是維護一份簽章聚合器白名單，預先填入 gas 測量值（包含一個靜態值和一個按操作計算的動態值）。
1. 將估算出的 gas 加到上述 `preVerificationGas` 的計算結果中，並回傳此值。

**這個做法有幾個問題：**

1. bundler 假設一個 `target` 的 bundle 大小，這是有風險的。有兩種可能：
   a. bundler 等到真的能打包目標數量的操作，但這會因延遲而損害使用者體驗
   b. bundler 打包少於目標數量，自行承擔差額成本
1. 使用者無法依照想要多快被納入來「出價」高或低。
   a. 在基於上限的做法中，若使用者想確保快速被納入，可以高估成本；若實際成本較低，也不會被多收費。但在這種做法中，由於 `preVerificationGas` 是靜態的，使用者永遠必須支付全額出價。
1. 在 P2P 網路中，不同的 bundler 可能有不同的目標 bundle 大小。
   a. 這表示用於估算的 bundler，可能會高估或低估 mempool 中另一個 bundler 所需的 `preVerificationGas`。
   b. 這些鏈下的假設會損害 mempool 的互通性。

由於上述複雜性，Rundler 目前尚未支援簽章聚合器。我們很可能會加入上述方法的支援，並假設一個（較小，從 1 開始的）bundle 大小，這會使得使用簽章聚合器的成本變得相當高昂。

## entry point 的潛在變更

上述問題都源自於 entry point 合約未能計量使用者操作所產生的顯著 gas 用量。依賴 `preVerificationGas` 來收取這部分的 gas 費用，會造成嚴重的使用者體驗問題，因為使用者必須支付超過實際使用量的費用，才能提高其 UO 快速上鏈的機率。

解決這些問題的一種方法，是修改 entry point 合約，使其能計量這部分額外的 gas 使用量，並將其歸入基於上限的 gas 欄位中。

### L2-L1 calldata gas 計量

<CalloutBlock>

💡 這只是針對一個困難問題所提出的非常粗略的解決方案大綱。我們很樂意聽取社群的想法！

</CalloutBlock>

L1 calldata gas 成本可以在鏈上計量，使用者操作只需為實際產生的確切成本付費，而不用負擔額外的餘裕。應該使用基於上限的欄位。

一種可能的解決方案是引入一個新欄位 `daCallDataGasLimit`（da 代表 Data Availability，暫定名稱）。這個欄位會是 L2\+ 版本 entry point 的原生欄位，用於那些交易 calldata 被發布到不同系統、因此必須以另一種方式收費的鏈。

**entry point 的邏輯會如下：**

1. 在部署時，entry point 會與一個帶有單一函式 `measureUserOperationDaGas\(UserOperation userOp\)` 的輔助合約 `daGasMeter` 建立關聯
   a. 此函式接收一個使用者操作，並精確測量其使用了多少 DA gas。
   b. 例如，在 Arbitrum 上，該計量器可以對一個記憶體中的使用者操作呼叫 [`gasEstimateL1Component`](https://github.com/OffchainLabs/nitro-contracts/blob/2ba206505edd15ad1e177392c454e89479959ca5/src/node-interface/NodeInterface.sol#L112)，以確定*幾乎*精確的 L1 gas 使用量（無法考量因多個操作打包在一起而產生的額外壓縮效果）。
1. entry point 會在驗證前為每個使用者操作呼叫此函式，並可將這部分 gas 歸入 `daCalldataGasLimit `。
1. 為避免 revert，如果使用的 DA gas 超過 `daCalldataGasLimit`，entry point 不會 revert，而是直接將其限制在該上限，bundler 則需自行承擔超出此上限部分的 gas 費用。bundler 可以在鏈下透過確保任何被打包的操作在其上限欄位中有足夠高的緩衝，來因應這個問題。

**這個做法有幾個相當顯著的缺點：**

1. 不同鏈上的 entry point 合約會有不同的位址。
1. 測量過程中的 gas 使用量。
   a. 這個問題較不嚴重，因為在 L2\+ 上，執行 gas 成本通常遠低於資料 gas 成本。

## 簽章聚合器 gas 計量

entry point 合約可以修改為測量其呼叫 [`aggregator.validateSignatures`](https://github.com/eth-infinitism/account-abstraction/blob/5e78f8635552e3c55fbced072056a77c8f1e8b75/contracts/core/EntryPoint.sol#L137) 所使用的 gas 量，再除以聚合的使用者操作數量，並透過從 `verificationGasLimit` 中扣除，將 gas 用量平均歸屬給各個操作。

每個列入白名單的簽章聚合器都應對應一個靜態值（驗證聚合簽章的基礎成本）和一個動態值（每增加一個聚合操作所增加的成本）。舉例來說，一個 BLS 簽章聚合器可以將其靜態值設為一次性的 [簽章驗證](https://github.com/eth-infinitism/account-abstraction/blob/5e78f8635552e3c55fbced072056a77c8f1e8b75/contracts/samples/bls/BLSSignatureAggregator.sol#L70) 成本，動態值則設為 [每個操作的雜湊運算](https://github.com/eth-infinitism/account-abstraction/blob/5e78f8635552e3c55fbced072056a77c8f1e8b75/contracts/samples/bls/BLSSignatureAggregator.sol#L68) 成本。bundler 可以在 `eth\_estimateUserOperationGas` 期間，將靜態值除以目標 bundle 大小再加上動態值，以此提高 `verificationGasLimit`。

#### 我們如何設定目標 bundle 大小？

一種可能的解決方案，是在 `eth\_estimateUserOperationGas` 的參數中加入一個 `minimumBundleSize`，對應使用者希望被納入的最小 bundle 大小（因此也是最高的 gas）。願意支付更多費用的使用者可以降低他們的最小 bundle 大小，以縮短被納入所需的時間。

bundler 接著需要有相應的邏輯，確保只將某個 UO 納入至少與該 UO 最小 bundle 大小相符的 bundle 中。這個最小 bundle 大小可以在模擬期間，透過追蹤帳戶驗證步驟所使用的 gas 量，從 `verificationGasLimit ` 中扣除，再根據剩餘部分計算出最小大小。bundler 可以將此值與該 UO 一併儲存在其 mempool 中，做為打包時的提示。

<CalloutBlock>

💡 這個做法的一個缺點是，它假設簽章聚合器對每個操作的 gas 用量是均勻的。如果並非如此，計量工作可以轉移到聚合器合約上，並由其驗證函式回傳。bundler 也需要有方法在鏈下計算這些非均勻的值。

</CalloutBlock>

### 這對簽章聚合器開發者意味著什麼？

簽章聚合器很可能需要由 bundler 直接列入白名單，方法是提供計算鏈下簽章的方法，以及其靜態/動態 gas 成本的組成。冷啟動問題將會相當棘手，尤其是考量到使用者無法「出價」來加快操作速度。

### 這對帳戶客戶端開發者意味著什麼？

了解上述所說明的 L2 差異相當重要，尤其是在估算 gas 費用時。在 Optimism 上，如果你使用網路提供的 priority fee，可能會大幅超付使用者操作的費用，因為這個 priority fee 現在也適用於 L1 成本。若使用 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 gas 估算的下一篇文章，將逐步說明使用者操作費用估算的流程。如果你錯過了第一或第二部分，可以了解 ERC-4337 gas 估算、dummy 值以及 token 轉帳問題的運作方式。

- [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)
