Transakcja Solana to kontener, który zawiera jedną lub więcej instrukcji. Każda instrukcja to operacja — przesłanie tokenów, utworzenie konta, wywołanie programu. Sieć wykonuje wszystkie instrukcje w transakcji sekwencyjnie i atomowo: albo każda instrukcja zakończy się powodzeniem, albo cała transakcja zostanie odrzucona i cofnięta.
Oznacza to, że możesz spakować wiele przelewów w jedną transakcję. Zamiast wysyłać trzy oddzielne transakcje, aby zapłacić trzem odbiorcom, wysyłasz jedną transakcję z trzema instrukcjami przelewu. To szybsze (jedno potwierdzenie zamiast trzech) i tańsze (jedna opłata bazowa zamiast trzech). Poniżej znajduje się przykładowa ilustracja, jak płatności (w tym obrazie nazywane „dropami”) są grupowane w jedną transakcję, a wiele transakcji jest wysyłanych, aby obsłużyć większą partię.
Schemat płatności zbiorczych
Źródło: QuickNode - Jak wysyłać transakcje zbiorcze na Solanie
Więcej informacji o transakcjach i instrukcjach znajdziesz w przewodnikach Transakcje oraz Instrukcje.
Poniższy przewodnik pokazuje, jak załadować wiele instrukcji przelewu do jednej transakcji w celu realizacji płatności zbiorczych.
Grupowanie instrukcji w jednej transakcji
Transakcja Solana może zawierać wiele przelewów do różnych odbiorców. Podpisujesz tylko raz, płacisz jedną opłatę transakcyjną, a wszystkie przelewy są rozliczane razem. Jeśli którykolwiek przelew się nie powiedzie, cała transakcja zostaje odrzucona.
Zobacz Jak działają płatności na Solanie, aby poznać podstawowe pojęcia dotyczące płatności.
Grupowanie wielu przelewów wymaga osobnego zbudowania każdej instrukcji, a następnie połączenia ich w jednej transakcji.
Poniższe kroki pokazują główny przebieg. Pełny, działający kod znajdziesz w sekcji Demo.
Wyprowadź konta tokenów
Najpierw wyprowadź adresy Associated Token Account (ATA) dla nadawcy oraz każdego odbiorcy. ATA to deterministyczne adresy oparte na portfelu i mincie.
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});
Utwórz instrukcje transferu
Utwórz osobną instrukcję transferu dla każdego odbiorcy. Każda instrukcja określa:
- adres źródłowego token account
- adres docelowego token account
- autoryzację (adres właściciela źródłowego token account)
- kwotę w jednostkach bazowych (skorygowaną o liczbę miejsc po przecinku dla danego mintu)
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});
Wyślij jako jedną transakcję
Dodaj wszystkie instrukcje transferu do jednej transakcji. Dzięki temu wszystkie transfery zostaną wykonane atomowo — albo wszystkie się powiodą, albo cała transakcja zostanie odrzucona.
Zweryfikuj salda
Po przeprowadzeniu transferu zbiorczego zweryfikuj salda tokenów dla wszystkich
stron za pomocą pomocnika splToken.
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);
Demo
// 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// =============================================================================
Skalowanie dzięki planowaniu transakcji
Pojedyncza transakcja ma ograniczenia rozmiaru — około 1232 bajtów. Przy dużych operacjach zbiorczych (np. wypłaty dla setek pracowników, masowe airdropy) przekroczysz ten limit i będziesz musiał podzielić pracę na kilka transakcji.
Możesz oczywiście stworzyć własną logikę rozdzielania transakcji, ale pakiet
@solana/instruction-plans
(część Solana Kit) obsługuje to na dwóch poziomach:
Plany instrukcji definiują operacje oraz ograniczenia dotyczące ich kolejności:
- Sekwencyjne — instrukcje, które muszą być wykonane w określonej kolejności
- Równoległe — instrukcje, które mogą być wykonane w dowolnej kolejności
- Niedzielne — instrukcje, które muszą pozostać razem w tej samej transakcji
Plany transakcji są generowane na podstawie planów instrukcji. Planer inteligentnie pakuje instrukcje w transakcje o optymalnym rozmiarze, zachowując Twoje ograniczenia dotyczące kolejności. Otrzymany plan transakcji może być:
- Wykonany — podpisany i wysłany do sieci, przy czym transakcje równoległe są wysyłane jednocześnie
- Symulowany — testowo uruchomiony w sieci w celu weryfikacji przed wysłaniem
- Serializowany — skompilowany do formatu base64 dla zewnętrznych usług podpisywania lub procesów wielostronnych
To dwupoziomowe podejście pozwala myśleć w kategoriach operacji ("przelej do Alice, potem przelej do Boba"), podczas gdy biblioteka zajmuje się mechaniką doboru rozmiaru transakcji, pakowaniem i równoległym wykonywaniem.
Is this page helpful?