> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stellarsight.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Position on upto

> The discovery-side requirements for the Stellar upto scheme, as argued upstream.

**Posted** on [`stellar/x402-stellar#72`](https://github.com/stellar/x402-stellar/issues/72)
as [comment 5303705529](https://github.com/stellar/x402-stellar/issues/72#issuecomment-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 write `scheme_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](https://github.com/x402-foundation/x402/pull/3018).
* **@bomanaps** replied that they had reached the same design independently, pushed for
  `validAfter` to be enforced on-chain, and asked about the atomic pull-max / pay-actual /
  refund shape.
* **@Iam0TI** then opened [x402-foundation/x402#3134](https://github.com/x402-foundation/x402/pull/3134),
  the proposed Stellar binding as a spec, implemented and tested on testnet.

So the design has converged and a spec is under review. A fourth private design would add a
data point, not a decision. The two questions still genuinely open in the thread —
`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 an `upto` 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

* **`validAfter` on-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](https://github.com/x402-foundation/x402/pull/3134), replied the
same day ([thread](https://github.com/stellar/x402-stellar/issues/72#issuecomment-5304348824)).
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.**

* `validAfter` is already bound. It sits in the `require_auth_for_args` tuple and is
  enforced with `if now < valid_after` inside `settle`. 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 with `autoRevoke` always true closes
  the same exposure window without the custody hop. Better argument, position withdrawn.

**Accepted, and it defines the scope of our work.**

Pricing metadata belongs at the discovery layer, not in the payment scheme:
`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:

```
extensions.bazaar.pricing = {
  model:   "per-call" | "per-token" | "per-second" | "per-byte" | "tiered" | "custom",
  unit:    { amount: "12", per: "1000 tokens" },   // optional, omitted when meaningless
  typical: "350",                                  // optional, what a median call settles
  note:    "free-form, for models that fit none of the above"
}
```

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

## What this commits us to

Implement whatever #3134 converges on rather than a variant of it, and say so publicly when
we do. The funded work (Tranche 2) is the pricing metadata the thread delegated to discovery,
proposed upstream as its own PR against the bazaar extension; a **second conformant
implementation with a published interop report** — three implementations exist on Stellar and
no two have been tested against each other, so interoperability is currently an assertion
rather than a measured property; and the catalog-side implementation of the same shape.

One process note, recorded because it is the more useful half of this exchange: we were
wrong twice in public and said so in the thread within hours, rather than defending the
positions. A design document that only preserves the arguments its author won is not
evidence of judgement.
