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 clientclient } from "./client";
const { const hash: anyhash } = await import clientclient.anysendUserOperation({
uo: {
target: string;
data: string;
value: bigint;
}uo: {
target: stringtarget: "0xTARGET_ADDRESS",
data: stringdata: "0x",
value: bigintvalue: 0n,
},
});
import {
function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates 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: Chainsepolia,
} 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(): HexgeneratePrivateKey } from "viem/accounts";
const const alchemyTransport: AlchemyTransportalchemyTransport = function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates 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: stringapiKey: "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: AlchemyTransportThe RPC transport
transport: const alchemyTransport: AlchemyTransportalchemyTransport,
policyId?: string | string[] | undefinedpolicyId: "YOUR_POLICY_ID",
chain?: Chain | undefinedChain for the client.
chain: const sepolia: Chainsepolia,
account?: LightAccount<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>, "v2.0.0"> | undefinedaccount: 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: Chainchain: const sepolia: Chainsepolia,
transport: AlchemyTransporttransport: const alchemyTransport: AlchemyTransportalchemyTransport,
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(): HexgeneratePrivateKey()),
}),
});
Batch User Operations
To batch user operations, you can just pass an array of user operations to the sendUserOperation
method.
import { import clientclient } from "./client";
const { const hash: anyhash } = await import clientclient.anysendUserOperation({
uo: {
target: string;
data: string;
value: bigint;
}[]uo: [
{
target: stringtarget: "0xTARGET_ADDRESS",
data: stringdata: "0x",
value: bigintvalue: 0n,
},
{
target: stringtarget: "0xTARGET_ADDRESS_2",
data: stringdata: "0x",
value: bigintvalue: 0n,
},
],
});
import {
function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates 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: Chainsepolia,
} 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(): HexgeneratePrivateKey } from "viem/accounts";
const const alchemyTransport: AlchemyTransportalchemyTransport = function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates 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: stringapiKey: "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: AlchemyTransportThe RPC transport
transport: const alchemyTransport: AlchemyTransportalchemyTransport,
policyId?: string | string[] | undefinedpolicyId: "YOUR_POLICY_ID",
chain?: Chain | undefinedChain for the client.
chain: const sepolia: Chainsepolia,
account?: LightAccount<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>, "v2.0.0"> | undefinedaccount: 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: Chainchain: const sepolia: Chainsepolia,
transport: AlchemyTransporttransport: const alchemyTransport: AlchemyTransportalchemyTransport,
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(): HexgeneratePrivateKey()),
}),
});