Skip to main content
Tempo supports executing multiple operations in a single transaction using the calls array. This allows you to batch multiple contract calls together, saving on transaction overhead.
import { FC, useState } from 'react';
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isTempoWallet } from '@dynamic-labs/tempo';
import { encodeFunctionData, erc20Abi, Hex } from 'viem';

const BatchTransactionButton: FC = () => {
  const { primaryWallet } = useDynamicContext();
  const [txHash, setTxHash] = useState<string | null>(null);

  const onBatchTransaction = async () => {
    if (!primaryWallet || !isTempoWallet(primaryWallet)) {
      throw new Error('Tempo wallet not found');
    }

    const publicClient = await primaryWallet.getPublicClient();
    const walletClient = await primaryWallet.getWalletClient();

    const fromAddress = primaryWallet.address as Hex;
    const tokenAddress = '0x...' as Hex; // TIP-20 token
    const feeTokenAddress = '0x...' as Hex; // Fee token

    // Create multiple transfer calls
    const recipients = [
      { address: '0xRecipient1...' as Hex, amount: BigInt(1000000) },
      { address: '0xRecipient2...' as Hex, amount: BigInt(2000000) },
      { address: '0xRecipient3...' as Hex, amount: BigInt(3000000) },
    ];

    const calls = recipients.map(({ address, amount }) => ({
      data: encodeFunctionData({
        abi: erc20Abi,
        functionName: 'transfer',
        args: [address, amount],
      }),
      to: tokenAddress,
      value: BigInt(0),
    }));

    // Get nonce
    const nonce = await publicClient.getTransactionCount({
      address: fromAddress,
    });

    // Estimate gas (add extra buffer for multiple calls)
    const gasEstimate = await publicClient.estimateGas({
      account: fromAddress,
      data: calls[0].data,
      to: tokenAddress,
    });
    const gas = (gasEstimate * BigInt(calls.length) * BigInt(150)) / BigInt(100);

    // Get gas price
    const gasPrice = await publicClient.getGasPrice();

    // Build batch transaction
    const tempoTx = {
      calls,
      chainId: await publicClient.getChainId(),
      gas,
      maxFeePerGas: gasPrice,
      maxPriorityFeePerGas: gasPrice / BigInt(10),
      nonce,
      feeToken: feeTokenAddress,
    };

    // Sign and send
    const hash = await walletClient.sendTransaction(tempoTx);
    setTxHash(hash);

    console.log('Batch transaction hash:', hash);
  };

  return (
    <div>
      <button onClick={onBatchTransaction}>Send Batch Transaction</button>
      {txHash && <p>Transaction: {txHash}</p>}
    </div>
  );
};

Call Structure

Each call in the calls array has the following structure:
ParameterTypeDescription
toHexThe target contract address
dataHexThe encoded function call data
valuebigintThe amount of native value to send (typically 0 on Tempo)

Benefits of Batch Transactions

  1. Atomicity: All calls succeed or fail together
  2. Gas Efficiency: Single transaction overhead for multiple operations
  3. Better UX: Users only need to approve one transaction