mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-21 04:05:49 +00:00
Compare commits
1 Commits
remove-rba
...
add-code-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6df042c7b |
@@ -1,5 +1,4 @@
|
||||
# Batch
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
# 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: rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim, Balance: 100 XRP
|
||||
Wallet1: rGx6SACvYEvX8SRrvTPD91UhBmJ16pxL94, Balance: 100 XRP
|
||||
Wallet2: r3qetgSfAtyCpGc4rvKNz4LX3F3urMSJJy, Balance: 100 XRP
|
||||
|
||||
=== Creating Batch transaction... ===
|
||||
{
|
||||
"TransactionType": "Batch",
|
||||
"Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
|
||||
"Flags": 65536,
|
||||
"RawTransactions": [
|
||||
{
|
||||
"RawTransaction": {
|
||||
"TransactionType": "Payment",
|
||||
"Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
|
||||
"Destination": "rGx6SACvYEvX8SRrvTPD91UhBmJ16pxL94",
|
||||
"Amount": "2000000",
|
||||
"Flags": 1073741824
|
||||
}
|
||||
},
|
||||
{
|
||||
"RawTransaction": {
|
||||
"TransactionType": "Payment",
|
||||
"Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
|
||||
"Destination": "r3qetgSfAtyCpGc4rvKNz4LX3F3urMSJJy",
|
||||
"Amount": "5000000",
|
||||
"Flags": 1073741824
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
=== Submitting Batch transaction... ===
|
||||
|
||||
Batch transaction submitted successfully!
|
||||
Result:
|
||||
{
|
||||
"close_time_iso": "2025-11-17T12:04:50Z",
|
||||
"ctid": "C013313800030002",
|
||||
"hash": "AE118213B0A183528418ABC5F14E3BFD6524020C5DB1C060157A0D3FDE15B900",
|
||||
"ledger_hash": "621183809B68A794371C5EC6522105FF04E502C48EBDC8171B80224991E33394",
|
||||
"ledger_index": 1257784,
|
||||
"meta": {
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
|
||||
"Balance": "99999996",
|
||||
"Flags": 0,
|
||||
"OwnerCount": 0,
|
||||
"Sequence": 1257779
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"LedgerIndex": "42CC98AF0A28EDDDC7E359B5622CC5748BDE2A93E124AF5C32647ECA8F68D480",
|
||||
"PreviousFields": {
|
||||
"Balance": "100000000",
|
||||
"Sequence": 1257778
|
||||
},
|
||||
"PreviousTxnID": "081C42DAE12001735AC4E9A7F027636DF612DB17B4BFA2333F4DB8EA0C9D1E9F",
|
||||
"PreviousTxnLgrSeq": 1257778
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionIndex": 3,
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
},
|
||||
"tx_json": {
|
||||
"Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
|
||||
"Fee": "4",
|
||||
"Flags": 65536,
|
||||
"LastLedgerSequence": 1257802,
|
||||
"RawTransactions": [
|
||||
{
|
||||
"RawTransaction": {
|
||||
"Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
|
||||
"Amount": "2000000",
|
||||
"Destination": "rGx6SACvYEvX8SRrvTPD91UhBmJ16pxL94",
|
||||
"Fee": "0",
|
||||
"Flags": 1073741824,
|
||||
"Sequence": 1257779,
|
||||
"SigningPubKey": "",
|
||||
"TransactionType": "Payment"
|
||||
}
|
||||
},
|
||||
{
|
||||
"RawTransaction": {
|
||||
"Account": "rP9EsVosrmx2HyrmLgWJpJacX5ZrVVQsim",
|
||||
"Amount": "5000000",
|
||||
"Destination": "r3qetgSfAtyCpGc4rvKNz4LX3F3urMSJJy",
|
||||
"Fee": "0",
|
||||
"Flags": 1073741824,
|
||||
"Sequence": 1257780,
|
||||
"SigningPubKey": "",
|
||||
"TransactionType": "Payment"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Sequence": 1257778,
|
||||
"SigningPubKey": "ED7031CA5BA4EC745610AB495F5053F318C119E87567BE485A494773AD8ED4FBCE",
|
||||
"TransactionType": "Batch",
|
||||
"TxnSignature": "0610A277086943BC462C1A5F85BEB667B62B4BDA59525138B6014101C08297897A73D3D2D247CB37A06E1EA36267C53A51C0FDF32F3D8E974029BEDC41105B07",
|
||||
"ctid": "C013313800030002",
|
||||
"date": 816696290,
|
||||
"ledger_index": 1257784
|
||||
},
|
||||
"validated": true
|
||||
}
|
||||
|
||||
Batch transaction URL:
|
||||
https://devnet.xrpl.org/transactions/AE118213B0A183528418ABC5F14E3BFD6524020C5DB1C060157A0D3FDE15B900
|
||||
|
||||
=== 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
|
||||
|
||||
```sh
|
||||
npm install xrpl
|
||||
node multiAccountBatch.js
|
||||
```
|
||||
|
||||
The script should output the following:
|
||||
|
||||
```sh
|
||||
=== 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... ===
|
||||
{
|
||||
"TransactionType": "Batch",
|
||||
"Account": "rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA",
|
||||
"Flags": 65536,
|
||||
"RawTransactions": [
|
||||
{
|
||||
"RawTransaction": {
|
||||
"TransactionType": "Payment",
|
||||
"Account": "rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA",
|
||||
"Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG",
|
||||
"Amount": "50000000",
|
||||
"Flags": 1073741824
|
||||
}
|
||||
},
|
||||
{
|
||||
"RawTransaction": {
|
||||
"TransactionType": "Payment",
|
||||
"Account": "r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq",
|
||||
"Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG",
|
||||
"Amount": "50000000",
|
||||
"Flags": 1073741824
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
=== Submitting Batch transaction... ===
|
||||
|
||||
Batch transaction submitted successfully!
|
||||
Result:
|
||||
{
|
||||
"close_time_iso": "2025-11-17T12:08:31Z",
|
||||
"ctid": "C013317600000002",
|
||||
"hash": "1299D20C6B489DA5C632AE4DBE49475DBF42D9444C7E9C109CC9B8DD0FD55FEC",
|
||||
"ledger_hash": "E45ECF69057084CD02BA49A17E4D0C9154D33A98BB3C95A11B2EB9BE18F32C9B",
|
||||
"ledger_index": 1257846,
|
||||
"meta": {
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA",
|
||||
"Balance": "99999994",
|
||||
"Flags": 0,
|
||||
"OwnerCount": 0,
|
||||
"Sequence": 1257845
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"LedgerIndex": "2D9E0A02007241C38A8DF679E7E62AA0B273E8B12A5430B7B9D99300424F0E1F",
|
||||
"PreviousFields": {
|
||||
"Balance": "100000000",
|
||||
"Sequence": 1257844
|
||||
},
|
||||
"PreviousTxnID": "3153DE8DE922538A6BE54AA8F783CAD4B848A321AFF028D3E6DD0E80C4B9C237",
|
||||
"PreviousTxnLgrSeq": 1257844
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionIndex": 0,
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
},
|
||||
"tx_json": {
|
||||
"Account": "rfUpGXTzU3siTr4UovV6Wt86Vw3gQU4ttA",
|
||||
"BatchSigners": [
|
||||
{
|
||||
"BatchSigner": {
|
||||
"Account": "rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA",
|
||||
"SigningPubKey": "EDEB88C2868BD25BF03DB26050E16579FA6F8F9E3FF3172E0DC3DCBDA5408572EB",
|
||||
"TxnSignature": "9508568084596147CFDCFC18A62DC298A78AD1148BA4B0EB99BEE1CD37E5555FE3930810790D5708F9739B0E3F79772012C154CA33C2280BDD5B72473C17A607"
|
||||
}
|
||||
},
|
||||
{
|
||||
"BatchSigner": {
|
||||
"Account": "r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq",
|
||||
"SigningPubKey": "ED82F98DA6A3FC3E88D2EE3A5469D92C7070513BEF4DEE75CAB0BDAA81E8AE378D",
|
||||
"TxnSignature": "A482C8747F79857530474F1677599766C0BE283CB7E2A05AACF76E61BECCA16DCE3802D2D8244FBF4546A1C0E5EB70691255E3EFD2F8AC80B55357BDAB9ACD05"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Fee": "6",
|
||||
"Flags": 65536,
|
||||
"LastLedgerSequence": 1257864,
|
||||
"RawTransactions": [
|
||||
{
|
||||
"RawTransaction": {
|
||||
"Account": "rsi5D9bkczpbGykPxoGNBVVmFFFXGwm3QA",
|
||||
"Amount": "50000000",
|
||||
"Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG",
|
||||
"Fee": "0",
|
||||
"Flags": 1073741824,
|
||||
"Sequence": 1257842,
|
||||
"SigningPubKey": "",
|
||||
"TransactionType": "Payment"
|
||||
}
|
||||
},
|
||||
{
|
||||
"RawTransaction": {
|
||||
"Account": "r3ruQ92bqXwWxcR2w4cC1tW35og9h3UbBq",
|
||||
"Amount": "50000000",
|
||||
"Destination": "rHpve1GL2ZXUs3NB5iU91BrXBSwb5PbBrG",
|
||||
"Fee": "0",
|
||||
"Flags": 1073741824,
|
||||
"Sequence": 1257841,
|
||||
"SigningPubKey": "",
|
||||
"TransactionType": "Payment"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Sequence": 1257844,
|
||||
"SigningPubKey": "ED22A32B61EDF083315515831723BC18F8311F03886BBA375DFF46335BB7A75F0B",
|
||||
"TransactionType": "Batch",
|
||||
"TxnSignature": "156791D2DBFAEFC9B0AC29F2D8D0CDB25E13F92E70E6D5414FE31BD8573CA23D3F62F8B34FC1F117BD556B25E4F748095A24C4342108AB32F1B2BAFBF1443501",
|
||||
"ctid": "C013317600000002",
|
||||
"date": 816696511,
|
||||
"ledger_index": 1257846
|
||||
},
|
||||
"validated": true
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
=== 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
|
||||
```
|
||||
@@ -1,143 +0,0 @@
|
||||
/**
|
||||
* XRP Ledger Batch Transactions Tutorial
|
||||
*
|
||||
* 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"
|
||||
|
||||
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 },
|
||||
{ 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`)
|
||||
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 (requires 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: xrpl.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: xrpl.GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED
|
||||
}
|
||||
|
||||
// Send Batch transaction --------------------------------------------
|
||||
console.log("\n=== Creating Batch transaction... ===")
|
||||
const batchTx = {
|
||||
TransactionType: "Batch",
|
||||
Account: thirdPartyWallet.address,
|
||||
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 },
|
||||
{ RawTransaction: bobPayment },
|
||||
]
|
||||
}
|
||||
console.log(JSON.stringify(batchTx, null, 2))
|
||||
|
||||
// Validate the transaction structure
|
||||
xrpl.validate(batchTx)
|
||||
|
||||
// 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 --------------------------------
|
||||
// Each signer needs their own tx copy because signMultiBatch modifies the object.
|
||||
// Charlie signs the Batch transaction
|
||||
const charlieBatch = { ...autofilledBatchTx }
|
||||
xrpl.signMultiBatch(charlie, charlieBatch)
|
||||
|
||||
// Bob signs the Batch transaction
|
||||
const bobBatch = { ...autofilledBatchTx }
|
||||
xrpl.signMultiBatch(bob, bobBatch)
|
||||
|
||||
// Combine inner transaction signatures.
|
||||
// This returns a signed transaction blob (hex string) ready for submission.
|
||||
const combinedSignedTx = xrpl.combineBatchSigners([charlieBatch, bobBatch])
|
||||
|
||||
// Submit the signed blob with the third-party's wallet
|
||||
console.log("\n=== Submitting 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}`)
|
||||
await client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
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. ---")
|
||||
await client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Verify balances after 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`)
|
||||
|
||||
await client.disconnect()
|
||||
@@ -1,120 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
* 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"
|
||||
|
||||
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 }, { 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`)
|
||||
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 (requires Fee: 0 and empty SigningPubKey).
|
||||
|
||||
// Transaction 1
|
||||
const payment1 = {
|
||||
TransactionType: "Payment",
|
||||
Account: sender.address,
|
||||
Destination: wallet1.address,
|
||||
Amount: xrpl.xrpToDrops(2),
|
||||
Flags: xrpl.GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED
|
||||
}
|
||||
|
||||
// Transaction 2
|
||||
const payment2 = {
|
||||
TransactionType: "Payment",
|
||||
Account: sender.address,
|
||||
Destination: wallet2.address,
|
||||
Amount: xrpl.xrpToDrops(5),
|
||||
Flags: xrpl.GlobalFlags.tfInnerBatchTxn // THIS IS REQUIRED
|
||||
}
|
||||
|
||||
// Send Batch transaction --------------------------------------------
|
||||
console.log("\n=== Creating Batch transaction... ===")
|
||||
const batchTx = {
|
||||
TransactionType: "Batch",
|
||||
Account: sender.address,
|
||||
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 },
|
||||
{ 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("\n=== Submitting Batch transaction... ===")
|
||||
const submitResponse = await client.submitAndWait(batchTx, {
|
||||
wallet: sender,
|
||||
// "autofill" will automatically add Fee: "0" and SigningPubKey: "" to inner transactions.
|
||||
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}`)
|
||||
await client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
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. ---")
|
||||
await client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Verify balances after 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`)
|
||||
|
||||
await client.disconnect()
|
||||
4
_code-samples/walk-owner-directory/README.md
Normal file
4
_code-samples/walk-owner-directory/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Walk Owner Directory
|
||||
Iterate over an account's owner directory and display how many ledger entries are in each page. In cases of highly active accounts, this can demonstrate the extent of "fragmentation" with skipped page numbers and non-full pages.
|
||||
|
||||
This code sample demonstrates the low-level structure of owner directories. If you don't need to see the breakdown by pages, you can use [`account_objects`](https://xrpl.org/docs/references/http-websocket-apis/public-api-methods/account-methods/account_objects) instead, since it provides a more convenient list of ledger entries attached to an account.
|
||||
@@ -0,0 +1,55 @@
|
||||
// iterate-owner-directory.js
|
||||
// Iterate over an account's owner directory and display how many ledger entries
|
||||
// are in each page. In cases of highly active accounts, it can demonstrate
|
||||
// the extent of "fragmentation" with skipped page numbers and non-full pages.
|
||||
|
||||
import xrpl from 'xrpl'
|
||||
|
||||
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
|
||||
await client.connect()
|
||||
|
||||
const owner = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" // Testnet faucet
|
||||
// const owner = "rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd" // TST issuer
|
||||
|
||||
// Set initial values for iterating
|
||||
let sub_index = 0 // Directory root
|
||||
let ledger_index = "validated"
|
||||
|
||||
// Query pages from the owner directory until they run out
|
||||
console.log("Page #\t\t\tEntry count")
|
||||
console.log("-----------------------------------")
|
||||
while (true) {
|
||||
// console.log(`Getting directory page ${sub_index}`)
|
||||
const resp = await client.request({
|
||||
"command": "ledger_entry",
|
||||
"directory": {
|
||||
"owner": owner,
|
||||
"sub_index": sub_index
|
||||
},
|
||||
"ledger_index": ledger_index
|
||||
})
|
||||
if (resp.error) {
|
||||
console.error("ledger_entry failed with error",resp.error)
|
||||
break
|
||||
}
|
||||
|
||||
// Consistently iterate the same ledger: query by index after the first
|
||||
if (ledger_index === "validated") {
|
||||
ledger_index = resp.result.ledger_index
|
||||
}
|
||||
|
||||
console.log(`${sub_index}\t\t\t${resp.result.node.Indexes.length}`)
|
||||
// console.log(`This page contains ${resp.result.node.Indexes.length} items.`)
|
||||
|
||||
// Continue onto another page if this one has more
|
||||
if (resp.result.node.hasOwnProperty("IndexNext")) {
|
||||
// The directory continues onto another page.
|
||||
// IndexNext is returned as hex but sub_index needs decimal
|
||||
sub_index = parseInt(resp.result.node.IndexNext, 16)
|
||||
} else {
|
||||
console.info("This is the last page of the directory")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
client.disconnect()
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"xrpl": "^4.4.3"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
# iterate-owner-directory.py
|
||||
# Iterate over an account's owner directory and display how many ledger entries
|
||||
# are in each page. In cases of highly active accounts, it can demonstrate
|
||||
# the extent of "fragmentation" with skipped page numbers and non-full pages.
|
||||
from xrpl.clients import JsonRpcClient
|
||||
from xrpl.models.requests import LedgerEntry
|
||||
from xrpl.clients import XRPLRequestFailureException
|
||||
|
||||
OWNER_ADDRESS = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Testnet faucet
|
||||
# OWNER_ADDRESS = "rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd" # TST issuer
|
||||
|
||||
client = JsonRpcClient("https://s.altnet.rippletest.net:51234/")
|
||||
|
||||
# Set initial values for iterating
|
||||
sub_index = 0 # Directory root
|
||||
ledger_index = "validated"
|
||||
|
||||
# Query pages from the owner directory until they run out
|
||||
print("Page #\t\t\tEntry count")
|
||||
print("-----------------------------------")
|
||||
while True:
|
||||
# Construct the LedgerEntry request for the directory page
|
||||
directory_request = LedgerEntry(
|
||||
directory={
|
||||
"owner": OWNER_ADDRESS,
|
||||
"sub_index": sub_index
|
||||
},
|
||||
ledger_index=ledger_index
|
||||
)
|
||||
|
||||
# Send the request
|
||||
try:
|
||||
response = client.request(directory_request)
|
||||
except Exception as e:
|
||||
print(f"\nError: ledger_entry failed: {e}")
|
||||
break
|
||||
|
||||
# The 'ledger_index' is consistently set after the first successful query.
|
||||
# This ensures subsequent pages are read from the same ledger version.
|
||||
if ledger_index == "validated":
|
||||
ledger_index = response.result["ledger_index"]
|
||||
|
||||
# The entries are stored in the 'Indexes' field of the 'DirectoryNode'
|
||||
entry_count = len(response.result["node"]["Indexes"])
|
||||
print(f"{sub_index}\t\t\t{entry_count}")
|
||||
|
||||
# Check for the next page indicator
|
||||
if "IndexNext" in response.result["node"].keys():
|
||||
# The directory continues onto another page.
|
||||
# Convert IndexNext from hex to decimal for sub_index.
|
||||
hex_next = response.result["node"]["IndexNext"]
|
||||
sub_index = int(hex_next, 16)
|
||||
else:
|
||||
print("\nThis is the last page of the directory.")
|
||||
break
|
||||
1
_code-samples/walk-owner-directory/py/requirements.txt
Normal file
1
_code-samples/walk-owner-directory/py/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
xrpl-py==4.3.1
|
||||
@@ -1,70 +0,0 @@
|
||||
---
|
||||
category: 2025
|
||||
date: "2025-11-19"
|
||||
template: '../../@theme/templates/blogpost'
|
||||
seo:
|
||||
title: Introducing XRP Ledger version 2.6.2
|
||||
description: rippled version 2.6.2 is now available. This version contains a new amendment and a critical bug fix.
|
||||
labels:
|
||||
- rippled Release Notes
|
||||
markdown:
|
||||
editPage:
|
||||
hide: true
|
||||
---
|
||||
# Introducing XRP Ledger version 2.6.2
|
||||
|
||||
Version 2.6.2 of `rippled`, the reference server implementation of the XRP Ledger protocol, is now available. This release adds a new `fixDirectoryLimit` amendment and a critical bug fix.
|
||||
|
||||
|
||||
## Action Required
|
||||
|
||||
If you run an XRP Ledger server, upgrade to version 2.6.2 as soon as possible to ensure service continuity.
|
||||
|
||||
|
||||
## Install / Upgrade
|
||||
|
||||
On supported platforms, see the [instructions on installing or updating `rippled`](../../docs/infrastructure/installation/index.md).
|
||||
|
||||
| Package | SHA-256 |
|
||||
|:--------|:--------|
|
||||
| [RPM for Red Hat / CentOS (x86-64)](https://repos.ripple.com/repos/rippled-rpm/stable/rippled-2.6.2-1.el9.x86_64.rpm) | `e3b041906a75c3c52cc6423219d7ba9c199a5d736d2e3978a5ce0ac5ef693fdf` |
|
||||
| [DEB for Ubuntu / Debian (x86-64)](https://repos.ripple.com/repos/rippled-deb/pool/stable/rippled_2.6.2-1_amd64.deb) | `0887b5a77c43c362ea7680b83df40b955a5748b712924acf2212b2de29e3373b` |
|
||||
|
||||
For other platforms, please [build from source](https://github.com/XRPLF/rippled/blob/master/BUILD.md). The most recent commit in the git log should be the change setting the version:
|
||||
|
||||
```text
|
||||
commit df24ee077438e03673a9c6661c41e8f070b90cd9
|
||||
Author: Vladislav Vysokikh <vvysokikh@gmail.com>
|
||||
Date: Tue Nov 18 09:28:59 2025 +0000
|
||||
|
||||
Version 2.6.2
|
||||
```
|
||||
|
||||
|
||||
## Full Changelog
|
||||
|
||||
### Amendments
|
||||
|
||||
The following amendment is open for voting with this release:
|
||||
|
||||
- **fixDirectoryLimit** - Removes directory page limits. Object reserve requirements provide enough incentive to avoid creating unnecessary objects on the XRP Ledger. ([#5935](https://github.com/XRPLF/rippled/pull/5935))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed an assertion failure when all the inner transactions of a `Batch` transaction were invalid. ([#5670](https://github.com/XRPLF/rippled/pull/5670))
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
The following GitHub users contributed to this release:
|
||||
|
||||
- RippleX Engineering
|
||||
- RippleX Docs
|
||||
- RippleX Product
|
||||
|
||||
|
||||
## Bug Bounties and Responsible Disclosures
|
||||
|
||||
We welcome reviews of the `rippled` code and urge researchers to responsibly disclose any issues they may find.
|
||||
|
||||
To report a bug, please send a detailed report to: <bugs@xrpl.org>
|
||||
@@ -10,7 +10,6 @@
|
||||
- group: '2025'
|
||||
expanded: false
|
||||
items:
|
||||
- page: 2025/rippled-2.6.2.md
|
||||
- page: 2025/rippled-2.6.1.md
|
||||
- page: 2025/vulnerabilitydisclosurereport-bug-sep2025.md
|
||||
- page: 2025/devnet-reset-oct.md
|
||||
|
||||
@@ -27,7 +27,7 @@ A transaction that fails with a `tec` code destroys the XRP paid as a [transacti
|
||||
| `tecCANT_ACCEPT_OWN_NFTOKEN_OFFER` | 157 | The transaction tried to accept an offer that was placed by the same account to buy or sell a [non-fungible token](../../../../concepts/tokens/nfts/index.md). {% amendment-disclaimer name="NonFungibleTokensV1_1" /%} |
|
||||
| `tecCLAIM` | 100 | Unspecified failure, with transaction cost destroyed. |
|
||||
| `tecCRYPTOCONDITION_ERROR` | 146 | This [EscrowCreate][] or [EscrowFinish][] transaction contained a malformed or mismatched crypto-condition. |
|
||||
| `tecDIR_FULL` | 121 | The transaction tried to add an object (such as a trust line, Check, Escrow, or Payment Channel) to an account's owner directory, but that account cannot own any more objects in the ledger.<br>This error is effectively impossible to receive if {% amendment-disclaimer name="fixDirectoryLimit" compact=true /%} is enabled. |
|
||||
| `tecDIR_FULL` | 121 | The transaction tried to add an object (such as a trust line, Check, Escrow, or Payment Channel) to an account's owner directory, but that account cannot own any more objects in the ledger. |
|
||||
| `tecDUPLICATE` | 149 | The transaction tried to create an object (such as a [DepositPreauth][] authorization) that already exists. |
|
||||
| `tecDST_TAG_NEEDED` | 143 | The [Payment transaction][] omitted a [destination tag](../../../../concepts/transactions/source-and-destination-tags.md), but the destination account has the `lsfRequireDestTag` flag enabled. |
|
||||
| `tecEMPTY_DID` | 187 | The transaction tried to create a [DID entry][] with no contents. A DID must not be empty. {% amendment-disclaimer name="DID" /%} |
|
||||
|
||||
@@ -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. See [RawTransactions](#rawtransactions). |
|
||||
| `RawTransactions` | Array | Array | Yes | The list of transactions to apply. |
|
||||
| `BatchSigners` | Array | Array | No | The signatures authorizing a multi-account `Batch` transaction. |
|
||||
|
||||
### RawTransactions
|
||||
|
||||
`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.
|
||||
`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.
|
||||
|
||||
Each inner transaction:
|
||||
|
||||
@@ -169,7 +169,6 @@ 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. |
|
||||
|
||||
|
||||
@@ -40,18 +40,14 @@ Create an on-ledger [check](../../../../concepts/payment-types/checks.md), which
|
||||
|
||||
## Error Cases
|
||||
|
||||
Besides errors that can occur for all transactions, {% $frontmatter.seo.title %} transactions can result in the following [transaction result codes](../transaction-results/index.md):
|
||||
|
||||
| Error Code | Description |
|
||||
|:-----------|:------------|
|
||||
| `tecNO_PERMISSION` | The `Destination` account is blocking incoming Checks. {% amendment-disclaimer name="DisallowIncoming" /%} |
|
||||
| `temREDUNDANT` | The `Destination` is the sender of the transaction. |
|
||||
| `tecNO_DST` | The `Destination` [account](../../../../concepts/accounts/index.md) does not exist in the ledger. |
|
||||
| `tecDST_TAG_NEEDED` | The `Destination` account has the `RequireDest` flag enabled but the transaction does not include a `DestinationTag` field. |
|
||||
| `tecFROZEN` | `SendMax` specifies a token which is [frozen](../../../../concepts/tokens/fungible-tokens/freezes.md). |
|
||||
| `tecEXPIRED` | The `Expiration` of the transaction is in the past. |
|
||||
| `tecINSUFFICIENT_RESERVE` | The sender does not have enough XRP to meet the [owner reserve](../../../../concepts/accounts/reserves.md#owner-reserves) after adding the Check. |
|
||||
| `tecDIR_FULL` | Either the sender or the destination of the Check cannot own more objects in the ledger.<br>This error is effectively impossible to receive if {% amendment-disclaimer name="fixDirectoryLimit" compact=true /%} is enabled. |
|
||||
- If the `Destination` account is blocking incoming Checks, the transaction fails with the result code `tecNO_PERMISSION`. {% amendment-disclaimer name="DisallowIncoming" /%}
|
||||
- If the `Destination` is the sender of the transaction, the transaction fails with the result code `temREDUNDANT`.
|
||||
- If the `Destination` [account](../../../../concepts/accounts/index.md) does not exist in the ledger, the transaction fails with the result code `tecNO_DST`.
|
||||
- If the `Destination` account has the `RequireDest` flag enabled but the transaction does not include a `DestinationTag` field, the transaction fails with the result code `tecDST_TAG_NEEDED`.
|
||||
- If `SendMax` specifies a token which is [frozen](../../../../concepts/tokens/fungible-tokens/freezes.md), the transaction fails with the result `tecFROZEN`.
|
||||
- If the `Expiration` of the transaction is in the past, the transaction fails with the result `tecEXPIRED`.
|
||||
- If the sender does not have enough XRP to meet the [owner reserve](../../../../concepts/accounts/reserves.md#owner-reserves) after adding the Check, the transaction fails with the result `tecINSUFFICIENT_RESERVE`.
|
||||
- If either the sender or the destination of the Check cannot own more objects in the ledger, the transaction fails with the result `tecDIR_FULL`.
|
||||
|
||||
## See Also
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ Besides errors that can occur for all transactions, {% $frontmatter.seo.title %}
|
||||
|
||||
| Error Code | Description |
|
||||
|:--------------------------|:------------|
|
||||
| `tecDIR_FULL` | The sender owns too many items in the ledger already.<br>This error is effectively impossible to receive if {% amendment-disclaimer name="fixDirectoryLimit" compact=true /%} is enabled. |
|
||||
| `tecDIR_FULL` | The sender owns too many items in the ledger already. |
|
||||
| `tecINSUFFICIENT_RESERVE` | The sender does not have enough XRP to meet the [reserve requirement](/docs/concepts/accounts/reserves.md) of creating a new Delegate ledger entry. |
|
||||
| `tecNO_PERMISSION` | At least one permission in the `Permissions` list is not delegatable. See [Permission Values](../../data-types/permission-values.md) for which permissions are not delegatable. |
|
||||
| `tecNO_TARGET` | The account specified in the `Authorize` field does not exist in the ledger. |
|
||||
|
||||
@@ -56,7 +56,7 @@ Besides errors that can occur for all transactions, {% $frontmatter.seo.title %}
|
||||
| `temDISABLED` | The [NonFungibleTokensV1 amendment][] is not enabled. |
|
||||
| `temBAD_AMOUNT` | The `Amount` field is not valid. For example, the amount was zero for a buy offer, or the amount is denominated in fungible tokens but the `NFToken` has the [`lsfOnlyXRP` flag](../../data-types/nftoken.md#nftoken-flags) enabled. |
|
||||
| `temBAD_EXPIRATION` | The specified `Expiration` time is invalid (for example, `0`). |
|
||||
| `tecDIR_FULL` | The sender already owns too many objects in the ledger, or there are already too many offers to buy or sell this token.<br>This error is effectively impossible to receive if {% amendment-disclaimer name="fixDirectoryLimit" compact=true /%} is enabled. |
|
||||
| `tecDIR_FULL` | The sender already owns too many objects in the ledger, or there are already too many offers to buy or sell this token. |
|
||||
| `tecEXPIRED` | The specified `Expiration` time has already passed. |
|
||||
| `tecFROZEN` | The `Amount` is denominated in fungible tokens, but one of the trust lines that would receive tokens from this offer is [frozen](../../../../concepts/tokens/fungible-tokens/freezes.md). This could be the seller's trust line or the `NFToken`'s issuer's trust line (if the `NFToken` has a transfer fee). |
|
||||
| `tecINSUFFICIENT_RESERVE` | The sender does not have enough XRP to meet the [reserve requirement](../../../../concepts/accounts/reserves.md) after placing this offer. |
|
||||
|
||||
@@ -58,7 +58,7 @@ Transactions of the OfferCreate type support additional values in the [`Flags` f
|
||||
|
||||
| Error Code | Description |
|
||||
|:-------------------------|:--------------------------------------------------|
|
||||
| `tecDIR_FULL` | The owner owns too many items in the ledger, or the order book contains too many Offers at the same exchange rate already.<br>This error is effectively impossible to receive if {% amendment-disclaimer name="fixDirectoryLimit" compact=true /%} is enabled. |
|
||||
| `tecDIR_FULL` | The owner owns too many items in the ledger, or the order book contains too many Offers at the same exchange rate already. |
|
||||
| `tecEXPIRED` | The transaction specifies an `Expiration` time that has already passed. |
|
||||
| `tecFROZEN` | The transaction involves a token on a [frozen](../../../../concepts/tokens/fungible-tokens/freezes.md) trust line (including local and global freezes). The `TakerPays` (buy amount) token has been deep-frozen by the issuer. |
|
||||
| `tecINSUF_RESERVE_OFFER` | The owner does not have enough XRP to meet the reserve requirement of adding a new offer ledger entry, and the transaction did not convert any currency. (If the transaction successfully traded any amount, the transaction succeeds with the result code `tesSUCCESS`, but does not create an offer ledger entry for the remainder.) |
|
||||
|
||||
@@ -55,7 +55,7 @@ Besides errors that can occur for all transactions, {% $frontmatter.seo.title %}
|
||||
|
||||
| Error Code | Description |
|
||||
|:--------------------------|:------------|
|
||||
| `tecDIR_FULL` | The transaction would create a new PermissionedDomain, but the sender's owner directory is full.<br>This error is effectively impossible to receive if {% amendment-disclaimer name="fixDirectoryLimit" compact=true /%} is enabled. |
|
||||
| `tecDIR_FULL` | The transaction would create a new PermissionedDomain, but the sender's owner directory is full. |
|
||||
| `tecINSUFFICIENT_RESERVE` | The transaction would create a new PermissionedDomain, but the sender does not have enough XRP to meet the increased owner reserve. |
|
||||
| `tecNO_ENTRY` | The transaction attempted to modify a Domain that does not exist. Check the `DomainID` field of the transaction. |
|
||||
| `tecNO_ISSUER` | At least one of the issuers specified in the `AcceptedCredentials` field is does not exist in the XRP Ledger. Check the `Issuer` field of each member of the array. |
|
||||
|
||||
@@ -43,7 +43,7 @@ Besides errors that can occur for all transactions, {% $frontmatter.seo.title %}
|
||||
| Error Code | Description |
|
||||
|:--------------------------|:-------------------------------------------------|
|
||||
| `temINVALID_COUNT` | The `TicketCount` field is invalid. It must be an integer from 1 to 250. |
|
||||
| `tecDIR_FULL` | This transaction would cause the account to own more than the limit of 250 Tickets at a time, or more than the maximum number of ledger objects in general.<br>The maximum ledger objects error is effectively impossible to receive if {% amendment-disclaimer name="fixDirectoryLimit" compact=true /%} is enabled. |
|
||||
| `tecDIR_FULL` | This transaction would cause the account to own more than the limit of 250 Tickets at a time, or more than the maximum number of ledger objects in general. |
|
||||
| `tecINSUFFICIENT_RESERVE` | The sending account does not have enough XRP to meet the [owner reserve](../../../../concepts/accounts/reserves.md) of all the requested Tickets. |
|
||||
|
||||
## See Also
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
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 /%}
|
||||
@@ -1,172 +0,0 @@
|
||||
---
|
||||
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. 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 %}
|
||||
{% 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 %}
|
||||
|
||||
### 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="// Calculate and verify" /%}
|
||||
{% /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 %}
|
||||
|
||||
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 %}
|
||||
{% tab label="Javascript" %}
|
||||
{% code-snippet file="/_code-samples/batch/js/multiAccountBatch.js" language="js" from="// Verify balances after transaction" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
## 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" /%}
|
||||
@@ -1,162 +0,0 @@
|
||||
---
|
||||
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. 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" %}
|
||||
{% 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="// Calculate and verify" /%}
|
||||
{% /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 %}
|
||||
|
||||
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 %}
|
||||
{% tab label="Javascript" %}
|
||||
{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Verify balances after transaction" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
## 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" /%}
|
||||
@@ -42,6 +42,9 @@ metadataGlobs:
|
||||
redocly_category: Community
|
||||
seo:
|
||||
siteUrl: https://xrpl.org/
|
||||
rbac:
|
||||
reunite:
|
||||
xrpl-org-editors: maintain
|
||||
analytics:
|
||||
gtm:
|
||||
includeInDevelopment: true
|
||||
|
||||
@@ -742,19 +742,6 @@ Changes the way Checks transactions affect account metadata, so that Checks are
|
||||
Without this amendment, Checks transactions ([CheckCreate][], [CheckCash][], and [CheckCancel][]) only update the account history of the sender. With this amendment, those transactions affect both the sending and receiving accounts. This amendment has no effect unless the [Checks amendment](#checks) is also enabled.
|
||||
|
||||
|
||||
### fixDirectoryLimit
|
||||
[fixDirectoryLimit]: #fixdirectorylimit
|
||||
|
||||
| Amendment | fixdirectorylimit |
|
||||
|:-------------|:----------------------|
|
||||
| Amendment ID | 41765F664A8D67FF03DDB1C1A893DE6273690BA340A6C2B07C8D29D0DD013D3A |
|
||||
| Status | Open for Voting |
|
||||
| Default Vote (Latest stable release) | No |
|
||||
| Pre-amendment functionality retired? | No |
|
||||
|
||||
This amendment removes the directory page limit. Object reserve requirements provide enough incentive to avoid creating unnecessary objects on the XRP Ledger.
|
||||
|
||||
|
||||
### fixDisallowIncomingV1
|
||||
[fixDisallowIncomingV1]: #fixdisallowincomingv1
|
||||
|
||||
|
||||
@@ -295,11 +295,6 @@
|
||||
- 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:
|
||||
|
||||
Reference in New Issue
Block a user