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.

Viem's Account Abstraction API allows a developer to specify an array of Calls that will be executed as a user operation via Viem's sendUserOperation method. The Delegation Toolkit encodes and executes the provided calls.

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. See Viem's BundlerClient for details on how to interact with the bundler.

note

If a user operation is sent from a smart contract account that has not been deployed, the toolkit configures the user operation to automatically deploy the account.

Prerequisites

Send a user operation

The following is a simplified example of sending a user operation using Viem Core SDK. Viem Core 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.

// Configuration arguments are excluded here for clarity.
const bundler = createBundlerClient({...});
const delegatorSmartAccount = await toMetaMaskSmartAccount({...})

// Appropriate fee per gas must be determined for the specific bundler being used.
const maxFeePerGas = 1n;
const maxPriorityFeePerGas = 1n;

const userOperationHash = await bundler.sendUserOperation({
account: delegatorSmartAccount,
calls: [
{
to: "0x1234567890123456789012345678901234567890",
value: parseEther("1")
}
],
maxFeePerGas,
maxPriortyFeePerGas
});

Estimate fee per gas

Different bundlers have different ways to estimate maxFeePerGas and maxPriorityFeePerGas, and can reject requests with insufficient values. The following example uses constant values, but the Hello Gator example use Pimlico's Alto bundler, which fetches user operation gas price using the RPC method pimlico_getUserOperationPrice.

import { createPimlicoClient } from "permissionless/clients/pimlico";

// ...

const pimlicoClient = createPimlicoClient({
transport: http(BUNDLER_URL),
});

// ...

const { fast: fee } = await pimlicoClient.getUserOperationGasPrice();

const userOperationHash = await bundler.sendUserOperation({
account: delegatorSmartAccount,
calls: [
{
to: "0x1234567890123456789012345678901234567890",
value: parseEther("1")
}
],
...fee
});

Wait for the transaction receipt

After submitting the user operation, it's crucial to wait for the receipt to ensure that it has been successfully included in the blockchain. Use the waitForUserOperationReceipt method provided by the bundler client.

const receipt = await bundler.waitForUserOperationReceipt({
hash: userOperationHash
});

console.log(receipt.receipt.transactionHash);
note

The receipt object is the UserOperationReceipt, while receipt.receipt is the TransactionReceipt.