Skip to main content

Send a user operation

User operations are the ERC-4337 counterpart to traditional blockchain transactions. They incorporate significant enhancements that improve user experience and provide greater flexibility in account management and transaction execution.

User operations consist of executions (represented by an ExecutionStruct) that express the user's intent. Before submission, additional required and optional information, such as gas fees and payment details, must be included.

User operations are not directly sent to the network. Instead, they are sent to a bundler, which validates, optimizes, and aggregates them before network submission. The bundler provides methods such as estimateUserOpGas, getUserOpByHash, and pollForReceipt to facilitate the overall process.

Prerequisites

Send a user operation using Viem SDK

The following is a simplified example of sending a user operation using Viem SDK. Viem SDK offers more granular control for developers who require it.

In the example, a user operation is created with the necessary gas limits. This user operation is passed to a bundler instance, and the EntryPoint address is retrieved from the client.

const userOp = await client.createUserOp(
data,
{
verificationGasLimit: 100000000n,
preVerificationGas: 100000000n,
callGasLimit: 100000000n,
}
);

const bundlerUrl = "<bundler-url>";
const bundler = createBundlerClient(bundlerUrl);

const hash = await bundler.sendUserOp(
userOp,
client.account.environment.EntryPoint,
)
warning

In this example, verificationGasLimit, preVerificationGas, and callGasLimit are set to arbitrarily high values. This might result in overpayment for the user operation, and might even cause the bundler to reject the user operation, so we recommend replacing these values with a correct estimate before submitting to the bundler.

Poll for the transaction hash

You can use the hash of the user operation to poll the bundler to access the transaction receipt, which contains the on-chain transactionHash.

For example:

const { result: receipt } = await bundler.pollForReceipt(hash);
console.log(receipt.receipt.transactionHash);