How to simulate a User Operation
This guide will show you how to simulate a UserOperation
(UO) with Account Kit by adding support for UO simulation on an AlchemySmartAccountClient
and sending a User Operation from that client only if simulation passes. By the end of this guide, you will have a basic understanding of how to safely send UOs with the aa-sdk
.
1. Using alchemyUserOperationSimulator
middleware
To simulate User Operations, we must create an Alchemy Client and pass in the useSimulation
flag to true.
Then, whenever you call a method on the client which generates the UO to send (e.g. sendUserOperation
, sendTransaction
, sendTransactions
, buildUserOperation
, or buildUserOperationFromTx
), the client will also simulate which assets change as a result of the UO, and if simulation fails, the client will not send the UO unnecessarily!
import { createModularAccountAlchemyClient } from "@alchemy/aa-alchemy";
import { LocalAccountSigner, sepolia } from "@alchemy/aa-core";
export const smartAccountClient = await createModularAccountAlchemyClient({
apiKey: "YOUR_API_KEY",
chain: sepolia,
signer: LocalAccountSigner.mnemonicToAccountSigner("OWNER_MNEMONIC"),
useSimulation: true,
});
const uo = await smartAccountClient.sendUserOperation({
uo: {
target: "0xTARGET_ADDRESS",
data: "0xDATA",
value: 1n,
},
});
const txHash = await smartAccountClient.waitForUserOperationTransaction(uo);
console.log(txHash);
2. Using simulateUserOperation
You can also selectively simulate UOs by calling the simulateUserOperation
method before sending a UO. You would be responsible for catching any errors like how it is done below, but this is a nice alternative to always running simulation.
import { createModularAccountAlchemyClient } from "@alchemy/aa-alchemy";
import {
LocalAccountSigner,
UserOperationCallData,
sepolia,
} from "@alchemy/aa-core";
export const smartAccountClient = await createModularAccountAlchemyClient({
apiKey: "YOUR_API_KEY",
chain: sepolia,
signer: LocalAccountSigner.mnemonicToAccountSigner("OWNER_MNEMONIC"),
});
const uoStruct: UserOperationCallData = {
target: "0xTARGET_ADDRESS",
data: "0xDATA",
value: 1n,
};
const uoSimResult = await smartAccountClient.simulateUserOperation({
uo: uoStruct,
});
if (uoSimResult.error) {
console.error(uoSimResult.error.message);
}
const uo = await smartAccountClient.sendUserOperation({ uo: uoStruct });
const txHash = await smartAccountClient.waitForUserOperationTransaction(uo);
console.log(txHash);