Send User Operations

Once you’ve completed the Quickstart, you’re ready to start sending user operations!

User operations can be sent either as single user operations or as a batch of user operations. In this guide, we’ll cover both methods.

Single User Operation

import { 
import client
client
} from "./client";
const {
const hash: any
hash
} = await
import client
client
.
any
sendUserOperation
({
uo: { target: string; data: string; value: bigint; }
uo
: {
target: string
target
: "0xTARGET_ADDRESS",
data: string
data
: "0x",
value: bigint
value
: 0n,
}, });
import {
  
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
,
function createAlchemySmartAccountClient<TChain extends Chain = Chain, TAccount extends SmartContractAccount | undefined = SmartContractAccount | undefined, TContext extends UserOperationContext | undefined = UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<TChain, TAccount, TContext>): AlchemySmartAccountClient<TChain, TAccount, Record<string, never>, TContext>
createAlchemySmartAccountClient
,
const sepolia: Chain
sepolia
,
} from "@account-kit/infra"; import {
function createLightAccount<TTransport extends Transport = Transport, TSigner extends SmartAccountSigner = SmartAccountSigner<any>, TLightAccountVersion extends LightAccountVersion<"LightAccount"> = "v2.0.0">(config: CreateLightAccountParams<TTransport, TSigner, TLightAccountVersion>): Promise<LightAccount<TSigner, TLightAccountVersion>>
createLightAccount
} from "@account-kit/smart-contracts";
// You can replace this with any signer you'd like // We're using a LocalAccountSigner to generate a local key to sign with import {
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
} from "@aa-sdk/core";
import {
function generatePrivateKey(): Hex
generatePrivateKey
} from "viem/accounts";
const
const alchemyTransport: AlchemyTransport
alchemyTransport
=
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
({
apiKey: string
apiKey
: "YOUR_API_KEY",
}); export const
const client: { [x: string]: never; account: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">; ... 85 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
client
=
createAlchemySmartAccountClient<Chain, LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">, UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<...>): { ...; }
createAlchemySmartAccountClient
({
transport: AlchemyTransport

The RPC transport

transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
policyId?: string | string[] | undefined
policyId
: "YOUR_POLICY_ID",
chain?: Chain | undefined

Chain for the client.

chain
:
const sepolia: Chain
sepolia
,
account?: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0"> | undefined
account
: await
createLightAccount<AlchemyTransport, LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">(config: CreateLightAccountParams<...>): Promise<...>
createLightAccount
({
chain: Chain
chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport
transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
signer: LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>
signer
:
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
.
LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>.privateKeyToAccountSigner(key: Hex): LocalAccountSigner<PrivateKeyAccount>

Creates a LocalAccountSigner instance using the provided private key.

privateKeyToAccountSigner
(
function generatePrivateKey(): Hex
generatePrivateKey
()),
}), });

Batch User Operations

To batch user operations, you can just pass an array of user operations to the sendUserOperation method.

import { 
import client
client
} from "./client";
const {
const hash: any
hash
} = await
import client
client
.
any
sendUserOperation
({
uo: { target: string; data: string; value: bigint; }[]
uo
: [
{
target: string
target
: "0xTARGET_ADDRESS",
data: string
data
: "0x",
value: bigint
value
: 0n,
}, {
target: string
target
: "0xTARGET_ADDRESS_2",
data: string
data
: "0x",
value: bigint
value
: 0n,
}, ], });
import {
  
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
,
function createAlchemySmartAccountClient<TChain extends Chain = Chain, TAccount extends SmartContractAccount | undefined = SmartContractAccount | undefined, TContext extends UserOperationContext | undefined = UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<TChain, TAccount, TContext>): AlchemySmartAccountClient<TChain, TAccount, Record<string, never>, TContext>
createAlchemySmartAccountClient
,
const sepolia: Chain
sepolia
,
} from "@account-kit/infra"; import {
function createLightAccount<TTransport extends Transport = Transport, TSigner extends SmartAccountSigner = SmartAccountSigner<any>, TLightAccountVersion extends LightAccountVersion<"LightAccount"> = "v2.0.0">(config: CreateLightAccountParams<TTransport, TSigner, TLightAccountVersion>): Promise<LightAccount<TSigner, TLightAccountVersion>>
createLightAccount
} from "@account-kit/smart-contracts";
// You can replace this with any signer you'd like // We're using a LocalAccountSigner to generate a local key to sign with import {
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
} from "@aa-sdk/core";
import {
function generatePrivateKey(): Hex
generatePrivateKey
} from "viem/accounts";
const
const alchemyTransport: AlchemyTransport
alchemyTransport
=
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
({
apiKey: string
apiKey
: "YOUR_API_KEY",
}); export const
const client: { [x: string]: never; account: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">; ... 85 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
client
=
createAlchemySmartAccountClient<Chain, LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">, UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<...>): { ...; }
createAlchemySmartAccountClient
({
transport: AlchemyTransport

The RPC transport

transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
policyId?: string | string[] | undefined
policyId
: "YOUR_POLICY_ID",
chain?: Chain | undefined

Chain for the client.

chain
:
const sepolia: Chain
sepolia
,
account?: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0"> | undefined
account
: await
createLightAccount<AlchemyTransport, LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">(config: CreateLightAccountParams<...>): Promise<...>
createLightAccount
({
chain: Chain
chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport
transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
signer: LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>
signer
:
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
.
LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>.privateKeyToAccountSigner(key: Hex): LocalAccountSigner<PrivateKeyAccount>

Creates a LocalAccountSigner instance using the provided private key.

privateKeyToAccountSigner
(
function generatePrivateKey(): Hex
generatePrivateKey
()),
}), });