Skip to content
This repository was archived by the owner on Aug 22, 2026. It is now read-only.
This repository was archived by the owner on Aug 22, 2026. It is now read-only.

Facilitator rejects cross-currency Payments (SendMax asset ≠ Amount asset) with verify_failed:unsupported_payment_features — feature request with full repro and on-ledger counter-proof #3

Description

@vkostich

Summary

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.

Environment

  • x402-xrpl==0.1.4 (PyPI), Python 3.12, Windows
  • network xrpl:1 (testnet), facilitator https://xrpl-facilitator-testnet.t54.ai
  • Date of runs: 2026-06-12 (US Pacific, early AM)
  • Payer: rUoQ8iuNNaSM8Vo394ZcWi6NjGE4QGLkXh
  • payTo: rp6L9vgThTwPoCvBMAZ3o7t3qtVHpqCwAx
  • invoiceId of rejected run (for your logs): 77052C46486246828FB5B89F0499DF48

Reproduction

  1. Merchant middleware quotes: amount 0.01, asset
    524C555344000000000000000000000000000000 (RLUSD), issuer
    rQhWct2fv4Vc4KRjRgMrxa8xPN9Zx9iLKV, sourceTag 21000001.

  2. 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.

  3. 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.

  4. 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:

Same-asset IOU exact payments 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 exact when:

  • 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions