XRPL Batch, delegation, MPTs, vaults and loans: what to check before signing.
Three amendments that change what one signature can do are now enabled on the XRP Ledger mainnet: Batch (several transactions under one signature), permission delegation (another account signs for yours) and Multi-Purpose Tokens (MPTs, a second kind of token next to trust lines). An AI agent that signs XRPL transactions, or a wallet that shows them to a person, has to read them differently now. A check that only looks at the outer TransactionType sees Batch and misses what is inside. Two more are on their way: vaults and loans, which RLUSD credit funds are being built on.
This guide says what each one can hide, what to check before signing, and how presign-guard, our pre-sign check for agents, judges them.
Batch (XLS-56): read every inner transaction
A Batch wraps 2 to 8 transactions, the inner transactions, under one signature. A mode flag decides what happens when one of them fails:
tfAllOrNothing(65536): all succeed, or none is applied.tfOnlyOne(131072): only the first that succeeds is applied.tfUntilFailure(262144): applied in order until the first failure.tfIndependent(524288): each is applied whatever the others do.
Inner transactions can come from other accounts too; those accounts sign them in BatchSigners. That makes a swap between two accounts possible in one step.
What it can hide. Everything an ordinary transaction can do, next to something harmless. A Batch with a small payment and a SetRegularKey to someone else's key gives the account away, just as the SetRegularKey alone would. So does a SignerListSet with other signers, or an AccountDelete that sends all remaining XRP to the destination.
What to check.
- Judge every inner transaction as if it were signed on its own: a new key, signer list, account delete, partial payment, fake RLUSD, a destination that refuses it.
- When inner transactions come from more than one account, check the mode. Anything but all-or-nothing means your part can go through while the other account's part fails, for example because it lacks the funds.
- A Batch must hold 2 to 8 transactions, set exactly one mode flag and contain no other Batch; the ledger refuses anything else.
presign-guard does all three. Each reason carries details.inner, the place of the inner transaction, and details.account when another account sends it. A Batch that pays a shop and sets a regular key:
{
"type": "xrpl",
"network": "xrpl:0",
"tx": {
"TransactionType": "Batch",
"Account": "rYOUR…ACCOUNT",
"Flags": 65536,
"RawTransactions": [
{ "RawTransaction": { "TransactionType": "Payment", "Account": "rYOUR…ACCOUNT",
"Destination": "rSHOP…", "Amount": "2000000", "Flags": 1073741824, "Fee": "0", "SigningPubKey": "" } },
{ "RawTransaction": { "TransactionType": "SetRegularKey", "Account": "rYOUR…ACCOUNT",
"RegularKey": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe", "Flags": 1073741824, "Fee": "0", "SigningPubKey": "" } }
]
}
}
comes back red (other reasons left out):
{
"verdict": "red",
"reasons": [
{ "code": "XRPL_BATCH", "severity": "info",
"details": { "mode": "tfAllOrNothing", "transactions": ["Payment", "SetRegularKey"] } },
{ "code": "XRPL_REGULAR_KEY_CHANGE", "severity": "red",
"subject": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
"details": { "inner": 1,
"effect": "the regular key can sign every transaction for the account, including moving all its funds" } }
]
}
A Batch of several accounts that isn't all-or-nothing is orange, XRPL_BATCH_NOT_ATOMIC, with the mode and the accounts.
Permission delegation (XLS-75): who may sign for you
DelegateSet lets another account, the delegate, sign certain transaction types for yours, spending your funds, until you remove the permission. It takes up to 10 permissions in Permissions: transaction types such as Payment, or granular permissions such as TrustlineAuthorize or AccountDomainSet. The ledger refuses account-level types (SetRegularKey, SignerListSet, AccountSet, AccountDelete, DelegateSet, Batch), and an empty list removes every permission of that delegate.
What it can hide. A delegate needs no key of yours. With Payment it can pay itself; with CheckCreate, EscrowCreate or a payment channel it writes you a check, escrow or channel it can claim; with OfferCreate or an NFT offer it trades your funds against its own offer at any price. For an issuer, PaymentMint lets it create new tokens in your name.
What to check. Who the delegate is (and that it exists: otherwise the transaction fails), and whether any permission moves funds. Delegating those makes sense only to an account you control, such as your own agent's account with a small budget.
presign-guard calls permissions that move funds red, XRPL_DELEGATE_FUNDS, and others orange, XRPL_DELEGATE_SET; removing permissions is info. Permission values are read by name or by number (a transaction type's code plus 1, granular ones from 65537). Delegating Payment to another account:
{
"verdict": "red",
"reasons": [
{ "code": "XRPL_DELEGATE_FUNDS", "severity": "red",
"subject": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
"details": { "permissions": ["Payment"], "movesFunds": ["Payment"],
"effect": "this account can then move your funds, to itself too, without your signature until you remove the permission; unless you control it, this gives your funds away" } }
]
}
MPTs (XLS-33): tokens with the issuer's rules built in
A Multi-Purpose Token is issued once (MPTokenIssuanceCreate) with its rules as flags: whether the issuer can claw tokens back, lock them, require authorisation, and whether holders may transfer them to each other, plus a transfer fee of up to 50%. A holder opts in with MPTokenAuthorize. The issuance ID is 24 bytes: the issuer's sequence number and its account ID, so the issuer can be read from the ID itself. Its metadata (XLS-89) usually carries a ticker.
What to check before opting in. The same as for a trust line: can the issuer claw back, has it locked the token, what fee does it take, and does the issuance exist at all. A ticker of RLUSD from anyone but Ripple is a fake.
What to check before paying in an MPT. The payment fails when the issuer has locked the token or the destination's balance, when transfers between holders aren't allowed (only to and from the issuer), or when the destination hasn't opted in or isn't authorised.
presign-guard reports these as XRPL_ISSUER_CAN_CLAW_BACK, XRPL_ISSUER_FROZEN, XRPL_TRANSFER_FEE and XRPL_MPT_NOT_FOUND for an opt-in; XRPL_FAKE_RLUSD (red) for a fake ticker; and XRPL_MPT_LOCKED, XRPL_MPT_NOT_TRANSFERABLE, XRPL_DESTINATION_NO_MPTOKEN and XRPL_DESTINATION_NOT_AUTHORIZED for a payment. Every issuer, delegate, signer and destination is also screened against the OFAC SDN list.
Vaults and loans (XLS-65, XLS-66): checked before they go live
Two more amendments are in validator voting: Single Asset Vaults (XLS-65), pools that take deposits of one token and hand out shares, and the Lending Protocol (XLS-66), fixed-term loans paid out of a vault without collateral. On 20 October SingleAssetVault had 20 and LendingProtocol 18 of the 25 validator votes needed; an amendment goes live after it has held that majority for two weeks. Clearpool's RLUSD credit fund with Ripple and Cicada Partners is being built on them, tested on devnet for now. presign-guard already judges both, so an agent is covered from the first day.
How it works. A vault owner creates a vault for one asset. Depositors send VaultDeposit and get shares, an MPT; VaultWithdraw turns shares back into the asset. The owner can attach a loan broker and lend the vault's assets out: a LoanSet, signed by both the broker's owner and the borrower, pays the principal minus the origination fee to the borrower, who repays in fixed installments with LoanPay. If the borrower stops paying, the broker defaults the loan after the grace period: its first-loss capital covers part, the depositors take the rest. A closed-ended vault is a fixed-term fund: it takes deposits until its subscription date and allows no withdrawals until its redemption date.
What a deposit can hide.
- The vault lends your deposit out, unsecured, to borrowers its owner picks. The return comes with credit risk, and only the part not lent out can be withdrawn at a time.
- A fixed-term fund locks the deposit for months.
- A paper loss (
LossUnrealized, from loans that are overdue): a deposit is priced at the vault's full value, a withdrawal at the value minus the loss. Put in 1,000 and you can take out less straight away; if the loans default, the loss is real. - The vault's token: a fake RLUSD, or an issuer that can claw back.
What a withdrawal can hide. A Destination. VaultWithdraw can send what you withdraw to another account, so a request to "withdraw your funds" with someone else's address in it is a payment to them.
What a loan can hide. Its cost. The interest rate is only part of it: the origination fee comes off the principal, a service fee is added to every payment, and late interest, a late fee and a short grace period follow a missed payment. Work out what the borrower receives, what they pay back in total, and the yearly rate that comes to. Rates in a LoanSet are in 1/10 basis points: 18000 is 18%.
presign-guard reads the vault, loan broker or loan from the ledger. A deposit into a vault that lends is orange, XRPL_VAULT_LENDS, with the share lent out and the owner's first-loss cover; so are XRPL_VAULT_LOCKUP, XRPL_VAULT_PAPER_LOSS and XRPL_WITHDRAW_TO_OTHER. A LoanSet always gets the terms, info XRPL_LOAN_TERMS, and is orange, XRPL_LOAN_COSTLY, when interest and fees cost the borrower more than 36% a year. A transaction that would fail is orange too: a vault that doesn't exist or is full, a fund closed to deposits or in its term, more than the vault has available, a payment on someone else's loan or a late one without tfLoanLatePayment. Until the amendments are live, every vault and loan comes back as not found. The examples below ran against a stand-in ledger, since no vault exists on mainnet yet. A deposit of 1,000 RLUSD into a fixed-term fund with a loan broker:
{
"type": "xrpl",
"tx": {
"TransactionType": "VaultDeposit",
"Account": "rYOUR…ACCOUNT",
"VaultID": "9C4C5C0B…87980",
"Amount": { "currency": "524C555344000000000000000000000000000000",
"issuer": "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De", "value": "1000" }
}
}
comes back orange (other reasons left out):
{
"verdict": "orange",
"reasons": [
{ "code": "XRPL_VAULT_LOCKUP", "severity": "orange",
"details": { "token": "RLUSD",
"subscriptionEnds": "2026-11-21T00:00:00.000Z", "redemptionFrom": "2027-05-21T00:00:00.000Z" } },
{ "code": "XRPL_VAULT_LENDS", "severity": "orange",
"subject": "rVAULT…OWNER",
"details": { "token": "RLUSD", "loanBrokers": 1, "lentOutPct": 0,
"effect": "the vault lends deposits out without collateral, to borrowers its owner picks: a default beyond the owner's first-loss cover is a loss for the depositors, and only the part not lent out can be withdrawn at a time" } }
]
}
A three-month loan of 10,000 RLUSD at 18% (InterestRate: 18000), with a 500 origination fee and a service fee of 25 per payment, costs the borrower about 55% a year:
{
"verdict": "orange",
"reasons": [
{ "code": "XRPL_LOAN_COSTLY", "severity": "orange",
"details": { "token": "RLUSD", "principal": "10000", "borrowerReceives": 9500,
"totalRepaid": 10372.338856, "interestRatePct": 18, "effectiveAnnualPct": 55 } },
{ "code": "XRPL_LOAN_TERMS", "severity": "info",
"details": { "borrower": "rYOUR…ACCOUNT", "lender": "rVAULT…OWNER",
"payments": 3, "every": "30 days", "paymentEach": 3457.446285,
"lateInterestRatePct": 5, "latePaymentFee": "100", "gracePeriod": "3 days" } }
]
}
The vault owner, lender and borrower are screened against the OFAC SDN list like every other party.
How an agent checks a transaction
Free, verdict only. Add presign-guard's MCP server to your agent and call presign_quick_check with {"type": "xrpl", "tx": {…}}. For Claude Code:
claude mcp add --transport http presign-guard https://presign-guard.fizzl.eu/mcp
With the reasons, $0.01. presign_check over MCP, or POST https://presign-guard.fizzl.eu/v1/check with the body above. It answers 402 Payment Required first; an x402 client pays $0.01 in RLUSD on the XRP Ledger, or in USDC on Base, Solana, Algorand or Stellar, and gets the verdict. A failed check is never charged and never comes back green. Sign only on green; on orange, read the reasons first; on red, don't sign.
For agents that pay with their own wallet, presign-guard-wallet and the MCP server presign-guard-wallet-mcp run the check before every signature on their own.
Questions
What is an XRPL Batch transaction?
A Batch (XLS-56) wraps 2 to 8 transactions, the inner transactions, under one signature. A mode flag says what happens when one fails: all or nothing (tfAllOrNothing), only the first that succeeds (tfOnlyOne), until the first failure (tfUntilFailure), or each on its own (tfIndependent). Inner transactions of other accounts are signed by those accounts in BatchSigners.
Can a Batch hide an account takeover?
Yes. A SetRegularKey, SignerListSet or AccountDelete can sit next to an ordinary payment inside one Batch. A wallet or agent that only reads the outer TransactionType sees "Batch". Check every inner transaction as if it were signed on its own; presign-guard does that and calls a new regular key for someone else red, with the place of the inner transaction in details.inner.
What does DelegateSet do?
DelegateSet (XLS-75) lets another account, the delegate, sign certain transaction types for your account, with your funds, until you remove the permission. It takes up to 10 permissions: transaction types such as Payment, or granular ones such as TrustlineAuthorize. Account-level types (SetRegularKey, SignerListSet, AccountSet, AccountDelete) can't be delegated. An empty Permissions list removes them all.
Is delegating Payment to another account safe?
Only to an account you control. A delegate with Payment, CheckCreate, EscrowCreate, a payment channel, OfferCreate or NFT offer permission can send your funds to itself without your signature. presign-guard calls those permissions red (XRPL_DELEGATE_FUNDS) and other permissions orange (XRPL_DELEGATE_SET).
What should I check before holding an MPT?
Opting in to a Multi-Purpose Token (MPTokenAuthorize) is like a trust line: look at what the issuer can do. Its issuance shows whether it can claw tokens back, whether it has locked them, and its transfer fee (up to 50%). A ticker of RLUSD from anyone but Ripple is a fake. A payment in an MPT fails when it is locked, when the issuer doesn't allow transfers between holders, or when the destination hasn't opted in.
What should an agent check before depositing into an XRPL vault?
Whether the vault lends deposits out (loans are unsecured and the vault owner picks the borrowers), whether it is a fixed-term fund that locks the deposit until its redemption date, whether it carries a paper loss (LossUnrealized: deposits are priced at the full value, withdrawals at the value minus the loss), and what the issuer of its token can do. presign-guard reports these as XRPL_VAULT_LENDS, XRPL_VAULT_LOCKUP and XRPL_VAULT_PAPER_LOSS, all orange.
What does an XRPL loan (LoanSet) really cost?
More than its InterestRate: the origination fee comes off the principal paid out, and a service fee is added to every payment. presign-guard shows the terms (what the borrower receives, the payments, the total repaid and the effective yearly rate) and flags a loan that costs more than 36% a year with its fees as XRPL_LOAN_COSTLY. Rates in a LoanSet are in 1/10 basis points: 18000 is 18%.
Can I check an XRPL transaction for free?
Yes, the verdict only: presign-guard's MCP server (https://presign-guard.fizzl.eu/mcp) has a free tool, presign_quick_check, limited per hour, that takes {"type": "xrpl", "tx": {…}}. The full check with reasons, POST /v1/check or the presign_check tool, costs $0.01, paid over x402 in RLUSD on the XRP Ledger or USDC on Base, Solana, Algorand or Stellar.
Links
- xrpl.org: Batch, DelegateSet, permission values, MPTokenIssuance, Vault
- The specifications: XLS-65 Single Asset Vault and XLS-66 Lending Protocol
- presign-guard on GitHub: every XRPL reason code
- Fizzl for agents: the wallet and checks for Base, Solana, the XRP Ledger, Algorand and Tempo