Skip to main content
Batch multiple contract calls into a single transaction. Operations either all succeed or all fail—no partial execution.

Why Batching?

  • One signature for complex flows
  • Atomic execution—no stuck states
  • Lower gas costs than separate transactions
  • Composable—output of one call feeds into the next

Basic Batch

const approveInstruction = await account.buildComposable({
  type: "approve",
  data: {
    spender: UNISWAP_ROUTER,
    tokenAddress: USDC,
    chainId: base.id,
    amount: parseUnits("100", 6)
  }
});

const swapInstruction = await account.buildComposable({
  type: "default",
  data: {
    chainId: base.id,
    to: UNISWAP_ROUTER,
    abi: UniswapAbi,
    functionName: "exactInputSingle",
    args: [{ tokenIn: USDC, tokenOut: WETH, amountIn: parseUnits("100", 6), /* ... */ }]
  }
});

// Execute both in one transaction
const quote = await meeClient.getQuote({
  instructions: [approveInstruction, swapInstruction],
  feeToken: { address: USDC, chainId: base.id }
});

const { hash } = await meeClient.executeQuote({ quote });

Composable Operations

Use runtime injection when the next step depends on the previous output:
import { runtimeERC20BalanceOf } from "@biconomy/abstractjs";
import { balanceNotZeroConstraint } from "../utils/balanceNotZero.util";

// Step 1: Swap USDC → WETH
const swap = await account.buildComposable({
  type: "default",
  data: {
    chainId: base.id,
    to: UNISWAP_ROUTER,
    abi: UniswapAbi,
    functionName: "exactInputSingle",
    args: [{
      tokenIn: USDC,
      tokenOut: WETH,
      amountIn: parseUnits("100", 6),
      amountOutMinimum: 0n,
      // ...
    }]
  }
});

// Step 2: Deposit ALL received WETH into Morpho
const deposit = await account.buildComposable({
  type: "default",
  data: {
    chainId: base.id,
    to: MORPHO_POOL,
    abi: MorphoAbi,
    functionName: "deposit",
    args: [
      runtimeERC20BalanceOf({
        tokenAddress: WETH,
        targetAddress: account.addressOn(base.id, true),
        constraints: [balanceNotZeroConstraint]
      }),
      account.addressOn(base.id, true)
    ]
  }
});

const quote = await meeClient.getQuote({
  instructions: [swap, deposit],
  feeToken: { address: USDC, chainId: base.id }
});

Fusion Mode (External Wallets)

For MetaMask/Rabby users, use a trigger to fund the operation:
const trigger = {
  chainId: base.id,
  tokenAddress: USDC,
  amount: parseUnits("100", 6)
};

const quote = await meeClient.getFusionQuote({
  trigger,
  instructions: [approveInstruction, swapInstruction, depositInstruction],
  feeToken: { address: USDC, chainId: base.id }
});

const { hash } = await meeClient.executeFusionQuote({ fusionQuote: quote });

Example: Swap → Stake in One Click

import { runtimeERC20BalanceOf } from "@biconomy/abstractjs";
import { balanceNotZeroConstraint } from "../utils/balanceNotZero.util";

const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
const WETH = "0x4200000000000000000000000000000000000006";
const MORPHO_POOL = "0xA2Cac0023a4797b4729Db94783405189a4203AFc";

// 1. Approve Uniswap
const approve = await account.buildComposable({
  type: "approve",
  data: { spender: UNISWAP_ROUTER, tokenAddress: USDC, chainId: base.id, amount: inputAmount }
});

// 2. Swap USDC → WETH
const swap = await account.buildComposable({
  type: "default",
  data: {
    chainId: base.id,
    to: UNISWAP_ROUTER,
    abi: UniswapAbi,
    functionName: "exactInputSingle",
    args: [{ tokenIn: USDC, amountIn: inputAmount, tokenOut: WETH, /* ... */ }]
  }
});

// 3. Approve Morpho
const approveMorpho = await account.buildComposable({
  type: "approve",
  data: {
    spender: MORPHO_POOL,
    tokenAddress: WETH,
    chainId: base.id,
    amount: runtimeERC20BalanceOf({
      tokenAddress: WETH,
      targetAddress: account.addressOn(base.id, true),
      constraints: [balanceNotZeroConstraint]
    })
  }
});

// 4. Deposit to Morpho
const deposit = await account.buildComposable({
  type: "default",
  data: {
    chainId: base.id,
    to: MORPHO_POOL,
    abi: [{ name: "deposit", inputs: [{ name: "assets", type: "uint256" }, { name: "receiver", type: "address" }], type: "function" }],
    functionName: "deposit",
    args: [
      runtimeERC20BalanceOf({
        tokenAddress: WETH,
        targetAddress: account.addressOn(base.id, true),
        constraints: [balanceNotZeroConstraint]
      }),
      account.addressOn(base.id, true)
    ]
  }
});

// Execute all four operations with one signature
const quote = await meeClient.getQuote({
  instructions: [approve, swap, approveMorpho, deposit],
  feeToken: { address: USDC, chainId: base.id }
});

const { hash } = await meeClient.executeQuote({ quote });

Next Steps

Cross-Chain Orchestration

Execute flows across multiple chains