3. Batch Transactions

Bundle multiple operations into a single atomic Safe transaction using the MultiSend contract.

What you'll learn

  • Create a batch transaction containing multiple ETH transfers

  • Understand how the Protocol Kit uses the MultiSend contract under the hood

  • Sign, propose, and execute a batch as a single on-chain transaction

Prerequisites

  • Node.js 18+

  • A Safe on Sepolia where you control an owner key

  • A private key for a Safe owner: testnet only

  • Sepolia ETH in the Safe to cover the batch transfers and gas

  • viem: used for receipt verification

Install the SDK:

npm install @safe-global/api-kit @safe-global/protocol-kit @safe-global/types-kit viem

Configuration

import SafeApiKitModule from "@safe-global/api-kit";
import SafeModule from "@safe-global/protocol-kit";
import { OperationType } from "@safe-global/types-kit";
import type { MetaTransactionData } from "@safe-global/types-kit";
import { createPublicClient, http } from "viem";
import { sepolia } from "viem/chains";

// ESM interop-safe constructor resolution (required in some runtimes)
const SafeApiKit =
  typeof SafeApiKitModule === "function"
    ? SafeApiKitModule
    : (SafeApiKitModule as unknown as { default: typeof SafeApiKitModule }).default;
const Safe =
  typeof SafeModule === "function"
    ? SafeModule
    : (SafeModule as unknown as { default: typeof SafeModule }).default;

const CHAIN_ID = 11155111n; // Sepolia
const TX_SERVICE_URL = `https://app.multisig.ledger.com/api/safe-transaction-service/${CHAIN_ID}`;
const RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com";
const SAFE_ADDRESS = "0xYourSafeAddress";
const OWNER_PRIVATE_KEY = "your-private-key"; // Testnet only!

The TX_SERVICE_URL points at the Ledger-hosted Transaction Service. Transactions proposed here appear in the Ledger Multisig UIarrow-up-right.

Supported chains: Ethereum (1), Optimism (10), BSC (56), Polygon (137), Base (8453), Arbitrum (42161), Sepolia (11155111).

Step-by-step

1

Initialize

2

Define multiple operations

Create an array of MetaTransactionData objects. Each one represents a distinct operation. Here we batch two ETH transfers:

You can mix operation types in a batch: ETH transfers, contract calls, ERC-20 approvals, and more. Each operation has its own to, value, data, and operation fields.

3

Create the batch transaction

Pass the array to createTransaction. When the array contains more than one item, the Protocol Kit automatically wraps them in a call to the MultiSend contract.

Under the hood, the Protocol Kit:

1

ABI-encodes each operation into the MultiSend format.

2

Sets to to the MultiSend contract address (chain-specific, deployed by Safe).

3

Sets operation to DelegateCall (required for MultiSend).

4

Packs all operations into the data field.

You don't need to interact with the MultiSend contract directly. The SDK handles it.

4

Sign the batch

5

Propose to the Transaction Service

REST API: POST /v1/safes/{address}/multisig-transactions/

The batch transaction appears as a single entry in the Ledger Multisig UI.

6

Execute

Both transfers happen atomically in a single on-chain transaction. Either all succeed or all revert.

Key concepts

What is MultiSend?

MultiSend is a contract deployed by the Safe team on every supported chain. It lets you batch multiple operations into a single transaction by:

  • Encoding each operation (target, value, data, operation type) into a packed byte array

  • Executing all of them via delegatecall from the Safe

Because it uses delegatecall, the individual operations execute in the context of the Safe. msg.sender in each sub-call is the Safe itself.

Why batch?

  • Gas efficiency: One on-chain transaction instead of N

  • Atomicity: All operations succeed or all revert

  • Single approval flow: Owners sign once for the entire batch

  • Lower nonce usage: The batch consumes only one Safe nonce

Tips and pitfalls

  • Indexing lag. After the batch executes on-chain, the Transaction Service may take 10–60 seconds to index the result. If you query immediately, isExecuted might still be false.

  • Always verify the receipt. executeTransaction can return a hash even when the transaction eventually reverts. Wait for the receipt and check receipt.status.

  • DelegateCall is set automatically. When you pass multiple transactions to createTransaction, the SDK sets the operation to DelegateCall for the outer MultiSend call. Don't override this manually.

  • Mixing ETH and contract calls. You can freely mix native ETH transfers (data "0x") and contract interactions (encoded calldata) in the same batch. See ERC-20 Token Transfers for encoding contract calls.

  • Order matters. Operations execute sequentially in the order you define them. If operation B depends on the result of operation A (e.g., approve then transferFrom), put A first.

  • Error in one reverts all. If any operation in the batch fails, the entire MultiSend transaction reverts. Test each operation individually before batching.

Next steps

4. Delegate Managementchevron-right

Last updated