What are user operations?
Written by Brady Werkheiser
Reviewed by Logan Ross
User operations are objects that contain transaction details to be executed on behalf of the sender’s smart contract account. User operations are a pseudo-transaction object that enables Account Abstraction to work without requiring consensus-layer changes to Ethereum and layer 2 blockchains that support ERC-4337.
To develop smart contract wallets (SCWs) or to make a decentralized application compatible with SCWs, it is beneficial to understand the parameters that define a user operation, how user op fields are populated during the user op construction process, how user ops are bundled by a bundler, and validated and executed by a paymaster.
Enable new users to create embedded email and passkey wallets using our Embedded Accounts and account abstraction infrastructure!
WATCH: Build and Execute User Operations in Solidity
What are the fields contained in a user operation?
User operations (UOs) contain fields that are similar to regular transactions (e.g. sender, to, calldata, maxFeePerGas, maxPriorityFee, signature, and nonce), but they also has new fields that are specific to the user operation struct including callGasLimit, verificationGasLimit, preVerificationGas, and paymasterAndData.
The definitions for UO fields can be found in the official ERC-4337 specification. They are summarized here:
callGasLimit - the gas to be used by the main execution call
verificationGasLimit - the gas to be used to complete the verification step
preVerificationGas - the gas to pay the bundler to cover the pre-verification execution and calldata costs
paymasterAndData - the sponsoring paymaster’s address plus data to send to the paymaster
An explanation of the design and architecture of user operations can be read in part 1 of the “You Could Invented Account Abstraction” series written by David Philipson, an engineer on Alchemy’s AA infra team.
Next, let’s learn the standard workflow for correctly populating the fields inside of a user operation.
What is the flow of sending a user operation?
The typical flow for sending a user operation (user op) to a bundler consists of multiple steps:
Construct a partial user op with sender, nonce, initCode, and callData filled in
Estimate gas for the partial user op to a bundler RPC via eth_estimateUserOperationGas
Populate preVerificationGas, verificationGasLimit, callGasLimit
If using a paymaster that doesn’t rely on the contents of the user op, like an ERC20 paymaster, paymasterAndData can be populated here.
Estimate the required gas fees for the operation and populate maxFeePerGas and maxPriorityFeePerGas
(Optional) Send the user op to a sponsoring paymaster for signing, and populate paymasterAndData
This must be done at this step as the paymaster’s signature requires all the above fields to be populated.
Sign the user op, populate signature, and send the user op to a bundler via eth_sendUserOperation
While developers can use native ethers.js to get the values for each user operation field, there are a few web3 developer tools that make UO construction easier.
What tool can developers use to construct user operations?
User operation construction tools like Alchemy’s open-source AA SDK make building user operations easier than making user ops with native ethers.js.
Alchemy’s AA SDK
Alchemy’s AA SDK is built with viem to provide developers with a lightweight bundle. The aa-sdk on GitHub also supports ethers.js signers and providers through the aa-ethers library.
One of the main benefits of using the aa-sdk to construct user operations is two utility methods:
sendUserOperation - handles gas estimation, requesting paymasterAndData, signing, and more
sendTransaction - converts transaction object data (from, to, data, and value) into a user operation
The order of operations for constructing a user operation is complicated, and Alchemy’s AA-SDK simplifies UO construction by running a stack of operations to getDummyPaymasterData estimate the gas with estimateGas, then getFeeData, and finally getPaymasterAndData.
After getting the values for the user op fields, it takes the target, callData, and an optional value to build and sign the user operation. It then sends the user op to a bundler and receives a hash of the user op.
If you want to use Alchemy’s paymaster, a separate paymaster, or you plan on adding support for your own SmartAccounts, read the Alchemy AA SDK documentation for a full explanation of how to easily create UOs.
How are user operations added to the user op mempool?
Before a user op can be added to the mempool it must pass a series of checks to ensure it follows the expected behavior as outlined in the ERC-4337 specification. The user op must also pass a validation simulation check to ensure it is valid and can pay for its gas (either by the sender’s wallet or through a paymaster sponsorship policy).
1. The Validity of User Ops are Checked
The initial series of checks are explained in the “Client behavior upon receiving a UserOperation” section of the ERC-4337 specification, and these are summarized below:
The sender is an existing contract, or the initCode (which is used to create a contract) is not empty (but not both)
If the initCode is not empty (because the user op creates an account), determine if the factory is staked or not
The verificationGasLimit is sufficiently low (less than or equal to MAX_VERIFICATION_GAS)
The preVerificationGas is high enough to pay for the calldata gas and and overhead gas fees
The paymasterAndData is either empty, or starts with the paymaster address
If a paymaster exists, it must have a nonempty code on chain, funds to pay for the user op, and not be banned
The callgas is at least the cost of a CALL with non-zero value
The maxFeePerGas and maxPriorityFeePerGas are above the minimum value the client will accept
The sender doesn’t have another user op in the pool (or the user op is constructed to replace an existing entry)
There are many rules which can impact these checks, and they are fully explained in the specification.
For this introduction to user operations, we simply aim to communicate the high-level checks which are made to qualify the validity of a user op.
2. The User Operation is Simulated
Once a user op passes these fundamental checks, the client needs to simulate the user operation to validate that the user op is capable of paying for its execution, either by using its own funds or with a paymaster.
To simulate a user op, a bundler calls the simulateValidation() method which then calls the validateUserOp function on the sender’s account or validatePaymasterUserOp on the paymaster’s contract account if a paymaster will be used to sponsor the gas fees for executing the user op.
After the simulateValidation() method is called, it will revert with a ValidationResult response. The function being reverted is the intentional behavior, and this is considered a successful result.
If ValidationResult reverts with a different error, then the user op did not pass the validation simulation and it is not added to the mempool. User ops are excluded from the mempool if they return sigFail, and UOs may also be omitted from the mempool if the validUntil response expires.
Note: If initCode is present in the user op, an account will be created by an account factory, and then the simulation process will proceed using the newly created account.
Now that the user operation has been constructed, checked, simulated, and added to the mempool, it can be sent to the entry point contract for validation and execution!
How are user operations validated and executed?
For the transaction details inside of a user operation to get published onchain, the entry point contract needs to validate the user operation, and if the UO passes validation, then the entry point contract will execute the transaction and then repay the bundler for gas fees.
The basic steps of user op validation and execution are:
The bundler sends user ops to the singleton entry point contract via the handleOps() method
For each op, the entry point calls validateOp on the op’s sender wallet*
If any ops fail the validation step, they are discarded
Next, call executeOp or each op on the op’s sender wallet, tracking how much gas was used
Transfer ETH from the sender’s wallet or a paymaster to the bundler to pay the gas used to execute each op
*All the validations are run and only then are all the executions for the validated user ops run.
Here is a diagram showing how the user operations are validated and executed on behalf of the sender’s smart contract wallets by the entry point contract:
User operations are the pseudo-transaction objects that allow smart contract wallets to act as a user’s primary wallet on Ethereum and equivalent L2s. Smart contact wallets introduce web3 UX benefits to make blockchain more accessible, and it is made possible by AA infrastructure providers on Ethereum and L2s.
If your web3 product supports smart contract wallets, explore Alchemy’s AA infrastructure including our Gas Manager API and Bundler API for Ethereum, Polygon, Arbitrum, Optimism, and popular testnets like Sepolia!
Related overviews
Learn the Function of Paymaster Smart Contracts in ERC-4337
Learn How ERC-4337 Compliant Smart Contract Wallets Work
Learn How this Piece of AA Infrastructure Bundles User Operations to Unlock the Full Power of ERC-4337