Table of Contents:
ERC-4337 Bundlers serve two core functions:
- Predicting the gas costs for a UserOperation, i.e.,
- Packaging and submitting the UserOperation to the chain, i.e.,
Among these, predicting the gas costs for UserOperations is one of the most challenging parts of the Bundler. As we recently open-sourced our own Bundler implementation, this article will discuss the problems we have encountered in the process of gas prediction and their corresponding solutions.
We will also focus on enacting a gas fee prediction, which is not within the scope of the ERC-4337 protocol but is an inevitable matter regarding Bundler implementations.
Let’s first consider that, in an AA framework, the user's account is a contract. When the EVM executes a transaction involving a contract, loading said contract typically incurs gas costs. Additionally, the user's UserOp will be encapsulated in a transaction and sent to the chain, where it will be executed by the unified EntryPoint contract. Therefore, even for a regular transfer, the gas consumption will be several times higher than that of a regular EOA transfer.
In theory, to deal with the above, one can set a large GasLimit to avoid different complex situations. This is quite simple, although it requires the user's account to have a considerable balance to deduct this fee in advance, which turns out impractical. An ideal solution would be to have a way to accurately estimate gas consumption, allowing users to make regular transactions within a reasonable range, greatly improving user experience and reducing transaction barriers.
According to the official documentation of ERC-4337, there are three fields related to gas estimation:
Let's look into these fields individually and provide a gas prediction method for each.
First, we need to understand that a UserOperation is a structure packaged into a transaction by the Bundler’s Signer, and sent to the chain for execution. During the execution process, the gas consumed comes from the Signer, and after execution, it is calculated and returned to the Signer.
In the Ethereum model, a certain amount of gas is deducted before executing a transaction, which can be summarized as follows:
- Creating a contract deducts 53,000 gas, while calling a contract deducts 21,000 gas.
- Gas is deducted based on the length and byte type of the contract’s code.
In other words, a certain amount of implicit gas is consumed before executing a transaction and cannot be calculated during execution. Therefore, the UserOperation needs to specify preVerificationGas to subsidize the Signer. However, this implicit gas can be calculated off-chain. The official SDK provides relevant interfaces that we can call:
As the name suggests, this is the GasLimit assigned during the verification phase. There are three cases where this GasLimit is used:
- If UserOp.sender does not exist, UserOp.initCode is executed to initialize the Account.
- Account.validateUserOp is executed to validate the signature.
- If PaymasterAndData exists:
- Paymaster.validatePaymasterUserOp is called during the verification phase.
- Paymaster.postOp is called during the completion phase.
As we can see,
verificationGasLimit represents the overall gas limit for all the operations mentioned above. However, it is not a strict limit and may not be accurate, as the invocations of
validateUserOp are independent. This means that in a worst-case scenario, the actual gas consumption could be twice the
Therefore, to ensure that the gas consumption of
validatePaymasterUserOp does not exceed the
verificationGasLimit, we need to predict the gas consumption of these three operations.
The gas consumption of
createSender can be accurately predicted using the traditional
from need to be set as the entryPoint address? Because most Accounts will set a source (i.e., EntryPoint) when they are created. Calling
validateUserOp will validate the source.
For other methods like
validatePaymasterUserOp, gas consumption is currently difficult to predict. However, due to the nature of these methods, which is to check the validity of UserOp (most likely to validate the signature), gas consumption is not very high. In practice, a GasLimit of 100,000 can cover the consumption of these methods. Therefore, based on the above, we can set
callGasLimit represents the gas consumption of executing
callData by an Account and is the most important part of gas predictions. So, how do we predict the gas consumption of this process? We can use the conventional
estimateGas as follows:
Here, we simulate calling the method of the Sender Account from the
EntryPoint, bypassing the source check of the Account, and also bypassing the signature verification step in
validateUserOp (because the UserOp in the
eth_estimateUserOperationGas interface does not have a signature).
There is a problem here, which is that this prediction is based on the assumption that the Sender Account exists. If it is the first transaction of the Account (the Account has not been deployed yet and needs to execute
initCode first), this prediction will revert due to the non-existence of the Account. It is not possible to accurately estimate the callGasLimit.
How to get the callGasLimit for the first UserOperation
Since we cannot obtain an accurate callGasLimit for the first transaction, do we have other solutions? We do. We can first estimate the TotalGasUsed of the entire UserOp, and then subtract the
createSenderGas from the total TotalGasUsed to get an approximate value.
otherVerificationGasUsed refers to the actual gas consumption of
validatePaymasterUserOp. Based on this, the gas consumption of these methods is not expected to be significant (typically within 100,000). Therefore, we can consider
otherVerificationGasUsed as part of the
How to get
HandleOps without a signature
eth_estimateUserOperation interface, the
UserOperation passed does not necessarily include a signature. This means that we cannot use
eth_estimateGas(entryPoint.handleOps) to obtain the gas required for executing the UserOp. This estimation will result in an error because the EntryPoint reverts if the signature validation fails.
So, how can we obtain an accurate
GasUsed without a signature? The answer is to use the
simulateHandleOp method provided by the EntryPoint contract. This method allows us to simulate the entire execution process of the transaction without the need for a signature. It achieves this by bypassing signature validation during the
validate phase and not returning a value. However, note that this method ultimately reverts, which means it can only be called using
We can determine that the second parameter,
paid, represents the gas used.
Therefore, if we set
gasPrice to 1,
paid will equal
We notice that the
UserOp does not have a
gasPrice field, but instead has
maxPriorityFeePerGas, similar to EIP-1559. However, this is only the design of the
UserOp and does not imply that the AA protocol cannot run on non-EIP-1559 chains. In fact, in the implementation of EntryPoint,
maxPriorityFeePerGas are used to calculate a more reasonable
gasPrice. Let's look at the formula:
For chains that do not support EIP-1559, we can consider the
basefee as 0. Therefore, we just need to set
maxPriorityFeePerGas to 1, making the
To summarize, we have covered how to simulate the specific
UserOp without a signature. This allows us to approximate the
Gas Fee prediction, also known as
maxPriorityFeePerGas, is also very important. This is because the Bundler's Signer cannot lose money.
Firstly, if the gasFee of the user's UserOp is less than the Signer's gasFee, then after executing the UserOp, the calculated fee of the UserOp is not enough to subsidize the Signer's fee, causing the Signer to lose money. The Bundler's Signer does not bear the responsibility of the UserOp fee, it only sends transactions. Therefore, the Signer needs to deposit a certain balance in advance. If there is a loss, it will directly affect the execution of subsequent UserOps and the normal operation of the Bundler. Also, because the Signer has costs, Bundler usually maintains a limited number of Signers. If the Bundler needs to support multiple chains, the maintenance cost will also increase. The entity responsible for paying the UserOp fee should be the Sender itself and the Paymaster.
Of course, the ideal situation is that the gasFee of the UserOp should be close to the Signer's gasFee. Therefore, I believe that the Bundler should return the recommended
eth_estimateUserOperationGas. This can greatly reduce the fee of the UserOp.
if the gasFee of the UserOp is very low, we can also put the UserOp that is lower than the Signer's gasFee into the UserOp pool and wait until the Signer's gasFee is low enough to package the UserOp. However, in practice, this type of UserOp often needs to wait a long time to be executed, which is not good for user experience.
Therefore, under normal circumstances, we can return slightly higher
maxPriorityFeePerGas than the Signer's gasFee, so that the UserOp can be executed immediately when sent.
L2 Fee Estimation
The above solution can only solve the L1 Fee Estimation. Why can't it be applied to L2?
Because L2 relies on L1 as a data security guarantee. After executing a certain number of L2 Transactions, a Rollup proof is generated and sent to L1. Therefore, the L2 Transaction Fee includes an implicit L1 Fee.
This calculation method of the L2 transaction fee creates a problem: Many wallets, such as MetaMask, do not include the L1 fee. If your balance is just enough to satisfy GasPrice * GasLimit, the transaction you send will most likely fail.
If, in an L2, we make the GasPrice of UserOp close to the Signer's GasPrice, undoubtedly, the Signer will bear the cost of the L1 fee, which is not expected. Fortunately, the L1 fee can be calculated.
Usually, L2s provide a GasPriceOracle contract that allows you to quickly obtain the L1 fee.
For example, in Scroll/Base/opBNB/Optimism etc, selecting Optimism as an example, given a gas price oracle, one could simply call the getL1Fee method to obtain the specific L1 fee and convert it into the GasPrice of UserOp.
Other L2s like Taiko have already converted the L1 Fee into the GasPrice, so we don't need to calculate the L1 Fee again.
By now, we have basically solved the gas prediction problem. However, it should be noted that the gas price of some chains fluctuates greatly, as happens in Polygon. As such, a gas price may become invalid in a short period of time. In practice, we must multiply the predicted gas fee for chains with large fluctuations by a coefficient to mitigate this situation.
You can refer to the Particle Network’s open-source Bundler’s implementation here: https://github.com/Particle-Network/particle-bundler-server
Particle Network is creating an advanced framework for better Web3’s user and developer experiences with our Smart Wallet-as-a-Service (Smart WaaS) offering. As the article highlighted, AA is a game-changer in how dApps and blockchain-based services can be designed, making them more user-friendly, flexible, and adaptive to complex scenarios. To help developers swiftly transition and make the most out of AA, Particle Network's Smart WaaS can enable developers to construct advanced applications, going beyond the typical constraints of the EOA framework.
Particle's Smart WaaS is set apart by its modular design, ensuring developers have the freedom and flexibility they need. Whether it's selecting specific smart account implementations, choosing Bundlers, or plugging into third-party tools, the options are vast and tailored to developer needs. Particle's emphasis on combining the benefits of WaaS with the capabilities of AA translates into a seamless transition for developers, further enhanced by its native support for AA within the platform. With Particle’s Smart Wallet-as-a-Service Modular Stack, developers get a comprehensive toolkit to integrate AA into their dApps effortlessly, heralding a new era of advanced, user-centric blockchain applications.
Particle Network's Modular Smart Wallet-as-a-Service solutions are 100% free for developers and teams. If you have any inquiries about integrating with us, feel free to book a meeting with one of our agents!
About Particle Network
Particle Network is building the Intent-Centric Access Layer of Web3. Particle's Modular Smart Wallet-as-a-Service tools allow developers to tap into MPC-TSS and social logins to enable self-custodial, dApp-embedded wallets accessible through users' Web2 accounts. This also allows them to tap into ERC-4337 account abstraction, enabling a seamless experience with maximum flexibility. Particle's next evolutionary steps include the introduction of Omnichain Abstraction, a Confidential zkStack, and the Intent Fusion Protocol, elevating users' experience within dApps and paving the way for mass Web3 adoption.