Send User Operations

Once your users have been authenticated, you can start sending user operations!

Single user operation

In the below example, we use LightAccount as the underlying Smart Contract type. You can also use MultiOwnerModularAccount if you want to provide your users with an ERC-6900 compliant modular account, or you can use MultiOwnerLightAccount if you want to support an account with multiple owners.

import { 
function watchSmartAccountClient<TAccount extends SupportedAccountTypes, TChain extends Chain | undefined = Chain | undefined>(params: GetSmartAccountClientParams<TChain, TAccount>, config: AlchemyAccountsConfig): (onChange: (client: GetSmartAccountClientResult<TChain, SupportedAccount<TAccount>>) => void) => () => void

Watches for changes to the smart account client and triggers the provided callback when a change is detected.

watchSmartAccountClient
} from "@account-kit/core";
import {
import config
config
} from "./config.js";
// How you actually store this state variable // depends on the framework you're using let
let clientState: any
clientState
;
// The watch smart account client will handle all of the possible state changes // that can impact this client: // - Signer status // - Account instantiation // - Chain changes const
const clientSubscription: () => void
clientSubscription
=
watchSmartAccountClient<"LightAccount", Chain | undefined>(params: GetSmartAccountClientParams<Chain | undefined, "LightAccount">, config: AlchemyAccountsConfig): (onChange: (client: GetSmartAccountClientResult<...>) => void) => () => void

Watches for changes to the smart account client and triggers the provided callback when a change is detected.

watchSmartAccountClient
(
{
type: "LightAccount"
type
: "LightAccount",
},
import config
config
,
)((
clientState_: GetSmartAccountClientResult<Chain | undefined, LightAccount<AlchemySigner>>
clientState_
) => {
let clientState: any
clientState
=
clientState_: GetSmartAccountClientResult<Chain | undefined, LightAccount<AlchemySigner>>
clientState_
;
}); if (
let clientState: undefined
clientState
== null ||
let clientState: never
clientState
.
any
isLoadingClient
) {
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:


const name = 'Will Robinson'; console.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ```

Example using the `Console` class:

```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err);

myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson'; myConsole.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout

See util.format() for more information.

log
("Loading...");
} const
const client: any
client
=
let clientState: undefined
clientState
.
any
client
;
await
const client: any
client
.
any
sendUserOperation
({
uo: { target: string; data: string; value: bigint; }
uo
: {
target: string
target
: "0xtarget",
data: string
data
: "0x",
value: bigint
value
: 0n,
}, });
import { 
const createConfig: (params: CreateConfigProps) => AlchemyAccountsConfig

Creates an AlchemyAccountsConfig object that can be used in conjunction with the actions exported from @account-kit/core.

The config contains core and client stores that can be used to manage account state in your application.

createConfig
} from "@account-kit/core";
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
,
const sepolia: Chain
sepolia
} from "@account-kit/infra";
export const
const config: AlchemyAccountsConfig
config
=
function createConfig(params: CreateConfigProps): AlchemyAccountsConfig

Creates an AlchemyAccountsConfig object that can be used in conjunction with the actions exported from @account-kit/core.

The config contains core and client stores that can be used to manage account state in your application.

createConfig
({
transport: AlchemyTransport
transport
:
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" }),
chain: Chain
chain
:
const sepolia: Chain
sepolia
,
// optional if you want to sponsor gas
policyId?: string | string[] | undefined
policyId
: "YOUR_POLICY_ID",
});

Batch user operation

It’s also possible to send user operations in batch using the same approach!

import { 
function watchSmartAccountClient<TAccount extends SupportedAccountTypes, TChain extends Chain | undefined = Chain | undefined>(params: GetSmartAccountClientParams<TChain, TAccount>, config: AlchemyAccountsConfig): (onChange: (client: GetSmartAccountClientResult<TChain, SupportedAccount<TAccount>>) => void) => () => void

Watches for changes to the smart account client and triggers the provided callback when a change is detected.

watchSmartAccountClient
} from "@account-kit/core";
import {
import config
config
} from "./config.js";
// How you actually store this state variable // depends on the framework you're using let
let clientState: any
clientState
;
// The watch smart account client will handle all of the possible state changes // that can impact this client: // - Signer status // - Account instantiation // - Chain changes const
const clientSubscription: () => void
clientSubscription
=
watchSmartAccountClient<"LightAccount", Chain | undefined>(params: GetSmartAccountClientParams<Chain | undefined, "LightAccount">, config: AlchemyAccountsConfig): (onChange: (client: GetSmartAccountClientResult<...>) => void) => () => void

Watches for changes to the smart account client and triggers the provided callback when a change is detected.

watchSmartAccountClient
(
{
type: "LightAccount"
type
: "LightAccount",
},
import config
config
,
)((
clientState_: GetSmartAccountClientResult<Chain | undefined, LightAccount<AlchemySigner>>
clientState_
) => {
let clientState: any
clientState
=
clientState_: GetSmartAccountClientResult<Chain | undefined, LightAccount<AlchemySigner>>
clientState_
;
}); if (
let clientState: undefined
clientState
== null ||
let clientState: never
clientState
.
any
isLoadingClient
) {
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:


const name = 'Will Robinson'; console.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ```

Example using the `Console` class:

```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err);

myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson'; myConsole.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout

See util.format() for more information.

log
("Loading...");
} const
const client: any
client
=
let clientState: undefined
clientState
.
any
client
;
await
const client: any
client
.
any
sendUserOperation
({
uo: { target: string; data: string; value: bigint; }[]
uo
: [
{
target: string
target
: "0xtarget",
data: string
data
: "0x",
value: bigint
value
: 0n,
}, {
target: string
target
: "0xtarget",
data: string
data
: "0x",
value: bigint
value
: 0n,
}, ], });
import { 
const createConfig: (params: CreateConfigProps) => AlchemyAccountsConfig

Creates an AlchemyAccountsConfig object that can be used in conjunction with the actions exported from @account-kit/core.

The config contains core and client stores that can be used to manage account state in your application.

createConfig
} from "@account-kit/core";
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
,
const sepolia: Chain
sepolia
} from "@account-kit/infra";
export const
const config: AlchemyAccountsConfig
config
=
function createConfig(params: CreateConfigProps): AlchemyAccountsConfig

Creates an AlchemyAccountsConfig object that can be used in conjunction with the actions exported from @account-kit/core.

The config contains core and client stores that can be used to manage account state in your application.

createConfig
({
transport: AlchemyTransport
transport
:
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" }),
chain: Chain
chain
:
const sepolia: Chain
sepolia
,
// optional if you want to sponsor gas
policyId?: string | string[] | undefined
policyId
: "YOUR_POLICY_ID",
});