Send User Operations

Once your users have been authenticated, you can start sending user operations! Account Kit makes it really easy to send user operations using React hooks.

Sending user operations is really easy, since all you have to do is use the useSendUserOperation hook. If you want to sponsor the gas for a user, see our guide.

Single user operation

In the below example, we use ModularAccountV2 as the underlying Smart Contract type, which is our default and recommended account. To learn more about all the account options, check out our guide on choosing a smart account.

import React from "react";
import {
  type 
type UseSendUserOperationResult<TEntryPointVersion extends GetEntryPointFromAccount<TAccount>, TAccount extends SupportedAccounts = SupportedAccounts> = { sendUserOperation: UseMutateFunction<SendUserOperationWithEOA<TEntryPointVersion>, Error, SendUserOperationParameters<TAccount>, unknown>; sendUserOperationAsync: UseMutateAsyncFunction<SendUserOperationWithEOA<TEntryPointVersion>, Error, SendUserOperationParameters<TAccount>, unknown>; sendUserOperationResult: SendUserOperationWithEOA<TEntryPointVersion> | undefined; isSendingUserOperation: boolean; error: Error | null; }
UseSendUserOperationResult
,
function useSendUserOperation<TEntryPointVersion extends GetEntryPointFromAccount<TAccount>, TAccount extends SupportedAccounts = SupportedAccounts>(params: UseSendUserOperationArgs<TEntryPointVersion, TAccount>): UseSendUserOperationResult<TEntryPointVersion, TAccount>

A hook that returns functions for sending user operations. You can also optionally wait for a user operation to be mined and get the transaction hash before returning using waitForTx. Like any method that takes a smart account client, throws an error if client undefined or is signer not authenticated.

useSendUserOperation
,
function useSmartAccountClient<TChain extends Chain | undefined = Chain | undefined, TAccount extends SupportedAccountTypes | undefined = "ModularAccountV2">(args: UseSmartAccountClientProps<TChain, TAccount>): UseSmartAccountClientResult<TChain, SupportedAccount<TAccount extends undefined ? "ModularAccountV2" : TAccount>>
useSmartAccountClient
,
} from "@account-kit/react"; export default function
function MyOpSenderComponent(): JSX.Element
MyOpSenderComponent
() {
const {
const client: { account: ModularAccountV2<AlchemySigner>; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; ... 84 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; } | undefined
client
} =
useSmartAccountClient<Chain | undefined, "ModularAccountV2">(args: UseSmartAccountClientProps<Chain | undefined, "ModularAccountV2">): UseSmartAccountClientResult<Chain | undefined, ModularAccountV2<...>>
useSmartAccountClient
({});
const {
const sendUserOperation: UseMutateFunction<SendUserOperationWithEOA<keyof EntryPointRegistryBase<unknown>>, Error, SendUserOperationParameters<SupportedAccounts>, unknown>
sendUserOperation
,
const isSendingUserOperation: boolean
isSendingUserOperation
} =
useSendUserOperation<keyof EntryPointRegistryBase<unknown>, SupportedAccounts>(params: UseSendUserOperationArgs<keyof EntryPointRegistryBase<unknown>, SupportedAccounts>): UseSendUserOperationResult<...>

A hook that returns functions for sending user operations. You can also optionally wait for a user operation to be mined and get the transaction hash before returning using waitForTx. Like any method that takes a smart account client, throws an error if client undefined or is signer not authenticated.

useSendUserOperation
({
client: { account: SupportedAccounts; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; ... 84 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; } | { ...; } | { ...; } | { ...; } | undefined
client
,
// optional parameter that will wait for the transaction to be mined before returning
waitForTxn?: boolean | undefined
waitForTxn
: true,
onSuccess?: ((data: SendUserOperationWithEOA<keyof EntryPointRegistryBase<unknown>>, variables: SendUserOperationParameters<SupportedAccounts>, context: unknown) => Promise<unknown> | unknown) | undefined
onSuccess
: ({
hash: `0x${string}`
hash
,
request: UserOperationRequest<keyof EntryPointRegistryBase<unknown>> | undefined
request
}) => {
// [optional] Do something with the hash and request },
onError?: ((error: Error, variables: SendUserOperationParameters<SupportedAccounts>, context: unknown) => Promise<unknown> | unknown) | undefined
onError
: (
error: Error
error
) => {
// [optional] Do something with the error }, }); return ( <
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
<
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick
={() =>
const sendUserOperation: (variables: SendUserOperationParameters<SupportedAccounts>, options?: MutateOptions<SendUserOperationWithEOA<keyof EntryPointRegistryBase<unknown>>, Error, SendUserOperationParameters<...>, unknown> | undefined) => void
sendUserOperation
({
uo: UserOperationCallData | BatchUserOperationCallData
uo
: {
target: `0x${string}`
target
: "0xTARGET_ADDRESS",
data: `0x${string}`
data
: "0x",
value?: bigint | undefined
value
: 0n,
}, }) }
React.ButtonHTMLAttributes<HTMLButtonElement>.disabled?: boolean | undefined
disabled
={
const isSendingUserOperation: boolean
isSendingUserOperation
}
> {
const isSendingUserOperation: boolean
isSendingUserOperation
? "Sending..." : "Send UO"}
</
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
</
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
); }

Batch user operation

It’s also possible to send user operations in batch using the same hook.

import React from "react";
import {
  type 
type UseSendUserOperationResult<TEntryPointVersion extends GetEntryPointFromAccount<TAccount>, TAccount extends SupportedAccounts = SupportedAccounts> = { sendUserOperation: UseMutateFunction<SendUserOperationWithEOA<TEntryPointVersion>, Error, SendUserOperationParameters<TAccount>, unknown>; sendUserOperationAsync: UseMutateAsyncFunction<SendUserOperationWithEOA<TEntryPointVersion>, Error, SendUserOperationParameters<TAccount>, unknown>; sendUserOperationResult: SendUserOperationWithEOA<TEntryPointVersion> | undefined; isSendingUserOperation: boolean; error: Error | null; }
UseSendUserOperationResult
,
function useSendUserOperation<TEntryPointVersion extends GetEntryPointFromAccount<TAccount>, TAccount extends SupportedAccounts = SupportedAccounts>(params: UseSendUserOperationArgs<TEntryPointVersion, TAccount>): UseSendUserOperationResult<TEntryPointVersion, TAccount>

A hook that returns functions for sending user operations. You can also optionally wait for a user operation to be mined and get the transaction hash before returning using waitForTx. Like any method that takes a smart account client, throws an error if client undefined or is signer not authenticated.

useSendUserOperation
,
function useSmartAccountClient<TChain extends Chain | undefined = Chain | undefined, TAccount extends SupportedAccountTypes | undefined = "ModularAccountV2">(args: UseSmartAccountClientProps<TChain, TAccount>): UseSmartAccountClientResult<TChain, SupportedAccount<TAccount extends undefined ? "ModularAccountV2" : TAccount>>
useSmartAccountClient
,
} from "@account-kit/react"; export default function
function MyOpSenderComponent(): JSX.Element
MyOpSenderComponent
() {
const {
const client: { account: ModularAccountV2<AlchemySigner>; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; ... 84 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; } | undefined
client
} =
useSmartAccountClient<Chain | undefined, "ModularAccountV2">(args: UseSmartAccountClientProps<Chain | undefined, "ModularAccountV2">): UseSmartAccountClientResult<Chain | undefined, ModularAccountV2<...>>
useSmartAccountClient
({});
const {
const sendUserOperation: UseMutateFunction<SendUserOperationWithEOA<keyof EntryPointRegistryBase<unknown>>, Error, SendUserOperationParameters<SupportedAccounts>, unknown>
sendUserOperation
,
const isSendingUserOperation: boolean
isSendingUserOperation
} =
useSendUserOperation<keyof EntryPointRegistryBase<unknown>, SupportedAccounts>(params: UseSendUserOperationArgs<keyof EntryPointRegistryBase<unknown>, SupportedAccounts>): UseSendUserOperationResult<...>

A hook that returns functions for sending user operations. You can also optionally wait for a user operation to be mined and get the transaction hash before returning using waitForTx. Like any method that takes a smart account client, throws an error if client undefined or is signer not authenticated.

useSendUserOperation
({
client: { account: SupportedAccounts; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; ... 84 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; } | { ...; } | { ...; } | { ...; } | undefined
client
,
// optional parameter that will wait for the transaction to be mined before returning
waitForTxn?: boolean | undefined
waitForTxn
: true,
onSuccess?: ((data: SendUserOperationWithEOA<keyof EntryPointRegistryBase<unknown>>, variables: SendUserOperationParameters<SupportedAccounts>, context: unknown) => Promise<unknown> | unknown) | undefined
onSuccess
: ({
hash: `0x${string}`
hash
,
request: UserOperationRequest<keyof EntryPointRegistryBase<unknown>> | undefined
request
}) => {
// [optional] Do something with the hash and request },
onError?: ((error: Error, variables: SendUserOperationParameters<SupportedAccounts>, context: unknown) => Promise<unknown> | unknown) | undefined
onError
: (
error: Error
error
) => {
// [optional] Do something with the error }, }); return ( <
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
<
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick
={() =>
const sendUserOperation: (variables: SendUserOperationParameters<SupportedAccounts>, options?: MutateOptions<SendUserOperationWithEOA<keyof EntryPointRegistryBase<unknown>>, Error, SendUserOperationParameters<...>, unknown> | undefined) => void
sendUserOperation
({
uo: UserOperationCallData | BatchUserOperationCallData
uo
: [
{
target: `0x${string}`
target
: "0xTARGET_ADDRESS",
data: `0x${string}`
data
: "0x",
value?: bigint | undefined
value
: 0n,
}, {
target: `0x${string}`
target
: "0xTARGET_ADDRESS",
data: `0x${string}`
data
: "0x",
value?: bigint | undefined
value
: 0n,
}, ], }) }
React.ButtonHTMLAttributes<HTMLButtonElement>.disabled?: boolean | undefined
disabled
={
const isSendingUserOperation: boolean
isSendingUserOperation
}
> {
const isSendingUserOperation: boolean
isSendingUserOperation
? "Sending..." : "Send UO"}
</
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
</
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
); }