Calculate inner transaction hash to verify success

This commit is contained in:
Maria Shodunke
2025-11-14 09:36:03 -08:00
parent 382a10bda9
commit 7f16532b07
6 changed files with 226 additions and 131 deletions

View File

@@ -16,22 +16,22 @@ node singleAccountBatch.js
The script should output the following: The script should output the following:
```sh ```sh
Funding new wallets from faucet... === Funding new wallets from faucet... ===
Sender: raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e, Balance: 100 XRP Sender: rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim, Balance: 100 XRP
Wallet1: r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC, Balance: 100 XRP Wallet1: rGx6SACvYEvX8SRrvTPD91UhBmJ16pxL94, Balance: 100 XRP
Wallet2: rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn, Balance: 100 XRP Wallet2: r3qetgSfAtyCpGc4rvKNz4LX3F3urMSJJy, Balance: 100 XRP
Creating batch transaction: === Creating Batch transaction... ===
{ {
"TransactionType": "Batch", "TransactionType": "Batch",
"Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
"Flags": 65536, "Flags": 65536,
"RawTransactions": [ "RawTransactions": [
{ {
"RawTransaction": { "RawTransaction": {
"TransactionType": "Payment", "TransactionType": "Payment",
"Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
"Destination": "r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC", "Destination": "rGx6SACvYEvX8SRrvTPD91UhBmJ16pxL94",
"Amount": "2000000", "Amount": "2000000",
"Flags": 1073741824 "Flags": 1073741824
} }
@@ -39,8 +39,8 @@ Creating batch transaction:
{ {
"RawTransaction": { "RawTransaction": {
"TransactionType": "Payment", "TransactionType": "Payment",
"Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
"Destination": "rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn", "Destination": "r3qetgSfAtyCpGc4rvKNz4LX3F3urMSJJy",
"Amount": "5000000", "Amount": "5000000",
"Flags": 1073741824 "Flags": 1073741824
} }
@@ -48,90 +48,100 @@ Creating batch transaction:
] ]
} }
Submitting batch transaction... === Submitting Batch transaction... ===
Batch transaction submitted successfully! Batch transaction submitted successfully!
Result: Result:
{ {
"close_time_iso": "2025-11-03T14:16:21Z", "close_time_iso": "2025-11-17T12:04:50Z",
"ctid": "C00D458B00020002", "ctid": "C013313800030002",
"hash": "A93D3C2BDB5D600E592B64B84E66D789237D029267129EBC659EE483E532DD95", "hash": "AE118213B0A183528418ABC5F14E3BFD6524020C5DB1C060157A0D3FDE15B900",
"ledger_hash": "BE6B7C12E551305F09E942D6FA3FC8546F024AE5C53FC495DA6ABF78461E7019", "ledger_hash": "621183809B68A794371C5EC6522105FF04E502C48EBDC8171B80224991E33394",
"ledger_index": 869771, "ledger_index": 1257784,
"meta": { "meta": {
"AffectedNodes": [ "AffectedNodes": [
{ {
"ModifiedNode": { "ModifiedNode": {
"FinalFields": { "FinalFields": {
"Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
"Balance": "99999996", "Balance": "99999996",
"Flags": 0, "Flags": 0,
"OwnerCount": 0, "OwnerCount": 0,
"Sequence": 869767 "Sequence": 1257779
}, },
"LedgerEntryType": "AccountRoot", "LedgerEntryType": "AccountRoot",
"LedgerIndex": "6238B6901FEBD1492C03546C7965A01F184C4E37B696304B86F78F4ADB7831B1", "LedgerIndex": "42CC98AF0A28EDDDC7E359B5622CC5748BDE2A93E124AF5C32647ECA8F68D480",
"PreviousFields": { "PreviousFields": {
"Balance": "100000000", "Balance": "100000000",
"Sequence": 869766 "Sequence": 1257778
}, },
"PreviousTxnID": "559F102041D84FF9DA17483355C3C96A0F8923D9C9C7971BBB15C972DD1F37D6", "PreviousTxnID": "081C42DAE12001735AC4E9A7F027636DF612DB17B4BFA2333F4DB8EA0C9D1E9F",
"PreviousTxnLgrSeq": 869766 "PreviousTxnLgrSeq": 1257778
} }
} }
], ],
"TransactionIndex": 2, "TransactionIndex": 3,
"TransactionResult": "tesSUCCESS" "TransactionResult": "tesSUCCESS"
}, },
"tx_json": { "tx_json": {
"Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
"Fee": "4", "Fee": "4",
"Flags": 65536, "Flags": 65536,
"LastLedgerSequence": 869789, "LastLedgerSequence": 1257802,
"RawTransactions": [ "RawTransactions": [
{ {
"RawTransaction": { "RawTransaction": {
"Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
"Amount": "2000000", "Amount": "2000000",
"Destination": "r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC", "Destination": "rGx6SACvYEvX8SRrvTPD91UhBmJ16pxL94",
"Fee": "0", "Fee": "0",
"Flags": 1073741824, "Flags": 1073741824,
"Sequence": 869767, "Sequence": 1257779,
"SigningPubKey": "", "SigningPubKey": "",
"TransactionType": "Payment" "TransactionType": "Payment"
} }
}, },
{ {
"RawTransaction": { "RawTransaction": {
"Account": "raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e", "Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
"Amount": "5000000", "Amount": "5000000",
"Destination": "rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn", "Destination": "r3qetgSfAtyCpGc4rvKNz4LX3F3urMSJJy",
"Fee": "0", "Fee": "0",
"Flags": 1073741824, "Flags": 1073741824,
"Sequence": 869768, "Sequence": 1257780,
"SigningPubKey": "", "SigningPubKey": "",
"TransactionType": "Payment" "TransactionType": "Payment"
} }
} }
], ],
"Sequence": 869766, "Sequence": 1257778,
"SigningPubKey": "EDFECFB87A29F93E52BBA0BA5A14A59B520BB0E39F33943A2FDC1101D34349270D", "SigningPubKey": "ED7031CA5BA4EC745610AB495F5053F318C119E87567BE485A494773AD8ED4FBCE",
"TransactionType": "Batch", "TransactionType": "Batch",
"TxnSignature": "E08E300BDE1700C7CC27F3DA9B784907F637518E1C7E0978E57BFE5D1511A3B6A4269235FC2D9EAA550182A5F2B59415A442CE59555B9B9A0A79AB4030C9F701", "TxnSignature": "0610A277086943BC462C1A5F85BEB667B62B4BDA59525138B6014101C08297897A73D3D2D247CB37A06E1EA36267C53A51C0FDF32F3D8E974029BEDC41105B07",
"ctid": "C00D458B00020002", "ctid": "C013313800030002",
"date": 815494581, "date": 816696290,
"ledger_index": 869771 "ledger_index": 1257784
}, },
"validated": true "validated": true
} }
Final balances after batch transaction: Batch transaction URL:
Sender: raNwujquxJ7QTLhfbkKN6sZa7RBPHV671e, Balance: 92.999996 XRP https://devnet.xrpl.org/transactions/AE118213B0A183528418ABC5F14E3BFD6524020C5DB1C060157A0D3FDE15B900
Wallet1: r4JMmKToZRMVT3mGWPnKHFEHsSMQEWigLC, Balance: 102 XRP
Wallet2: rKfPgHASYuttoF1HfU56V31WbJvZn3w8xn, Balance: 105 XRP
Transaction URL: === Verifying inner transactions... ===
https://devnet.xrpl.org/transactions/A93D3C2BDB5D600E592B64B84E66D789237D029267129EBC659EE483E532DD95
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 ## Multi-Account Batch Transaction
@@ -144,23 +154,23 @@ node multiAccountBatch.js
The script should output the following: The script should output the following:
```sh ```sh
Funding new wallets from faucet... === Funding new wallets from faucet... ===
Alice: rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa, Balance: 100 XRP Alice: rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG, Balance: 100 XRP
Bob: rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7, Balance: 100 XRP Bob: r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq, Balance: 100 XRP
Charlie: rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K, Balance: 100 XRP Charlie: rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA, Balance: 100 XRP
Third-party wallet: rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc, Balance: 100 XRP Third-party wallet: rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA, Balance: 100 XRP
Creating batch transaction: === Creating Batch transaction... ===
{ {
"TransactionType": "Batch", "TransactionType": "Batch",
"Account": "rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc", "Account": "rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA",
"Flags": 65536, "Flags": 65536,
"RawTransactions": [ "RawTransactions": [
{ {
"RawTransaction": { "RawTransaction": {
"TransactionType": "Payment", "TransactionType": "Payment",
"Account": "rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K", "Account": "rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA",
"Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", "Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG",
"Amount": "50000000", "Amount": "50000000",
"Flags": 1073741824 "Flags": 1073741824
} }
@@ -168,8 +178,8 @@ Creating batch transaction:
{ {
"RawTransaction": { "RawTransaction": {
"TransactionType": "Payment", "TransactionType": "Payment",
"Account": "rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7", "Account": "r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq",
"Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", "Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG",
"Amount": "50000000", "Amount": "50000000",
"Flags": 1073741824 "Flags": 1073741824
} }
@@ -177,35 +187,35 @@ Creating batch transaction:
] ]
} }
Submitting batch transaction... === Submitting Batch transaction... ===
Batch transaction submitted successfully! Batch transaction submitted successfully!
Result: Result:
{ {
"close_time_iso": "2025-11-03T14:15:00Z", "close_time_iso": "2025-11-17T12:08:31Z",
"ctid": "C00D457000000002", "ctid": "C013317600000002",
"hash": "8CBCCD88B8ABC248797B84ABB92066961C1CB5FE75ACE2115ADCA6B74C85993A", "hash": "1299D20C6B489DA5C632AE4DBE49475DBF42D9444C7E9C109CC9B8DD0FD55FEC",
"ledger_hash": "2217A0DBB38B870187B412533B939724095359A050B21E071A2A114BF57CFB60", "ledger_hash": "E45ECF69057084CD02BA49A17E4D0C9154D33A98BB3C95A11B2EB9BE18F32C9B",
"ledger_index": 869744, "ledger_index": 1257846,
"meta": { "meta": {
"AffectedNodes": [ "AffectedNodes": [
{ {
"ModifiedNode": { "ModifiedNode": {
"FinalFields": { "FinalFields": {
"Account": "rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc", "Account": "rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA",
"Balance": "99999994", "Balance": "99999994",
"Flags": 0, "Flags": 0,
"OwnerCount": 0, "OwnerCount": 0,
"Sequence": 869743 "Sequence": 1257845
}, },
"LedgerEntryType": "AccountRoot", "LedgerEntryType": "AccountRoot",
"LedgerIndex": "1E9BA043B9C6518582D0FF73A08DCD8B6958195735086CF7295E5EB6433FB453", "LedgerIndex": "2D9E0A02007241C38A8DF679E7E62AA0B273E8B12A5430B7B9D99300424F0E1F",
"PreviousFields": { "PreviousFields": {
"Balance": "100000000", "Balance": "100000000",
"Sequence": 869742 "Sequence": 1257844
}, },
"PreviousTxnID": "F7019BC55D80438FDDB01C2549CCC3F7DAF9791F8645E0269D63979EAEC5BBA6", "PreviousTxnID": "3153DE8DE922538A6BE54AA8F783CAD4B848A321AFF028D3E6DD0E80C4B9C237",
"PreviousTxnLgrSeq": 869742 "PreviousTxnLgrSeq": 1257844
} }
} }
], ],
@@ -213,69 +223,79 @@ Result:
"TransactionResult": "tesSUCCESS" "TransactionResult": "tesSUCCESS"
}, },
"tx_json": { "tx_json": {
"Account": "rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc", "Account": "rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA",
"BatchSigners": [ "BatchSigners": [
{ {
"BatchSigner": { "BatchSigner": {
"Account": "rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K", "Account": "rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA",
"SigningPubKey": "EDC566D7DA8186BBD30DDAE1FB770FCE7F248949194E1A2E70B18CFA060B140B59", "SigningPubKey": "EDEB88C2868BD25BF03DB26050E16579FA6F8F9E3FF3172E0DC3DCBDA5408572EB",
"TxnSignature": "31639BFA1359DD24345776EAEEACCF61C1CDC792988679263D113E80A22D837E20ACD2B25E482FCA769990C004D747836370C6BAD14524559639BBEBA5813002" "TxnSignature": "9508568084596147CFDCFC18A62DC298A78AD1148BA4B0EB99BEE1CD37E5555FE3930810790D5708F9739B0E3F79772012C154CA33C2280BDD5B72473C17A607"
} }
}, },
{ {
"BatchSigner": { "BatchSigner": {
"Account": "rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7", "Account": "r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq",
"SigningPubKey": "EDEF1966B325000407940E4C0792E3CCC3E27F51D132BDC53DCC2B1998E7C32A34", "SigningPubKey": "ED82F98DA6A3FC3E88D2EE3A5469D92C7070513BEF4DEE75CAB0BDAA81E8AE378D",
"TxnSignature": "6BF9860B0E2E134FB302329D711BAA7B6314395D39523982DBBC037E84FB17AB5E8E736DB3DB0019B4477686AF2D91E5D2B49409698A95219376B2E318D3E501" "TxnSignature": "A482C8747F79857530474F1677599766C0BE283CB7E2A05AACF76E61BECCA16DCE3802D2D8244FBF4546A1C0E5EB70691255E3EFD2F8AC80B55357BDAB9ACD05"
} }
} }
], ],
"Fee": "6", "Fee": "6",
"Flags": 65536, "Flags": 65536,
"LastLedgerSequence": 869762, "LastLedgerSequence": 1257864,
"RawTransactions": [ "RawTransactions": [
{ {
"RawTransaction": { "RawTransaction": {
"Account": "rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K", "Account": "rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA",
"Amount": "50000000", "Amount": "50000000",
"Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", "Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG",
"Fee": "0", "Fee": "0",
"Flags": 1073741824, "Flags": 1073741824,
"Sequence": 869740, "Sequence": 1257842,
"SigningPubKey": "", "SigningPubKey": "",
"TransactionType": "Payment" "TransactionType": "Payment"
} }
}, },
{ {
"RawTransaction": { "RawTransaction": {
"Account": "rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7", "Account": "r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq",
"Amount": "50000000", "Amount": "50000000",
"Destination": "rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa", "Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG",
"Fee": "0", "Fee": "0",
"Flags": 1073741824, "Flags": 1073741824,
"Sequence": 869738, "Sequence": 1257841,
"SigningPubKey": "", "SigningPubKey": "",
"TransactionType": "Payment" "TransactionType": "Payment"
} }
} }
], ],
"Sequence": 869742, "Sequence": 1257844,
"SigningPubKey": "ED2B56D6FB4E8C236A6B07E8D8AD9A4938606144E31779918F99525CA6B3C56664", "SigningPubKey": "ED22A32B61EDF083315515831723BC18F8311F03886BBA375DFF46335BB7A75F0B",
"TransactionType": "Batch", "TransactionType": "Batch",
"TxnSignature": "9C51C1F2CB0E8BCEA1FADD3992249DE72AC46FC86AB2FB023A597FBD5C4CCB3337967E9AAFFB5F1C0CBC91128F3FD194F78F207E461BE1FF906C496B94EC410E", "TxnSignature": "156791D2DBFAEFC9B0AC29F2D8D0CDB25E13F92E70E6D5414FE31BD8573CA23D3F62F8B34FC1F117BD556B25E4F748095A24C4342108AB32F1B2BAFBF1443501",
"ctid": "C00D457000000002", "ctid": "C013317600000002",
"date": 815494500, "date": 816696511,
"ledger_index": 869744 "ledger_index": 1257846
}, },
"validated": true "validated": true
} }
Final balances after batch transaction: Batch transaction URL:
Alice: rfCBfRGpcGJLwdbfz1M6HYoAL8nZyHRHHa, Balance: 200 XRP https://devnet.xrpl.org/transactions/1299D20C6B489DA5C632AE4DBE49475DBF42D9444C7E9C109CC9B8DD0FD55FEC
Bob: rKPUDuS2jQNpAMhkNncqC9rKJDpL2gXDN7, Balance: 50 XRP
Charlie: rnz3Da7phfR6tgTZoPYF5psYTiHTshTB8K, Balance: 50 XRP
Third-party wallet: rU8LsCmVjSdf7hSmiGBtBDtt2WhHxp7Zpc, Balance: 99.999994 XRP
Transaction URL: === Verifying inner transactions ===
https://devnet.xrpl.org/transactions/8CBCCD88B8ABC248797B84ABB92066961C1CB5FE75ACE2115ADCA6B74C85993A
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
=== 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
``` ```

View File

@@ -3,18 +3,17 @@
* *
* This tutorial demonstrates how to use the Batch transaction feature (XLS-56) * This tutorial demonstrates how to use the Batch transaction feature (XLS-56)
* to perform a multi-account batch transaction. * 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 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/") const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233/")
await client.connect() await client.connect()
// Create and fund wallets // 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: alice } = await client.fundWallet()
const { wallet: bob } = await client.fundWallet() const { wallet: bob } = await client.fundWallet()
const { wallet: charlie } = await client.fundWallet() const { wallet: charlie } = await client.fundWallet()
@@ -35,7 +34,7 @@ const charliePayment = {
Account: charlie.address, Account: charlie.address,
Destination: alice.address, Destination: alice.address,
Amount: xrpl.xrpToDrops(50), Amount: xrpl.xrpToDrops(50),
Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED Flags: xrpl.GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED
} }
// Transaction 2: Bob pays Alice // Transaction 2: Bob pays Alice
@@ -44,15 +43,15 @@ const bobPayment = {
Account: bob.address, Account: bob.address,
Destination: alice.address, Destination: alice.address,
Amount: xrpl.xrpToDrops(50), Amount: xrpl.xrpToDrops(50),
Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED Flags: xrpl.GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED
} }
// Send Batch transaction -------------------------------------------- // Send Batch transaction --------------------------------------------
console.log("\nCreating batch transaction:") console.log("\n=== Creating Batch transaction... ===")
const batchTx = { const batchTx = {
TransactionType: "Batch", TransactionType: "Batch",
Account: thirdPartyWallet.address, 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. // Must include a minimum of 2 transactions and a maximum of 8 transactions.
RawTransactions: [ RawTransactions: [
{ RawTransaction: charliePayment }, { 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. // Each signer needs their own tx copy because signMultiBatch modifies the object.
// Charlie signs the Batch transaction // Charlie signs the Batch transaction
const charlieBatch = { ...autofilledBatchTx } const charlieBatch = { ...autofilledBatchTx }
signMultiBatch(charlie, charlieBatch) xrpl.signMultiBatch(charlie, charlieBatch)
// Bob signs the Batch transaction // Bob signs the Batch transaction
const bobBatch = { ...autofilledBatchTx } const bobBatch = { ...autofilledBatchTx }
signMultiBatch(bob, bobBatch) xrpl.signMultiBatch(bob, bobBatch)
// Combine inner transaction signatures. // Combine inner transaction signatures.
// This returns a signed transaction blob (hex string) ready for submission. // 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 // 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, const submitResponse = await client.submitAndWait(combinedSignedTx,
{ wallet: thirdPartyWallet } { wallet: thirdPartyWallet }
) )
@@ -97,15 +96,39 @@ if (submitResponse.result.meta.TransactionResult !== "tesSUCCESS") {
console.log("\nBatch transaction submitted successfully!") console.log("\nBatch transaction submitted successfully!")
console.log("Result:\n", JSON.stringify(submitResponse.result, null, 2)) 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 // 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(`Alice: ${alice.address}, Balance: ${await client.getXrpBalance(alice.address)} XRP`)
console.log(`Bob: ${bob.address}, Balance: ${await client.getXrpBalance(bob.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(`Charlie: ${charlie.address}, Balance: ${await client.getXrpBalance(charlie.address)} XRP`)
console.log(`Third-party wallet: ${thirdPartyWallet.address}, Balance: ${await client.getXrpBalance(thirdPartyWallet.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() await client.disconnect()

View File

@@ -1,6 +1,6 @@
{ {
"dependencies": { "dependencies": {
"xrpl": "^4.4.2" "xrpl": "^4.4.3"
}, },
"type": "module" "type": "module"
} }

View File

@@ -4,17 +4,17 @@
* This example demonstrates how to use the Batch transactions feature (XLS-56) * This example demonstrates how to use the Batch transactions feature (XLS-56)
* to create a single-account batch transaction that sends payments * to create a single-account batch transaction that sends payments
* to multiple destinations in one atomic operation. * 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 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/") const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233/")
await client.connect() await client.connect()
// Create and fund wallets // 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: sender } = await client.fundWallet()
const { wallet: wallet1 } = await client.fundWallet() const { wallet: wallet1 } = await client.fundWallet()
const { wallet: wallet2 } = await client.fundWallet() const { wallet: wallet2 } = await client.fundWallet()
@@ -33,7 +33,7 @@ const payment1 = {
Account: sender.address, Account: sender.address,
Destination: wallet1.address, Destination: wallet1.address,
Amount: xrpl.xrpToDrops(2), Amount: xrpl.xrpToDrops(2),
Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED Flags: xrpl.GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED
} }
// Transaction 2 // Transaction 2
@@ -42,15 +42,15 @@ const payment2 = {
Account: sender.address, Account: sender.address,
Destination: wallet2.address, Destination: wallet2.address,
Amount: xrpl.xrpToDrops(5), Amount: xrpl.xrpToDrops(5),
Flags: GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED Flags: xrpl.GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED
} }
// Send Batch transaction -------------------------------------------- // Send Batch transaction --------------------------------------------
console.log("\nCreating batch transaction:") console.log("\n=== Creating Batch transaction... ===")
const batchTx = { const batchTx = {
TransactionType: "Batch", TransactionType: "Batch",
Account: sender.address, 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. // Must include a minimum of 2 transactions and a maximum of 8 transactions.
RawTransactions: [ RawTransactions: [
{ RawTransaction: payment1 }, { RawTransaction: payment1 },
@@ -63,7 +63,7 @@ console.log(JSON.stringify(batchTx, null, 2))
xrpl.validate(batchTx) xrpl.validate(batchTx)
// Submit and wait for validation // Submit and wait for validation
console.log("\nSubmitting batch transaction...") console.log("\n=== Submitting Batch transaction... ===")
const submitResponse = await client.submitAndWait(batchTx, { const submitResponse = await client.submitAndWait(batchTx, {
wallet: sender, wallet: sender,
// "autofill" will automatically add Fee: "0" and SigningPubKey: "". // "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("\nBatch transaction submitted successfully!")
console.log("Result:\n", JSON.stringify(submitResponse.result, null, 2)) 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 // 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(`Sender: ${sender.address}, Balance: ${await client.getXrpBalance(sender.address)} XRP`)
console.log(`Wallet1: ${wallet1.address}, Balance: ${await client.getXrpBalance(wallet1.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`) 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() await client.disconnect()

View File

@@ -120,13 +120,13 @@ With all the required signatures gathered, the third-party wallet can now submit
{% /tab %} {% /tab %}
{% /tabs %} {% /tabs %}
### 6. Check Batch transaction result ### 7. Check Batch transaction result
To check the result of the Batch transaction submission: To check the result of the Batch transaction submission:
{% tabs %} {% tabs %}
{% tab label="Javascript" %} {% 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 %} {% /tab %}
{% /tabs %} {% /tabs %}
@@ -136,7 +136,23 @@ 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). 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 %} {% /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="// Calculate and verify" before="// Verify balances after transaction" /%}
{% /tab %}
{% /tabs %}
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 %} {% tabs %}
{% tab label="Javascript" %} {% tab label="Javascript" %}
@@ -144,8 +160,6 @@ To verify that the inner transactions have been successful, check the account ba
{% /tab %} {% /tab %}
{% /tabs %} {% /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 ## See Also
- **Concepts**: - **Concepts**:

View File

@@ -114,7 +114,7 @@ To check the result of the Batch transaction submission:
{% tabs %} {% tabs %}
{% tab label="Javascript" %} {% 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 %} {% /tab %}
{% /tabs %} {% /tabs %}
@@ -126,7 +126,23 @@ 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). For example, see the [following transaction on the XRPL Explorer](https://devnet.xrpl.org/transactions/20CFCE5CF75E93E6D1E9C1E42F8E8C8C4CB1786A65BE23D2EA77EAAB65A455C5/simple).
{% /admonition %} {% /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="// Calculate and verify" before="// Verify balances after transaction" /%}
{% /tab %}
{% /tabs %}
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 %} {% tabs %}
{% tab label="Javascript" %} {% tab label="Javascript" %}
@@ -134,8 +150,6 @@ To verify that the inner transactions have been successful, check the account ba
{% /tab %} {% /tab %}
{% /tabs %} {% /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 ## See Also
- **Concepts**: - **Concepts**: