You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The testnet facilitator rejects otherwise-valid cross-currency XRPL
Payments under the exact scheme. The blob parses (no longer invalid_tx_blob); the rejection is explicit: verify_failed:unsupported_payment_features. Supporting this would let
merchants quote in RLUSD while agents pay from XRP balances in a single
transaction, with conversion handled by the protocol-native DEX/AMM —
a capability unique to XRPL among x402 settlement chains.
Payer holds XRP only. ripple_path_find returns source_amount: "16184" (drops) for 0.01 RLUSD with paths_computed: [] — i.e., direct conversion via the default path.
Construct Payment: Amount = {0.01 RLUSD, issuer above},
SendMax = 100000 drops, Paths omitted (empty path set is invalid
in xrpl-py's binarycodec), InvoiceID memo bound to the quote,
SourceTag 21000001, tfPartialPayment NOT set. Sign locally.
Retry resource with PAYMENT-SIGNATURE. Facilitator response
(verbatim body):
Amount/DeliverMax exactly equals the quoted {value, currency, issuer}
Destination == payTo; InvoiceID matches the challenge
tfPartialPayment is NOT set (ledger then guarantees delivery of
the full Amount or the tx fails — closes the classic partial-payment
exploit)
SendMax may be any asset; Paths may be absent (default path) or present
Settlement criterion unchanged: tesSUCCESS
Related minor findings (client-side, can file separately if preferred)
to_currency_hex (client/presigned_payment_payer.py) accepts only
3-char or 40-hex codes; the literal "RLUSD" raises ValueError.
Quoting the 160-bit hex works; auto-encoding 4–20 char codes would
improve DX.
Passing paths=[] to xrpl-py's Payment raises IndexError in
binarycodec path_set.py; a guard that omits empty paths in any future
cross-currency payer avoids it.
Happy to share the full repro scripts, run additional funded testnet
cases, or draft a spec PR. Building an XRPL x402 merchant gateway and
would love to see this land — it's a genuine differentiator for XRPL
in the x402 ecosystem.
Summary
The testnet facilitator rejects otherwise-valid cross-currency XRPL
Payments under the
exactscheme. The blob parses (no longerinvalid_tx_blob); the rejection is explicit:verify_failed:unsupported_payment_features. Supporting this would letmerchants quote in RLUSD while agents pay from XRP balances in a single
transaction, with conversion handled by the protocol-native DEX/AMM —
a capability unique to XRPL among x402 settlement chains.
Environment
xrpl:1(testnet), facilitator https://xrpl-facilitator-testnet.t54.aiReproduction
Merchant middleware quotes: amount
0.01, asset524C555344000000000000000000000000000000(RLUSD), issuerrQhWct2fv4Vc4KRjRgMrxa8xPN9Zx9iLKV, sourceTag 21000001.Payer holds XRP only.
ripple_path_findreturnssource_amount: "16184"(drops) for 0.01 RLUSD withpaths_computed: []— i.e., direct conversion via the default path.Construct Payment: Amount = {0.01 RLUSD, issuer above},
SendMax = 100000 drops, Paths omitted (empty path set is invalid
in xrpl-py's binarycodec), InvoiceID memo bound to the quote,
SourceTag 21000001, tfPartialPayment NOT set. Sign locally.
Retry resource with PAYMENT-SIGNATURE. Facilitator response
(verbatim body):
{"x402Version":2,"resource":{"url":"http://127.0.0.1:8402/hello",
"description":"RailGate demo endpoint","mimeType":"application/json"},
"accepts":[{"scheme":"exact","network":"xrpl:1","amount":"0.01",
"asset":"524C555344000000000000000000000000000000",
"payTo":"rp6L9vgThTwPoCvBMAZ3o7t3qtVHpqCwAx","maxTimeoutSeconds":600,
"extra":{"sourceTag":21000001,
"issuer":"rQhWct2fv4Vc4KRjRgMrxa8xPN9Zx9iLKV",
"invoiceId":"77052C46486246828FB5B89F0499DF48"}}],
"error":"verify_failed:unsupported_payment_features","extensions":{}}
Pre-submit hash of the rejected (never-settled) blob:
509540BDE9273F6AC25835DDEF9F88032B770C2417DD7B271196D7CB74D7326C
Evidence the ledger supports this exact shape
The identical transaction shape (XRP SendMax → RLUSD Amount, default
path, no explicit Paths) settles when submitted directly:
mid + 0.5% fee:
https://testnet.xrpl.org/transactions/4E2ADADD66B3257D8FA8BF8F7A3023D3EA162387B7F9C38C25D37A8565E8DD2C
rPn7kHwycXzS5XNj167CnKwC2t82yAAdNW (~525k XRP / ~326k RLUSD, 0.5% fee).
Same-asset IOU
exactpayments already verify and settle correctly —confirmed funded run:
https://testnet.xrpl.org/transactions/9B3D9C9B37730D87D212219F983F36DC9EF8261E9CE3A340134CEC09F44CEDBE
So the gap is specifically: SendMax asset ≠ Amount asset.
Proposed verifier semantics (for discussion)
Accept a Payment under
exactwhen:the full Amount or the tx fails — closes the classic partial-payment
exploit)
Related minor findings (client-side, can file separately if preferred)
to_currency_hex(client/presigned_payment_payer.py) accepts only3-char or 40-hex codes; the literal "RLUSD" raises ValueError.
Quoting the 160-bit hex works; auto-encoding 4–20 char codes would
improve DX.
paths=[]to xrpl-py's Payment raises IndexError inbinarycodec path_set.py; a guard that omits empty paths in any future
cross-currency payer avoids it.
Happy to share the full repro scripts, run additional funded testnet
cases, or draft a spec PR. Building an XRPL x402 merchant gateway and
would love to see this land — it's a genuine differentiator for XRPL
in the x402 ecosystem.