批量支付

Solana 交易是一个包含一个或多个指令的容器。每个指令都是一个操作——转账代币、创建账户、调用程序。网络会按顺序且原子性地执行交易中的所有指令:要么所有指令都成功,要么整个交易失败并回滚。

这意味着你可以在一笔交易中打包多笔转账。与其为三位收款人分别发送三笔交易,不如在一笔交易中包含三条转账指令。这样更快(只需一次确认而不是三次),也更省钱(只需支付一次基础费用而不是三次)。下图展示了如何将多笔支付(在图中称为 "drops")批量打包进一笔交易,并通过多笔交易处理更大规模的批量支付。

批量支付示意图批量支付示意图

来源: QuickNode - 如何在 Solana 上发送批量交易

关于交易和指令的更多信息,请参阅 交易指令 指南。

下面的操作演示了如何将多条转账指令加载到一笔交易中,实现批量支付。

将多条指令批量打包进一笔交易

一笔 Solana 交易可以包含对不同收款人的多笔转账。你只需签名一次,支付一次交易费用,所有转账会一起结算。如果有任何一笔转账失败,整个交易都会被拒绝。

参见 Solana 支付原理 了解核心支付概念。

批量转账需要分别构建每条指令,然后将它们合并到一笔交易中。

以下步骤展示了核心流程。完整可运行代码请参见 演示

派生 Token 账户

首先,为发送方和每个接收方派生 Associated Token Account (ATA) 地址。ATA 是基于钱包和 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
});

创建转账指令

为每个接收方分别创建一条转账指令。每条指令需指定:

  • 源 token account 地址
  • 目标 token account 地址
  • authority(源 token account 拥有者地址)
  • 金额(以基础单位表示,需根据 mint 的 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
});

作为单笔交易发送

将所有转账指令添加到一笔交易中。这样可以原子性地执行所有转账,要么全部成功,要么整个交易失败。

验证余额

批量转账完成后,使用 splToken helper 验证所有参与方的 token 余额。

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);

派生 Token 账户

首先,为发送方和每个接收方派生 Associated Token Account (ATA) 地址。ATA 是基于钱包和 mint 的确定性地址。

创建转账指令

为每个接收方分别创建一条转账指令。每条指令需指定:

  • 源 token account 地址
  • 目标 token account 地址
  • authority(源 token account 拥有者地址)
  • 金额(以基础单位表示,需根据 mint 的 decimals 调整)

作为单笔交易发送

将所有转账指令添加到一笔交易中。这样可以原子性地执行所有转账,要么全部成功,要么整个交易失败。

验证余额

批量转账完成后,使用 splToken helper 验证所有参与方的 token 余额。

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
// 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.

通过交易规划实现扩展

单笔交易有大小限制——大约 1232 字节。对于大批量操作(如数百名员工的工资发放、大规模空投),会超出此限制,需要将操作拆分为多笔交易。

当然你可以自行实现交易分发逻辑,但 @solana/instruction-plans 包(Solana Kit 的一部分)可在两个层面处理此问题:

指令计划 用于定义你的操作及其顺序约束:

  • 顺序执行 — 必须按顺序执行的指令
  • 并行执行 — 可以以任意顺序执行的指令
  • 不可拆分 — 必须保持在同一笔交易中的指令

交易计划 是由指令计划生成的。规划器会智能地将指令打包进最优大小的交易中,同时遵循你的顺序约束。最终生成的交易计划可以:

  • 执行 — 签名并发送到网络,支持并行交易同时发送
  • 模拟 — 在网络上进行试运行,发送前进行验证
  • 序列化 — 编译为 base64,便于外部签名服务或多方协作流程

这种两层结构让你可以专注于操作本身(“先转账给 Alice,再转账给 Bob”),而库会自动处理交易大小、打包和并行执行等细节。

Is this page helpful?

Table of Contents

Edit Page

管理者

©️ 2026 Solana 基金会版权所有
取得联系