Skip to content

Commit 2e9d97c

Browse files
committed
Add Batch transactions tutorials
1 parent 1e09559 commit 2e9d97c

File tree

9 files changed

+813
-2
lines changed

9 files changed

+813
-2
lines changed

_code-samples/batch/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Batch
22

3-
Code samples showing how to create and submit a [Batch transaction](../../docs/concepts/transactions/batch-transactions.md).
4-
Both for simple and multi account batch transactions.
3+
Code samples showing how to create and submit a [Batch transaction](https://xrpl.org/docs/concepts/transactions/batch-transactions).
4+
5+
Both for single and multi account batch transactions.

_code-samples/batch/js/README.md

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
# Send a Batch Transaction
2+
3+
Code samples showing how to create and submit a [Batch transaction](https://xrpl.org/docs/concepts/transactions/batch-transactions) with Javascript.
4+
5+
Both for single and multi account batch transactions.
6+
7+
## Single Account Batch Transaction
8+
9+
Quick setup and usage:
10+
11+
```sh
12+
npm install xrpl
13+
node singleAccountBatch.js
14+
```
15+
16+
The script should output the following:
17+
18+
```sh
19+
Funding new wallets from faucet...
20+
Sender: rLJMPR83xig3rdeCkGJeM4mC3nih89PAor, Balance: 100 XRP
21+
Wallet1: rMouwiN1MvK6k45WwoZpyZ5xrWAF8oKPvc, Balance: 100 XRP
22+
Wallet2: r3xPijsGPRiaAd4F7oRyhpFzN7ayHA2xbe, Balance: 100 XRP
23+
24+
Creating batch transaction:
25+
{
26+
"TransactionType": "Batch",
27+
"Account": "rLJMPR83xig3rdeCkGJeM4mC3nih89PAor",
28+
"Flags": 65536,
29+
"RawTransactions": [
30+
{
31+
"RawTransaction": {
32+
"TransactionType": "Payment",
33+
"Account": "rLJMPR83xig3rdeCkGJeM4mC3nih89PAor",
34+
"Destination": "rMouwiN1MvK6k45WwoZpyZ5xrWAF8oKPvc",
35+
"Amount": "2000000",
36+
"Flags": 1073741824
37+
}
38+
},
39+
{
40+
"RawTransaction": {
41+
"TransactionType": "Payment",
42+
"Account": "rLJMPR83xig3rdeCkGJeM4mC3nih89PAor",
43+
"Destination": "r3xPijsGPRiaAd4F7oRyhpFzN7ayHA2xbe",
44+
"Amount": "5000000",
45+
"Flags": 1073741824
46+
}
47+
}
48+
]
49+
}
50+
51+
Submitting batch transaction...
52+
53+
Batch transaction submitted successfully!
54+
Result:
55+
{
56+
"close_time_iso": "2025-10-31T18:53:12Z",
57+
"ctid": "C00C127300000002",
58+
"hash": "E3BB5CED757C86F7C8052D578E76A135B70469C57601C177753B2802EF5EC8CE",
59+
"ledger_hash": "2025024A2227B97F231C8CF48BA426F765F43918F3EA6D8887EC898F6B362BE6",
60+
"ledger_index": 791155,
61+
"meta": {
62+
"AffectedNodes": [
63+
{
64+
"ModifiedNode": {
65+
"FinalFields": {
66+
"Account": "rLJMPR83xig3rdeCkGJeM4mC3nih89PAor",
67+
"Balance": "99999996",
68+
"Flags": 0,
69+
"OwnerCount": 0,
70+
"Sequence": 791150
71+
},
72+
"LedgerEntryType": "AccountRoot",
73+
"LedgerIndex": "2994421B90E2B5D9F8F6108C627F0A22B5FE4ED040AA111546FC47C685E2FDAD",
74+
"PreviousFields": {
75+
"Balance": "100000000",
76+
"Sequence": 791149
77+
},
78+
"PreviousTxnID": "C1AE7F96FC5EC751AB22FEAC8550DC2CBE93C35E74DDFCAFE18D22138C9B0ADE",
79+
"PreviousTxnLgrSeq": 791149
80+
}
81+
}
82+
],
83+
"TransactionIndex": 0,
84+
"TransactionResult": "tesSUCCESS"
85+
},
86+
"tx_json": {
87+
"Account": "rLJMPR83xig3rdeCkGJeM4mC3nih89PAor",
88+
"Fee": "4",
89+
"Flags": 65536,
90+
"LastLedgerSequence": 791173,
91+
"RawTransactions": [
92+
{
93+
"RawTransaction": {
94+
"Account": "rLJMPR83xig3rdeCkGJeM4mC3nih89PAor",
95+
"Amount": "2000000",
96+
"Destination": "rMouwiN1MvK6k45WwoZpyZ5xrWAF8oKPvc",
97+
"Fee": "0",
98+
"Flags": 1073741824,
99+
"Sequence": 791150,
100+
"SigningPubKey": "",
101+
"TransactionType": "Payment"
102+
}
103+
},
104+
{
105+
"RawTransaction": {
106+
"Account": "rLJMPR83xig3rdeCkGJeM4mC3nih89PAor",
107+
"Amount": "5000000",
108+
"Destination": "r3xPijsGPRiaAd4F7oRyhpFzN7ayHA2xbe",
109+
"Fee": "0",
110+
"Flags": 1073741824,
111+
"Sequence": 791151,
112+
"SigningPubKey": "",
113+
"TransactionType": "Payment"
114+
}
115+
}
116+
],
117+
"Sequence": 791149,
118+
"SigningPubKey": "EDFA3A5B4468D81BBEFE70591D39A1E9DF3DF26F8493EE3B86DBEB2B9AB546995A",
119+
"TransactionType": "Batch",
120+
"TxnSignature": "11B6EDB2C1E07FB038E6893B403EB3A55E778F62A12D98BE4144EFDEA3324F789CCE9A9DAB0C1B92207664059EA428FAA40BEDC8792D9D5CDF667761058D4502",
121+
"ctid": "C00C127300000002",
122+
"date": 815251992,
123+
"ledger_index": 791155
124+
},
125+
"validated": true
126+
}
127+
128+
Final balances after batch transaction:
129+
Sender: rLJMPR83xig3rdeCkGJeM4mC3nih89PAor, Balance: 92.999996 XRP
130+
Wallet1: rMouwiN1MvK6k45WwoZpyZ5xrWAF8oKPvc, Balance: 102 XRP
131+
Wallet2: r3xPijsGPRiaAd4F7oRyhpFzN7ayHA2xbe, Balance: 105 XRP
132+
133+
Transaction URL:
134+
https://devnet.xrpl.org/transactions/E3BB5CED757C86F7C8052D578E76A135B70469C57601C177753B2802EF5EC8CE
135+
```
136+
137+
## Multi-Account Batch Transaction
138+
139+
```sh
140+
npm install xrpl
141+
node multiAccountBatch.js
142+
```
143+
144+
The script should output the following:
145+
146+
```sh
147+
Funding new wallets from faucet...
148+
Alice: rHtnemvZvTJTcuTqZt6FkzcPuVa7cP2kHJ, Balance: 100 XRP
149+
Bob: rBt8Dc6C56bsquwQ8aCoeE5Bj5dGbxwjjz, Balance: 100 XRP
150+
Third-party wallet: rJ2wMMnqconQU72495QaK43aniBUw4bgJW, Balance: 100 XRP
151+
152+
Creating batch transaction:
153+
{
154+
"TransactionType": "Batch",
155+
"Account": "rJ2wMMnqconQU72495QaK43aniBUw4bgJW",
156+
"Flags": 65536,
157+
"RawTransactions": [
158+
{
159+
"RawTransaction": {
160+
"TransactionType": "Payment",
161+
"Account": "rHtnemvZvTJTcuTqZt6FkzcPuVa7cP2kHJ",
162+
"Destination": "rBt8Dc6C56bsquwQ8aCoeE5Bj5dGbxwjjz",
163+
"Amount": "2000000",
164+
"Flags": 1073741824
165+
}
166+
},
167+
{
168+
"RawTransaction": {
169+
"TransactionType": "Payment",
170+
"Account": "rBt8Dc6C56bsquwQ8aCoeE5Bj5dGbxwjjz",
171+
"Destination": "rHtnemvZvTJTcuTqZt6FkzcPuVa7cP2kHJ",
172+
"Amount": "5000000",
173+
"Flags": 1073741824
174+
}
175+
}
176+
]
177+
}
178+
179+
Submitting batch transaction...
180+
181+
Batch transaction submitted successfully!
182+
Result:
183+
{
184+
"close_time_iso": "2025-10-31T18:54:31Z",
185+
"ctid": "C00C128C00000002",
186+
"hash": "6A1E452F5030E03708FD6256843D0756108D5D7835C3DF0EA80B5822235CCA8F",
187+
"ledger_hash": "B66C70A083564D8E35F2C108FAF6D81233843FB07110C9FD5CE8FFE153A36168",
188+
"ledger_index": 791180,
189+
"meta": {
190+
"AffectedNodes": [
191+
{
192+
"ModifiedNode": {
193+
"FinalFields": {
194+
"Account": "rJ2wMMnqconQU72495QaK43aniBUw4bgJW",
195+
"Balance": "99999994",
196+
"Flags": 0,
197+
"OwnerCount": 0,
198+
"Sequence": 791179
199+
},
200+
"LedgerEntryType": "AccountRoot",
201+
"LedgerIndex": "5D8841B176A9B995E10887F3F3F979101D4E5F41220A2041D298194CC8EE005B",
202+
"PreviousFields": {
203+
"Balance": "100000000",
204+
"Sequence": 791178
205+
},
206+
"PreviousTxnID": "061355DDC745F003117DBD4F518B3F63A4E916323B1572723EF403535C2E5E30",
207+
"PreviousTxnLgrSeq": 791178
208+
}
209+
}
210+
],
211+
"TransactionIndex": 0,
212+
"TransactionResult": "tesSUCCESS"
213+
},
214+
"tx_json": {
215+
"Account": "rJ2wMMnqconQU72495QaK43aniBUw4bgJW",
216+
"BatchSigners": [
217+
{
218+
"BatchSigner": {
219+
"Account": "rBt8Dc6C56bsquwQ8aCoeE5Bj5dGbxwjjz",
220+
"SigningPubKey": "ED3007467638672F4B5DC640C245CB138433972E6091E17B0C4CD3AE93CFC6124C",
221+
"TxnSignature": "C4FE2D7EC5B84286D8F388EA2BAED489889498808DACD4FD0F01BCF2645C7EAF179FAA55928BD62A82070EAD22608161EE062416BB024DACE45226794416F703"
222+
}
223+
},
224+
{
225+
"BatchSigner": {
226+
"Account": "rHtnemvZvTJTcuTqZt6FkzcPuVa7cP2kHJ",
227+
"SigningPubKey": "ED6625FF17FA0E58D1AD429E414C54945216DB8CE6311360DC85D0C1A62D42E176",
228+
"TxnSignature": "E8974C437B9DD84EC0C61CF814BB5ADF284467B2073671517197D1AFFB68A33C56A59A6FA17A9A9686259A4496AC873898190DDD548DBFC5397E8972472D2104"
229+
}
230+
}
231+
],
232+
"Fee": "6",
233+
"Flags": 65536,
234+
"LastLedgerSequence": 791198,
235+
"RawTransactions": [
236+
{
237+
"RawTransaction": {
238+
"Account": "rHtnemvZvTJTcuTqZt6FkzcPuVa7cP2kHJ",
239+
"Amount": "2000000",
240+
"Destination": "rBt8Dc6C56bsquwQ8aCoeE5Bj5dGbxwjjz",
241+
"Fee": "0",
242+
"Flags": 1073741824,
243+
"Sequence": 791174,
244+
"SigningPubKey": "",
245+
"TransactionType": "Payment"
246+
}
247+
},
248+
{
249+
"RawTransaction": {
250+
"Account": "rBt8Dc6C56bsquwQ8aCoeE5Bj5dGbxwjjz",
251+
"Amount": "5000000",
252+
"Destination": "rHtnemvZvTJTcuTqZt6FkzcPuVa7cP2kHJ",
253+
"Fee": "0",
254+
"Flags": 1073741824,
255+
"Sequence": 791176,
256+
"SigningPubKey": "",
257+
"TransactionType": "Payment"
258+
}
259+
}
260+
],
261+
"Sequence": 791178,
262+
"SigningPubKey": "EDB3FADCE6F61F4D7D5699BC551637B60401A87D84B6BA195FF695E16C4C633E37",
263+
"TransactionType": "Batch",
264+
"TxnSignature": "9DCFEBC79511F12522ED839C52E473D11A29198CAD0699BE8A022BDB1DDF4CFD1241431BDDD8953F387AEB1D17E0EB35A0E3EBDDF9FB189D1531C6A2DF922403",
265+
"ctid": "C00C128C00000002",
266+
"date": 815252071,
267+
"ledger_index": 791180
268+
},
269+
"validated": true
270+
}
271+
272+
Final balances after batch transaction:
273+
Alice: rHtnemvZvTJTcuTqZt6FkzcPuVa7cP2kHJ, Balance: 103 XRP
274+
Bob: rBt8Dc6C56bsquwQ8aCoeE5Bj5dGbxwjjz, Balance: 97 XRP
275+
Third-party wallet: rJ2wMMnqconQU72495QaK43aniBUw4bgJW, Balance: 99.999994 XRP
276+
277+
Transaction URL:
278+
https://devnet.xrpl.org/transactions/6A1E452F5030E03708FD6256843D0756108D5D7835C3DF0EA80B5822235CCA8F
279+
```
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* XRP Ledger Batch Transactions Tutorial
3+
*
4+
* This tutorial demonstrates how to use the Batch transaction feature (XLS-56)
5+
* to perform a multi-account batch transaction.
6+
*/
7+
8+
import xrpl from "xrpl"
9+
import { BatchFlags } from 'xrpl/dist/npm/models/transactions/batch.js'
10+
import { GlobalFlags } from 'xrpl/dist/npm/models/transactions/common.js'
11+
import { signMultiBatch, combineBatchSigners } from 'xrpl/dist/npm/Wallet/batchSigner.js'
12+
13+
const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233/")
14+
await client.connect()
15+
16+
// Create and fund wallets
17+
console.log("Funding new wallets from faucet...")
18+
const { wallet: alice } = await client.fundWallet()
19+
const { wallet: bob } = await client.fundWallet()
20+
const { wallet: charlie } = await client.fundWallet()
21+
const { wallet: thirdPartyWallet } = await client.fundWallet()
22+
23+
console.log(`Alice: ${alice.address}, Balance: ${await client.getXrpBalance(alice.address)} XRP`)
24+
console.log(`Bob: ${bob.address}, Balance: ${await client.getXrpBalance(bob.address)} XRP`)
25+
console.log(`Charlie: ${charlie.address}, Balance: ${await client.getXrpBalance(charlie.address)} XRP`)
26+
console.log(`Third-party wallet: ${thirdPartyWallet.address}, Balance: ${await client.getXrpBalance(thirdPartyWallet.address)} XRP`)
27+
28+
// Create inner transactions --------------------------------------------
29+
// REQUIRED: Inner transactions MUST have the tfInnerBatchTxn flag (0x40000000).
30+
// This marks them as part of a batch (allows Fee: 0 and empty SigningPubKey).
31+
32+
// Transaction 1: Charlie pays Alice
33+
const charliePayment = {
34+
TransactionType: "Payment",
35+
Account: charlie.address,
36+
Destination: alice.address,
37+
Amount: xrpl.xrpToDrops(50),
38+
Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED
39+
}
40+
41+
// Transaction 2: Bob pays Alice
42+
const bobPayment = {
43+
TransactionType: "Payment",
44+
Account: bob.address,
45+
Destination: alice.address,
46+
Amount: xrpl.xrpToDrops(50),
47+
Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED
48+
}
49+
50+
// Send Batch transaction --------------------------------------------
51+
console.log("\nCreating batch transaction:")
52+
const batchTx = {
53+
TransactionType: "Batch",
54+
Account: thirdPartyWallet.address,
55+
Flags: BatchFlags.tfAllOrNothing, // tfAllOrNothing: All inner transactions must succeed
56+
RawTransactions: [
57+
{ RawTransaction: charliePayment },
58+
{ RawTransaction: bobPayment },
59+
]
60+
}
61+
console.log(JSON.stringify(batchTx, null, 2))
62+
63+
// Validate the transaction structure
64+
xrpl.validate(batchTx)
65+
66+
// Set the expected number of signers for this transaction.
67+
// "autofill" will automatically add Fee: "0" and SigningPubKey: "".
68+
const autofilledBatchTx = await client.autofill(batchTx, 2)
69+
70+
// Gather batch signatures --------------------------------
71+
// Each signer needs their own tx copy because signMultiBatch modifies the object.
72+
// Charlie signs the Batch transaction
73+
const charlieBatch = { ...autofilledBatchTx }
74+
signMultiBatch(charlie, charlieBatch)
75+
76+
// Bob signs the Batch transaction
77+
const bobBatch = { ...autofilledBatchTx }
78+
signMultiBatch(bob, bobBatch)
79+
80+
// Combine inner transaction signatures.
81+
// This returns a signed transaction blob (hex string) ready for submission.
82+
const combinedSignedTx = combineBatchSigners([charlieBatch, bobBatch])
83+
84+
// Submit the signed blob with the third-party's wallet
85+
console.log("\nSubmitting batch transaction...")
86+
const submitResponse = await client.submitAndWait(combinedSignedTx,
87+
{ wallet: thirdPartyWallet }
88+
)
89+
90+
// Check Batch transaction result --------------------------------
91+
if (submitResponse.result.meta.TransactionResult !== "tesSUCCESS") {
92+
const resultCode = submitResponse.result.meta.TransactionResult
93+
console.warn(`\nTransaction failed with result code ${resultCode}`)
94+
process.exit(1)
95+
}
96+
97+
console.log("\nBatch transaction submitted successfully!")
98+
console.log("Result:\n", JSON.stringify(submitResponse.result, null, 2))
99+
100+
// Verify balances after transaction
101+
console.log("\nFinal balances after batch transaction:")
102+
console.log(`Alice: ${alice.address}, Balance: ${await client.getXrpBalance(alice.address)} XRP`)
103+
console.log(`Bob: ${bob.address}, Balance: ${await client.getXrpBalance(bob.address)} XRP`)
104+
console.log(`Charlie: ${charlie.address}, Balance: ${await client.getXrpBalance(charlie.address)} XRP`)
105+
console.log(`Third-party wallet: ${thirdPartyWallet.address}, Balance: ${await client.getXrpBalance(thirdPartyWallet.address)} XRP`)
106+
107+
// View the transaction on the XRPL Explorer
108+
console.log(`\nTransaction URL:\nhttps://devnet.xrpl.org/transactions/${submitResponse.result.hash}`)
109+
110+
await client.disconnect()

0 commit comments

Comments
 (0)