Architecture · derived from the code, not a design document

One pass over a paginated feed:
order, group, join, divide, rank.

No database, no model, no cache in the product path, no key anywhere. The engine is six stdlib-only Python modules; the pages are static HTML rendered from committed receipts; one serverless function exists solely to add a CORS header CoinMarketCap omits. Every function named on this page exists in middleman/, scripts/ or api/ — if a claim here is not greppable, it is a bug in this page.

6
engine modules · stdlib only
6
keyless endpoints · 4 load-bearing
0
runtime dependencies · 0 keys
1
privileged function · 58 lines
The shape of the thing

Six endpoints in. One join. Receipts out. The browser closes the loop.

Read the middle column top to bottom: that is the whole product. Everything to its left is CoinMarketCap's keyless /public-api; everything to its right turns a run into a receipt a judge can re-derive offline; the band on top is what Vercel serves.

Middleman architecture: CoinMarketCap endpoints feed the six-module engine; the scripts write tapes and receipts; the receipts render the static site; the browser port calls one keyless proxyLeft: six keyless CoinMarketCap endpoints. Middle: tape.pull, detect.order and group, detect.middlemen, cost.pool_row, recommend.route, and enrich for labels. Right: scripts/middleman.py and scripts/seed.py drive the pull; seed writes data/tape_*.json and docs/proof/*.json, verify_tape.py re-derives one from the other, and render_site.py turns the proof into site/*.html. Top: Vercel serves the pages; the paste box runs site/middleman.js which calls /api/swaps, a keyless passthrough to the same transactions endpoint. Vercel · middleman.edycu.dev — site/ + two functions under api/ /api/swaps · api/swaps.jskeyless passthrough · CORS · 60 s cache site/middleman.jsthe engine, ported to the browser / · /evidence · /judge · /pitchstatic · zero requests to render site/*.htmlgenerated, never hand-edited /api/healthapi/health.js · no upstream call paste a token CoinMarketCap · /public-api middleman/ · six stdlib modules scripts/ · data/ · docs/proof/ /v1/dex/tokens/transactions100 prints per page · lastId cursor /v1/dex/token/poolspool address · venue · liquidity /v1/dex/security/detailbuy / sell tax /v4/dex/pairs/quotes/latest24 h buy / sell counts /v4/dex/spot-pairs/latestthe hero rule → scripts/middleman.py /v1/dex/platform/listexplorer links → the pages tape.pull()backoff 15/30/60 s · a receipt per call detect.order() · detect.group()(int(h), int(lgid)) · (en, t0a, t1a) detect.middlemen()round-trips · sandwiches · organic cost.pool_row()a1/a0 · quote-to-fill p50 / p90 recommend.route()argmin organic p90 · cap ↑ 0.05 % enrich.*pool address · liquidity · taxes · 24 h coverage scripts/middleman.pythe door a judge walks through scripts/seed.pytapes · receipts · census, per chain scripts/render_site.py--check gates drift docs/proof/*.jsonnumbers · URLs · statuses · hashes data/tape_*.jsonrows verbatim · page hashes verify_tape.py, offline the same keyless URL
← scroll the diagram sideways →
CoinMarketCap endpoint engine stage (middleman/) committed data script, page or function a boundary: API · engine · receipts · site
Two doors, one engine

scripts/middleman.py is the door a judge walks through: live, keyless, zero flags — it asks /v4/dex/spot-pairs/latest for the day's #1 pair (the hero rule) and hands it to tape.pull(). scripts/seed.py is the same call per chain plus the watchlist; it writes the tapes and the receipts the site is rendered from.

The loop at the top

Paste a token on the front page and site/middleman.js — the engine ported to the browser, parity-tested against Python on every tape — runs on rows fetched through /api/swaps: the identical keyless CoinMarketCap URL, body returned untouched, plus the one header CMC omits (Access-Control-Allow-Origin) and a 60 s CDN cache so ten judges pasting one token are one upstream call.

Per call, per stage

The fetch leaves a receipt. The join is the product. The rest is division.

Per call: tape.get() is one keyless GET with backoff; tape.pull() walks the lastId cursor for up to 8 pages of 100 prints, keys every print by (tx, lgid) — the only unique key across pages — and returns (prints, meta), so an API error can never be reported as a property of the token. Every call, success or failure, leaves a receipt: URL, HTTP status, UTC, elapsed, sha256 of the body.

StageFunctionWhat it does
Fetchtape.pull()Keyless GET with backoff (15 / 30 / 60 s), cursor from data.lastId on the envelope, prints keyed by (tx, lgid), a receipt per call.
Orderdetect.order() · detect.group()Sort by (int(h), int(lgid)) — both arrive as strings; recover pools from (en, t0a, t1a), because the feed carries no pool address.
Joindetect.middlemen()The product. Same maker, same block, next print on the other side, size within 5 % → round-trip; 1–4 other makers enclosed and take > 0 → sandwich; the rest organic.
Pricecost.pool_row()a1 / a0 per print, never the feed's rounded q; quote-to-fill against the previous organic print; p50 / p90 per pool, nearest-rank; the example block with its arithmetic.
Routerecommend.route() · recommend.cap_pct()Lowest organic p90 among pools with ≥ 50 organic prints; the cap is that p90 rounded up to the next 0.05 %. States the rule, never widens it.
Labelenrich.*token_pools + label_pools (pool address, venue, liquidity), security (transfer taxes — never counted as a middleman), pair_quotes (the window's share of the pool's 24 h count), hero_pair, platforms (explorer templates). A label call that fails leaves that label blank; the table still renders.
Rendercli.analyse() → compute() → render()The table, the route line and the receipt on stdout; main() exits 75 (EX_TEMPFAIL) when nothing landed because every fetch was throttled.
The six fields the product rests on

One object per swap. Six fields make the join possible; the rest is provenance.

FieldType on the wireWhat it makes possible
maaddressThe maker. "The same wallet on both sides of a block" becomes a count, not a suspicion.
hstringThe block. Both legs of a middleman are in one block; cast to int before comparing.
lgidstringThe position inside the block. "Between" is exact — a log index, not a timestamp. Cast to int.
tp"buy" | "sell"The side, so two legs can be opposite.
a0, a1numbersBase and quote amounts. a1 / a0 is the price a print paid; |Δa0| / a0 ≤ 5 % is the size match.
en, t0a, t1astring | null, addressesThe pool, reconstructed — the feed carries no pool address.

tx is carried for the same-transaction flag and the explorer link; v (USD) weights the round-trip share of volume. q is not read — it is rounded or zero on Uniswap v4 rows (FEEDBACK.md (opens in a new tab) #4).

The arithmetic, in full

Per pool, in chain order. Nothing here is a heuristic you cannot recompute by hand.

middleman/detect.py, cost.py, recommend.py:

# the join
legs(a, b)     = a.ma == b.ma and int(a.h) == int(b.h) and a.tp != b.tp
                 and |b.a0 − a.a0| / a.a0 ≤ 0.05

for each print a by wallet A, not yet a leg:
    b        = A's NEXT print in the same block          (none → a is organic so far)
    between  = the prints strictly between a and b
    if legs(a, b):
        take = b.a1 − a.a1  if a is a buy  else  a.a1 − b.a1
        sandwich   if 1 ≤ |between| ≤ 4 and every one is another maker printing in
                   a's direction, none already a leg, and take > 0   (between = victims)
        round-trip otherwise                                        (between recorded)

# the division
organic        = prints that are neither leg of a match     (victims stay organic)
q_i            = a1_i / a0_i
q2f_i          = (q_i / q_{i−1} − 1) × 1e4   for a buy      over consecutive ORGANIC prints
                 (1 − q_i / q_{i−1}) × 1e4   for a sell     adverse-signed
p50, p90       = nearest-rank, rank = ceil(p/100 × n)        always a real observation

# the rank
route          = argmin organic p90 among pools with ≥ 50 organic prints
cap            = ceil(p90 / 5 bps) × 0.05 %,  never below 0.05 %

naive is the same quote-to-fill series over all prints, legs included, computed only so it can be shown losing: on the hero pair it read 55.3 bps against 15.5 organic.

Failure handling

Every failure maps to a distinct outcome, so an outage is never blamed on a token.

middleman/tape.py. pull() returns (prints, meta); the error lives in meta, never in the rows.

ConditionBehaviour
HTTP 429 · any 5xx · connection droppedTransient. Retried up to 3 times, 15 s → 30 s → 60 s.
Transient on every retryThrottled. Returned with throttled: True; pages that already landed are kept, the window is marked partial and said out loud. When nothing landed the CLI exits 75 (EX_TEMPFAIL) with throttle_advice() — the two ways through.
Any other 4xxPermanent. Returned at once — retrying a 400 wastes the reader's time.
DNS · refused · timeoutThe caller's network. Returned as an error, not flagged as throttling.
Malformed JSONA contract problem. Returned at once.
Any HTTP error bodyDescribed by describe_http_error() as the status, CMC's error code and message — never a body sliced mid-string.
A label call fails (enrich.*)The table still renders; that label is blank. Context never takes down a run that has its number.
Routes · the deployment

Four static pages, two functions, no key on the server either.

RouteTypeServes
/staticsite/index.html — the table, the route line, the raw rows, the census strip, the receipt, the paste box. Rendered for the hero token; the Ethereum receipts are embedded so the token buttons switch tables with zero requests.
/evidencestaticsite/evidence.html — every call behind every receipt: URL, HTTP status, UTC, hash, credits; the rules verbatim; the endpoints table.
/judgestaticsite/judge.html — one page for one reader: the claim, the 30-second path, the receipt block, the reproduce command, the limitations. tests/test_judge_surface.py serves it and asserts 200 + the claim with no credentials.
/pitchstaticsite/pitch/index.html — twelve slides rendered from the same receipts through scripts/site_templates/deck.html; the version stamp is middleman.__version__.
/api/swapsserverless · api/swaps.jsGET ?platform=&address=[&lastId=] → the identical keyless CMC URL, body untouched under raw, plus Access-Control-Allow-Origin: * and Cache-Control: s-maxage=60. Upstream status passed through; a 429 comes back as a 429 with the CLI command as hint. Validates the platform and the address shape; holds no key. tests/test_proxy_boundary.py drives it under node with fetch stubbed: it can reach exactly one keyless host, and never forwards a caller's key, token, cookie or forwarded-host header.
/api/healthserverless · api/health.jsServer clock, the receipts' capture time, the census totals. No upstream call.

The four pages are generated, never hand-edited: scripts/render_site.py fills {{slot}} templates from docs/proof/*.json, aborts on any unfilled slot, and --check fails make check if the committed HTML is not what the receipts render. scripts/serve.js serves the same routes from a fresh clone on port 8101. This page is the one hand-written exception: it holds no number a receipt owns.

Repository layout

Six modules, eight scripts, four templates, two functions, ten tapes.

middleman/
  tape.py         the fetch: keyless GET with backoff, receipts, the lastId cursor, (tx, lgid) identity
  detect.py       order · group · middlemen — the join
  cost.py         a1/a0 · quote-to-fill · percentiles · pool_row · the example block
  recommend.py    route · cap_pct
  enrich.py       hero_pair · token_pools · label_pools · security · pair_quotes · platforms
  cli.py          analyse · compute · render · main — the table and the receipt
scripts/
  middleman.py    the door a judge walks through (python3 scripts/middleman.py)
  spike.py        the day-1 question, answered live → docs/proof/spike.json
  seed.py         the hero rule per chain + the watchlist → data/ + docs/proof/ + census.json
  verify_tape.py  every receipt re-derived from its tape, offline; exit 1 on drift
  bench.py        p50/p95 of the fetch (live) and the engine (replay)
  render_site.py  docs/proof/*.json → site/ through slot templates; --check gates drift
  check_submission_readiness.py   placeholders and stale test counts
  serve.js        site/ + api/ locally, the way Vercel routes them
  site_templates/ index.html · evidence.html · judge.html · deck.html
site/
  index.html · evidence.html · judge.html · pitch/index.html   generated — edit the templates or the receipts
  architecture/index.html      this page — hand-written, holds no number a receipt owns
  middleman.js    the engine ported to the browser + the page's interactions
  assets/         icon, social card, three OFL fonts
api/
  swaps.js · health.js         the two Vercel functions
data/
  tape_<sym>.json             10 tapes, rows verbatim, page hashes — the raw material
docs/
  METHOD.md                   the definitions, the invariant, the exclusions
  proof/                      spike.json · live_run.json · <sym>.json ×10 · census.json · platforms.json · bench_*.json
tests/                        offline by default; the live tests are marked and run apart
Deliberate non-architecture

What is not here, and why each absence is a decision.

Database
Every number is recomputed from a fetch or a committed tape. There is nothing to persist.
Cache in the product
A cached tape is a stale tape; the claim is about the current window. The proxy's 60 s CDN cache exists only so ten judges pasting one token are one upstream call.
A key
Every endpoint the product calls is on /public-api. CMC_API_KEY is accepted as an escape hatch for a throttled IP — read from the environment at call time, never from disk, never printed, never in the deployment — and a keyed run says so on its first line and in its receipt.
MEV labels, mempool, bundles
Not available on this API, and not needed: a middleman has to print, and the print order names them.
Sandwiches as the headline
Measured at 2 in 8,000 prints. The README says so; the detector stays because a zero it can prove is worth more than a rate it cannot.
A framework, a bundler, a font CDN
One HTML file per page, one script, three self-hosted fonts. The page makes zero requests to render.
Dependencies
Runtimenone — requirements.txt is a comment. Python 3.11 standard library: urllib, json, hashlib, math, statistics, time.
ServerlessNode 20 fetch, fs, path. No package.json, no node_modules.
Dev onlypytest, pytest-cov, ruff, pip-audit, hypothesis (requirements-dev.txt). node for the parity test and the local server.