stellar/x402-stellar#72
as comment 5303705529,
15 August 2026. This file is the text plus the reasoning behind it, versioned alongside the
code that will implement whatever the thread settles on.
Why this is not a competing spec
An earlier draft of this document argued that four independent implementations disagreed and that somebody should writescheme_upto_stellar.md. Reading the thread before posting made
that framing wrong, and it is worth recording why rather than quietly replacing it:
- @tolgayayci opened the issue with a full design and both schemes settling on testnet, with real USDC, from keypair and smart accounts, plus a fix upstream in x402-foundation/x402#3018.
- @bomanaps replied that they had reached the same design independently, pushed for
validAfterto be enforced on-chain, and asked about the atomic pull-max / pay-actual / refund shape. - @Iam0TI then opened x402-foundation/x402#3134, the proposed Stellar binding as a spec, implemented and tested on testnet.
validAfter on-chain versus verify-time, and residual-allowance hygiene — belong to the
people who have implemented settlement, and both already have advocates there.
What we contributed instead
The requirements the discovery layer places on the spec. These are invisible from the settlement side, which is why three implementers had not raised them, and they are the part we are positioned to see: we run the catalog. 1. What does a listing advertise as the price of anupto resource?
PaymentRequirements.amount is a single value. For exact it is the price. For upto the
only figure a seller can honestly publish before the call is the ceiling, which is not the
price and is usually much larger. A catalog that puts a ceiling in the field an agent reads
as “cost” makes every metered service look expensive beside a fixed-price one — a bias
against exactly the services upto exists to enable. The spec need not solve pricing, but it
should say whether amount on an upto requirement is the ceiling, and ideally let a
resource state a typical or unit price alongside it. Otherwise every catalog invents its own
convention and cross-catalog price comparison stops being portable, which is the thing
discovery is for.
2. Budget filters silently exclude the cheap case.
“Find me something under X” is the natural agent behaviour. If a listing carries only the
ceiling, the filter drops every metered service whose ceiling exceeds the budget even when
the settled amount would land far below it. A unitPrice or typicalAmount hint, even
non-normative, makes that filter correct instead of conservative.
3. Usage signals stop being comparable once amounts vary.
Catalogs rank partly on observed settlements. Under exact, counting settlements is a
reasonable proxy for usage. Under upto, a 0.001 settle and a 5.0 settle count the same, so
an endpoint called constantly for trivial amounts outranks a substantial one — and gaming it
gets cheaper the smaller the settlements are. If the settle response carries the actual
amount in a stable place, saying so normatively lets catalogs weight by value rather than by
count, which is materially harder to fake.
Where we came down on the thread’s open questions
validAfteron-chain: agreed with @bomanaps and @Iam0TI, and discovery adds a reason. A listing is a cached claim that a client may act on much later; the more of the validity window the ledger enforces rather than the facilitator, the less a stale catalog entry can be turned into a payment nobody intended.- Allowance hygiene: the atomic pull-max, pay-actual, refund-remainder shape reads better than leaving a residual to expire, because the payer’s worst case should be bounded by the transaction they signed rather than by an expiry they have to track. Stated as a preference, not a finding — we have implemented neither.
How the thread answered
Posted 15 August; @Iam0TI, who authored the Stellar binding in x402-foundation/x402#3134, replied the same day (thread). Two of our points were corrected and one was accepted, and the acceptance is the one that matters here: Corrected, and withdrawn on the thread.validAfteris already bound. It sits in therequire_auth_for_argstuple and is enforced withif now < valid_afterinsidesettle. Our point had been handled before we made it, which we should have checked in the diff rather than inferring from the thread.- Allowance hygiene: the atomic pull-max, pay-actual, refund-remainder shape we preferred
makes the contract take custody of money the payer never intended to spend, purely to hand
most of it back.
approve→transfer_from→ revoke withautoRevokealways true closes the same exposure window without the custody hop. Better argument, position withdrawn.
PaymentRequirements.amount stays unambiguously the authorization ceiling, and discovery
decides how the settled amount is used. The reasoning is that upto spans per-token,
per-second, per-byte, per-call, storage and multi-dimensional pricing, which share no unit,
so a single unitPrice in the scheme would be wrong for most products.
That is a spec author publicly delegating the problem to the layer this project builds. What
we proposed back, and would open as a separate PR against the bazaar extension so #3134 stays
narrow:
model and note are always expressible; unit and typical are optional precisely
because the product types listed above show they are not universal. A catalog that receives
none of them falls back to the ceiling and says so.
The part that only a catalog can do: a seller-declared typical is a claim, and the catalog
is the sole party positioned to check it. Once the settled actual amount is exposed
consistently, a catalog holding a resource’s settlement history compares declared-typical
against observed-median and down-ranks the gap. That makes the field self-correcting instead
of another number sellers optimise, and no settlement contract could enforce it even in
principle — which is further evidence the metadata belongs where the thread put it.