Payments are the fundamental mechanism for transferring value on the XRP Ledger. They enable the movement of XRP, IOUs, and Multi-Purpose Tokens (MPTs) between accounts, either directly or through intermediary paths that leverage trust lines and order books.
A Payment transaction can operate in different modes:
- Direct XRP Payment: Simple transfer of XRP from one account to another
- Cross-Currency Payment: Converting one currency to another through intermediary steps, using paths through the decentralized exchange. With the MPTokensV2 amendment, cross-currency payments support all combinations of XRP, tokens, and MPTs.
- Since MPTokensV2, MPT->MPT payments use the cross-currency payment execution path. See MPT Payment Execution for a description of MPT transfer mechanics. Before MPTokensV2, the Payment transactor performed these steps directly rather than through the cross-currency engine.
The payment system uses the path finding algorithm and the Payment Engine to discover the most efficient routes for cross-currency transactions,
automatically handling currency conversion, fees, and liquidity constraints. Payments can optionally specify a DomainID field; when specified, the payment will consume offers only from that domain's order book (domain offers and hybrid offers within that domain). Without a DomainID, payments consume from the open order book (open offers and hybrid offers), not from any domain's order book. See Domain and Hybrid Offers and PermissionedDomains documentation for details.
Payments execute either fully or partially (when tfPartialPayment flag is set), or fail with an error code if insufficient liquidity exists.
The Payment transaction interacts with different ledger entry types depending on the payment mode:
- Direct XRP payments: Modify only
AccountRootentries to update XRP balances - Direct MPT payments: Modify
MPTokenentries (sender and receiver balances), andMPTokenIssuanceentry (to track outstanding amount and transfer fee burns) - Cross-currency payments: May modify
AccountRoot,RippleState(trust lines),DirectoryNode(owner directories and order book directories),Offer(consuming or partially consuming offers from the order book),AMM(when AMM liquidity is used for swaps),MPTokenIssuanceentry (to track outstanding amount), andMPTokenentries for sender and receiver holders
The AccountRoot ledger entry represents an account on the XRP Ledger and stores the account's XRP balance, sequence
number, and various flags that control account behavior.
The key of the AccountRoot object is the result
of SHA512-Half of the following values
concatenated in order:
- The
AccountRootspace key0x0061(lowercasea) - The
AccountIDof the account.
Please see AccountRoot Fields
Please see AccountRoot Flags
Key flags relevant to payments:
lsfRequireDestTag: Requires incoming payments to include a destination taglsfDepositAuth: Requires authorization for incoming payments (except XRP under specific conditions)lsfPasswordSpent: Tracks whether the account has used its one-time free SetRegularKey transaction. Cleared when the account receives a direct XRP payment to allow another free regular key change
See Reserves for complete details on reserve requirements.
Payment-specific reserve behavior:
When a Payment transaction creates a new destination account (destination does not exist), the payment must deliver at
least the base reserve amount in XRP. If the XRP amount is below the base reserve, the payment fails with
tecNO_DST_INSUF_XRP.
See Trust Lines Documentation for complete details on
RippleState ledger entry.
See MPT Documentation for complete details on MPT related ledger entries.
See AMM Documentation for complete details on AMM related ledger entries.
The Payment transaction transfers value from one account to another, supporting XRP, IOUs, and MPTs. It can operate as a simple direct transfer or use pathfinding for cross-currency conversions.
Fields are described in Payment Fields
Flags are described in Payment Flags
Automatic Pathfinding with build_path
The sign and submit RPC commands accept a build_path parameter that triggers automatic pathfinding before signing/submitting the transaction:
{
"command": "sign",
"tx_json": {
"TransactionType": "Payment",
"Account": "rSource...",
"Destination": "rDest...",
"Amount": {
"currency": "USD",
"value": "100",
"issuer": "rGateway..."
}
},
"build_path": true
}When build_path is true:
- Pathfinding runs automatically using the
path_search_oldconfiguration value - Up to 4 best paths are found and inserted into the
Pathsfield1 - Rejected if
Pathsis already specified - Rejected for XRP-to-XRP payments
Static validation2
The following preflight failure conditions apply. Cases that depend on a specific amendment are noted inline:
temDISABLED:- transaction contains
sfCredentialIDsand the Credentials amendment is not enabled. - transaction contains
sfDomainIDand the PermissionedDEX amendment is not enabled. Amountis an MPT and the MPTokensV1 amendment is not enabled.
- transaction contains
temINVALID_FLAG: transaction flags contain invalid flags for the payment type.temMALFORMED:sfCredentialIDsarray is empty or exceeds maximum size of 8. To leave credential IDs out, leave out the entire field.sfCredentialIDsarray contains duplicate credential IDssfDomainIDis present but is all zeros. To omit the domain, leave out the entire field. Enforced under thefixCleanup3_2_0amendment.3
temBAD_AMOUNT:Amountis XRP and mantissa is bigger than100000000000000000ull.SendMaxis XRP and mantissa is bigger than100000000000000000ull.4SendMaxis specified but is negative or zero.Amountis negative or zero.DeliverMinis specified withouttfPartialPayment.DeliverMinis XRP and mantissa is bigger than100000000000000000ull, orDeliverMinis negative or zero.5DeliverMinasset (currency and issuer, or MPT issuance) differs from theAmountasset.DeliverMinis greater thanAmount.- Any
STAmountfield in the transaction is non-canonical (failsisLegalNetorisLegalMPT); a universal check applied to all transaction types under thefixCleanup3_2_0amendment.6
temBAD_CURRENCY: eitherAmountorSendMax(or the implied source amount) is a non-native IOU that uses the XRP currency code, or (with MPTokensV2 enabled) an MPT whose issuer account is all zero.temDST_NEEDED: destination account is not specified.temREDUNDANT: payment is from account to itself with the same currency or MPT issuance and noPathsfield.Pathsare required because perhaps they will allow arbitrage.temBAD_SEND_XRP_MAX: XRP->XRP payment specifiesSendMax.temBAD_SEND_XRP_PATHS: XRP->XRP payment specifiesPaths.temBAD_SEND_XRP_PARTIAL: XRP->XRP payment hastfPartialPaymentflag.temBAD_SEND_XRP_LIMIT: XRP->XRP payment hastfLimitQualityflag, or MPT->MPT payment hastfLimitQualityflag (only if MPTokensV2 amendment is not enabled).temBAD_SEND_XRP_NO_DIRECT: XRP->XRP payment hastfNoRippleDirectflag, or MPT->MPT payment hastfNoRippleDirectflag (only if MPTokensV2 amendment is not enabled).
Validation against the ledger view7
- Destination account does not exist:
tecNO_DST: payment is not XRPtelNO_DST_PARTIAL:tfPartialPaymentflag is set. User cannot fund a new account with a partial payment.tecNO_DST_INSUF_XRP: XRP amount is below reserve.
tecDST_TAG_NEEDED: destination account haslsfRequireDestTagflag set and transaction did not specifyDestinationTagfield.telBAD_PATH_COUNT:- the
Pathsfield contains more than 6 paths. - any
PathinPathshas more than 8 elements.
- the
tecBAD_CREDENTIALS: Credential validation failed:- Any credential ID in
sfCredentialIDsdoesn't exist in the ledger - Any credential doesn't belong to the source account
- Any credential isn't accepted (missing
lsfAcceptedflag)
- Any credential ID in
tecNO_PERMISSION:DomainIDis present and either sending or receiving account is not in Domain (not the domain owner and does not hold a valid, non-expired accepted credential).terNO_DELEGATE_PERMISSION: Transaction specifies a delegate but:- The delegate authorization doesn't exist in the ledger
- The delegate doesn't have transaction-level permission for Payment
- For granular permissions: the payment is not a direct payment (has
PathsorSendMaxwith different asset), OR - For granular permissions: neither PaymentMint (when source is issuer) nor PaymentBurn (when destination is issuer) permission is granted
Validation during doApply
Direct XRP Payments:8
tefINTERNAL: Source account does not exist.tecUNFUNDED_PAYMENT: sending the payment would leave the source account below its required reserve. When the source account is the fee payer, it must also be able to cover the fee, which may be drawn from the reserve; in a delegated payment the delegate pays the fee, so it is not charged against the source.tecNO_PERMISSION: Destination is a pseudo-account.- If the destination has the
lsfDepositAuthflag set:- Payment succeeds if source == destination (paying yourself)
- Payment succeeds if destination balance <= base reserve AND payment amount <= base reserve (prevents account wedging)
- Otherwise, deposit preauthorization is verified and may fail with:
tecEXPIRED: Any credential insfCredentialIDsis expiredtecNO_PERMISSION: Source is not deposit preauthorized by destination (either by account or by credentials)
Cross-Currency Payments:
- If the destination has the
lsfDepositAuthflag set, deposit preauthorization is verified and may fail with:tecEXPIRED: Any credential insfCredentialIDsis expiredtecNO_PERMISSION: Source is not deposit preauthorized by destination (either by account or by credentials)
- RippleCalc, which is a thin wrapper that calls the Flow engine, is invoked to execute the provided payment paths. Flow may fail during path conversion or execution. See Flow Validation and Error Codes for complete details on error codes that can be returned during cross-currency payment execution.
tecPATH_PARTIAL:DeliverMinwas specified and the delivered amount is less thanDeliverMin
XRP Payments:
-
AccountRootobject is modified:- Source account: Balance decreased by payment amount
- Destination account (if exists): Balance increased by delivered amount
- Destination account (if exists and has
lsfPasswordSpentflag): Clear the flag
-
AccountRootobject is created:- When the destination account does not exist
- Fields set:
Account: Destination account IDBalance: Payment amountSequence: the sequence of the ledger in which the account is created
Cross-Currency Payments:
State changes for cross-currency payments depend on the execution path determined by RippleCalc/Flow. See Flow documentation for detailed mechanics of path execution.
-
AccountRootobjects are modified:- Source account: Balance decreased by actual amount consumed
- Destination account: Balance increased by delivered amount
- Intermediate accounts in payment path: Balances adjusted according to path execution
-
RippleStateobjects are modified:- Trust lines along the payment path: Balances updated according to transfers
- Trust line quality and flags may affect transfer amounts
-
Offerobjects may be modified or deleted:- When: BookStep consumes order book liquidity
- Modified: Offer is partially consumed, amounts reduced
- Deleted: Offer is fully consumed or becomes unfunded
-
DirectoryNodeobjects may be modified:- When: Offers are consumed and removed from order book directories
- Directory entries updated to reflect consumed offers
-
AMMobjects may be modified:- When: AMM liquidity is used for currency conversion
- AMM pool balances adjusted according to swap amounts
MPT Payments:
-
Source's
MPTokenis modified (if source is not the issuer):MPTAmount: Decreased by sent amount (including transfer fee if applicable)
-
Destination's
MPTokenis modified (if destination is not the issuer):MPTAmount: Increased by received amount
-
Transfer fee effect (when neither source nor destination is the issuer):
MPTokenIssuanceOutstandingAmount: Decreased by transfer fee amount- The fee is effectively burned from circulation, reducing total supply
-
MPTokenIssuanceis modified:- When source is issuer:
OutstandingAmountincreased by sent amount (issuer minting tokens) - When destination is issuer:
OutstandingAmountdecreased by received amount (tokens burned, removed from circulation) - When neither is issuer:
OutstandingAmountdecreased by the transfer fee amount (if the MPT has a transfer fee; unchanged if it has none)
- When source is issuer:
Important:
- The receiver must already have an
MPTokenentry before receiving a payment (unless the receiver is the issuer). If the receiver has noMPTokenand is not the issuer, the payment fails withtecNO_AUTH. Holders create theirMPTokenentries using theMPTokenAuthorizetransaction. - For an MPT that requires authorization (
lsfMPTRequireAuth), AMM, Vault, and LoanBroker pseudo-accounts are treated as authorized without needinglsfMPTAuthorizedon theirMPToken(under the SingleAssetVault or MPTokensV2 amendment). TheMPTokenmust still exist. - The issuer never has an
MPTokenentry and cannot hold a balance of their own issuance. When MPTs are sent to the issuer, they are burned from circulation by decreasingOutstandingAmount.
All payment types are processed through the Payment transaction but follow different execution paths based on the currencies and parameters involved:
- Direct XRP payments: Execute simple balance transfers when
Amountis XRP and noSendMaxand noPathsare specified - Cross-currency payments: Invoke the Flow engine when
SendMaxis specified,Pathsare provided, orAmountis an IOU (or an MPT when the MPTokensV2 amendment is enabled). - Direct MPT payments: Execute MPToken transfers when
Amountholds an MPT issue and MPTokensV2 amendment is not enabled
The Payment transaction determines which path to take during the doApply phase based on these conditions.
Direct XRP payments are the simplest payment type, transferring XRP directly from one account to another without any intermediate steps or currency conversion. The execution varies based on whether the destination account exists.
When the destination account exists: The payment decreases the source account's Balance by the payment amount and increases the destination account's Balance by the same amount. If the destination account has the lsfPasswordSpent flag set, it is cleared to allow another free SetRegularKey transaction.
When the destination account does not exist: A new AccountRoot entry is created for the destination with the payment amount as its initial balance. The account's Sequence is set to the current ledger sequence. The payment must meet the base reserve requirement (see Reserves), or it fails with tecNO_DST_INSUF_XRP.
All validation checks are performed before execution, including reserve requirements, deposit authorization, and destination tags. See Failure Conditions for complete validation rules.
Cross-currency payments convert one currency to another through intermediary steps, leveraging MPTs, trust lines, order books, and AMM liquidity. These payments are executed when the payment specifies SendMax, includes Paths, or has a non-XRP Amount (an IOU, or an MPT when the MPTokensV2 amendment is enabled).
Cross-currency payment execution involves two complementary components: path finding and flow execution.
Before a payment can execute, viable routes from source to destination must be discovered. This is handled by the Path Finding Protocol, which searches the ledger graph to find potential paths through trust lines (for tokens), order books (for XRP, tokens, and MPTs), and AMM pools (for XRP, tokens, and MPTs).
Path finding can be initiated in three ways:
- User-specified paths: Clients can provide explicit
Pathsin the Payment transaction, bypassing path finding entirely - RPC path finding: Clients use RPC endpoints to discover paths before submitting the transaction:
path_find- Modern interface with streaming subscriptions (path_find create,path_find status,path_find close)ripple_path_find- Legacy one-shot path finding (deprecated but still supported)
- Default paths: When no paths are specified, the payment engine automatically generates simple default paths (direct trust line to issuer, XRP-bridged paths)
The RPC endpoints accept parameters like source_account, destination_account, destination_amount, and optionally source_currencies (to constrain which currencies the pathfinder considers as potential sources). For each source currency, the pathfinder creates a separate search instance, discovers paths through the ledger graph, simulates each path to measure quality and liquidity, ranks paths by quality, and returns the best paths to the client.
Once paths are available (whether from RPC, user-specified, or default paths), the Payment transaction executes by delegating to the Flow engine. Flow converts the paths into strands - sequences of executable steps that move value:
- DirectStepI: Transfers tokens between accounts via trust lines
- BookStep: Converts currencies through order books and AMM pools
- XRPEndpointStep: Transfers XRP to/from accounts
- MPTEndpointStep: Transfers MPTs to/from accounts
Flow ranks strands by quality (exchange rate) and iteratively consumes liquidity from the best available strands, dynamically re-ranking as their quality changes, until the payment amount is satisfied or all liquidity is exhausted. Each strand evaluation uses a two-pass algorithm: a reverse pass works backwards from the desired output to calculate required input, then a forward pass verifies and executes the actual transfer.
See Flow documentation for detailed mechanics of path execution, strand evaluation, and step-by-step state changes.
Footnotes
-
Static validation (preflight):
checkExtraFeatures,getFlagsMask,preflight↩ -
Both Amount and SendMax checked via isLegalNet:
Payment.cpp↩ -
DeliverMin checked for legal amount and positive value:
Payment.cpp↩ -
Validation against ledger view (preclaim):
checkPermission,preclaim↩ -
Direct XRP payment execution:
Payment.cpp↩