Browser Extension
Batch and Bulk Signing
Sign several transactions with one approval, or submit an atomic XLS-56 Batch.
Joey supports two different ways to handle several transactions at once. Don't confuse them:
Bulk (signTransactionBulk) |
XLS-56 Batch |
|
|---|---|---|
| What it is | Up to 32 separate, ordinary transactions approved together | One ledger transaction that carries 2–8 inner transactions |
| Atomic? | No. Earlier transactions stay applied if a later one fails. | According to the batch mode flag, for example all-or-nothing |
| How to send | joey.signTransactionBulk({ tx_list, submit }) |
signTransaction or signAndSubmitTransaction with TransactionType: 'Batch' |
| Availability | All versions | When joey.supportsBatch() is true (SDK 0.5.0+) |
Bulk signing
const results = await joey.signTransactionBulk({
tx_list: [{ tx_json: trustSet }, { tx_json: payment }],
submit: true,
})
- One approval and one password cover the whole list. The user can't approve some entries and reject others. With a Ledger account, each transaction is still confirmed on the device.
submit: truesigns everything first, then submits strictly in order.submit: falsereturns the signed blobs for you to submit.- Autofill reads the ledger once and assigns consecutive
Sequencenumbers, with staggeredLastLedgerSequencevalues. AnySequenceyou set yourself is kept.
Partial failure
If an entry fails during submission, the call rejects. The error's data describes every entry:
import type { JoeyRpcError, SignTransactionBulkFailure } from '@joeywallet/wallet-sdk'
try {
await joey.signTransactionBulk({ tx_list, submit: true })
} catch (e) {
const data = (e as JoeyRpcError).data as SignTransactionBulkFailure | undefined
if (!data) throw e
console.log('First entry that did not succeed:', data.failedIndex) // zero-based
for (const entry of data.results) {
switch (entry.status) {
case 'submitted': break // validated with tesSUCCESS
case 'failed': break // definitely failed; see entry.engine_result
case 'unknown': break // may still validate: check entry.hash, don't assume failure
case 'signed': break // never broadcast; can still be submitted as-is, in order
case 'stranded': break // never broadcast and can no longer apply; sign again
}
}
}
- The error message looks like
transaction at index 2 of 5 did not succeed: tecUNFUNDED_PAYMENT. - Joey watches each submitted entry for up to 10 minutes. Entries still pending after that come back as
unknown.
XLS-56 Batch
Check support first. An older extension refuses every Batch with 4100.
import { BATCH_FLAGS, TF_INNER_BATCH_TXN, type BatchTransaction } from '@joeywallet/wallet-sdk'
if (!joey.supportsBatch()) {
// Fall back to separate transactions
}
const batch: BatchTransaction = {
TransactionType: 'Batch',
Account: user,
Flags: BATCH_FLAGS.tfAllOrNothing, // exactly one mode flag
Sequence: n,
Fee: fee, // base fee × (2 + inner transactions + co-signers)
LastLedgerSequence: current + 15,
RawTransactions: [
{
RawTransaction: {
TransactionType: 'Payment', Account: user, Destination: a, Amount: '1000000',
Sequence: n + 1, Fee: '0', SigningPubKey: '', Flags: TF_INNER_BATCH_TXN,
},
},
{
RawTransaction: {
TransactionType: 'Payment', Account: user, Destination: b, Amount: '2000000',
Sequence: n + 2, Fee: '0', SigningPubKey: '', Flags: TF_INNER_BATCH_TXN,
},
},
],
}
const result = await joey.signAndSubmitTransaction({ tx_json: batch, autofill: false })
// Decide success from result.batch, never from engine_result alone
if (result.batch?.applied !== 'all') {
// 'none' | 'some' | 'unknown'
// Inspect result.batch.inner[i]: { hash, account, status, engine_result }
}
Warning
A rolled-back all-or-nothing batch still returns tesSUCCESS: the fee is charged but nothing is applied. Always check result.batch.applied.
Rules Joey enforces
Violations fail with -32602 before the user is asked.
- The approval screen shows every inner transaction separately, and each must pass the same refusal rules as a normal transaction.
- Inner transactions:
Fee: "0", emptySigningPubKey, no signature, thetfInnerBatchTxnflag set, and no nestedBatch. The user's inner transactions use the outerSequence+1, +2, … and have noLastLedgerSequence. - Exactly one mode flag, and the
Batchmust be the only transaction in its request. - Batches that include other accounts need
autofill: false, and a valid single-signatureBatchSignersentry for each other account. Joey never creates aBatchSigneritself. - Ledger accounts can't sign a
Batch. - With autofill on and no other accounts involved, Joey only fills fields you left out.