Routing and health
When a merchant offers several ways to pay — USDC on Base or USDC on Solana — something has to choose. tx402 chooses with a fixed key cascade over a set of candidates it has actually probed, so the choice is explainable after the fact rather than a matter of which RPC answered first.
Every requirement becomes a candidate
Section titled “Every requirement becomes a candidate”Nothing is silently dropped. A requirement with no configured signer, an unreadable balance, or
an insufficient balance all become candidates carrying a rejectionReasons list. They are
ranked below the viable ones, not removed.
That is what makes the failure message useful: when nothing is viable, tx402 raises
InsufficientLiquidityError with per-network deficits, so you learn you are $0.03 short on
Base rather than just “no route”.
The ordering cascade
Section titled “The ordering cascade”Candidates are ordered by these keys, in this order (SPEC §6.4 step 18, plus SPEC §6.5’s open-circuit rule):
- Viable first — can this route actually pay?
- Circuit closed first — an endpoint whose circuit is open is ranked last, and is used only when every compatible endpoint is open.
- Your preference —
routing.preferNetworks, in the order you listed them. - Lower buyer fee.
- Higher health score.
- Lower observed latency.
- Original requirement index — the merchant’s own ordering, as the final tie-break.
The open-circuit key sits above preference deliberately. SPEC §6.5 says an open endpoint “is ranked last”, which is a stronger statement than a low health score — a large enough preference bonus would outrank a mere score — so it cannot be folded into the number.
Viability is first, and preference cannot lift a candidate above it. If you prefer Base and
your Base balance is short, tx402 pays on Solana. A preference is a tie-break, never a filter. If
you need it to be a filter, use policy.allowedNetworks, which refuses rather than reorders.
Balances are read concurrently, and every probe is awaited
Section titled “Balances are read concurrently, and every probe is awaited”All balance probes run together, and requirements sharing a network, asset, and owner collapse onto one query. There is a 600 ms budget per provider and a maximum of two providers per network.
Crucially, every probe finishes before anything is ordered. A “first viable candidate wins” shortcut would make the selection depend on which RPC happened to answer first, which is exactly what determinism forbids. A test drives the preferred network to answer last and asserts it still wins.
Determinism, precisely
Section titled “Determinism, precisely”SPEC §6.4 step 19 requires identical output for identical inputs and health state. That is a property of the ordering function, and it holds: the same candidates in any order produce the same ranking, and an exact tie on every key above the index falls through to the merchant’s own ordering.
Health and circuit breaking
Section titled “Health and circuit breaking”tx402 keeps one health index per client, shared by every RPC pool. There is exactly one place circuit state lives, so two layers cannot disagree about the same provider.
| Setting | Value |
|---|---|
| EWMA alpha (latency and success) | 0.20 |
| Failure window | last 20 observations |
| Opens at | 5 consecutive failures or ≥50 % failures with ≥10 samples |
| Open duration | 30 seconds |
| Half-open probes | 1 |
| Closes after | 1 successful probe |
| Idle retention | 30 minutes |
| Maximum endpoints | 128, LRU |
A new endpoint starts at 0.80. A successful probe discards the failure history that opened the circuit — otherwise a recovered endpoint would re-open on its next single failure.
Chain identity failures open immediately
Section titled “Chain identity failures open immediately”An eth_chainId mismatch or a Solana genesis-hash mismatch does not go into the failure
window. It opens the circuit at once and moves to the next RPC.
The reason is that these are not reliability observations to average. They say the endpoint is serving a different chain than it claims, which is a correctness fact, not a flaky one, and averaging it into a window would let a misconfigured or hostile endpoint stay in rotation while its score decayed.
tx402 verifies chain identity on the same endpoint that serves the balance, on every read — not once at startup — because an endpoint that was correct a minute ago is not necessarily the endpoint answering now.
Resetting
Section titled “Resetting”tx402.resetHealth();Clears in-memory health metrics and circuit state. It does not clear the spend ledger — health is an observation about the network, budget is a record of your money, and conflating them would let a health reset hand back budget.