Batch Payments

A Solana transaction is a container that holds one or more instructions. Each instruction is an operation—transfer tokens, create an account, call a program. The network executes all instructions in a transaction sequentially and atomically: either every instruction succeeds, or the entire transaction fails and rolls back.

This means you can pack multiple transfers into a single transaction. Instead of sending three separate transactions to pay three recipients, you send one transaction with three transfer instructions. This is faster (one confirmation instead of three) and cheaper (one base fee instead of three). Here's an illustrative example of how payments (referred to as "drops" in this image) are batched into a single transaction and multiple transactions are sent to handle the larger batch.

Batch payments diagramBatch payments diagram

Source: QuickNode - How to Send Bulk Transactions on Solana

For more information on transactions and instructions, see the Transactions and Instructions guides.

The walkthrough below shows how to load multiple transfer instructions into a single transaction for batch payments.

Batching Instructions into a Single Transaction

A Solana transaction can contain multiple transfers to different recipients. You sign once, pay one transaction fee, and all transfers settle together. If any transfer fails, the entire transaction is rejected.

See How Payments Work on Solana for core payment concepts.

Batching multiple transfers requires building each instruction separately, then combining them into a single transaction.

The steps below show the core flow. See the Demo for complete runnable code.

Derive Token Accounts

First, derive the Associated Token Account (ATA) addresses for the sender and each recipient. ATAs are deterministic addresses based on the wallet and mint.

Batch Payments
const [senderAta] = await findAssociatedTokenPda({
mint: mint.address,
owner: sender.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipient1Ata] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient1.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipient2Ata] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient2.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});

Create Transfer Instructions

Create a separate transfer instruction for each recipient. Each instruction specifies the:

  • source token account address
  • destination token account address
  • authority (source token account owner address)
  • amount in base units (adjusted for the mint's decimals)
Batch Payments
const [senderAta] = await findAssociatedTokenPda({
mint: mint.address,
owner: sender.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipient1Ata] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient1.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipient2Ata] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient2.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const transfer1Instruction = getTransferInstruction({
source: senderAta,
destination: recipient1Ata,
authority: sender.address,
amount: 250_000n
});
const transfer2Instruction = getTransferInstruction({
source: senderAta,
destination: recipient2Ata,
authority: sender.address,
amount: 250_000n
});

Send as Single Transaction

Add all transfer instructions into a single transaction. This executes all transfers atomically, either all transfers succeed or the entire transaction fails.

Verify Balances

After the batch transfer, verify the token balances for all parties using the splToken helper.

Batch Payments
const [senderAta] = await findAssociatedTokenPda({
mint: mint.address,
owner: sender.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipient1Ata] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient1.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipient2Ata] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient2.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const transfer1Instruction = getTransferInstruction({
source: senderAta,
destination: recipient1Ata,
authority: sender.address,
amount: 250_000n
});
const transfer2Instruction = getTransferInstruction({
source: senderAta,
destination: recipient2Ata,
authority: sender.address,
amount: 250_000n
});
const signature = await client.transaction.prepareAndSend({
authority: sender,
instructions: [transfer1Instruction, transfer2Instruction],
version: 0
});
const splToken = client.splToken({
mint: mint.address,
tokenProgram: "auto"
});
const senderBalance = await splToken.fetchBalance(sender.address);
const recipient1Balance = await splToken.fetchBalance(recipient1.address);
const recipient2Balance = await splToken.fetchBalance(recipient2.address);

Derive Token Accounts

First, derive the Associated Token Account (ATA) addresses for the sender and each recipient. ATAs are deterministic addresses based on the wallet and mint.

Create Transfer Instructions

Create a separate transfer instruction for each recipient. Each instruction specifies the:

  • source token account address
  • destination token account address
  • authority (source token account owner address)
  • amount in base units (adjusted for the mint's decimals)

Send as Single Transaction

Add all transfer instructions into a single transaction. This executes all transfers atomically, either all transfers succeed or the entire transaction fails.

Verify Balances

After the batch transfer, verify the token balances for all parties using the splToken helper.

Batch Payments
const [senderAta] = await findAssociatedTokenPda({
mint: mint.address,
owner: sender.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipient1Ata] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient1.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipient2Ata] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient2.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});

