SEP-41 and stroop math
Rail402 settles any SEP-41 token, with USDC as the default. Every SEP-41 asset is reachable from Soroban through its Stellar Asset Contract (SAC), and every amount is an integer at 7 decimals, called a “stroop.” One USDC is10000000 atomic units.
The one rule that matters end to end: no floating point ever touches an amount. Number("9007199254740993") silently loses precision, and an amount is the single value where that is unacceptable. Rail402 uses integer and bigint arithmetic from the wire to the ledger:
- Amounts arrive as integer strings and are validated as such before anything else.
parseAmountguards the digits and returnsundefinedrather than throwing, so one malformed amount in one catalog row cannot crash a comparator. That path is attacker-influenceable, since amounts arrive in federated listings and seller402challenges. - Budget comparisons (
withinBudget) and formatting (formatAtomicAmount) are bigint throughout. - The
uptoserver reports 7 decimals per the Stellar SEP-41 convention, and theuptofacilitator guards every attacker-supplied amount through the same integer parse before signing.
Provable asset identity
A SAC address is a one-way hash of an asset’s code, issuer, and the network passphrase. That is a security property worth using: a scam issuer that mints a token also called “USDC” derives a different SAC than the canonical one, so the SAC address itself distinguishes the real asset from a look-alike. Rail402 uses this in discovery. Client-suppliedextra.stellar metadata is stripped and the facilitator re-derives the asset identity itself, attaching a derived identity only for assets on a pinned registry (testnet USDC’s SAC is CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA, which is exactly @x402/stellar’s canonical testnet USDC address). A listing claiming to be paid in “USDC” but resolving to a different SAC cannot borrow the real asset’s identity. A test re-derives every SAC in the registry so it cannot rot.
Trustline pre-flight
A Stellar account needs a trustline to a SEP-41 asset before it can receive it, and the one people forget is the receiver’s: the seller’spayTo. Rail402 surfaces this to agents as an advisory pre-flight, and the conditions under which it runs are themselves a Stellar-correctness statement.
The pre-flight runs only when all three hold, and is omitted (never guessed) otherwise:
- the asset has a derived identity, because a SAC hash is one-way, so without the
(code, issuer)behind it there is nothing to look up; - the issuer is non-null, since native XLM needs no trustline;
- the
payTois aG…classic account, because aC…contract account holds SAC balances in contract storage, where trustlines do not exist.
ok, missing, unauthorized, or unknown, each with a non-null reason (a 404 account, a zero limit, is_authorized: false). It is advisory, cached, fire-and-forget behind settlement, and never gates cataloging: a missing trustline is information for the buyer, not a reason to refuse a listing. At settlement, a genuinely missing recipient trustline has its own code, invalid_exact_stellar_payload_missing_trustline_recipient, so the cause is never a mystery.
Asset identity and trustline pre-flight are one feature, not two: the trustline lookup is impossible without the derived
(code, issuer), which is why the derivation gates it.Soroban resource limits and simulation
Verify and settle must stay within Soroban’s per-transaction read, write, instruction, and memory limits. Rail402 simulates before submitting and treats a simulation failure as a first-class, coded outcome rather than an exception. The error-enrichment layer even re-simulates a failed transaction once, off the hot path, to recover the host error the base package discarded, turning an opaque simulation failure into a specific reason (a negative amount, a balance out of range, a missing trustline, a policy refusal). This also feeds the fee ceiling: the fee the facilitator will pay is the simulation-derivedminResourceFee + BASE_FEE, checked against MAX_TRANSACTION_FEE_STROOPS before the facilitator signs. A settlement that would exceed the operator’s resource-fee budget is refused with a reason, not signed and regretted.
Throughput
Agent traffic is bursty, and every sponsored settlement is signed by a facilitator account, so the sequence number of that account is the natural bottleneck. Rail402 addresses it on two axes.A channel-account pool
FACILITATOR_STELLAR_SECRET plus a comma-separated FACILITATOR_STELLAR_CHANNEL_SECRETS gives N independent signers, each with its own sequence lane. Settlements round-robin across them, so N signers give N independent sequences. Duplicate secrets are rejected at startup, since two entries for one account share a sequence and buy nothing.Per-signer serialization
Within a single signer, the read-then-submit critical section is serialized by a per-key promise-chain lock. Different signers run fully parallel; the same signer runs strictly serial, which is what avoids
txBadSeq. Confirmation polling stays outside the lock, since the sequence is already spent at submit time.FACILITATOR_STELLAR_FEE_BUMP_SECRET) decouples who pays the fee from whose sequence advances, so fee capacity and sequencing scale independently. The exact scheme delegates sequence handling to @x402/stellar; the upto scheme adds the lane lock itself because it owns its submission path. Burst behavior is measured and recorded in docs/status/burst.json.
No persistent on-chain state on the hot path
The per-request schemes hold no persistent on-chain state, so there is no rent or TTL to manage on the settlement path. An on-chain registry would introduce that concern, and Rail402 deliberately does not ship one (the catalog is off-chain by default). Nothing in a payment adds a second transaction that would roughly double settlement cost. The one contract that does hold state,upto’s nonce record, uses Soroban temporary() storage with a bounded TTL tied to the authorization window, so it self-expires and needs no rent management.
Next steps
The settlement path
Auth entries and simulation in context.
Expiration and replay
The ledger window these limits sit inside.
The discovery trust boundary
Where asset identity and trustline pre-flight surface to agents.
Stellar essentials
The lighter primer on the same mechanics.