Solanaトランザクションは、1つ以上のinstructionsを保持するコンテナです。各instructionは、トークンの転送、アカウントの作成、プログラムの呼び出しなどの操作です。ネットワークは、トランザクション内のすべてのinstructionsを順次かつアトミックに実行します。すべてのinstructionが成功するか、トランザクション全体が失敗してロールバックされます。
つまり、複数の転送を単一のトランザクションにまとめることができます。3人の受取人に支払うために3つの個別のトランザクションを送信する代わりに、3つの転送instructionsを含む1つのトランザクションを送信します。これにより、より高速(3回ではなく1回の確認)かつより安価(3つではなく1つの基本手数料)になります。以下は、支払い(この画像では「drops」と呼ばれています)が単一のトランザクションにバッチ処理され、より大きなバッチを処理するために複数のトランザクションが送信される様子を示す例です。
バッチ支払いの図
出典: QuickNode - How to Send Bulk Transactions on Solana
トランザクションとinstructionsの詳細については、トランザクションおよびInstructionsガイドを参照してください。
以下のウォークスルーでは、バッチ支払いのために複数の転送instructionsを単一のトランザクションに読み込む方法を示します。
単一のトランザクションへのinstructionsのバッチ処理
Solanaトランザクションには、異なる受取人への複数の転送を含めることができます。1回署名し、1つのトランザクション手数料を支払うと、すべての転送が一緒に決済されます。いずれかの転送が失敗すると、トランザクション全体が拒否されます。
コア支払いの概念については、Solanaでの支払いの仕組みを参照してください。
複数の転送をバッチ処理するには、各instructionを個別に構築してから、それらを単一のトランザクションに結合する必要があります。
以下の手順は、コアフローを示しています。完全な実行可能コードについては、デモを参照してください。
トークンアカウントの導出
まず、送信者と各受信者のAssociated Token Account(ATA)アドレスを導出します。ATAは、ウォレットとミントに基づいた決定論的なアドレスです。
転送instructionsの作成
各受信者に対して個別の転送instructionを作成します。各instructionでは以下を指定します。
- 送信元トークンアカウントアドレス
- 送信先トークンアカウントアドレス
- 権限(送信元トークンアカウント所有者アドレス)
- 基本単位での金額(ミントの小数点以下桁数に調整)
単一トランザクションとして送信
すべての転送instructionsを単一のトランザクションに追加します。これにより、すべての転送がアトミックに実行され、すべての転送が成功するか、トランザクション全体が失敗するかのいずれかになります。
残高の確認
バッチ転送後、splTokenヘルパーを使用して、すべての関係者のトークン残高を確認します。
デモ
// Generate keypairs for sender and two recipientsconst 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 tokensconst { client, mint } = await demoSetup(sender, recipient1, recipient2);console.log("\nMint Address:", mint.address);// Derive the Associated Token Accounts addresses (ATAs) for sender and recipientsconst [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 eachconst 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/clientconst 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 helperconst 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// =============================================================================
トランザクションプランニングによるスケーリング
単一のトランザクションにはサイズ制限があり、約1232バイトです。大規模なバッチ操作(数百人の従業員への給与支払い、大量のエアドロップ)の場合、この制限を超えるため、複数のトランザクションに作業を分割する必要があります。
独自のトランザクション分散ロジックを作成することもできますが、@solana/instruction-plansパッケージ(Solana
Kitの一部)は、これを2つのレベルで処理します。
Instruction plansは、操作とその順序制約を定義します。
- Sequential — 順番に実行する必要があるinstructions
- Parallel — 任意の順序で実行できるinstructions
- Non-divisible — 同じトランザクション内で一緒に保持する必要があるinstructions
Transaction plansは、instruction plansから生成されます。プランナーは、順序制約を尊重しながら、instructionsを最適なサイズのトランザクションにインテリジェントにパックします。生成されたトランザクションプランは、次のように使用できます。
- Executed — 署名してネットワークに送信し、並列トランザクションは同時に送信されます
- Simulated — 送信前に検証するためにネットワークに対してドライランを実行します
- Serialized — 外部署名サービスまたはマルチパーティワークフロー用にbase64にコンパイルします
この2レベルのアプローチにより、ライブラリがトランザクションのサイジング、パッキング、並列実行のメカニクスを処理する一方で、操作の観点(「Aliceに転送してから、Bobに転送する」)で考えることができます。
Is this page helpful?