From 3e41224ef0ee9c68d425b36aef0b8ceb080f2c91 Mon Sep 17 00:00:00 2001 From: Maria Shodunke Date: Tue, 14 Oct 2025 11:09:07 +0100 Subject: [PATCH 1/5] Add Batch transactions tutorials --- _code-samples/batch/README.md | 5 +- _code-samples/batch/js/README.md | 281 ++++++++++++++++++ _code-samples/batch/js/multiAccountBatch.js | 110 +++++++ _code-samples/batch/js/package.json | 6 + _code-samples/batch/js/singleAccountBatch.js | 90 ++++++ .../how-tos/use-batch-transactions/index.md | 14 + .../send-a-multi-account-batch-transaction.md | 158 ++++++++++ ...send-a-single-account-batch-transaction.md | 148 +++++++++ sidebars.yaml | 5 + 9 files changed, 815 insertions(+), 2 deletions(-) create mode 100644 _code-samples/batch/js/README.md create mode 100644 _code-samples/batch/js/multiAccountBatch.js create mode 100644 _code-samples/batch/js/package.json create mode 100644 _code-samples/batch/js/singleAccountBatch.js create mode 100644 docs/tutorials/how-tos/use-batch-transactions/index.md create mode 100644 docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md create mode 100644 docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md diff --git a/_code-samples/batch/README.md b/_code-samples/batch/README.md index c0852466c27..87edb5a79a0 100644 --- a/_code-samples/batch/README.md +++ b/_code-samples/batch/README.md @@ -1,4 +1,5 @@ # Batch -Code samples showing how to create and submit a [Batch transaction](../../docs/concepts/transactions/batch-transactions.md). -Both for simple and multi account batch transactions. +Code samples showing how to create and submit a [Batch transaction](https://xrpl.org/docs/concepts/transactions/batch-transactions). + +Both for single and multi account batch transactions. diff --git a/_code-samples/batch/js/README.md b/_code-samples/batch/js/README.md new file mode 100644 index 00000000000..70b0d6d2b12 --- /dev/null +++ b/_code-samples/batch/js/README.md @@ -0,0 +1,281 @@ +# Send a Batch Transaction + +Code samples showing how to create and submit a [Batch transaction](https://xrpl.org/docs/concepts/transactions/batch-transactions) with Javascript. + +Both for single and multi account batch transactions. + +## Single Account Batch Transaction + +Quick setup and usage: + +```sh +npm install xrpl +node singleAccountBatch.js +``` + +The script should output the following: + +```sh +Funding new wallets from faucet... +Sender: raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e, Balance: 100 XRP +Wallet1: r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC, Balance: 100 XRP +Wallet2: rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn, Balance: 100 XRP + +Creating batch transaction: +{ + "TransactionType": "Batch", + "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Flags": 65536, + "RawTransactions": [ + { + "RawTransaction": { + "TransactionType": "Payment", + "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Destination": "r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC", + "Amount": "2000000", + "Flags": 1073741824 + } + }, + { + "RawTransaction": { + "TransactionType": "Payment", + "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Destination": "rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn", + "Amount": "5000000", + "Flags": 1073741824 + } + } + ] +} + +Submitting batch transaction... + +Batch transaction submitted successfully! +Result: + { + "close_time_iso": "2025-11-03T14:16:21Z", + "ctid": "C00D458B00020002", + "hash": "A93D3C2BDB5D600E592B64B84E66D789237D029267129EBC659EE483E532DD95", + "ledger_hash": "BE6B7C12E551305F09E942D6FA3FC8546F024AE5C53FC495DA6ABF78461E7019", + "ledger_index": 869771, + "meta": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Balance": "99999996", + "Flags": 0, + "OwnerCount": 0, + "Sequence": 869767 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "6238B6901FEBD1492C03546C7965A01F184C4E37B696304B86F78F4ADB7831B1", + "PreviousFields": { + "Balance": "100000000", + "Sequence": 869766 + }, + "PreviousTxnID": "559F102041D84FF9DA17483355C3C96A0F8923D9C9C7971BBB15C972DD1F37D6", + "PreviousTxnLgrSeq": 869766 + } + } + ], + "TransactionIndex": 2, + "TransactionResult": "tesSUCCESS" + }, + "tx_json": { + "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Fee": "4", + "Flags": 65536, + "LastLedgerSequence": 869789, + "RawTransactions": [ + { + "RawTransaction": { + "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Amount": "2000000", + "Destination": "r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC", + "Fee": "0", + "Flags": 1073741824, + "Sequence": 869767, + "SigningPubKey": "", + "TransactionType": "Payment" + } + }, + { + "RawTransaction": { + "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Amount": "5000000", + "Destination": "rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn", + "Fee": "0", + "Flags": 1073741824, + "Sequence": 869768, + "SigningPubKey": "", + "TransactionType": "Payment" + } + } + ], + "Sequence": 869766, + "SigningPubKey": "EDFECFB87A29F93E52BBA0BA5A14A59B520BB0E39F33943A2FDC1101D34349270D", + "TransactionType": "Batch", + "TxnSignature": "E08E300BDE1700C7CC27F3DA9B784907F637518E1C7E0978E57BFE5D1511A3B6A4269235FC2D9EAA550182A5F2B59415A442CE59555B9B9A0A79AB4030C9F701", + "ctid": "C00D458B00020002", + "date": 815494581, + "ledger_index": 869771 + }, + "validated": true +} + +Final balances after batch transaction: +Sender: raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e, Balance: 92.999996 XRP +Wallet1: r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC, Balance: 102 XRP +Wallet2: rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn, Balance: 105 XRP + +Transaction URL: +https://devnet.xrpl.org/transactions/A93D3C2BDB5D600E592B64B84E66D789237D029267129EBC659EE483E532DD95 +``` + +## Multi-Account Batch Transaction + +```sh +npm install xrpl +node multiAccountBatch.js +``` + +The script should output the following: + +```sh +Funding new wallets from faucet... +Alice: rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa, Balance: 100 XRP +Bob: rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7, Balance: 100 XRP +Charlie: rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K, Balance: 100 XRP +Third-party wallet: rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc, Balance: 100 XRP + +Creating batch transaction: +{ + "TransactionType": "Batch", + "Account": "rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc", + "Flags": 65536, + "RawTransactions": [ + { + "RawTransaction": { + "TransactionType": "Payment", + "Account": "rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K", + "Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", + "Amount": "50000000", + "Flags": 1073741824 + } + }, + { + "RawTransaction": { + "TransactionType": "Payment", + "Account": "rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7", + "Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", + "Amount": "50000000", + "Flags": 1073741824 + } + } + ] +} + +Submitting batch transaction... + +Batch transaction submitted successfully! +Result: + { + "close_time_iso": "2025-11-03T14:15:00Z", + "ctid": "C00D457000000002", + "hash": "8CBCCD88B8ABC248797B84ABB92066961C1CB5FE75ACE2115ADCA6B74C85993A", + "ledger_hash": "2217A0DBB38B870187B412533B939724095359A050B21E071A2A114BF57CFB60", + "ledger_index": 869744, + "meta": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Account": "rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc", + "Balance": "99999994", + "Flags": 0, + "OwnerCount": 0, + "Sequence": 869743 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "1E9BA043B9C6518582D0FF73A08DCD8B6958195735086CF7295E5EB6433FB453", + "PreviousFields": { + "Balance": "100000000", + "Sequence": 869742 + }, + "PreviousTxnID": "F7019BC55D80438FDDB01C2549CCC3F7DAF9791F8645E0269D63979EAEC5BBA6", + "PreviousTxnLgrSeq": 869742 + } + } + ], + "TransactionIndex": 0, + "TransactionResult": "tesSUCCESS" + }, + "tx_json": { + "Account": "rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc", + "BatchSigners": [ + { + "BatchSigner": { + "Account": "rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K", + "SigningPubKey": "EDC566D7DA8186BBD30DDAE1FB770FCE7F248949194E1A2E70B18CFA060B140B59", + "TxnSignature": "31639BFA1359DD24345776EAEEACCF61C1CDC792988679263D113E80A22D837E20ACD2B25E482FCA769990C004D747836370C6BAD14524559639BBEBA5813002" + } + }, + { + "BatchSigner": { + "Account": "rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7", + "SigningPubKey": "EDEF1966B325000407940E4C0792E3CCC3E27F51D132BDC53DCC2B1998E7C32A34", + "TxnSignature": "6BF9860B0E2E134FB302329D711BAA7B6314395D39523982DBBC037E84FB17AB5E8E736DB3DB0019B4477686AF2D91E5D2B49409698A95219376B2E318D3E501" + } + } + ], + "Fee": "6", + "Flags": 65536, + "LastLedgerSequence": 869762, + "RawTransactions": [ + { + "RawTransaction": { + "Account": "rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K", + "Amount": "50000000", + "Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", + "Fee": "0", + "Flags": 1073741824, + "Sequence": 869740, + "SigningPubKey": "", + "TransactionType": "Payment" + } + }, + { + "RawTransaction": { + "Account": "rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7", + "Amount": "50000000", + "Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", + "Fee": "0", + "Flags": 1073741824, + "Sequence": 869738, + "SigningPubKey": "", + "TransactionType": "Payment" + } + } + ], + "Sequence": 869742, + "SigningPubKey": "ED2B56D6FB4E8C236A6B07E8D8AD9A4938606144E31779918F99525CA6B3C56664", + "TransactionType": "Batch", + "TxnSignature": "9C51C1F2CB0E8BCEA1FADD3992249DE72AC46FC86AB2FB023A597FBD5C4CCB3337967E9AAFFB5F1C0CBC91128F3FD194F78F207E461BE1FF906C496B94EC410E", + "ctid": "C00D457000000002", + "date": 815494500, + "ledger_index": 869744 + }, + "validated": true +} + +Final balances after batch transaction: +Alice: rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa, Balance: 200 XRP +Bob: rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7, Balance: 50 XRP +Charlie: rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K, Balance: 50 XRP +Third-party wallet: rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc, Balance: 99.999994 XRP + +Transaction URL: +https://devnet.xrpl.org/transactions/8CBCCD88B8ABC248797B84ABB92066961C1CB5FE75ACE2115ADCA6B74C85993A +``` diff --git a/_code-samples/batch/js/multiAccountBatch.js b/_code-samples/batch/js/multiAccountBatch.js new file mode 100644 index 00000000000..575967a4e0b --- /dev/null +++ b/_code-samples/batch/js/multiAccountBatch.js @@ -0,0 +1,110 @@ +/** + * XRP Ledger Batch Transactions Tutorial + * + * This tutorial demonstrates how to use the Batch transaction feature (XLS-56) + * to perform a multi-account batch transaction. +*/ + +import xrpl from "xrpl" +import { BatchFlags } from 'xrpl/dist/npm/models/transactions/batch.js' +import { GlobalFlags } from 'xrpl/dist/npm/models/transactions/common.js' +import { signMultiBatch, combineBatchSigners } from 'xrpl/dist/npm/Wallet/batchSigner.js' + +const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233/") +await client.connect() + +// Create and fund wallets +console.log("Funding new wallets from faucet...") +const { wallet: alice } = await client.fundWallet() +const { wallet: bob } = await client.fundWallet() +const { wallet: charlie } = await client.fundWallet() +const { wallet: thirdPartyWallet } = await client.fundWallet() + +console.log(`Alice: ${alice.address}, Balance: ${await client.getXrpBalance(alice.address)} XRP`) +console.log(`Bob: ${bob.address}, Balance: ${await client.getXrpBalance(bob.address)} XRP`) +console.log(`Charlie: ${charlie.address}, Balance: ${await client.getXrpBalance(charlie.address)} XRP`) +console.log(`Third-party wallet: ${thirdPartyWallet.address}, Balance: ${await client.getXrpBalance(thirdPartyWallet.address)} XRP`) + +// Create inner transactions -------------------------------------------- +// REQUIRED: Inner transactions MUST have the tfInnerBatchTxn flag (0x40000000). +// This marks them as part of a batch (allows Fee: 0 and empty SigningPubKey). + +// Transaction 1: Charlie pays Alice +const charliePayment = { + TransactionType: "Payment", + Account: charlie.address, + Destination: alice.address, + Amount: xrpl.xrpToDrops(50), + Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED +} + +// Transaction 2: Bob pays Alice +const bobPayment = { + TransactionType: "Payment", + Account: bob.address, + Destination: alice.address, + Amount: xrpl.xrpToDrops(50), + Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED +} + +// Send Batch transaction -------------------------------------------- +console.log("\nCreating batch transaction:") +const batchTx = { + TransactionType: "Batch", + Account: thirdPartyWallet.address, + Flags: BatchFlags.tfAllOrNothing, // tfAllOrNothing: All inner transactions must succeed + RawTransactions: [ + { RawTransaction: charliePayment }, + { RawTransaction: bobPayment }, + ] +} +console.log(JSON.stringify(batchTx, null, 2)) + +// Validate the transaction structure +xrpl.validate(batchTx) + +// Set the expected number of signers for this transaction. +// "autofill" will automatically add Fee: "0" and SigningPubKey: "". +const autofilledBatchTx = await client.autofill(batchTx, 2) + +// Gather batch signatures -------------------------------- +// Each signer needs their own tx copy because signMultiBatch modifies the object. +// Charlie signs the Batch transaction +const charlieBatch = { ...autofilledBatchTx } +signMultiBatch(charlie, charlieBatch) + +// Bob signs the Batch transaction +const bobBatch = { ...autofilledBatchTx } +signMultiBatch(bob, bobBatch) + +// Combine inner transaction signatures. +// This returns a signed transaction blob (hex string) ready for submission. +const combinedSignedTx = combineBatchSigners([charlieBatch, bobBatch]) + +// Submit the signed blob with the third-party's wallet +console.log("\nSubmitting batch transaction...") +const submitResponse = await client.submitAndWait(combinedSignedTx, + { wallet: thirdPartyWallet } +) + +// Check Batch transaction result -------------------------------- +if (submitResponse.result.meta.TransactionResult !== "tesSUCCESS") { + const resultCode = submitResponse.result.meta.TransactionResult + console.warn(`\nTransaction failed with result code ${resultCode}`) + process.exit(1) +} + +console.log("\nBatch transaction submitted successfully!") +console.log("Result:\n", JSON.stringify(submitResponse.result, null, 2)) + +// Verify balances after transaction +console.log("\nFinal balances after batch transaction:") +console.log(`Alice: ${alice.address}, Balance: ${await client.getXrpBalance(alice.address)} XRP`) +console.log(`Bob: ${bob.address}, Balance: ${await client.getXrpBalance(bob.address)} XRP`) +console.log(`Charlie: ${charlie.address}, Balance: ${await client.getXrpBalance(charlie.address)} XRP`) +console.log(`Third-party wallet: ${thirdPartyWallet.address}, Balance: ${await client.getXrpBalance(thirdPartyWallet.address)} XRP`) + +// View the transaction on the XRPL Explorer +console.log(`\nTransaction URL:\nhttps://devnet.xrpl.org/transactions/${submitResponse.result.hash}`) + +await client.disconnect() diff --git a/_code-samples/batch/js/package.json b/_code-samples/batch/js/package.json new file mode 100644 index 00000000000..139f3bae724 --- /dev/null +++ b/_code-samples/batch/js/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "xrpl": "^4.4.2" + }, + "type": "module" +} diff --git a/_code-samples/batch/js/singleAccountBatch.js b/_code-samples/batch/js/singleAccountBatch.js new file mode 100644 index 00000000000..a7a069d7b14 --- /dev/null +++ b/_code-samples/batch/js/singleAccountBatch.js @@ -0,0 +1,90 @@ +/** + * Single Account Batch Transaction Example + * + * This example demonstrates how to use the Batch transactions feature (XLS-56) + * to create a single-account batch transaction that sends payments + * to multiple destinations in one atomic operation. +*/ + +import xrpl from "xrpl" +import { BatchFlags } from 'xrpl/dist/npm/models/transactions/batch.js' +import { GlobalFlags } from 'xrpl/dist/npm/models/transactions/common.js' + +const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233/") +await client.connect() + +// Create and fund wallets +console.log("Funding new wallets from faucet...") +const { wallet: sender } = await client.fundWallet() +const { wallet: wallet1 } = await client.fundWallet() +const { wallet: wallet2 } = await client.fundWallet() + +console.log(`Sender: ${sender.address}, Balance: ${await client.getXrpBalance(sender.address)} XRP`) +console.log(`Wallet1: ${wallet1.address}, Balance: ${await client.getXrpBalance(wallet1.address)} XRP`) +console.log(`Wallet2: ${wallet2.address}, Balance: ${await client.getXrpBalance(wallet2.address)} XRP`) + +// Create inner transactions -------------------------------------------- +// REQUIRED: Inner transactions MUST have the tfInnerBatchTxn flag (0x40000000). +// This marks them as part of a batch (allows Fee: 0 and empty SigningPubKey). + +// Transaction 1 +const payment1 = { + TransactionType: "Payment", + Account: sender.address, + Destination: wallet1.address, + Amount: xrpl.xrpToDrops(2), + Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED +} + +// Transaction 2 +const payment2 = { + TransactionType: "Payment", + Account: sender.address, + Destination: wallet2.address, + Amount: xrpl.xrpToDrops(5), + Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED +} + +// Send Batch transaction -------------------------------------------- +console.log("\nCreating batch transaction:") +const batchTx = { + TransactionType: "Batch", + Account: sender.address, + Flags: BatchFlags.tfAllOrNothing, // tfAllOrNothing: All inner transactions must succeed + RawTransactions: [ + { RawTransaction: payment1 }, + { RawTransaction: payment2 } + ] +} +console.log(JSON.stringify(batchTx, null, 2)) + +// Validate the transaction structure before submitting +xrpl.validate(batchTx) + +// Submit and wait for validation +console.log("\nSubmitting batch transaction...") +const submitResponse = await client.submitAndWait(batchTx, { + wallet: sender, + // "autofill" will automatically add Fee: "0" and SigningPubKey: "". + autofill: true +}) + +// Check Batch transaction result -------------------------------- +if (submitResponse.result.meta.TransactionResult !== "tesSUCCESS") { + const resultCode = submitResponse.result.meta.TransactionResult + console.warn(`\nTransaction failed with result code ${resultCode}`) + process.exit(1) +} +console.log("\nBatch transaction submitted successfully!") +console.log("Result:\n", JSON.stringify(submitResponse.result, null, 2)) + +// Verify balances after transaction +console.log("\nFinal balances after batch transaction:") +console.log(`Sender: ${sender.address}, Balance: ${await client.getXrpBalance(sender.address)} XRP`) +console.log(`Wallet1: ${wallet1.address}, Balance: ${await client.getXrpBalance(wallet1.address)} XRP`) +console.log(`Wallet2: ${wallet2.address}, Balance: ${await client.getXrpBalance(wallet2.address)} XRP`) + +// View the transaction on the XRPL Explorer +console.log(`\nTransaction URL:\nhttps://devnet.xrpl.org/transactions/${submitResponse.result.hash}`) + +await client.disconnect() diff --git a/docs/tutorials/how-tos/use-batch-transactions/index.md b/docs/tutorials/how-tos/use-batch-transactions/index.md new file mode 100644 index 00000000000..d64f91fc482 --- /dev/null +++ b/docs/tutorials/how-tos/use-batch-transactions/index.md @@ -0,0 +1,14 @@ +--- +seo: + description: Batch multiple transactions together and execute them as a single unit. +metadata: + indexPage: true +labels: + - Batch + - Transactions +--- +# Use Batch Transactions + +Batch multiple transactions together and execute them as a single unit. + +{% child-pages /%} diff --git a/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md b/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md new file mode 100644 index 00000000000..60ebc74a950 --- /dev/null +++ b/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md @@ -0,0 +1,158 @@ +--- +seo: + description: Send a Batch transaction containing transactions from multiple accounts. +metadata: + indexPage: true +labels: + - Batch + - Transactions +--- +# Send a Multi-Account Batch Transaction + +This tutorial shows you how to create a [Batch transaction][] containing transactions from multiple accounts, where each account must sign the Batch transaction. Any account, even one not involved in the inner transactions, can submit the batch. + +## Goals + +By the end of this tutorial, you will be able to: + +- Create a Batch transaction with multiple inner transactions, signed by multiple accounts, and submitted by a third party account. +- Configure the Batch transaction to ensure atomicity, so that either all inner transactions succeed or they all fail. + +## Prerequisites + +To complete this tutorial, you should: + +- Have a basic understanding of the XRP Ledger. +- Have an XRP Ledger client library set up in your development environment. This page provides examples for the following: + - **JavaScript** with the [xrpl.js library](https://github.com/XRPLF/xrpl.js). See [Get Started Using JavaScript](../../javascript/build-apps/get-started.md) for setup steps. + +## Source Code + +You can find the complete source code for this tutorial's examples in the [code samples section of this website's repository](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/batch/). + +## Steps + +The example in this tutorial demonstrates a scenario where Bob and Charlie both owe Alice 50 XRP each, and a third-party (such as a payment processor) submits the Batch transaction atomically to ensure Alice is paid by both parties. + +### 1. Install dependencies + +{% tabs %} +{% tab label="Javascript" %} +From the code sample folder, use npm to install dependencies: + +```bash +npm install xrpl +``` + +{% /tab %} +{% /tabs %} + +### 2. Set up client and accounts + +To get started, import the client library and instantiate a client to connect to the XRPL. Then, create the accounts for Alice, Bob, Charlie, and the third-party. + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="import xrpl" before="// Create inner transactions" /%} +{% /tab %} +{% /tabs %} + +### 3. Prepare inner transactions + +Next, prepare the inner transactions that will be included in the batch. + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Create inner transactions" to="// Send Batch transaction" /%} +{% /tab %} +{% /tabs %} + +The first transaction sends a payment of 50 XRP from Charlie to Alice, and the second sends a payment of 50 XRP from Bob to Alice. Both transactions must include the `tfInnerBatchTxn` (0x40000000) flag to indicate that they are inner transactions of a batch. + +Inner transactions must have a Fee of **0** and an empty string for the `SigningPubKey`. The outer Batch transaction handles the overall fee and signing for all inner transactions. + +{% admonition type="info" name="Note" %} +The `Fee` and `SigningPubKey` fields are omitted as the client library's _autofill_ functionality automatically populates these when submitting the Batch transaction. + +You typically don't need to set these manually, but if you do, ensure `Fee` is set to 0 and `SigningPubKey` is an empty string. +{% /admonition %} + +### 4. Prepare Batch transaction + +Create the Batch transaction and provide the inner transactions. The key fields to note are: + +| Field | Value | +|:---------------- |:---------- | +| TransactionType | The type of transaction, in this case `Batch`.| +| Account | The wallet address of the account that is sending the Batch transaction. | +| Flags | The flags for the Batch transaction. For this example the transaction is configured with the `tfAllOrNothing` (0x00010000) flag to ensure that either all inner transactions succeed or they all fail atomically. See [Batch Flags](../../../references/protocol/transactions/types/batch.md#batch-flags) for other options. | +| RawTransactions | Contains the list of inner transactions to be applied. There can be up to **8** transactions included. These transactions can come from one account or multiple accounts. | +| BatchSigners | The list of signatures required for the Batch transaction. This is required because there are multiple accounts' transactions included in the batch. | + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Send Batch transaction" before="// Gather batch signatures" /%} +{% /tab %} +{% /tabs %} + +Because we used `autofill`, the client library automatically fills in any missing fields, like `Fee` and `SigningPubKey`. Additionally, we specify the expected number of signers (2 in this case). + +### 5. Gather batch signatures + +To add the `BatchSigners` field, you need to collect signatures from each account that's sending a transaction within the batch. In this case we need two signatures, one from Charlie and one from Bob. Each sender must sign the Batch transaction to authorize their payment. + +{% tabs %} +{% tab label="Javascript" %} +The **xrpl.js** library provides a helper function, `signMultiBatch()`, to sign the Batch transaction for each account. + +Then, to combine the signatures into a single signed Batch transaction, use the `combineBatchSigners()` utility function. +{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Gather batch signatures" to="// Submit" /%} +{% /tab %} +{% /tabs %} + +### 6. Submit Batch transaction + +With all the required signatures gathered, the third-party wallet can now submit the Batch transaction. + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Submit" before="// Check Batch transaction" /%} +{% /tab %} +{% /tabs %} + +### 6. Check Batch transaction result + +To check the result of the Batch transaction submission: + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Check Batch transaction" before="// Verify balances after transaction" /%} +{% /tab %} +{% /tabs %} + +The code checks for a `tesSUCCESS` result and displays the response details. + +{% admonition type="warning" name="Warning" %} +A `tesSUCCESS` result indicates that the Batch transaction was processed successfully, but does not guarantee the inner transactions succeeded. For example, see the [following transaction on the XRPL Explorer](https://devnet.xrpl.org/transactions/20CFCE5CF75E93E6D1E9C1E42F8E8C8C4CB1786A65BE23D2EA77EAAB65A455C5/simple). +{% /admonition %} + +To verify that the inner transactions have been successful, check the account balances to confirm the expected changes. + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Verify balances after transaction" /%} +{% /tab %} +{% /tabs %} + +Because the Batch transaction is configured with a `tfAllOrNothing` flag, if any inner transaction fails, **all** inner transactions wil fail, and only the Batch transaction fee is deducted from the **third-party wallet**. + +## See Also + +- **Concepts**: + - [Batch Transactions](../../../concepts/transactions/batch-transactions.md) +- **Tutorials**: + - [Send a Single Account Batch Transaction](send-a-single-account-batch-transaction.md) +- **References**: + - [Batch Transaction][] + +{% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md b/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md new file mode 100644 index 00000000000..5689e63d076 --- /dev/null +++ b/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md @@ -0,0 +1,148 @@ +--- +seo: + description: Send a Batch transaction from a single account. +metadata: + indexPage: true +labels: + - Batch + - Transactions +--- +# Send a Single Account Batch Transaction + +A [Batch transaction][] allows you to group multiple transactions together and execute them as a single atomic operation. + +This tutorial shows you how to create a Batch transaction where a single account submits multiple transactions that either all succeed together or all fail together. + +## Goals + +By the end of this tutorial, you will be able to: + +- Create a Batch transaction with multiple inner transactions, signed and submitted by a single account. +- Configure the Batch transaction to ensure atomicity, so that either all inner transactions succeed or they all fail. + +## Prerequisites + +To complete this tutorial, you should: + +- Have a basic understanding of the XRP Ledger. +- Have an XRP Ledger client library set up in your development environment. This page provides examples for the following: + - **JavaScript** with the [xrpl.js library](https://github.com/XRPLF/xrpl.js). See [Get Started Using JavaScript](../../javascript/build-apps/get-started.md) for setup steps. + +## Source Code + +You can find the complete source code for this tutorial's examples in the [code samples section of this website's repository](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/batch/). + +## Steps + +The example in this tutorial demonstrates a scenario where an account sends multiple payments that must be processed atomically in one Batch transaction. + +### 1. Install dependencies + +{% tabs %} +{% tab label="Javascript" %} +From the code sample folder, use npm to install dependencies: + +```bash +npm install xrpl +``` + +{% /tab %} +{% /tabs %} + +### 2. Set up client and accounts + +To get started, import the client library and instantiate a client to connect to the XRPL. For this tutorial you need a funded account for the Batch transaction **sender**, and two other accounts to **receive** the payments. + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="import xrpl" before="// Create inner transactions" /%} +{% /tab %} +{% /tabs %} + +### 3. Prepare inner transactions + +Next, prepare the inner transactions that will be included in the batch. + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Create inner transactions" to="// Send Batch transaction" /%} +{% /tab %} +{% /tabs %} + +The first transaction sends a payment of 2 XRP from the sender to `wallet1`, and the second transaction sends 5 XRP from the sender to `wallet2`. Both transactions must include the `tfInnerBatchTxn` (0x40000000) flag to indicate that they are inner transactions of a batch. + +Inner transactions must have a Fee of **0** and an empty string for the `SigningPubKey`. The outer Batch transaction handles the overall fee and signing for all inner transactions. + +{% admonition type="info" name="Note" %} +The `Fee` and `SigningPubKey` fields are omitted as the client library's _autofill_ functionality automatically populates these when submitting the Batch transaction. + +You typically don't need to set these manually, but if you do, ensure `Fee` is set to 0 and `SigningPubKey` is an empty string. +{% /admonition %} + +### 4. Prepare Batch transaction + +Create the Batch transaction and provide the inner transactions. The key fields to note are: + +| Field | Value | +|:---------------- |:---------- | +| TransactionType | The type of transaction, in this case `Batch`.| +| Account | The wallet address of the account that is sending the Batch transaction. | +| Flags | The flags for the Batch transaction. For this example the transaction is configured with the `tfAllOrNothing` (0x00010000) flag to ensure that either all inner transactions succeed or they all fail atomically. See [Batch Flags](../../../references/protocol/transactions/types/batch.md#batch-flags) for other options. | +| RawTransactions | Contains the list of inner transactions to be applied. There can be up to **8** transactions included. These transactions can come from one account or multiple accounts. | + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Send Batch transaction" before="// Submit" /%} +{% /tab %} +{% /tabs %} + +### 5. Submit Batch transaction + +Now the sender can submit the Batch transaction: + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Submit" before="// Check Batch transaction" /%} +{% /tab %} +{% /tabs %} + +Because `autofill` is set to `true`, the client library automatically fills in any missing fields, like the `Fee` and `SigningPubKey`, before submitting the batch. + +### 6. Check Batch transaction result + +To check the result of the Batch transaction submission: + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Check Batch transaction" before="// Verify balances after transaction" /%} +{% /tab %} +{% /tabs %} + +The code checks for a `tesSUCCESS` result and displays the response details. + +{% admonition type="warning" name="Warning" %} +A `tesSUCCESS` result indicates that the Batch transaction was processed successfully, but does not guarantee the inner transactions succeeded. + +For example, see the [following transaction on the XRPL Explorer](https://devnet.xrpl.org/transactions/20CFCE5CF75E93E6D1E9C1E42F8E8C8C4CB1786A65BE23D2EA77EAAB65A455C5/simple). +{% /admonition %} + +To verify that the inner transactions have been successful, check the account balances to confirm the expected changes. + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Verify balances after transaction" /%} +{% /tab %} +{% /tabs %} + +Because the Batch transaction is configured with a `tfAllOrNothing` flag, if any inner transaction fails, **all** inner transactions wil fail, and only the Batch transaction fee is deducted from the **sender**. + +## See Also + +- **Concepts**: + - [Batch Transactions](../../../concepts/transactions/batch-transactions.md) +- **Tutorials**: + - [Send a Multi-Account Batch Transaction](send-a-multi-account-batch-transaction.md) +- **References**: + - [Batch Transaction][] + +{% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/sidebars.yaml b/sidebars.yaml index ee47bcbc2d4..18fc89bf593 100644 --- a/sidebars.yaml +++ b/sidebars.yaml @@ -295,6 +295,11 @@ - page: docs/tutorials/how-tos/manage-account-settings/require-destination-tags.md - page: docs/tutorials/how-tos/manage-account-settings/offline-account-setup.md - page: docs/tutorials/how-tos/manage-account-settings/use-tickets.md + - page: docs/tutorials/how-tos/use-batch-transactions/index.md + expanded: false + items: + - page: docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md + - page: docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md - page: docs/tutorials/how-tos/use-specialized-payment-types/index.md expanded: false items: From d2cf306ec648aeb89f7f5d89553563cc96bdce6b Mon Sep 17 00:00:00 2001 From: Maria Shodunke Date: Thu, 6 Nov 2025 12:12:33 +0000 Subject: [PATCH 2/5] Mention minimum number of transactions required in a Batch --- _code-samples/batch/js/multiAccountBatch.js | 1 + _code-samples/batch/js/singleAccountBatch.js | 1 + docs/references/protocol/transactions/types/batch.md | 4 ++-- .../send-a-multi-account-batch-transaction.md | 2 +- .../send-a-single-account-batch-transaction.md | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/_code-samples/batch/js/multiAccountBatch.js b/_code-samples/batch/js/multiAccountBatch.js index 575967a4e0b..e35e694dcf2 100644 --- a/_code-samples/batch/js/multiAccountBatch.js +++ b/_code-samples/batch/js/multiAccountBatch.js @@ -53,6 +53,7 @@ const batchTx = { TransactionType: "Batch", Account: thirdPartyWallet.address, Flags: BatchFlags.tfAllOrNothing, // tfAllOrNothing: All inner transactions must succeed + // Must include a minimum of 2 transactions and a maximum of 8 transactions. RawTransactions: [ { RawTransaction: charliePayment }, { RawTransaction: bobPayment }, diff --git a/_code-samples/batch/js/singleAccountBatch.js b/_code-samples/batch/js/singleAccountBatch.js index a7a069d7b14..6ab0b105917 100644 --- a/_code-samples/batch/js/singleAccountBatch.js +++ b/_code-samples/batch/js/singleAccountBatch.js @@ -51,6 +51,7 @@ const batchTx = { TransactionType: "Batch", Account: sender.address, Flags: BatchFlags.tfAllOrNothing, // tfAllOrNothing: All inner transactions must succeed + // Must include a minimum of 2 transactions and a maximum of 8 transactions. RawTransactions: [ { RawTransaction: payment1 }, { RawTransaction: payment2 } diff --git a/docs/references/protocol/transactions/types/batch.md b/docs/references/protocol/transactions/types/batch.md index d7d0df02746..2482a9834f5 100644 --- a/docs/references/protocol/transactions/types/batch.md +++ b/docs/references/protocol/transactions/types/batch.md @@ -121,12 +121,12 @@ In this example, two users are atomically swapping their tokens: XRP for GKO. | Field | JSON Type | [Internal Type][] | Required? | Description | |:------------------|:----------|:------------------|:----------|:------------| | `Flags` | Number | UInt32 | Yes | A bit-flag for this transaction. Exactly one must be specified to represent the batch mode of the transaction. See: [Batch Flags](#batch-flags). | -| `RawTransactions` | Array | Array | Yes | The list of transactions to apply. | +| `RawTransactions` | Array | Array | Yes | The list of transactions to apply. See [RawTransactions](#rawtransactions). | | `BatchSigners` | Array | Array | No | The signatures authorizing a multi-account `Batch` transaction. | ### RawTransactions -`RawTransactions` contains the list of inner transactions to be applied. There can be up to 8 transactions included. These transactions can come from one account or multiple accounts. +`RawTransactions` contains the list of inner transactions to be applied. There must be a minimum of **2** transactions and a maximum of **8** transactions. These transactions can come from one account or multiple accounts. Each inner transaction: diff --git a/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md b/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md index 60ebc74a950..6db5916b98b 100644 --- a/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md +++ b/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md @@ -86,7 +86,7 @@ Create the Batch transaction and provide the inner transactions. The key fields | TransactionType | The type of transaction, in this case `Batch`.| | Account | The wallet address of the account that is sending the Batch transaction. | | Flags | The flags for the Batch transaction. For this example the transaction is configured with the `tfAllOrNothing` (0x00010000) flag to ensure that either all inner transactions succeed or they all fail atomically. See [Batch Flags](../../../references/protocol/transactions/types/batch.md#batch-flags) for other options. | -| RawTransactions | Contains the list of inner transactions to be applied. There can be up to **8** transactions included. These transactions can come from one account or multiple accounts. | +| RawTransactions | Contains the list of inner transactions to be applied. Must include a minimum of **2** transactions and a maximum of **8** transactions. These transactions can come from one account or multiple accounts. | | BatchSigners | The list of signatures required for the Batch transaction. This is required because there are multiple accounts' transactions included in the batch. | {% tabs %} diff --git a/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md b/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md index 5689e63d076..d7e48c20126 100644 --- a/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md +++ b/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md @@ -88,7 +88,7 @@ Create the Batch transaction and provide the inner transactions. The key fields | TransactionType | The type of transaction, in this case `Batch`.| | Account | The wallet address of the account that is sending the Batch transaction. | | Flags | The flags for the Batch transaction. For this example the transaction is configured with the `tfAllOrNothing` (0x00010000) flag to ensure that either all inner transactions succeed or they all fail atomically. See [Batch Flags](../../../references/protocol/transactions/types/batch.md#batch-flags) for other options. | -| RawTransactions | Contains the list of inner transactions to be applied. There can be up to **8** transactions included. These transactions can come from one account or multiple accounts. | +| RawTransactions | Contains the list of inner transactions to be applied. Must include a minimum of **2** transactions and a maximum of **8** transactions. These transactions can come from one account or multiple accounts. | {% tabs %} {% tab label="Javascript" %} From 382a10bda9cf1dcf8d82d114124ba85eae41d32e Mon Sep 17 00:00:00 2001 From: Maria Shodunke Date: Thu, 6 Nov 2025 16:52:31 +0000 Subject: [PATCH 3/5] Add temARRAY_EMPTY error for Batch transaction --- docs/references/protocol/transactions/types/batch.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/references/protocol/transactions/types/batch.md b/docs/references/protocol/transactions/types/batch.md index 2482a9834f5..518bf3e3cdc 100644 --- a/docs/references/protocol/transactions/types/batch.md +++ b/docs/references/protocol/transactions/types/batch.md @@ -169,6 +169,7 @@ A transaction is considered successful if it receives a `tesSUCCESS` result. | Error Code | Description | |:--------------------------|:--------------------------------------------------| +| `temARRAY_EMPTY` | The batch transaction contains zero or one inner transaction. You must submit at least two inner transactions. | | `temINVALID_INNER_BATCH` | An inner transaction is malformed. | | `temSEQ_AND_TICKET` | The transaction contains both a `TicketSequence` field and a non-zero `Sequence` value. A transaction can't include both fields, but must have at least one. | From 7f16532b07a76f9a9d35c080dc8565f9bd9afe60 Mon Sep 17 00:00:00 2001 From: Maria Shodunke Date: Fri, 14 Nov 2025 09:36:03 -0800 Subject: [PATCH 4/5] Calculate inner transaction hash to verify success --- _code-samples/batch/js/README.md | 206 ++++++++++-------- _code-samples/batch/js/multiAccountBatch.js | 55 +++-- _code-samples/batch/js/package.json | 2 +- _code-samples/batch/js/singleAccountBatch.js | 48 +++- .../send-a-multi-account-batch-transaction.md | 24 +- ...send-a-single-account-batch-transaction.md | 22 +- 6 files changed, 226 insertions(+), 131 deletions(-) diff --git a/_code-samples/batch/js/README.md b/_code-samples/batch/js/README.md index 70b0d6d2b12..6a0ffe0e51d 100644 --- a/_code-samples/batch/js/README.md +++ b/_code-samples/batch/js/README.md @@ -16,22 +16,22 @@ node singleAccountBatch.js The script should output the following: ```sh -Funding new wallets from faucet... -Sender: raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e, Balance: 100 XRP -Wallet1: r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC, Balance: 100 XRP -Wallet2: rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn, Balance: 100 XRP +=== Funding new wallets from faucet... === +Sender: rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim, Balance: 100 XRP +Wallet1: rGx6SACvYEvX8SRrvTPD91UhBmJ16pxL94, Balance: 100 XRP +Wallet2: r3qetgSfAtyCpGc4rvKNz4LX3F3urMSJJy, Balance: 100 XRP -Creating batch transaction: +=== Creating Batch transaction... === { "TransactionType": "Batch", - "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim", "Flags": 65536, "RawTransactions": [ { "RawTransaction": { "TransactionType": "Payment", - "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", - "Destination": "r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC", + "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim", + "Destination": "rGx6SACvYEvX8SRrvTPD91UhBmJ16pxL94", "Amount": "2000000", "Flags": 1073741824 } @@ -39,8 +39,8 @@ Creating batch transaction: { "RawTransaction": { "TransactionType": "Payment", - "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", - "Destination": "rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn", + "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim", + "Destination": "r3qetgSfAtyCpGc4rvKNz4LX3F3urMSJJy", "Amount": "5000000", "Flags": 1073741824 } @@ -48,90 +48,100 @@ Creating batch transaction: ] } -Submitting batch transaction... +=== Submitting Batch transaction... === Batch transaction submitted successfully! Result: { - "close_time_iso": "2025-11-03T14:16:21Z", - "ctid": "C00D458B00020002", - "hash": "A93D3C2BDB5D600E592B64B84E66D789237D029267129EBC659EE483E532DD95", - "ledger_hash": "BE6B7C12E551305F09E942D6FA3FC8546F024AE5C53FC495DA6ABF78461E7019", - "ledger_index": 869771, + "close_time_iso": "2025-11-17T12:04:50Z", + "ctid": "C013313800030002", + "hash": "AE118213B0A183528418ABC5F14E3BFD6524020C5DB1C060157A0D3FDE15B900", + "ledger_hash": "621183809B68A794371C5EC6522105FF04E502C48EBDC8171B80224991E33394", + "ledger_index": 1257784, "meta": { "AffectedNodes": [ { "ModifiedNode": { "FinalFields": { - "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim", "Balance": "99999996", "Flags": 0, "OwnerCount": 0, - "Sequence": 869767 + "Sequence": 1257779 }, "LedgerEntryType": "AccountRoot", - "LedgerIndex": "6238B6901FEBD1492C03546C7965A01F184C4E37B696304B86F78F4ADB7831B1", + "LedgerIndex": "42CC98AF0A28EDDDC7E359B5622CC5748BDE2A93E124AF5C32647ECA8F68D480", "PreviousFields": { "Balance": "100000000", - "Sequence": 869766 + "Sequence": 1257778 }, - "PreviousTxnID": "559F102041D84FF9DA17483355C3C96A0F8923D9C9C7971BBB15C972DD1F37D6", - "PreviousTxnLgrSeq": 869766 + "PreviousTxnID": "081C42DAE12001735AC4E9A7F027636DF612DB17B4BFA2333F4DB8EA0C9D1E9F", + "PreviousTxnLgrSeq": 1257778 } } ], - "TransactionIndex": 2, + "TransactionIndex": 3, "TransactionResult": "tesSUCCESS" }, "tx_json": { - "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim", "Fee": "4", "Flags": 65536, - "LastLedgerSequence": 869789, + "LastLedgerSequence": 1257802, "RawTransactions": [ { "RawTransaction": { - "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim", "Amount": "2000000", - "Destination": "r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC", + "Destination": "rGx6SACvYEvX8SRrvTPD91UhBmJ16pxL94", "Fee": "0", "Flags": 1073741824, - "Sequence": 869767, + "Sequence": 1257779, "SigningPubKey": "", "TransactionType": "Payment" } }, { "RawTransaction": { - "Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", + "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim", "Amount": "5000000", - "Destination": "rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn", + "Destination": "r3qetgSfAtyCpGc4rvKNz4LX3F3urMSJJy", "Fee": "0", "Flags": 1073741824, - "Sequence": 869768, + "Sequence": 1257780, "SigningPubKey": "", "TransactionType": "Payment" } } ], - "Sequence": 869766, - "SigningPubKey": "EDFECFB87A29F93E52BBA0BA5A14A59B520BB0E39F33943A2FDC1101D34349270D", + "Sequence": 1257778, + "SigningPubKey": "ED7031CA5BA4EC745610AB495F5053F318C119E87567BE485A494773AD8ED4FBCE", "TransactionType": "Batch", - "TxnSignature": "E08E300BDE1700C7CC27F3DA9B784907F637518E1C7E0978E57BFE5D1511A3B6A4269235FC2D9EAA550182A5F2B59415A442CE59555B9B9A0A79AB4030C9F701", - "ctid": "C00D458B00020002", - "date": 815494581, - "ledger_index": 869771 + "TxnSignature": "0610A277086943BC462C1A5F85BEB667B62B4BDA59525138B6014101C08297897A73D3D2D247CB37A06E1EA36267C53A51C0FDF32F3D8E974029BEDC41105B07", + "ctid": "C013313800030002", + "date": 816696290, + "ledger_index": 1257784 }, "validated": true } -Final balances after batch transaction: -Sender: raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e, Balance: 92.999996 XRP -Wallet1: r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC, Balance: 102 XRP -Wallet2: rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn, Balance: 105 XRP +Batch transaction URL: +https://devnet.xrpl.org/transactions/AE118213B0A183528418ABC5F14E3BFD6524020C5DB1C060157A0D3FDE15B900 -Transaction URL: -https://devnet.xrpl.org/transactions/A93D3C2BDB5D600E592B64B84E66D789237D029267129EBC659EE483E532DD95 +=== Verifying inner transactions... === + +Transaction 1 hash: D18EA54D5653BBB5C87F116978822EAB7A26EDFB1D6C41910F36D7484D4890E3 + - Status: tesSUCCESS (Ledger 1257784) + - Transaction URL: https://devnet.xrpl.org/transactions/D18EA54D5653BBB5C87F116978822EAB7A26EDFB1D6C41910F36D7484D4890E3 + +Transaction 2 hash: 5660DB400F08EE5543C54D4D65824A2142F9D5AC17294A4ABF654260F129B44E + - Status: tesSUCCESS (Ledger 1257784) + - Transaction URL: https://devnet.xrpl.org/transactions/5660DB400F08EE5543C54D4D65824A2142F9D5AC17294A4ABF654260F129B44E + +=== Final balances === +Sender: rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim, Balance: 92.999996 XRP +Wallet1: rGx6SACvYEvX8SRrvTPD91UhBmJ16pxL94, Balance: 102 XRP +Wallet2: r3qetgSfAtyCpGc4rvKNz4LX3F3urMSJJy, Balance: 105 XRP ``` ## Multi-Account Batch Transaction @@ -144,23 +154,23 @@ node multiAccountBatch.js The script should output the following: ```sh -Funding new wallets from faucet... -Alice: rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa, Balance: 100 XRP -Bob: rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7, Balance: 100 XRP -Charlie: rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K, Balance: 100 XRP -Third-party wallet: rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc, Balance: 100 XRP +=== Funding new wallets from faucet... === +Alice: rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG, Balance: 100 XRP +Bob: r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq, Balance: 100 XRP +Charlie: rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA, Balance: 100 XRP +Third-party wallet: rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA, Balance: 100 XRP -Creating batch transaction: +=== Creating Batch transaction... === { "TransactionType": "Batch", - "Account": "rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc", + "Account": "rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA", "Flags": 65536, "RawTransactions": [ { "RawTransaction": { "TransactionType": "Payment", - "Account": "rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K", - "Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", + "Account": "rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA", + "Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG", "Amount": "50000000", "Flags": 1073741824 } @@ -168,8 +178,8 @@ Creating batch transaction: { "RawTransaction": { "TransactionType": "Payment", - "Account": "rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7", - "Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", + "Account": "r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq", + "Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG", "Amount": "50000000", "Flags": 1073741824 } @@ -177,35 +187,35 @@ Creating batch transaction: ] } -Submitting batch transaction... +=== Submitting Batch transaction... === Batch transaction submitted successfully! Result: { - "close_time_iso": "2025-11-03T14:15:00Z", - "ctid": "C00D457000000002", - "hash": "8CBCCD88B8ABC248797B84ABB92066961C1CB5FE75ACE2115ADCA6B74C85993A", - "ledger_hash": "2217A0DBB38B870187B412533B939724095359A050B21E071A2A114BF57CFB60", - "ledger_index": 869744, + "close_time_iso": "2025-11-17T12:08:31Z", + "ctid": "C013317600000002", + "hash": "1299D20C6B489DA5C632AE4DBE49475DBF42D9444C7E9C109CC9B8DD0FD55FEC", + "ledger_hash": "E45ECF69057084CD02BA49A17E4D0C9154D33A98BB3C95A11B2EB9BE18F32C9B", + "ledger_index": 1257846, "meta": { "AffectedNodes": [ { "ModifiedNode": { "FinalFields": { - "Account": "rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc", + "Account": "rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA", "Balance": "99999994", "Flags": 0, "OwnerCount": 0, - "Sequence": 869743 + "Sequence": 1257845 }, "LedgerEntryType": "AccountRoot", - "LedgerIndex": "1E9BA043B9C6518582D0FF73A08DCD8B6958195735086CF7295E5EB6433FB453", + "LedgerIndex": "2D9E0A02007241C38A8DF679E7E62AA0B273E8B12A5430B7B9D99300424F0E1F", "PreviousFields": { "Balance": "100000000", - "Sequence": 869742 + "Sequence": 1257844 }, - "PreviousTxnID": "F7019BC55D80438FDDB01C2549CCC3F7DAF9791F8645E0269D63979EAEC5BBA6", - "PreviousTxnLgrSeq": 869742 + "PreviousTxnID": "3153DE8DE922538A6BE54AA8F783CAD4B848A321AFF028D3E6DD0E80C4B9C237", + "PreviousTxnLgrSeq": 1257844 } } ], @@ -213,69 +223,79 @@ Result: "TransactionResult": "tesSUCCESS" }, "tx_json": { - "Account": "rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc", + "Account": "rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA", "BatchSigners": [ { "BatchSigner": { - "Account": "rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K", - "SigningPubKey": "EDC566D7DA8186BBD30DDAE1FB770FCE7F248949194E1A2E70B18CFA060B140B59", - "TxnSignature": "31639BFA1359DD24345776EAEEACCF61C1CDC792988679263D113E80A22D837E20ACD2B25E482FCA769990C004D747836370C6BAD14524559639BBEBA5813002" + "Account": "rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA", + "SigningPubKey": "EDEB88C2868BD25BF03DB26050E16579FA6F8F9E3FF3172E0DC3DCBDA5408572EB", + "TxnSignature": "9508568084596147CFDCFC18A62DC298A78AD1148BA4B0EB99BEE1CD37E5555FE3930810790D5708F9739B0E3F79772012C154CA33C2280BDD5B72473C17A607" } }, { "BatchSigner": { - "Account": "rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7", - "SigningPubKey": "EDEF1966B325000407940E4C0792E3CCC3E27F51D132BDC53DCC2B1998E7C32A34", - "TxnSignature": "6BF9860B0E2E134FB302329D711BAA7B6314395D39523982DBBC037E84FB17AB5E8E736DB3DB0019B4477686AF2D91E5D2B49409698A95219376B2E318D3E501" + "Account": "r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq", + "SigningPubKey": "ED82F98DA6A3FC3E88D2EE3A5469D92C7070513BEF4DEE75CAB0BDAA81E8AE378D", + "TxnSignature": "A482C8747F79857530474F1677599766C0BE283CB7E2A05AACF76E61BECCA16DCE3802D2D8244FBF4546A1C0E5EB70691255E3EFD2F8AC80B55357BDAB9ACD05" } } ], "Fee": "6", "Flags": 65536, - "LastLedgerSequence": 869762, + "LastLedgerSequence": 1257864, "RawTransactions": [ { "RawTransaction": { - "Account": "rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K", + "Account": "rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA", "Amount": "50000000", - "Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", + "Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG", "Fee": "0", "Flags": 1073741824, - "Sequence": 869740, + "Sequence": 1257842, "SigningPubKey": "", "TransactionType": "Payment" } }, { "RawTransaction": { - "Account": "rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7", + "Account": "r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq", "Amount": "50000000", - "Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", + "Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG", "Fee": "0", "Flags": 1073741824, - "Sequence": 869738, + "Sequence": 1257841, "SigningPubKey": "", "TransactionType": "Payment" } } ], - "Sequence": 869742, - "SigningPubKey": "ED2B56D6FB4E8C236A6B07E8D8AD9A4938606144E31779918F99525CA6B3C56664", + "Sequence": 1257844, + "SigningPubKey": "ED22A32B61EDF083315515831723BC18F8311F03886BBA375DFF46335BB7A75F0B", "TransactionType": "Batch", - "TxnSignature": "9C51C1F2CB0E8BCEA1FADD3992249DE72AC46FC86AB2FB023A597FBD5C4CCB3337967E9AAFFB5F1C0CBC91128F3FD194F78F207E461BE1FF906C496B94EC410E", - "ctid": "C00D457000000002", - "date": 815494500, - "ledger_index": 869744 + "TxnSignature": "156791D2DBFAEFC9B0AC29F2D8D0CDB25E13F92E70E6D5414FE31BD8573CA23D3F62F8B34FC1F117BD556B25E4F748095A24C4342108AB32F1B2BAFBF1443501", + "ctid": "C013317600000002", + "date": 816696511, + "ledger_index": 1257846 }, "validated": true } -Final balances after batch transaction: -Alice: rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa, Balance: 200 XRP -Bob: rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7, Balance: 50 XRP -Charlie: rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K, Balance: 50 XRP -Third-party wallet: rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc, Balance: 99.999994 XRP +Batch transaction URL: +https://devnet.xrpl.org/transactions/1299D20C6B489DA5C632AE4DBE49475DBF42D9444C7E9C109CC9B8DD0FD55FEC + +=== Verifying inner transactions === + +Transaction 1 hash: 0F71979E3F641C980929F926640DCA886C30236ED0CD7C94B6CB36F0D42948AC + - Status: tesSUCCESS (Ledger 1257846) + - Transaction URL: https://devnet.xrpl.org/transactions/0F71979E3F641C980929F926640DCA886C30236ED0CD7C94B6CB36F0D42948AC + +Transaction 2 hash: BC124CB29334AA1079139A9BE186B69A0AC467797F147754E2406714854D2A50 + - Status: tesSUCCESS (Ledger 1257846) + - Transaction URL: https://devnet.xrpl.org/transactions/BC124CB29334AA1079139A9BE186B69A0AC467797F147754E2406714854D2A50 -Transaction URL: -https://devnet.xrpl.org/transactions/8CBCCD88B8ABC248797B84ABB92066961C1CB5FE75ACE2115ADCA6B74C85993A +=== Final balances === +Alice: rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG, Balance: 200 XRP +Bob: r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq, Balance: 50 XRP +Charlie: rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA, Balance: 50 XRP +Third-party wallet: rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA, Balance: 99.999994 XRP ``` diff --git a/_code-samples/batch/js/multiAccountBatch.js b/_code-samples/batch/js/multiAccountBatch.js index e35e694dcf2..f2aee48a082 100644 --- a/_code-samples/batch/js/multiAccountBatch.js +++ b/_code-samples/batch/js/multiAccountBatch.js @@ -3,18 +3,17 @@ * * This tutorial demonstrates how to use the Batch transaction feature (XLS-56) * to perform a multi-account batch transaction. + * Concept doc: https://xrpl.org/docs/concepts/transactions/batch-transactions + * Reference doc: https://xrpl.org/docs/references/protocol/transactions/types/batch */ import xrpl from "xrpl" -import { BatchFlags } from 'xrpl/dist/npm/models/transactions/batch.js' -import { GlobalFlags } from 'xrpl/dist/npm/models/transactions/common.js' -import { signMultiBatch, combineBatchSigners } from 'xrpl/dist/npm/Wallet/batchSigner.js' const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233/") await client.connect() // Create and fund wallets -console.log("Funding new wallets from faucet...") +console.log("=== Funding new wallets from faucet... ===") const { wallet: alice } = await client.fundWallet() const { wallet: bob } = await client.fundWallet() const { wallet: charlie } = await client.fundWallet() @@ -35,7 +34,7 @@ const charliePayment = { Account: charlie.address, Destination: alice.address, Amount: xrpl.xrpToDrops(50), - Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED + Flags: xrpl.GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED } // Transaction 2: Bob pays Alice @@ -44,15 +43,15 @@ const bobPayment = { Account: bob.address, Destination: alice.address, Amount: xrpl.xrpToDrops(50), - Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED + Flags: xrpl.GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED } // Send Batch transaction -------------------------------------------- -console.log("\nCreating batch transaction:") +console.log("\n=== Creating Batch transaction... ===") const batchTx = { TransactionType: "Batch", Account: thirdPartyWallet.address, - Flags: BatchFlags.tfAllOrNothing, // tfAllOrNothing: All inner transactions must succeed + Flags: xrpl.BatchFlags.tfAllOrNothing, // tfAllOrNothing: All inner transactions must succeed // Must include a minimum of 2 transactions and a maximum of 8 transactions. RawTransactions: [ { RawTransaction: charliePayment }, @@ -72,18 +71,18 @@ const autofilledBatchTx = await client.autofill(batchTx, 2) // Each signer needs their own tx copy because signMultiBatch modifies the object. // Charlie signs the Batch transaction const charlieBatch = { ...autofilledBatchTx } -signMultiBatch(charlie, charlieBatch) +xrpl.signMultiBatch(charlie, charlieBatch) // Bob signs the Batch transaction const bobBatch = { ...autofilledBatchTx } -signMultiBatch(bob, bobBatch) +xrpl.signMultiBatch(bob, bobBatch) // Combine inner transaction signatures. // This returns a signed transaction blob (hex string) ready for submission. -const combinedSignedTx = combineBatchSigners([charlieBatch, bobBatch]) +const combinedSignedTx = xrpl.combineBatchSigners([charlieBatch, bobBatch]) // Submit the signed blob with the third-party's wallet -console.log("\nSubmitting batch transaction...") +console.log("\n=== Submitting Batch transaction... ===") const submitResponse = await client.submitAndWait(combinedSignedTx, { wallet: thirdPartyWallet } ) @@ -97,15 +96,39 @@ if (submitResponse.result.meta.TransactionResult !== "tesSUCCESS") { console.log("\nBatch transaction submitted successfully!") console.log("Result:\n", JSON.stringify(submitResponse.result, null, 2)) +// View the transaction on the XRPL Explorer +console.log(`\nBatch transaction URL:\nhttps://devnet.xrpl.org/transactions/${submitResponse.result.hash}`) + +// Calculate and verify inner transaction hashes -------------------------------------------- +console.log("\n=== Verifying inner transactions ===") +const rawTransactions = submitResponse.result.tx_json.RawTransactions +let hasFailure = false + +for (let i = 0; i < rawTransactions.length; i++) { + const innerTx = rawTransactions[i].RawTransaction + const hash = xrpl.hashes.hashSignedTx(innerTx) + console.log(`\nTransaction ${i + 1} hash: ${hash}`) + + try { + const tx = await client.request({ command: 'tx', transaction: hash }) + const status = tx.result.meta?.TransactionResult + console.log(` - Status: ${status} (Ledger ${tx.result.ledger_index})`) + console.log(` - Transaction URL: https://devnet.xrpl.org/transactions/${hash}`) + } catch (error) { + hasFailure = true + console.log(` - Transaction not found: ${error}`) + } +} +if (hasFailure) { + console.error("\n--- Error: One or more inner transactions failed. ---") + process.exit(1) +} // Verify balances after transaction -console.log("\nFinal balances after batch transaction:") +console.log("\n=== Final balances ===") console.log(`Alice: ${alice.address}, Balance: ${await client.getXrpBalance(alice.address)} XRP`) console.log(`Bob: ${bob.address}, Balance: ${await client.getXrpBalance(bob.address)} XRP`) console.log(`Charlie: ${charlie.address}, Balance: ${await client.getXrpBalance(charlie.address)} XRP`) console.log(`Third-party wallet: ${thirdPartyWallet.address}, Balance: ${await client.getXrpBalance(thirdPartyWallet.address)} XRP`) -// View the transaction on the XRPL Explorer -console.log(`\nTransaction URL:\nhttps://devnet.xrpl.org/transactions/${submitResponse.result.hash}`) - await client.disconnect() diff --git a/_code-samples/batch/js/package.json b/_code-samples/batch/js/package.json index 139f3bae724..fab7850610c 100644 --- a/_code-samples/batch/js/package.json +++ b/_code-samples/batch/js/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "xrpl": "^4.4.2" + "xrpl": "^4.4.3" }, "type": "module" } diff --git a/_code-samples/batch/js/singleAccountBatch.js b/_code-samples/batch/js/singleAccountBatch.js index 6ab0b105917..2d52cbd8c09 100644 --- a/_code-samples/batch/js/singleAccountBatch.js +++ b/_code-samples/batch/js/singleAccountBatch.js @@ -4,17 +4,17 @@ * This example demonstrates how to use the Batch transactions feature (XLS-56) * to create a single-account batch transaction that sends payments * to multiple destinations in one atomic operation. + * Concept doc: https://xrpl.org/docs/concepts/transactions/batch-transactions + * Reference doc: https://xrpl.org/docs/references/protocol/transactions/types/batch */ import xrpl from "xrpl" -import { BatchFlags } from 'xrpl/dist/npm/models/transactions/batch.js' -import { GlobalFlags } from 'xrpl/dist/npm/models/transactions/common.js' const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233/") await client.connect() // Create and fund wallets -console.log("Funding new wallets from faucet...") +console.log("=== Funding new wallets from faucet... ===") const { wallet: sender } = await client.fundWallet() const { wallet: wallet1 } = await client.fundWallet() const { wallet: wallet2 } = await client.fundWallet() @@ -33,7 +33,7 @@ const payment1 = { Account: sender.address, Destination: wallet1.address, Amount: xrpl.xrpToDrops(2), - Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED + Flags: xrpl.GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED } // Transaction 2 @@ -42,15 +42,15 @@ const payment2 = { Account: sender.address, Destination: wallet2.address, Amount: xrpl.xrpToDrops(5), - Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED + Flags: xrpl.GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED } // Send Batch transaction -------------------------------------------- -console.log("\nCreating batch transaction:") +console.log("\n=== Creating Batch transaction... ===") const batchTx = { TransactionType: "Batch", Account: sender.address, - Flags: BatchFlags.tfAllOrNothing, // tfAllOrNothing: All inner transactions must succeed + Flags: xrpl.BatchFlags.tfAllOrNothing, // tfAllOrNothing: All inner transactions must succeed // Must include a minimum of 2 transactions and a maximum of 8 transactions. RawTransactions: [ { RawTransaction: payment1 }, @@ -63,7 +63,7 @@ console.log(JSON.stringify(batchTx, null, 2)) xrpl.validate(batchTx) // Submit and wait for validation -console.log("\nSubmitting batch transaction...") +console.log("\n=== Submitting Batch transaction... ===") const submitResponse = await client.submitAndWait(batchTx, { wallet: sender, // "autofill" will automatically add Fee: "0" and SigningPubKey: "". @@ -78,14 +78,38 @@ if (submitResponse.result.meta.TransactionResult !== "tesSUCCESS") { } console.log("\nBatch transaction submitted successfully!") console.log("Result:\n", JSON.stringify(submitResponse.result, null, 2)) +// View the batch transaction on the XRPL Explorer +console.log(`\nBatch transaction URL:\nhttps://devnet.xrpl.org/transactions/${submitResponse.result.hash}`) + +// Calculate and verify inner transaction hashes -------------------------------------------- +console.log("\n=== Verifying inner transactions... ===") +const rawTransactions = submitResponse.result.tx_json.RawTransactions +let hasFailure = false + +for (let i = 0; i < rawTransactions.length; i++) { + const innerTx = rawTransactions[i].RawTransaction + const hash = xrpl.hashes.hashSignedTx(innerTx) + console.log(`\nTransaction ${i + 1} hash: ${hash}`) + + try { + const tx = await client.request({ command: 'tx', transaction: hash }) + const status = tx.result.meta?.TransactionResult + console.log(` - Status: ${status} (Ledger ${tx.result.ledger_index})`) + console.log(` - Transaction URL: https://devnet.xrpl.org/transactions/${hash}`) + } catch (error) { + hasFailure = true + console.log(` - Transaction not found: ${error}`) + } +} +if (hasFailure) { + console.error("\n--- Error: One or more inner transactions failed. ---") + process.exit(1) +} // Verify balances after transaction -console.log("\nFinal balances after batch transaction:") +console.log("\n=== Final balances ===") console.log(`Sender: ${sender.address}, Balance: ${await client.getXrpBalance(sender.address)} XRP`) console.log(`Wallet1: ${wallet1.address}, Balance: ${await client.getXrpBalance(wallet1.address)} XRP`) console.log(`Wallet2: ${wallet2.address}, Balance: ${await client.getXrpBalance(wallet2.address)} XRP`) -// View the transaction on the XRPL Explorer -console.log(`\nTransaction URL:\nhttps://devnet.xrpl.org/transactions/${submitResponse.result.hash}`) - await client.disconnect() diff --git a/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md b/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md index 6db5916b98b..aa282a65c98 100644 --- a/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md +++ b/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md @@ -120,13 +120,13 @@ With all the required signatures gathered, the third-party wallet can now submit {% /tab %} {% /tabs %} -### 6. Check Batch transaction result +### 7. Check Batch transaction result To check the result of the Batch transaction submission: {% tabs %} {% tab label="Javascript" %} -{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Check Batch transaction" before="// Verify balances after transaction" /%} +{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Check Batch transaction" before="// Calculate and verify" /%} {% /tab %} {% /tabs %} @@ -136,15 +136,29 @@ The code checks for a `tesSUCCESS` result and displays the response details. A `tesSUCCESS` result indicates that the Batch transaction was processed successfully, but does not guarantee the inner transactions succeeded. For example, see the [following transaction on the XRPL Explorer](https://devnet.xrpl.org/transactions/20CFCE5CF75E93E6D1E9C1E42F8E8C8C4CB1786A65BE23D2EA77EAAB65A455C5/simple). {% /admonition %} -To verify that the inner transactions have been successful, check the account balances to confirm the expected changes. +Because the Batch transaction is configured with a `tfAllOrNothing` flag, if any inner transaction fails, **all** inner transactions wil fail, and only the Batch transaction fee is deducted from the **third-party wallet**. + +### 8. Verify inner transactions + +Since there is no way to check the status of inner transactions in the Batch transaction result, you need to calculate the inner transaction hashes and look them up on the ledger: {% tabs %} {% tab label="Javascript" %} -{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Verify balances after transaction" /%} +{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Calculate and verify" before="// Verify balances after transaction" /%} {% /tab %} {% /tabs %} -Because the Batch transaction is configured with a `tfAllOrNothing` flag, if any inner transaction fails, **all** inner transactions wil fail, and only the Batch transaction fee is deducted from the **third-party wallet**. +The code extracts the actual inner transactions from the batch response, calculates the hash of each inner transaction and looks up each transaction on the ledger using its hash. + +### 9. Verify balances + +You can also verify that the inner transactions executed successfully by checking the account balances to confirm the expected changes. + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Verify balances after transaction" /%} +{% /tab %} +{% /tabs %} ## See Also diff --git a/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md b/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md index d7e48c20126..24a938c7fed 100644 --- a/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md +++ b/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md @@ -114,7 +114,7 @@ To check the result of the Batch transaction submission: {% tabs %} {% tab label="Javascript" %} -{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Check Batch transaction" before="// Verify balances after transaction" /%} +{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Check Batch transaction" before="// Calculate and verify" /%} {% /tab %} {% /tabs %} @@ -126,15 +126,29 @@ A `tesSUCCESS` result indicates that the Batch transaction was processed success For example, see the [following transaction on the XRPL Explorer](https://devnet.xrpl.org/transactions/20CFCE5CF75E93E6D1E9C1E42F8E8C8C4CB1786A65BE23D2EA77EAAB65A455C5/simple). {% /admonition %} -To verify that the inner transactions have been successful, check the account balances to confirm the expected changes. +Because the Batch transaction is configured with a `tfAllOrNothing` flag, if any inner transaction fails, **all** inner transactions wil fail, and only the Batch transaction fee is deducted from the **third-party wallet**. + +### 7. Verify inner transactions + +Since there is no way to check the status of inner transactions in the Batch transaction result, you need to calculate the inner transaction hashes and look them up on the ledger: {% tabs %} {% tab label="Javascript" %} -{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Verify balances after transaction" /%} +{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Calculate and verify" before="// Verify balances after transaction" /%} {% /tab %} {% /tabs %} -Because the Batch transaction is configured with a `tfAllOrNothing` flag, if any inner transaction fails, **all** inner transactions wil fail, and only the Batch transaction fee is deducted from the **sender**. +The code extracts the actual inner transactions from the batch response, calculates the hash of each inner transaction and looks up each transaction on the ledger using its hash. + +### 8. Verify balances + +You can also verify that the inner transactions executed successfully by checking the account balances to confirm the expected changes. + +{% tabs %} +{% tab label="Javascript" %} +{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Verify balances after transaction" /%} +{% /tab %} +{% /tabs %} ## See Also From fb33561a98112c7db1cc5d270d4780b80716bba0 Mon Sep 17 00:00:00 2001 From: Maria Shodunke Date: Wed, 19 Nov 2025 10:20:16 +0000 Subject: [PATCH 5/5] Address review comments --- _code-samples/batch/README.md | 2 +- _code-samples/batch/js/README.md | 2 +- _code-samples/batch/js/multiAccountBatch.js | 25 ++++++++----- _code-samples/batch/js/singleAccountBatch.js | 17 +++++---- .../send-a-multi-account-batch-transaction.md | 36 +++++++++---------- ...send-a-single-account-batch-transaction.md | 30 ++++++++-------- 6 files changed, 63 insertions(+), 49 deletions(-) diff --git a/_code-samples/batch/README.md b/_code-samples/batch/README.md index 87edb5a79a0..7900423e5fa 100644 --- a/_code-samples/batch/README.md +++ b/_code-samples/batch/README.md @@ -2,4 +2,4 @@ Code samples showing how to create and submit a [Batch transaction](https://xrpl.org/docs/concepts/transactions/batch-transactions). -Both for single and multi account batch transactions. +Both for single and multi-account batch transactions. diff --git a/_code-samples/batch/js/README.md b/_code-samples/batch/js/README.md index 6a0ffe0e51d..b7a25fc6e99 100644 --- a/_code-samples/batch/js/README.md +++ b/_code-samples/batch/js/README.md @@ -2,7 +2,7 @@ Code samples showing how to create and submit a [Batch transaction](https://xrpl.org/docs/concepts/transactions/batch-transactions) with Javascript. -Both for single and multi account batch transactions. +Both for single and multi-account batch transactions. ## Single Account Batch Transaction diff --git a/_code-samples/batch/js/multiAccountBatch.js b/_code-samples/batch/js/multiAccountBatch.js index f2aee48a082..25a8307f091 100644 --- a/_code-samples/batch/js/multiAccountBatch.js +++ b/_code-samples/batch/js/multiAccountBatch.js @@ -13,11 +13,18 @@ const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233/") await client.connect() // Create and fund wallets -console.log("=== Funding new wallets from faucet... ===") -const { wallet: alice } = await client.fundWallet() -const { wallet: bob } = await client.fundWallet() -const { wallet: charlie } = await client.fundWallet() -const { wallet: thirdPartyWallet } = await client.fundWallet() +console.log("=== Funding new wallets from faucet... ==="); +const [ + { wallet: alice }, + { wallet: bob }, + { wallet: charlie }, + { wallet: thirdPartyWallet }, +] = await Promise.all([ + client.fundWallet(), + client.fundWallet(), + client.fundWallet(), + client.fundWallet(), +]); console.log(`Alice: ${alice.address}, Balance: ${await client.getXrpBalance(alice.address)} XRP`) console.log(`Bob: ${bob.address}, Balance: ${await client.getXrpBalance(bob.address)} XRP`) @@ -26,7 +33,7 @@ console.log(`Third-party wallet: ${thirdPartyWallet.address}, Balance: ${await c // Create inner transactions -------------------------------------------- // REQUIRED: Inner transactions MUST have the tfInnerBatchTxn flag (0x40000000). -// This marks them as part of a batch (allows Fee: 0 and empty SigningPubKey). +// This marks them as part of a batch (requires Fee: 0 and empty SigningPubKey). // Transaction 1: Charlie pays Alice const charliePayment = { @@ -63,8 +70,8 @@ console.log(JSON.stringify(batchTx, null, 2)) // Validate the transaction structure xrpl.validate(batchTx) -// Set the expected number of signers for this transaction. -// "autofill" will automatically add Fee: "0" and SigningPubKey: "". +// Set the expected number of signers, which is 2 (Bob and Charlie) in this case, for this transaction. +// "autofill" will automatically add Fee: "0" and SigningPubKey: "" to inner transactions. const autofilledBatchTx = await client.autofill(batchTx, 2) // Gather batch signatures -------------------------------- @@ -91,6 +98,7 @@ const submitResponse = await client.submitAndWait(combinedSignedTx, if (submitResponse.result.meta.TransactionResult !== "tesSUCCESS") { const resultCode = submitResponse.result.meta.TransactionResult console.warn(`\nTransaction failed with result code ${resultCode}`) + await client.disconnect() process.exit(1) } @@ -121,6 +129,7 @@ for (let i = 0; i < rawTransactions.length; i++) { } if (hasFailure) { console.error("\n--- Error: One or more inner transactions failed. ---") + await client.disconnect() process.exit(1) } diff --git a/_code-samples/batch/js/singleAccountBatch.js b/_code-samples/batch/js/singleAccountBatch.js index 2d52cbd8c09..6e8c9a7ebdc 100644 --- a/_code-samples/batch/js/singleAccountBatch.js +++ b/_code-samples/batch/js/singleAccountBatch.js @@ -14,10 +14,13 @@ const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233/") await client.connect() // Create and fund wallets -console.log("=== Funding new wallets from faucet... ===") -const { wallet: sender } = await client.fundWallet() -const { wallet: wallet1 } = await client.fundWallet() -const { wallet: wallet2 } = await client.fundWallet() +console.log("=== Funding new wallets from faucet... ==="); +const [{ wallet: sender }, { wallet: wallet1 }, { wallet: wallet2 }] = + await Promise.all([ + client.fundWallet(), + client.fundWallet(), + client.fundWallet(), + ]); console.log(`Sender: ${sender.address}, Balance: ${await client.getXrpBalance(sender.address)} XRP`) console.log(`Wallet1: ${wallet1.address}, Balance: ${await client.getXrpBalance(wallet1.address)} XRP`) @@ -25,7 +28,7 @@ console.log(`Wallet2: ${wallet2.address}, Balance: ${await client.getXrpBalance( // Create inner transactions -------------------------------------------- // REQUIRED: Inner transactions MUST have the tfInnerBatchTxn flag (0x40000000). -// This marks them as part of a batch (allows Fee: 0 and empty SigningPubKey). +// This marks them as part of a batch (requires Fee: 0 and empty SigningPubKey). // Transaction 1 const payment1 = { @@ -66,7 +69,7 @@ xrpl.validate(batchTx) console.log("\n=== Submitting Batch transaction... ===") const submitResponse = await client.submitAndWait(batchTx, { wallet: sender, - // "autofill" will automatically add Fee: "0" and SigningPubKey: "". + // "autofill" will automatically add Fee: "0" and SigningPubKey: "" to inner transactions. autofill: true }) @@ -74,6 +77,7 @@ const submitResponse = await client.submitAndWait(batchTx, { if (submitResponse.result.meta.TransactionResult !== "tesSUCCESS") { const resultCode = submitResponse.result.meta.TransactionResult console.warn(`\nTransaction failed with result code ${resultCode}`) + await client.disconnect() process.exit(1) } console.log("\nBatch transaction submitted successfully!") @@ -103,6 +107,7 @@ for (let i = 0; i < rawTransactions.length; i++) { } if (hasFailure) { console.error("\n--- Error: One or more inner transactions failed. ---") + await client.disconnect() process.exit(1) } diff --git a/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md b/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md index aa282a65c98..e0f1513e592 100644 --- a/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md +++ b/docs/tutorials/how-tos/use-batch-transactions/send-a-multi-account-batch-transaction.md @@ -9,14 +9,14 @@ labels: --- # Send a Multi-Account Batch Transaction -This tutorial shows you how to create a [Batch transaction][] containing transactions from multiple accounts, where each account must sign the Batch transaction. Any account, even one not involved in the inner transactions, can submit the batch. +This tutorial shows you how to create a [Batch transaction][] containing transactions from multiple accounts, where each account must sign the `Batch` transaction. Any account, even one not involved in the inner transactions, can submit the batch. ## Goals By the end of this tutorial, you will be able to: -- Create a Batch transaction with multiple inner transactions, signed by multiple accounts, and submitted by a third party account. -- Configure the Batch transaction to ensure atomicity, so that either all inner transactions succeed or they all fail. +- Create a `Batch` transaction with multiple inner transactions, signed by multiple accounts, and submitted by a third party account. +- Configure the `Batch` transaction to ensure atomicity, so that either all inner transactions succeed or they all fail. ## Prerequisites @@ -32,7 +32,7 @@ You can find the complete source code for this tutorial's examples in the [code ## Steps -The example in this tutorial demonstrates a scenario where Bob and Charlie both owe Alice 50 XRP each, and a third-party (such as a payment processor) submits the Batch transaction atomically to ensure Alice is paid by both parties. +The example in this tutorial demonstrates a scenario where Bob and Charlie both owe Alice 50 XRP each, and a third-party (such as a payment processor) submits the `Batch` transaction atomically to ensure Alice is paid by both parties. ### 1. Install dependencies @@ -69,25 +69,25 @@ Next, prepare the inner transactions that will be included in the batch. The first transaction sends a payment of 50 XRP from Charlie to Alice, and the second sends a payment of 50 XRP from Bob to Alice. Both transactions must include the `tfInnerBatchTxn` (0x40000000) flag to indicate that they are inner transactions of a batch. -Inner transactions must have a Fee of **0** and an empty string for the `SigningPubKey`. The outer Batch transaction handles the overall fee and signing for all inner transactions. +Inner transactions must have a Fee of **0** and an empty string for the `SigningPubKey`. The outer `Batch` transaction handles the overall fee and signing for all inner transactions. {% admonition type="info" name="Note" %} -The `Fee` and `SigningPubKey` fields are omitted as the client library's _autofill_ functionality automatically populates these when submitting the Batch transaction. +The `Fee` and `SigningPubKey` fields are omitted as the client library's _autofill_ functionality automatically populates these when submitting the `Batch` transaction. You typically don't need to set these manually, but if you do, ensure `Fee` is set to 0 and `SigningPubKey` is an empty string. {% /admonition %} ### 4. Prepare Batch transaction -Create the Batch transaction and provide the inner transactions. The key fields to note are: +Create the `Batch` transaction and provide the inner transactions. The key fields to note are: | Field | Value | |:---------------- |:---------- | | TransactionType | The type of transaction, in this case `Batch`.| -| Account | The wallet address of the account that is sending the Batch transaction. | -| Flags | The flags for the Batch transaction. For this example the transaction is configured with the `tfAllOrNothing` (0x00010000) flag to ensure that either all inner transactions succeed or they all fail atomically. See [Batch Flags](../../../references/protocol/transactions/types/batch.md#batch-flags) for other options. | +| Account | The wallet address of the account that is sending the `Batch` transaction. | +| Flags | The flags for the `Batch` transaction. For this example the transaction is configured with the `tfAllOrNothing` (0x00010000) flag to ensure that either all inner transactions succeed or they all fail atomically. See [Batch Flags](../../../references/protocol/transactions/types/batch.md#batch-flags) for other options. | | RawTransactions | Contains the list of inner transactions to be applied. Must include a minimum of **2** transactions and a maximum of **8** transactions. These transactions can come from one account or multiple accounts. | -| BatchSigners | The list of signatures required for the Batch transaction. This is required because there are multiple accounts' transactions included in the batch. | +| BatchSigners | The list of signatures required for the `Batch` transaction. This is required because there are multiple accounts' transactions included in the batch. | {% tabs %} {% tab label="Javascript" %} @@ -99,20 +99,20 @@ Because we used `autofill`, the client library automatically fills in any missin ### 5. Gather batch signatures -To add the `BatchSigners` field, you need to collect signatures from each account that's sending a transaction within the batch. In this case we need two signatures, one from Charlie and one from Bob. Each sender must sign the Batch transaction to authorize their payment. +To add the `BatchSigners` field, you need to collect signatures from each account that's sending a transaction within the batch. In this case we need two signatures, one from Charlie and one from Bob. Each sender must sign the `Batch` transaction to authorize their payment. {% tabs %} {% tab label="Javascript" %} -The **xrpl.js** library provides a helper function, `signMultiBatch()`, to sign the Batch transaction for each account. +The **xrpl.js** library provides a helper function, `signMultiBatch()`, to sign the `Batch` transaction for each account. -Then, to combine the signatures into a single signed Batch transaction, use the `combineBatchSigners()` utility function. +Then, to combine the signatures into a single signed `Batch` transaction, use the `combineBatchSigners()` utility function. {% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Gather batch signatures" to="// Submit" /%} {% /tab %} {% /tabs %} ### 6. Submit Batch transaction -With all the required signatures gathered, the third-party wallet can now submit the Batch transaction. +With all the required signatures gathered, the third-party wallet can now submit the `Batch` transaction. {% tabs %} {% tab label="Javascript" %} @@ -122,7 +122,7 @@ With all the required signatures gathered, the third-party wallet can now submit ### 7. Check Batch transaction result -To check the result of the Batch transaction submission: +To check the result of the `Batch` transaction submission: {% tabs %} {% tab label="Javascript" %} @@ -133,14 +133,14 @@ To check the result of the Batch transaction submission: The code checks for a `tesSUCCESS` result and displays the response details. {% admonition type="warning" name="Warning" %} -A `tesSUCCESS` result indicates that the Batch transaction was processed successfully, but does not guarantee the inner transactions succeeded. For example, see the [following transaction on the XRPL Explorer](https://devnet.xrpl.org/transactions/20CFCE5CF75E93E6D1E9C1E42F8E8C8C4CB1786A65BE23D2EA77EAAB65A455C5/simple). +A `tesSUCCESS` result indicates that the `Batch` transaction was processed successfully, but does not guarantee the inner transactions succeeded. For example, see the [following transaction on the XRPL Explorer](https://devnet.xrpl.org/transactions/20CFCE5CF75E93E6D1E9C1E42F8E8C8C4CB1786A65BE23D2EA77EAAB65A455C5/simple). {% /admonition %} -Because the Batch transaction is configured with a `tfAllOrNothing` flag, if any inner transaction fails, **all** inner transactions wil fail, and only the Batch transaction fee is deducted from the **third-party wallet**. +Because the `Batch` transaction is configured with a `tfAllOrNothing` flag, if any inner transaction fails, **all** inner transactions wil fail, and only the `Batch` transaction fee is deducted from the **third-party wallet**. ### 8. Verify inner transactions -Since there is no way to check the status of inner transactions in the Batch transaction result, you need to calculate the inner transaction hashes and look them up on the ledger: +Since there is no way to check the status of inner transactions in the `Batch` transaction result, you need to calculate the inner transaction hashes and look them up on the ledger: {% tabs %} {% tab label="Javascript" %} diff --git a/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md b/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md index 24a938c7fed..69e08208575 100644 --- a/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md +++ b/docs/tutorials/how-tos/use-batch-transactions/send-a-single-account-batch-transaction.md @@ -11,14 +11,14 @@ labels: A [Batch transaction][] allows you to group multiple transactions together and execute them as a single atomic operation. -This tutorial shows you how to create a Batch transaction where a single account submits multiple transactions that either all succeed together or all fail together. +This tutorial shows you how to create a `Batch` transaction where a single account submits multiple transactions that either all succeed together or all fail together. ## Goals By the end of this tutorial, you will be able to: -- Create a Batch transaction with multiple inner transactions, signed and submitted by a single account. -- Configure the Batch transaction to ensure atomicity, so that either all inner transactions succeed or they all fail. +- Create a `Batch` transaction with multiple inner transactions, signed and submitted by a single account. +- Configure the `Batch` transaction to ensure atomicity, so that either all inner transactions succeed or they all fail. ## Prerequisites @@ -34,7 +34,7 @@ You can find the complete source code for this tutorial's examples in the [code ## Steps -The example in this tutorial demonstrates a scenario where an account sends multiple payments that must be processed atomically in one Batch transaction. +The example in this tutorial demonstrates a scenario where an account sends multiple payments that must be processed atomically in one `Batch` transaction. ### 1. Install dependencies @@ -51,7 +51,7 @@ npm install xrpl ### 2. Set up client and accounts -To get started, import the client library and instantiate a client to connect to the XRPL. For this tutorial you need a funded account for the Batch transaction **sender**, and two other accounts to **receive** the payments. +To get started, import the client library and instantiate a client to connect to the XRPL. For this tutorial you need a funded account for the `Batch` transaction **sender**, and two other accounts to **receive** the payments. {% tabs %} {% tab label="Javascript" %} @@ -71,23 +71,23 @@ Next, prepare the inner transactions that will be included in the batch. The first transaction sends a payment of 2 XRP from the sender to `wallet1`, and the second transaction sends 5 XRP from the sender to `wallet2`. Both transactions must include the `tfInnerBatchTxn` (0x40000000) flag to indicate that they are inner transactions of a batch. -Inner transactions must have a Fee of **0** and an empty string for the `SigningPubKey`. The outer Batch transaction handles the overall fee and signing for all inner transactions. +Inner transactions must have a Fee of **0** and an empty string for the `SigningPubKey`. The outer `Batch` transaction handles the overall fee and signing for all inner transactions. {% admonition type="info" name="Note" %} -The `Fee` and `SigningPubKey` fields are omitted as the client library's _autofill_ functionality automatically populates these when submitting the Batch transaction. +The `Fee` and `SigningPubKey` fields are omitted as the client library's _autofill_ functionality automatically populates these when submitting the `Batch` transaction. You typically don't need to set these manually, but if you do, ensure `Fee` is set to 0 and `SigningPubKey` is an empty string. {% /admonition %} ### 4. Prepare Batch transaction -Create the Batch transaction and provide the inner transactions. The key fields to note are: +Create the `Batch` transaction and provide the inner transactions. The key fields to note are: | Field | Value | |:---------------- |:---------- | | TransactionType | The type of transaction, in this case `Batch`.| -| Account | The wallet address of the account that is sending the Batch transaction. | -| Flags | The flags for the Batch transaction. For this example the transaction is configured with the `tfAllOrNothing` (0x00010000) flag to ensure that either all inner transactions succeed or they all fail atomically. See [Batch Flags](../../../references/protocol/transactions/types/batch.md#batch-flags) for other options. | +| Account | The wallet address of the account that is sending the `Batch` transaction. | +| Flags | The flags for the `Batch` transaction. For this example the transaction is configured with the `tfAllOrNothing` (0x00010000) flag to ensure that either all inner transactions succeed or they all fail atomically. See [Batch Flags](../../../references/protocol/transactions/types/batch.md#batch-flags) for other options. | | RawTransactions | Contains the list of inner transactions to be applied. Must include a minimum of **2** transactions and a maximum of **8** transactions. These transactions can come from one account or multiple accounts. | {% tabs %} @@ -98,7 +98,7 @@ Create the Batch transaction and provide the inner transactions. The key fields ### 5. Submit Batch transaction -Now the sender can submit the Batch transaction: +Now the sender can submit the `Batch` transaction: {% tabs %} {% tab label="Javascript" %} @@ -110,7 +110,7 @@ Because `autofill` is set to `true`, the client library automatically fills in a ### 6. Check Batch transaction result -To check the result of the Batch transaction submission: +To check the result of the `Batch` transaction submission: {% tabs %} {% tab label="Javascript" %} @@ -121,16 +121,16 @@ To check the result of the Batch transaction submission: The code checks for a `tesSUCCESS` result and displays the response details. {% admonition type="warning" name="Warning" %} -A `tesSUCCESS` result indicates that the Batch transaction was processed successfully, but does not guarantee the inner transactions succeeded. +A `tesSUCCESS` result indicates that the `Batch` transaction was processed successfully, but does not guarantee the inner transactions succeeded. For example, see the [following transaction on the XRPL Explorer](https://devnet.xrpl.org/transactions/20CFCE5CF75E93E6D1E9C1E42F8E8C8C4CB1786A65BE23D2EA77EAAB65A455C5/simple). {% /admonition %} -Because the Batch transaction is configured with a `tfAllOrNothing` flag, if any inner transaction fails, **all** inner transactions wil fail, and only the Batch transaction fee is deducted from the **third-party wallet**. +Because the `Batch` transaction is configured with a `tfAllOrNothing` flag, if any inner transaction fails, **all** inner transactions wil fail, and only the `Batch` transaction fee is deducted from the **third-party wallet**. ### 7. Verify inner transactions -Since there is no way to check the status of inner transactions in the Batch transaction result, you need to calculate the inner transaction hashes and look them up on the ledger: +Since there is no way to check the status of inner transactions in the `Batch` transaction result, you need to calculate the inner transaction hashes and look them up on the ledger: {% tabs %} {% tab label="Javascript" %}