Debugging UserOps with Tenderly
In account abstraction (ERC-4337), the transactions sent by smart accounts are known as "UserOps." UserOps are similar to but not the same as regular transactions, so it may not be clear how to debug them.
In this guide, we will be using Tenderly to debug UserOps. Make sure you have signed up and created a Tenderly account.
The UserOp Structure
Let's begin by examining a typical UserOp example:
{
"sender": "0xd2f1a28cc13c95ac4671cee806593c920d81c1f8",
"nonce": "330",
"initCode": "0x",
"callData": "0x51945447000000000000000000000000a02cddfa44b8c01b4257f54ac1c43f75801e81750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit": "55000",
"verificationGasLimit": "1000000",
"preVerificationGas": "100000",
"maxFeePerGas": "39459600032",
"maxPriorityFeePerGas": "39459600000",
"paymasterAndData": "0x",
"signature": "0x00000000bb864a968f25011aeb6574bef934726c066e9b47c261f6e40a49a3065da7deb57b0b40fffe70ec9ff0c1572358cd09d14cf8e671fdb66facfae0cb0a2db3a9cb1c"
}
This UserOp structure will be our reference point as we navigate through the process of simulating and debugging UserOps. Understanding the components of this example is key to effectively using Tenderly's tools for our debugging needs.
UserOp Lifecycle
Before delving into the nuances of debugging UserOps, it's helpful to learn the lifecycle of a UserOp. Here it is:
If this looks daunting, let's focus on only the high level:
- A UserOp, with no gas estimates nor signature, is sent to a paymaster server, who simulates the UserOp and returns the gas estimates.
- If the UserOp has any errors in the validation or execution phase, the paymaster server will return an error since it can't properly simulate it.
- Now, the UserOp, with gas estimates and a proper signature, is sent to the bundler.
- At this point, the UserOp is not expected to revert during the validation phase, but it may nevertheless revert during the execution phase due to the on-chain state having changed between when the UserOp was sent to the paymaster and when it's submitted by the bundler.
Understanding UserOp Failures
A UserOp can fail at various stages, including during the paymaster call (if sponsored), the gas estimation call, or the final execution call. Identifying the failure point is straightforward by examining the method indicated in the error log of the failed UserOp. For instance:
- Paymaster Call Failures might involve methods such as
zd_sponsorUserOperation
(ZeroDev meta-paymaster),pm_sponsorUserOperation
(Pimlico), oralchemy_requestPaymasterAndData
(Alchemy). - Gas Estimation Call Failures are indicated by the
eth_estimateUserOperationGas
method. - Execution Call Failures are marked by the
eth_sendUserOperation
method.
Failures are generally classified into two categories:
- Validation Errors: These occur during the validation phase when transactions are deemed invalid due to issues like incorrect signatures or nonce values. They typically present as EntryPoint error codes (e.g., AA23: XXXX).
- Execution Errors: These occur during the execution phase when transactions are valid, but the contract interaction is reverted, often noted as
execution reverted
.
Using Tenderly for Simulation and Debugging
Adjusting Gas Limits for Simulation
For failures during paymaster or gas estimation calls, the UserOp gas limits (preVerificationGas
, callGasLimit
, verificationGasLimit
) may default to 0x
. Before simulating the UserOp, adjust these gas limits as shown below. Increase these values based on error feedback if the simulation fails:
{
"preVerificationGas": "0x186A0",
"callGasLimit": "0xD6D8",
"verificationGasLimit": "0xF4240"
}
If the failure occurs during eth_sendUserOperation
, the UserOp should already contain all necessary values for accurate simulation.
Debugging Execution Errors
To simulate a UserOp in Tenderly, follow these steps:
- Log in to your Tenderly account.
- Navigate to
Simulator
and clickNew Simulation
.
- Enter the EntryPoint contract address in
Insert any address
input and select the appropriate chain.- The EntryPoint address is
0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789
for v0.6 (default for ZeroDev SDK v5.1.x or below) and0x0000000071727De22E5E9d8BAf0edAc6f37da032
for v0.7 (default for ZeroDev SDK v5.2.x or above)
- The EntryPoint address is
- Choose
simulateHandleOp
, input the UserOp into the tuple field, and commence the simulation.
If the simulation fails, it typically indicates a problem with the end contract. Verify the initial calldata thoroughly.
Simulating End Contract Calls
To simulate an end contract call:
- Insert the Smart Contract Wallet address in
Insert any address
input (sender
field from UserOp). - Select the chain and enter the EntryPoint contract address in the
From
field. - Enter the
calldata
field from UserOp into theRaw input data
field and simulate the transaction.
Debugging Validation Errors
For validation errors, simulate the validation process by:
- Inserting the EntryPoint contract address and selecting the chain.
- Choosing
simulateValidation
, inputting the UserOp into the tuple field, and filling in thesender
andcalldata
fields accordingly before simulating the transaction.
When simulating the validation process for a UserOp in Tenderly, the output can provide insightful details into potential issues. For instance, after simulating a UserOp, you might receive a ValidationResult
like the one below:
ValidationResult[{"preOpGas":"437497","prefund":"2118037380807816","sigFailed":true,"validAfter":"0","validUntil":"1708466460","paymasterContext":"0x"},{"stake":"0","unstakeDelaySec":"0"},{"stake":"0","unstakeDelaySec":"0"},{"stake":"100000000000000000000","unstakeDelaySec":"86400"}]
On Tenderly, it might look like this:
This result indicates various aspects of the validation process, with a particular focus on the failure due to signature validation ("sigFailed": true
). Such output suggests that the UserOp failed validation because the signature did not match the expected parameters or was otherwise invalid.
Other resources
There are some resources that we find helpful: