Drop and Replace

What is Drop and Replace?

If fees change and your user operation gets stuck in the mempool, you can use drop and replace to resend the user operation with higher fees.

Here’s a quick example of how to use the useDropAndReplaceUserOperation() hook to drop and replace a user operation.

import React from "react";
import { 
class View
View
,
const Pressable: React.ForwardRefExoticComponent<PressableProps & React.RefAttributes<View>>
Pressable
,
class Text
Text
} from "react-native";
import {
function useDropAndReplaceUserOperation<TEntryPointVersion extends GetEntryPointFromAccount<TAccount>, TAccount extends SupportedAccounts = SupportedAccounts>({ client, ...mutationArgs }: UseDropAndReplaceUserOperationArgs<TEntryPointVersion, TAccount>): UseDropAndReplaceUserOperationResult<TEntryPointVersion, TAccount>

Custom hook that handles the drop and replace user operation for a given client and mutation arguments.

useDropAndReplaceUserOperation
,
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-native"; export function
function ComponentWithDropAndReplaceUO(): JSX.Element
ComponentWithDropAndReplaceUO
() {
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 sendUserOperationAsync: UseMutateAsyncFunction<SendUserOperationWithEOA<keyof EntryPointRegistryBase<unknown>>, Error, SendUserOperationParameters<SupportedAccounts>, unknown>
sendUserOperationAsync
,
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
,
}); const {
const dropAndReplaceUserOperation: UseMutateFunction<SendUserOperationResult<keyof EntryPointRegistryBase<unknown>>, Error, DropAndReplaceUserOperationParameters<SupportedAccounts>, unknown>
dropAndReplaceUserOperation
,
const isDroppingAndReplacingUserOperation: boolean
isDroppingAndReplacingUserOperation
} =
useDropAndReplaceUserOperation<keyof EntryPointRegistryBase<unknown>, SupportedAccounts>({ client, ...mutationArgs }: UseDropAndReplaceUserOperationArgs<keyof EntryPointRegistryBase<unknown>, SupportedAccounts>): UseDropAndReplaceUserOperationResult<...>

Custom hook that handles the drop and replace user operation for a given client and mutation arguments.

useDropAndReplaceUserOperation
({
client: { account: SupportedAccounts; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; ... 84 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; } | { ...; } | { ...; } | { ...; } | undefined
client
,
onSuccess?: ((data: SendUserOperationResult<keyof EntryPointRegistryBase<unknown>>, variables: DropAndReplaceUserOperationParameters<SupportedAccounts>, context: unknown) => Promise<unknown> | unknown) | undefined
onSuccess
: ({
hash: `0x${string}`
hash
,
request: UserOperationRequest<keyof EntryPointRegistryBase<unknown>>
request
}) => {
// [optional] Do something with the hash and request },
onError?: ((error: Error, variables: DropAndReplaceUserOperationParameters<SupportedAccounts>, context: unknown) => Promise<unknown> | unknown) | undefined
onError
: (
error: Error
error
) => {
// [optional] Do something with the error }, // [optional] ...additional mutationArgs }); return ( <
class View
View
>
<
const Pressable: React.ForwardRefExoticComponent<PressableProps & React.RefAttributes<View>>
Pressable
PressableProps.onPress?: ((event: GestureResponderEvent) => void) | null | undefined

Called when a single tap gesture is detected.

onPress
={async () => {
const {
const request: UserOperationRequest<keyof EntryPointRegistryBase<unknown>> | undefined
request
} = await
const sendUserOperationAsync: (variables: SendUserOperationParameters<SupportedAccounts>, options?: MutateOptions<SendUserOperationWithEOA<keyof EntryPointRegistryBase<unknown>>, Error, SendUserOperationParameters<...>, unknown> | undefined) => Promise<...>
sendUserOperationAsync
({
uo: UserOperationCallData | BatchUserOperationCallData
uo
: {
target: `0x${string}`
target
: "0xTARGET_ADDRESS",
data: `0x${string}`
data
: "0x",
value?: bigint | undefined
value
: 0n,
}, });
const dropAndReplaceUserOperation: (variables: DropAndReplaceUserOperationParameters<SupportedAccounts>, options?: MutateOptions<SendUserOperationResult<keyof EntryPointRegistryBase<unknown>>, Error, DropAndReplaceUserOperationParameters<...>, unknown> | undefined) => void
dropAndReplaceUserOperation
({
uoToDrop: UserOperationRequest<keyof EntryPointRegistryBase<unknown>>
uoToDrop
:
const request: UserOperationRequest<keyof EntryPointRegistryBase<unknown>> | undefined
request
,
}); }}
PressableProps.disabled?: boolean | null | undefined

Whether the press behavior is disabled.

disabled
={
const isSendingUserOperation: boolean
isSendingUserOperation
||
const isDroppingAndReplacingUserOperation: boolean
isDroppingAndReplacingUserOperation
}
> <
class View
View
>
<
class Text
Text
>
{
const isSendingUserOperation: boolean
isSendingUserOperation
? "Sending..." :
const isDroppingAndReplacingUserOperation: boolean
isDroppingAndReplacingUserOperation
? "Replacing..." : "Send then Replace UO"} </
class Text
Text
>
</
class View
View
>
</
const Pressable: React.ForwardRefExoticComponent<PressableProps & React.RefAttributes<View>>
Pressable
>
</
class View
View
>
); }

You can also build a more complex retry logic in a case you want more control over how many times you want to retry a failed user operation.