Demo

Demo
// Generate keypairs for sender and two recipients
const sender = (await generateKeypair()).signer;
const recipient1 = (await generateKeypair()).signer;
const recipient2 = (await generateKeypair()).signer;
console.log("Sender Address:", sender.address);
console.log("Recipient 1 Address:", recipient1.address);
console.log("Recipient 2 Address:", recipient2.address);
// Demo Setup: Create client, mint account, token accounts, and fund with initial tokens
const { client, mint } = await demoSetup(sender, recipient1, recipient2);
console.log("\nMint Address:", mint.address);
// Derive the Associated Token Accounts addresses (ATAs) for sender and recipients
const [senderAta] = await findAssociatedTokenPda({
mint: mint.address,
owner: sender.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipient1Ata] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient1.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipient2Ata] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient2.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
console.log("Sender Token Account:", senderAta.toString());
console.log("Recipient 1 Token Account:", recipient1Ata.toString());
console.log("Recipient 2 Token Account:", recipient2Ata.toString());
// =============================================================================
// Batch Token Payment Demo
// =============================================================================
// Create instructions to transfer tokens from sender to both recipients
// Transferring 250,000 base units = 0.25 tokens (with 6 decimals) to each
const transfer1Instruction = getTransferInstruction({
source: senderAta,
destination: recipient1Ata,
authority: sender.address,
amount: 250_000n // 0.25 tokens
});
const transfer2Instruction = getTransferInstruction({
source: senderAta,
destination: recipient2Ata,
authority: sender.address,
amount: 250_000n // 0.25 tokens
});
// Prepare and send both transfers in a single transaction using @solana/client
const signature = await client.transaction.prepareAndSend({
authority: sender,
instructions: [transfer1Instruction, transfer2Instruction],
version: 0
});
console.log("\n=== Batch Token Payment Complete ===");
console.log("Transaction Signature:", signature.toString());
// Fetch final token account balances using @solana/client SPL token helper
const splToken = client.splToken({
mint: mint.address,
tokenProgram: "auto"
});
const senderBalance = await splToken.fetchBalance(sender.address);
const recipient1Balance = await splToken.fetchBalance(recipient1.address);
const recipient2Balance = await splToken.fetchBalance(recipient2.address);
console.log("\nSender Token Account Balance:", senderBalance);
console.log("Recipient 1 Token Account Balance:", recipient1Balance);
console.log("Recipient 2 Token Account Balance:", recipient2Balance);
// =============================================================================
// Demo Setup Helper Function
// =============================================================================
Console
Click to execute the code.

Scaling with Transaction Planning

A single transaction has size limits—roughly 1232 bytes. For large batch operations (payroll for hundreds of employees, mass airdrops), you'll exceed this limit and need to split work across multiple transactions.

Though you are welcome to create your own transaction distribution logic, the @solana/instruction-plans package (part of Solana Kit) handles this at two levels:

Instruction plans define your operations and their ordering constraints:

  • Sequential — instructions that must execute in order
  • Parallel — instructions that can execute in any order
  • Non-divisible — instructions that must stay together in the same transaction

Transaction plans are generated from instruction plans. The planner intelligently packs instructions into optimally-sized transactions, respecting your ordering constraints. The resulting transaction plan can then be:

  • Executed — signed and sent to the network, with parallel transactions sent concurrently
  • Simulated — dry-run against the network to verify before sending
  • Serialized — compiled to base64 for external signing services or multi-party workflows

This two-level approach lets you think in terms of operations ("transfer to Alice, then transfer to Bob") while the library handles the mechanics of transaction sizing, packing, and parallel execution.

Is this page helpful?

सामग्री तालिका

पृष्ठ संपादित करें

द्वारा प्रबंधित

© 2026 सोलाना फाउंडेशन। सर्वाधिकार सुरक्षित।
जुड़े रहें