Everything on the bench comes from the collector at
workers/latency-bench in the route99 tree. This page is generated from that
code: every number, address and provider row below is read out of the collector's source,
and the test suite fails if this page and the code ever disagree. What you read here is what
runs.
A Cloudflare Worker cron fires every 30 min. Each firing serves one
tick group of providers (the panel is split so a run stays inside the platform's
per-invocation subrequest budget): each configured group is probed once per rotation, one timed
quote request per (provider × chain). Before the timed probes, each provider's hosts get
one untimed warm-up request, so a sample measures the service rather than TLS setup. The
worker's own location is not pinned; every run records the Cloudflare colo it measured from,
and requests carry Mozilla/5.0 (compatible; route99-latency-bench/1.0; +https://route99.xyz) with Referer: https://route99.xyz.
ms is request-out to last body byte in. Timeout: 10 s.ms and an err code — a fast 429 is an error, never a fast quote.via. Cloudflare strips its own
cf-ray from Worker subrequests, so CF-fronted providers stay unattributed.One trade per chain, wrapped native → the chain's dominant USD stable, ~$1–2k
notional — big enough for real routing, small enough to be polite. The taker
0x1111111111111111111111111111111111111111 never holds or receives anything: quotes are priced and
simulated, never executed.
| chain | id | sell (wrapped native) | buy (stable) | amount |
|---|---|---|---|---|
| Ethereum | 1 | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 | 0.5 |
| BSC | 56 | 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c | 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d | 1 |
| Base | 8453 | 0x4200000000000000000000000000000000000006 | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | 0.5 |
| Polygon | 137 | 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270 | 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 | 500 |
| HyperEVM | 999 | 0x5555555555555555555555555555555555555555 | 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb | 25 |
| Arbitrum | 42161 | 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1 | 0xaf88d065e77c8cC2239327C5EDb3A432268e5831 | 0.5 |
HyperEVM buys USD₺0 — the chain has no canonical USDC. A measured
swap-mix draft (workers/latency-bench/mix.draft.json) exists to widen this matrix
by pair class and size once per-swap distributions land.
| provider | tick | chains | auth (Worker secrets) | simulation | gap |
|---|---|---|---|---|---|
| Nordstern | 1 | eth bsc base pol hyp arb | none | from the quote | 300 ms |
| 0x | 2 | eth bsc base pol hyp arb | KEY_0X | from the quote | 300 ms |
| 1inch | 2 | eth bsc base pol arb | KEY_1INCH | from the quote | 1100 ms |
| Enso | 3 | eth bsc base pol hyp arb | KEY_ENSO | from the quote | 300 ms |
| Bebop | 3 | eth bsc base pol hyp arb | KEY_BEBOP + BEBOP_SOURCE | from the quote | 300 ms |
| Bitget | 2 | eth bsc base pol arb | BITGET_KEY + BITGET_SECRET + BITGET_PARTNER | — | 300 ms |
| Eisen | 3 | eth bsc base hyp arb | KEY_EISEN | from the quote | 300 ms |
| Fly | 3 | eth bsc base pol arb | KEY_FLY | — | 300 ms |
| KyberSwap | 0 | eth bsc base pol hyp arb | none | via one untimed build call | 2200 ms |
| LiFi | 1 | eth bsc base pol arb | none | from the quote | 300 ms |
| OogaBooga | 2 | hyp | KEY_OOGABOOGA | — | 300 ms |
| OpenOcean | 0 | eth bsc base pol hyp arb | none | — | 300 ms |
| SushiSwap | 0 | eth bsc base pol arb | none | from the quote | 300 ms |
| Velora | 1 | eth bsc base pol arb | none | via one untimed build call | 1100 ms |
Providers whose secrets are not set are recorded as skipped in every
run of their tick — a stated absence, never a silent hole or a fake failure. No public
quote API at all: Barter. The rotation covers only ticks that have a configured provider,
so setting a secret activates its tick with no deploy.
Where a quote carries executable calldata, the sample is re-validated through
Nordstern's swap-simulation API: an
eth_call with state overrides plants a proxy at the taker, funds and approves it
synthetically, executes the exact quoted calldata against the latest block, and reports success,
the real output delta and gas. The sim call is never timed into ms. Stored per
sample: the quoted amount q and sim (ok / revert with reason / error).
ok:0) is a fact about the provider's quote.
A simulator failure (sim.err) is a fact about the simulator and is never counted
against the provider. 502s retry from a per-run budget of 2.sim2:{agree:1}; anything else keeps the second verdict for examination.
sim2 is an instrument audit and never counts against the provider; a
systematic discrepancy now requires two competitors' infrastructures to agree.reason field.Every fourth edge slot is borrowed for a sweep from
twelve deliberately picked AWS datacenters, each running our own probe endpoint —
one Lambda function deployed identically to every region
(workers/latency-bench/lambda/), gated by a shared token. DNS resolves locally at
each endpoint, which is how a real integrator in that region experiences a DNS-geo-routed
provider. The sweep is transport only (dns / tcp / tls / ttfb per region, cumulative ms
from request start, one Base quote per provider; each invocation is a
cold-ish environment, so dns includes the resolver's cold path — consistently across
regions) and covers keyless providers only: not for trust — the fleet is ours
— but because per-key rate limits make twelve concurrent same-key requests a
violation, where twelve IPs sending one keyless request each is not. Currently that panel is
Nordstern, KyberSwap, LiFi, OpenOcean, SushiSwap, Velora. Unlike the Worker vantage, Lambda egress keeps
cf-ray intact, so Cloudflare-fronted providers are attributable per cell here.
| datacenter | region | endpoint |
|---|---|---|
| Virginia | us-east-1 | our Lambda endpoint (function URL) |
| Frankfurt | eu-central-1 | our Lambda endpoint (function URL) |
| London | eu-west-2 | our Lambda endpoint (function URL) |
| Tokyo | ap-northeast-1 | our Lambda endpoint (function URL) |
| Singapore | ap-southeast-1 | our Lambda endpoint (function URL) |
| Hong Kong | ap-east-1 | our Lambda endpoint (function URL) |
| Seoul | ap-northeast-2 | our Lambda endpoint (function URL) |
| Mumbai | ap-south-1 | our Lambda endpoint (function URL) |
| Dubai | me-central-1 | our Lambda endpoint (API Gateway front — the region lacks function URLs) |
| São Paulo | sa-east-1 | our Lambda endpoint (function URL) |
| Sydney | ap-southeast-2 | our Lambda endpoint (function URL) |
| Cape Town | af-south-1 | our Lambda endpoint (function URL) |
at +
tookMs bound every sample in time; raw days are served verbatim by the API the
charts read (/api/latency), so the site can never show what storage does not hold.n travels with every figure so thin data reads as thin.bench/gen-method.mjs from the collector's exported configuration, probe table,
panel registry and region list. npm test re-renders it and fails on any byte of
drift. If you are reading a number here, the collector is running with it.