0%
Overview page background
HomeOverviewsAccount Abstraction
What are user operations?

What are user operations?

Brady Werkheiser headshot

Written by Brady Werkheiser

Logan Ross headshot

Reviewed by Logan Ross

Published on 2023-06-265 min read

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.

Future-proof your app with Account Abstraction using Alchemy's Account Kit!

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:

  1. Construct a partial user op with sender, nonce, initCode, and callData filled in

  2. Estimate gas for the partial user op to a bundler RPC via eth_estimateUserOperationGas

  3. Populate preVerificationGas, verificationGasLimit, callGasLimit

  4. If using a paymaster that doesn’t rely on the contents of the user op, like an ERC20 paymaster, paymasterAndData can be populated here.

  5. Estimate the required gas fees for the operation and populate maxFeePerGas and maxPriorityFeePerGas

  6. (Optional) Send the user op to a sponsoring paymaster for signing, and populate paymasterAndData

  7. This must be done at this step as the paymaster’s signature requires all the above fields to be populated.

  8. 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:

  1. sendUserOperation - handles gas estimation, requesting paymasterAndData, signing, and more

  2. 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:

  1. The bundler sends user ops to the singleton entry point contract via the handleOps() method

  2. For each op, the entry point calls validateOp on the op’s sender wallet*

  3. If any ops fail the validation step, they are discarded

  4. Next, call executeOp or each op on the op’s sender wallet, tracking how much gas was used

  5. 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:

How user operations are validated and executed

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 for Ethereum, Polygon, Arbitrum, Optimism, and popular testnets like Sepolia!

Overview cards background graphic
Section background image

Build blockchain magic

Alchemy combines the most powerful web3 developer products and tools with resources, community and legendary support.

Get your API key