Skip to content

Send a Transaction

Sending a transaction from a Smart Account looks very similar to using viem or ethers, but under the hood, you are sending a UserOperation to a Bundler, which then forwards it to the EntryPoint contract.

If you are using @0xgasless/smart-account, the sendTransaction method handles the UserOperation lifecycle for you.

import { encodeFunctionData, parseAbi } from "viem";
// 1. Prepare your call data.
const nftContract = "0xYourNFTContractAddress";
const abi = parseAbi(["function safeMint(address to)"]);
const data = encodeFunctionData({
abi,
functionName: "safeMint",
args: [smartAccountAddress],
});
// 2. Send the transaction
const userOpResponse = await smartAccount.sendTransaction({
to: nftContract,
value: 0n, // Value in wei. 0n for most contract calls.
data: data, // Encoded function data.
});
// 3. Wait for the transaction to be mined on-chain
const receipt = await userOpResponse.wait();
console.log("Transaction mined:", receipt.receipt.transactionHash);
  1. The SDK fetches the next available nonce for your Smart Account.
  2. It requests gas estimates from the Bundler for the UserOperation.
  3. If a Paymaster is configured, it requests a signature from the Paymaster to sponsor the gas.
  4. It prompts your connected Signer (e.g., the Embedded Wallet iframe, or your local key) to sign the final UserOperation hash.
  5. It submits the signed UserOperation to the Bundler.

The UserOperation is not an Ethereum transaction immediately — it sits in the Bundler’s mempool until the Bundler submits a batch.

You have two ways to track it:

If you only need to know that the Bundler accepted the operation:

const { transactionHash } = await userOpResponse.waitForTxHash();
console.log("Pending UserOp Hash:", transactionHash);

Wait for the On-Chain Receipt (Slower, but final)

Section titled “Wait for the On-Chain Receipt (Slower, but final)”

If you need to know the exact block it was mined in and that the Ethereum network confirmed it:

const receipt = await userOpResponse.wait();
if (receipt.success === 'true') {
console.log("Success! Tx hash:", receipt.receipt.transactionHash);
}

If the transaction fails (e.g., the contract reverts, the bundler rejects it, or the user declines the signature), sendTransaction will throw an error.

Always wrap calls in a try/catch block:

try {
const response = await smartAccount.sendTransaction({...});
} catch (error) {
console.error("UserOperation failed to send:", error.message);
}