# Send batch transactions

> Batch multiple calls together into one transaction

> For the complete documentation index, see [llms.txt](/docs/llms.txt).

With Wallet APIs, batch multiple actions (such as token transfers, approvals, or swaps) into a single transaction. Users no longer need multiple confirmations or pop-ups to handle sequential actions. This speeds up how users interact with your app and improves the user experience.

## How it works

Smart wallets support running a batch of actions, called "calls", in a single transaction. These actions are atomic, which means if any of them fail (revert), the entire batched transaction will revert. This protects users from accidentally having part of a batch apply, but not all.

To use this feature, specify multiple calls to make. The parameter type accepts an array of calls, and each element should contain the action you want the smart wallet to take.

## Prerequisites

* API key from your [dashboard](https://dashboard.alchemy.com/apps)
* [Wallet APIs installed and configured in your project](/docs/wallets/pages/react/setup.mdx).

## Implementation

<Tabs>
  <Tab title="JavaScript" lanauge="javascript">
    <Info>`@alchemy/wallet-apis` (v5.x.x) is currently in beta but is the recommended replacement for `@account-kit/wallet-client` (v4.x.x). If you run into any issues, please [reach out](mailto:support@alchemy.com).</Info>

You can batch transactions using either the smart wallet client [`sendCalls`](/docs/wallets/reference/wallet-apis/functions/sendCalls) or [`prepareCalls`](/docs/wallets/reference/wallet-apis/functions/prepareCalls) actions.

<CodeBlocks>
  ```ts title="sendCalls.ts"
  import { type Address, erc20Abi, encodeFunctionData, parseUnits } from "viem";
  import { client } from "./client";
  import { swapAbi } from "./swapAbi";

  const DEMO_USDC_ADDRESS: Address = "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443";

  const DEMO_SWAP_ADDRESS: Address = "0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C";

  // Send a batch of calls — approve + swap in a single transaction
  const { id } = await client.sendCalls({
    calls: [
      // To batch, simply specify multiple calls in the calls array
      {
        // approve 5000 USDC to the swap contract
        to: DEMO_USDC_ADDRESS,
        data: encodeFunctionData({
          abi: erc20Abi,
          functionName: "approve",
          args: [DEMO_SWAP_ADDRESS, parseUnits("5000", 6)],
        }),
      },
      {
        // swap 5000 USDC to 1 WETH
        to: DEMO_SWAP_ADDRESS,
        data: encodeFunctionData({
          abi: swapAbi,
          functionName: "swapUSDCtoWETH",
          args: [parseUnits("5000", 6), parseUnits("1", 18)],
        }),
      },
    ],
  });

  // Wait for the transaction to be confirmed
  const result = await client.waitForCallsStatus({ id });

  console.log(result);
  ```

  ```ts title="client.ts"
  import type { Hex } from "viem";
  import { arbitrumSepolia } from "viem/chains";
  import { privateKeyToAccount } from "viem/accounts";
  import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";

  export const client = createSmartWalletClient({
    transport: alchemyWalletTransport({
      apiKey: "YOUR_API_KEY",
    }),
    chain: arbitrumSepolia,
    signer: privateKeyToAccount("0xYOUR_PRIVATE_KEY" as const),
    // Optional: sponsor gas for your users (see "Sponsor gas" guide)
    // paymaster: { policyId: "YOUR_POLICY_ID" },
  });
  ```

  ```ts title="swapAbi.ts"
  export const swapAbi = [
    {
      type: "constructor",
      inputs: [],
      stateMutability: "nonpayable",
    },
    {
      type: "function",
      name: "mint",
      inputs: [
        {
          name: "amount1",
          type: "uint256",
          internalType: "uint256",
        },
        {
          name: "amount2",
          type: "uint256",
          internalType: "uint256",
        },
      ],
      outputs: [],
      stateMutability: "nonpayable",
    },
    {
      type: "function",
      name: "swapUSDCtoWETH",
      inputs: [
        {
          name: "amountIn",
          type: "uint256",
          internalType: "uint256",
        },
        {
          name: "amountOut",
          type: "uint256",
          internalType: "uint256",
        },
      ],
      outputs: [],
      stateMutability: "nonpayable",
    },
    {
      type: "function",
      name: "swapWETHtoUSDC",
      inputs: [
        {
          name: "amountIn",
          type: "uint256",
          internalType: "uint256",
        },
        {
          name: "amountOut",
          type: "uint256",
          internalType: "uint256",
        },
      ],
      outputs: [],
      stateMutability: "nonpayable",
    },
    {
      type: "function",
      name: "usdc",
      inputs: [],
      outputs: [
        {
          name: "",
          type: "address",
          internalType: "contract ERC20Mintable",
        },
      ],
      stateMutability: "view",
    },
    {
      type: "function",
      name: "weth",
      inputs: [],
      outputs: [
        {
          name: "",
          type: "address",
          internalType: "contract ERC20Mintable",
        },
      ],
      stateMutability: "view",
    },
  ] as const;
  ```
</CodeBlocks>

  </Tab>
  <Tab title="API" language="bash">
    <Info>
  The examples provided use [foundry](https://getfoundry.sh/) and
  [jq](https://jqlang.org/) to prepare and parse API requests.
</Info>

See the [`wallet_prepareCalls` API
reference](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls)
for full descriptions of the parameters used in the following example.

<Steps>
  <Step title="Prepare calls">
    Prepare calls with multiple calls specified in the batch. Use your signer address directly as the `from` field to enable [EIP-7702](/docs/wallets/transactions/using-eip-7702) by default.
```bash twoslash
curl --request POST \
  --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
  --header 'accept: application/json' \
  --data '
{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "wallet_prepareCalls",
  "params": [
    {
      "calls": [
        {
          "to": "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443",
          "data": "'$(cast calldata "approve(address,uint256)" 0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C $(cast parse-units 5000 6))'"
        },
        {
          "to": "0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C",
          "data": "'$(cast calldata "swapUSDCtoWETH(uint256,uint256)" $(cast parse-units 5000 6) $(cast parse-units 1 18))'"
        }
      ],
      "from": "'$SIGNER_ADDRESS'",
      "chainId": "'$CHAIN_ID_HEX'"
    }
  ]
}'
```
  </Step>
  <Step title="Sign and send">
    Sign the returned signature request and send using `wallet_sendPreparedCalls`. If the account hasn't been delegated yet, you'll also need to sign the 7702 authorization. See [Send Transactions](/docs/wallets/transactions/send-transactions) for a complete example.
  </Step>
</Steps>

  </Tab>
</Tabs>

<Accordion title="Using @account-kit/wallet-client (v4.x.x)?">
The examples on this page use `@alchemy/wallet-apis` (v5.x.x). If you're using `@account-kit/wallet-client` (v4.x.x), the client setup looks like this:

```ts title="client.ts (v4.x.x)"
import { LocalAccountSigner } from "@aa-sdk/core";
import { createSmartWalletClient } from "@account-kit/wallet-client";
import { alchemy, sepolia } from "@account-kit/infra";

const signer = LocalAccountSigner.privateKeyToAccountSigner("0xYOUR_PRIVATE_KEY" as const);

export const client = createSmartWalletClient({
  transport: alchemy({ apiKey: "YOUR_API_KEY" }),
  chain: sepolia,
  signer,
  account: signer.address, // can also be passed per action as `from` or `account`
  // Optional: sponsor gas for your users (see "Sponsor gas" guide)
  policyId: "YOUR_POLICY_ID", 
});
```

Key v4.x.x differences:
* **Account address** must be specified on the client or per action (`from` or `account`). In v5.x.x, the client automatically uses the owner's address as the account address via [EIP-7702](/docs/wallets/transactions/using-eip-7702).
* **Chain imports** come directly from `@account-kit/infra` instead of `viem/chains`.
* **Numeric values** use hex strings: `value: "0x0"` instead of `value: BigInt(0)`.
* In v4.x.x, the **paymaster capability** on `prepareCalls` or `sendCalls` is called `paymasterService` instead of `paymaster`, or you can set the `policyId` directly on the client.
* **Owners** use `LocalAccountSigner` / `WalletClientSigner` from `@aa-sdk/core`. In v5.x.x, a viem `LocalAccount` or `WalletClient` is used directly.

See the [full migration guide](/docs/wallets/resources/migration-v5) for a complete cheat sheet.
</Accordion>


## Next steps

Build more:

* [Sponsor gas for users](/docs/wallets/transactions/sponsor-gas)