Skip to content
edycutjongPublic

About

🀝 Who stands between your quote and your fill, per pool β€” round-trips, sandwiches and the price an organic fill pays, from real DEX prints on the CoinMarketCap API. Keyless.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

50 Commits

Folders and files

Repository files navigation

Middleman icon

Middleman

Who stands between your quote and your fill, per pool.

Middleman β€” names the middleman: Two blue prints in one block turn orange the instant the same wallet is found on both sides. 66.2% of the busiest Uniswap v2 pair's volume was 3 wallets buying back what they just sold.

A DEX trader sees a quote and a fill and cannot tell what stood between them β€” a wallet printing on both sides of the block, or the pool itself. Middleman orders a token's real prints by block and log index, joins them on the maker address, names the middleman, and says where to route and what slippage cap to set.

Live, keyless, 2026-09-18T22:36Z: CoinMarketCap's #1 Uniswap v2 pair on Ethereum by transactions was busy with itself β€” 66.2 % of its volume was 3 wallets buying back what they just sold, inside one transaction, 29 times in 3.6 hours. Take the legs out and an organic fill there pays 15.5 bps, not the 55.3 the raw tape implies. 800 prints in 8.6 s for 0 credits. Receipt β†’


Judge Guide Live page For judges Evidence Pitch Deck API Feedback Built for Build with CMC


Python CI Release CoinMarketCap No API key Zero dependencies Tests License


πŸ“Έ See it in Action

No key. No signup. No install. One command:

python3 scripts/middleman.py
middleman 0.1.0 β€” keyless, live

hero rule: the base token of the #1 Ethereum Uniswap v2 pair by 24h transactions at capture time
  β†’ MOTO/WETH  0xbd965230588eaa536de6aa45e8ebbc01638535e0

MOTO Β· ethereum Β· 800 prints Β· 3.86 h Β· blocks 26006381–26007537 Β· captured 2026-09-18T23:00:20Z Β· keyless

  27.5% of Uniswap v2 / WETH volume is 1 wallet(s) buying back what they just sold (8 round-trips, same transaction)

  pool                         prints organic  round-trips                sandw  q→fill p50 / p90     tax     liq
  ----------------------------------------------------------------------------------------------------------------
  Uniswap v2 / WETH               771     755  8 Β· 1 wallet(s) Β· 27.5%        0  39.2 / 60.8 bps      0/0   $954k  β—€ route
  Uniswap v2 / USDC                16      16  β€”                              0  70.4 / 137.8 bps     0/0    $39k
  Uniswap v2 / USDT                13      13  β€”                              0  71.1 / 135.2 bps     0/0    $34k

  β–Ά route via Uniswap v2 / WETH Β· cap slippage at 0.65 %   (organic p90 60.8 bps, 1 candidate pool(s))
    rule: lowest organic p90 quote-to-fill among pools with β‰₯ 50 organic prints
    coverage: this window is 16% of the routed pool's 24h transactions (4969: 2696 buys / 2273 sells)

  block 26006381 β€” raw rows (round-trip)
    lgid   633  0x9c97c0a6a0…  sell a0       505,623.1418  a1       0.625752  a1/a0 1.2376e-06  β—€ leg
    lgid   642  0x9c97c0a6a0…  buy  a0       497,596.3088  a1       0.619495  a1/a0 1.2450e-06  β—€ leg
    lgid   721  0xdbde0f745f…  buy  a0           240.4182  a1       0.000300  a1/a0 1.2494e-06
    leg 1  lgid 633  0x9c97c0a6a00b1a9f74dbe09a4c55ec9f09f8af7d  sell  a1/a0 = 0.6257523552405421 / 505623.14176740864 = 1.2375864622280164e-06
    leg 2  lgid 642  0x9c97c0a6a00b1a9f74dbe09a4c55ec9f09f8af7d  buy  a1/a0 = 0.6194948316881366 / 497596.3088161665 = 1.2449747329556753e-06
    same wallet Β· same block 26006381 Β· same tx Β· |Ξ”a0| / a0 = 0.0159 ≀ 0.05 β†’ round-trip

wrote docs/proof/live_run.json  (11.3s wall clock, 0 credits β€” keyless)

That is a live call to CoinMarketCap's keyless /public-api surface. Nothing here is a fixture. This transcript is from 2026-09-18T23:00:18Z (run with --json so the receipt was kept: docs/proof/live_run.json; the bare command prints the same table). The page β€” and the numbers at the top of this file β€” come from the capture scripts/seed.py made 24 minutes earlier with the same engine and the same rule, when the same wallets were 66.2 % of the pool's volume across 29 round-trips (docs/proof/moto.json, the 800 raw prints in data/tape_moto.json). Between the two runs they went quiet; a third run the next morning found the window clean β€” zero round-trips, and the tool said so (docs/proof/live_run_quiet.json). Run it yourself and the number will differ again, because it comes from the market rather than from this file β€” and python3 scripts/verify_tape.py re-derives every committed receipt from its tape with the network unplugged. All four transcripts β€” the fourth, on 2026-09-20, found a different hero and 17 sandwiches β€” are in DEMO.md. Each transcript's first line stamps the version the engine declared when it was captured (0.1.0); the first tagged release is v1.0.0 (2026-09-20), and every receipt still re-derives from its tape under it.

Read the two orange rows. Same maker, same block, same transaction hash: sell 505,623 MOTO, buy 497,596 MOTO back, 1.6 % apart. Divide a1 by a0 and you have the price each leg paid. In the capture behind the page that wallet and two others did it 29 times β€” $127,254 of the pool's $192,275 β€” and the pair sits at the top of CoinMarketCap's activity ranking because of them.

The same table, in a browser: middleman.edycu.dev renders the capture with the raw rows one click away, every request behind it on /evidence, and a paste box that runs the same engine live on any token. One page for judges, no key and no setup: /judge.

The page: 66.2 % headline, the pool table, the route line
The table. 66.2 % Β· three pools Β· the route line
UNI: 19 pools ranked by organic quote-to-fill, route via Uniswap v3 / WETH, cap 0.20 %, and the sandwich block
UNI, live. 19 pools, the route, and the one sandwich
/evidence: 108 hashed requests, the rules verbatim, the re-derive commands
/evidence. 108 requests, hashed, credits 0
The paste box running the engine live on SHIB through the keyless proxy
Paste a token. SHIB, live, through the keyless proxy

Nine screenshots of live execution, including the raw-rows panel and the 390 px mobile view, are in docs/screenshots/.


πŸ’‘ The Problem & Solution

The Problem

Every DEX tool shows a trader two numbers before a swap β€” a quote and a slippage tolerance β€” and one after: the fill. The gap between them has three possible authors: a sandwicher printing around her inside the block, a wash bot whose round-trips make a pool look deep, or the pool's own thinness. Nothing tells her which. So she picks the pool with the most transactions (the one most likely to be washed) and sets a loose cap "to be safe" (the one thing a sandwicher needs). Every flow tool prints volume and transaction counts β€” exactly the numbers a round-tripping wallet inflates.

The Solution

CoinMarketCap's /v1/dex/tokens/transactions returns, per swap and with no key, the maker address (ma), the block (h), the log index (lgid), the side and both amounts. Order the prints by (h, lgid), group them into pools by (venue, base, quote), and join on the maker:

Shape Definition What it is
Round-trip the same wallet's next print in the block is the other side, size within 5 % buying back what it just sold β€” usually inside one transaction
Sandwich a round-trip whose legs enclose 1–4 other makers printing in the first leg's direction, and the wallet came out ahead the prints between were front-run
Organic everything else β€” victims included; their fills are real what a trader actually pays

Then, for every organic print, the basis points between the price it paid (a1 / a0) and the print before it in the same pool β€” the price you saw versus the price you got β€” p50 and p90 per pool. And one line: route via the pool with the lowest organic p90, cap slippage at that p90 rounded up to 0.05 %.

The hero is a rule, not a pick. The page shows whichever token is the base of the pair CoinMarketCap itself ranks #1 by 24 h transactions on Ethereum Uniswap v2 at capture time. Whatever that pair is on capture day is what the page shows.

The census β€” ten tokens, three chains, whatever they said

Token Β· chain Prints Round-trips Sandwiches Route Β· cap
MOTO Β· ethereum β€” hero by rule 800 29 Β· 3 wallets Β· 66.2 % of volume 0 Uniswap v2 / WETH Β· 0.65 %
SHIB Β· ethereum 800 0 0 ShibaSwap / WETH Β· 0.65 %
PEPE Β· ethereum 800 0 0 Uniswap v2 / WETH Β· 0.65 %
UNI Β· ethereum 800 0 1 Uniswap v3 / WETH Β· 0.20 %
LINK Β· ethereum 800 0 1 Uniswap v4 / ETH Β· 0.05 %
AAVE Β· ethereum 800 0 0 Uniswap v3 / WETH Β· 0.15 %
Mog Β· ethereum 800 0 0 Uniswap v2 / WETH Β· 0.65 %
SPX Β· ethereum 800 0 0 Uniswap v2 / WETH Β· 0.65 %
USDT Β· bsc β€” hero by rule 800 13 0 PancakeSwap v4 / KII Β· 0.05 %
USDC Β· solana β€” hero by rule 800 3 0 Tessera V / SOL Β· 0.05 %

8,000 prints Β· 2 sandwiches Β· 45 round-trips by 14 wallets. The two sandwiches are the same wallet, 0xae2fc483…, front-running a seller on UNI and on LINK in three separate transactions inside one block each time β€” for $0.95 and $2.19. The widest spread between one token's pools: LINK, 22.9Γ— (0.6 bps median on Uniswap v4 / ETH against 14.2 on Uniswap v3 / USDC). Every row's receipt is in docs/proof/ and every receipt re-derives from its tape.

What we got wrong β€” dated retraction

2026-09-18. This project was decided as the sandwich rate per pool, with a prediction that it might be near zero off Ethereum mainnet. The day-1 spike ran the join on 1,200 real prints and found zero same-block sandwiches; the ten-token census found two in 8,000. The pre-authorised fallback β€” victim overpay on same-block front-runs β€” measured a median of 0.0 bps. So the headline changed before the build, not the caveat after: the engine is the same feed, the same ordering, the same maker join; the sandwich is one named case of a middleman; and the number the page leads with is the one the join actually found β€” a round-trip share that the sponsor's own activity ranking is built on.

2026-09-19. The first cut of the join called four things on USDT/DGAI (PancakeSwap v3, BSC) sandwiches. They were two wallets washing around each other β€” each selling and buying back for exactly the quote it received, take zero. A sandwich now requires take > 0; a same-wallet return with nothing extracted is a round-trip whether or not other makers printed between the legs. The regression test is named for it.

2026-09-20. Two days after the census put the sandwich rate at 0.025 %, the bare command found 17 sandwiches in one 800-print window on the day's #1 pair (wildebeest/WETH, Receipt 4), led by the same wallet the census had caught twice. The rate is small on average, not small everywhere β€” so the census number stands as a census number, the headline stays the price an organic fill pays, and the sandwich is the named case the join was built to catch.


πŸ—οΈ Architecture & Tech Stack

pull the prints  β†’  order by (block, log index)  β†’  recover pools  β†’  join on the maker  β†’  price every organic fill  β†’  route + cap
Middleman architecture: the CLI pulls a token's swaps from CoinMarketCap's keyless DEX feed, orders them by block and log index, joins on the maker, names round-trips and sandwiches per pool, and writes a receipt the site, the JS engine and verify_tape.py all read

The same diagram as a page, light and dark, with the derivation beside it: middleman.edycu.dev/architecture

Architecture diagram as Mermaid (click to expand)
flowchart LR
  TX["/v1/dex/tokens/transactions<br/>ma Β· h Β· lgid Β· tp Β· a0 Β· a1 Β· tx Β· en Β· t0a Β· t1a"] --> PULL["tape.pull()<br/>keyless Β· lastId cursor Β· (tx, lgid) identity Β· receipts"]
  PULL --> ORDER["detect.order() Β· detect.group()<br/>(int(h), int(lgid)) Β· (en, t0a, t1a)"]
  ORDER --> JOIN["detect.middlemen()<br/>round-trips Β· sandwiches Β· organic"]
  JOIN --> COST["cost.pool_row()<br/>a1/a0 Β· quote-to-fill p50/p90"]
  COST --> ROUTE["recommend.route()<br/>argmin organic p90 Β· cap = p90 ↑ 0.05 %"]
  POOLS["/v1/dex/token/pools Β· /v1/dex/security/detail Β· /v4/dex/pairs/quotes/latest"] --> LABEL["enrich.*<br/>pool address Β· liquidity Β· taxes Β· 24 h coverage"] --> COST
  SP["/v4/dex/spot-pairs/latest"] -->|"the hero rule"| CLI["scripts/middleman.py<br/>live Β· keyless Β· zero flags"] --> PULL
  SEED["scripts/seed.py"] --> TAPE[("data/tape_*.json")] -->|"verify_tape.py"| PROOF[("docs/proof/*.json")] --> RENDER["render_site.py"] --> SITE["site/ β€” /, /evidence, /judge"]
  SITE -->|"paste a token"| JS["site/middleman.js<br/>the engine, ported"] --> FN["/api/swaps<br/>keyless proxy + CORS + 60 s cache"] --> TX
Loading

No server, no database, no model. The product is one join applied to data only CoinMarketCap publishes, so everything that is not the fetch, the join, or the arithmetic was removed.

Stage Function What it does
Fetch tape.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: URL, status, body hash, timing.
Order detect.order() Β· detect.group() Sort by (int(h), int(lgid)) β€” both arrive as strings; recover pools from (en, t0a, t1a) β€” the feed carries no pool address.
Join detect.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.
Price cost.pool_row() a1 / a0 per print, never the feed's rounded q; quote-to-fill against the previous print, p50 / p90 per pool; the example block with its arithmetic.
Route recommend.route() 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.
Label enrich.* Pool address and liquidity, transfer taxes (never counted as a middleman), the window's share of the pool's 24 h count, the hero rule, explorer templates.
Layer Technology
Language Python 3.11, stdlib only on the judged path β€” urllib, json, hashlib, statistics
Data CoinMarketCap DEX API, keyless /public-api surface β€” six endpoints, four load-bearing
Browser one static page + a 338-line port of the engine (site/middleman.js), parity-tested against Python on every tape
Deployment Vercel: static site/, two functions under api/ (a keyless CORS proxy and a health check)
Tests pytest Β· hypothesis (property-based) Β· live contract tests Β· node-driven boundary tests
Quality ruff Β· mypy Β· pytest-cov Β· pip-audit Β· gitleaks Β· CodeQL Β· Dependabot

Full derivation, every failure mode, and the deliberate non-architecture: ARCHITECTURE.md Β· the definitions: docs/METHOD.md. Rendered page, light and dark: middleman.edycu.dev/architecture.


πŸ† CoinMarketCap Integration

# Endpoint Role Key? Called from
1 /public-api/v1/dex/tokens/transactions the engine β€” ma, h, lgid, tp, a0, a1, tx, en, t0a, t1a per swap; lastId cursor πŸ”“ none middleman/tape.py Β· api/swaps.js
2 /public-api/v1/dex/token/pools pool address, venue, liqUsd β€” labels each (en, t0a, t1a) group, counts merged fee tiers πŸ”“ none middleman/enrich.py
3 /public-api/v1/dex/security/detail extra.buyTax / extra.sellTax β€” a transfer tax is never counted as a middleman πŸ”“ none middleman/enrich.py
4 /public-api/v4/dex/pairs/quotes/latest 24h_no_of_buys + 24h_no_of_sells β€” how much of a day the window covers πŸ”“ none middleman/enrich.py
5 /public-api/v4/dex/spot-pairs/latest the hero rule β€” #1 pair by no_of_transactions_24h, per chain πŸ”“ none middleman/enrich.py
6 /public-api/v1/dex/platform/list explorer URL templates for the transaction links πŸ”“ none middleman/enrich.py

Every call is on /evidence with its URL, status, timestamp, body hash and the envelope's credit_count β€” CMC reports 1 per keyless call against an account that does not exist (FEEDBACK.md Β§2); no key was sent, so 0 credits were charged on every receipt β€” and tests/test_published_counts.py fails if the code ever calls a path this table does not name.

Why only CoinMarketCap

The join needs the maker address, the block, the log index, the side and both amounts, per swap, for every pool of a token across a chain, keyless, with a cursor. That row shape is what /v1/dex/tokens/transactions returns, and nowhere else is it published without an indexer. The maker address is what lets "the same wallet on both sides" be counted rather than suspected; the log index is what makes "between" exact inside a block instead of a timestamp heuristic. CMC has no mempool view and no MEV labels β€” and the product does not need them, because a middleman has to print. /v1/dex/token/pools is what turns a (venue, base, quote) group back into an address with liquidity; /v1/dex/security/detail is what keeps a fee-on-transfer token from being scored as extraction; /v4/dex/pairs/quotes/latest is what tells a reader how much of the day 800 prints covered; /v4/dex/spot-pairs/latest is what makes the hero a rule instead of a pick.

Remove CoinMarketCap and you would need a multi-chain swap indexer with maker attribution, a per-DEX pool registry, a fee-on-transfer simulator, a second aggregator for pair counts and an explorer map β€” five systems β€” to recompute what eight keyless pages return.

Where the API got in the way is in FEEDBACK.md: eight dated, evidenced findings for the CMC product team, from the missing CORS header to q being rounded on Uniswap v4 rows β€” and what is genuinely excellent and worth protecting.


⛓️ Live Deployment

No wallet needed anywhere β€” every page is a read-only call. Nothing signs, nothing is submitted on chain; the product reads prints that already landed.

Route Serves
middleman.edycu.dev the table, the route line, the raw rows, the census strip, the receipt, the paste box β€” rendered from the committed capture, JavaScript-off safe
/judge one page for one reader: the claim, the 30-second path, the receipt block, the real reproduce command, the limitations β€” no key, no cookie, no session
/evidence every request behind every receipt: URL, HTTP status, UTC, sha256 of the body, credits
/pitch the pitch deck β€” 12 slides, arrow keys, P for speaker notes, Cmd+P for a PDF; every number a slot from the same receipts
/api/swaps?platform=&address= the identical keyless CMC URL with the one header CMC omits (Access-Control-Allow-Origin) and a 60 s CDN cache β€” api/swaps.js, 58 lines, holds no secret and can reach no other host
/api/health the server clock, the receipts' capture time, the census totals; no upstream call

The four pages are generated, never hand-edited: scripts/render_site.py renders them from docs/proof/*.json through {{slot}} templates, aborts if any slot is unfilled, and make check fails if the committed HTML is not what the receipts render. node scripts/serve.js serves the same routes from a fresh clone on port 8101.

middleman.edycu.dev is the canonical host β€” a custom domain on the same Vercel project, HTTPS enforced (http:// answers 308); middleman-cmc.vercel.app is the project's own alias and serves the identical build. On either host the paste box calls /api/swaps same-origin; served from anywhere else (a file server, a mirror) it reaches the canonical host by absolute URL (API_BASE in site/middleman.js).


πŸ“Š Engineering Rigor

Measurement Value
Live run wall clock 8.6 s β€” 800 prints of the hero token, 11 calls, clean path Β· 11.0 s and 11.6 s on the third and fourth runs
Credits used 0 β€” keyless, with every CMC variable explicitly unset, on all four receipts
Tests 257 β€” 251 offline, 6 live; each regression named for the defect it pins
Property-based verification of the join 1,000 generated blocks, 0 violations of middlemen()'s own definition
Parity the browser engine (site/middleman.js) vs the Python engine, every pool row of every committed tape
Permission boundary the one deployed function proven to reach exactly one keyless URL and never forward a caller's credential β€” tests/test_proxy_boundary.py
Re-derivation scripts/verify_tape.py β€” every published number from its tape, offline; make check fails on drift
Drift gates render_site.py --check (pages = receipts) Β· check_submission_readiness.py (no placeholder, no stale count on any judge-facing surface)
Live contract tests/test_live.py asserts the real row shape: fields present, h/lgid parse as ints, a1/a0 computable, t0a is the queried token
Branch coverage of the engine 97.6 % of middleman/, gated at 95 % β€” make test-coverage
Benchmarks engine p50 3.3 ms / 800 prints (p95 3.7 ms, n=200) Β· one keyless page p50 1.5 s, p95 16.4 s (n=8) β€” one iteration sat through the throttle backoff

The 1,000 is the number worth reading. Coverage says we ran the lines we wrote. The property test says that across 1,000 generated blocks middlemen() never violated its own definition: every print is a leg or organic and never both; every round-trip is the same wallet's next print on the other side at a matched size; every sandwich has one to four victims who all printed in the first leg's direction and are still in the organic sample; and the attacker came out ahead.

pytest tests/test_property.py --hypothesis-show-statistics     # β†’ 1000 passing, 0 failing

Attacks defeated

Control Why it is load-bearing Test
A same-wallet return that extracted nothing is a round-trip, not a sandwich the first cut scored two wash bots on BSC as four sandwiches β€” the retraction above tests/test_detect.py:185
The same wallet on both sides in two different blocks is not a round-trip "between" is a block-level fact; a timestamp heuristic would invent middlemen tests/test_detect.py:90
A front-run that came out behind is a round-trip, not a sandwich an extraction with a negative take is not extraction tests/test_detect.py:242
Block and log index sort as integers, not as the strings they arrive as "9" > "10" as strings β€” the join order would be wrong on every page boundary tests/test_detect.py:15
The price is a1 / a0, never the feed's rounded q q is rounded to two significant figures, or zero, on Uniswap v4 rows (FEEDBACK.md Β§4) tests/test_cost.py:13
The cursor is read from the envelope's lastId, not from the last swap a cursor off the last row re-fetched the same 100 prints forever tests/test_tape.py:78
A 500 is the same throttle wearing a different status the anonymous tier reports its limit as 500 as often as 429 (FEEDBACK.md Β§2) tests/test_tape.py:141
With every key variable unset, no credential header is sent the keyless claim is the whole reproducibility story tests/test_tape.py:228
seed.py refuses to record a keyed receipt a keyed run can never pass as one of the published keyless receipts tests/test_scripts.py:217
A tampered receipt is named by the offline re-derivation the tapes are the raw material; a number edited by hand fails make check tests/test_scripts.py:227
The proxy can only ever reach one keyless CoinMarketCap URL a public function on a shared IP must not be an open proxy tests/test_proxy_boundary.py:69
A caller's key, token, cookie or forwarded-host is never forwarded upstream the deployment holds no secret and will not carry yours tests/test_proxy_boundary.py:94
security/detail taxes are read from the list-wrapped, camel-cased block they actually arrive in a misread tax would score a fee-on-transfer token as extraction tests/test_enrich.py:146
When no pool qualifies, the rule is stated, not widened a routing rule that loosens itself to produce an answer is not a rule tests/test_recommend.py:51
An unfilled template slot stops the render rather than shipping the braces no placeholder can reach a page a judge reads tests/test_render_site.py:23
Every surface publishes the test count the suite has "251 tests" on a README that has 257 is the cheapest way to look careless tests/test_published_counts.py:16
/judge answers 200 with the claim to a client carrying no credentials a judge page that breaks on submission day is worse than none tests/test_judge_surface.py:89
The join never violates its own definition, across 1,000 generated blocks the property test, not the examples, is what makes the shapes above a definition tests/test_property.py:31

Honest limits (6)

  1. The window is the last 800 prints, not 24 hours β€” 3.6 h on the hero, 20 h on SHIB. The span is on every row; the receipt carries the window's share of the pool's 24 h count.
  2. Uniswap v3 fee tiers of one pair merge β€” the feed carries no pool address; a row marked Γ—n is n pools sharing (venue, base, quote). Filed as FEEDBACK.md Β§6.
  3. Quote-to-fill is a realised, fee-inclusive print-to-print move β€” p90 sits near 60 bps on every busy 0.30 % pool because consecutive opposite-side prints straddle the fee twice. It is used comparatively, across pools of one token in one window, where the bias is shared.
  4. A round-trip is a shape, not a verdict β€” same wallet, same block, opposite sides, size-matched. The rows and the definition are printed; nobody is labelled.
  5. The anonymous tier throttles per IP, reports it as 500 as often as 429, and sends no Retry-After. The tool backs off 15 / 30 / 60 s and exits 75 when the quota is spent. Filed as FEEDBACK.md Β§2.
  6. The deployed paste box shares one IP across every visitor. The page renders from committed receipts, the proxy caches 60 s, and the CLI is the primary live path.

πŸš€ Getting Started

Prerequisites

  • Python 3.11 or newer. That is the entire list.
  • No API key, no account, no pip install. The engine and the CLI are stdlib-only.

Installation

git clone https://github.com/edycutjong/middleman.git
cd middleman
python3 scripts/middleman.py

For judges: there is no account to create and no credential to configure β€” the judged path is keyless by design. Start at JUDGE.md or /judge.

If CoinMarketCap's anonymous tier is throttling your IP, the script backs off (15 s, 30 s, 60 s) and, if the quota is exhausted, exits 75 with a message that says so rather than printing a number. Optional escape hatch: a free key from coinmarketcap.com/api exported as CMC_API_KEY moves the identical calls to the keyed host. Never required β€” the default path is keyless, a keyed run announces itself on its first line, and every receipt here was taken with no key set.

python3 scripts/middleman.py                                   # the hero by rule, live, keyless
python3 scripts/middleman.py --address 0xbd965230588eaa536de6aa45e8ebbc01638535e0 --symbol MOTO --pages 8 --json moto.json
python3 scripts/middleman.py --address 0x… --symbol X --platform bsc  # any token; ethereum | bsc | solana
python3 scripts/middleman.py --watchlist                       # SHIB Β· PEPE Β· UNI

The library is importable β€” six pure functions, stdlib only:

from middleman import tape, detect, cost, recommend

prints, meta = tape.pull("ethereum", "0xbd965230588eaa536de6aa45e8ebbc01638535e0", pages=8)
for key, rows in detect.group(prints).items():
    rt, sw, organic = detect.middlemen(rows)
    row = cost.pool_row(key, rows, rt, sw, organic)

πŸ§ͺ Testing & CI

make setup           # dev deps only: pytest, pytest-cov, ruff, mypy, hypothesis, pip-audit
make lint            # ruff check + format check
make typecheck       # mypy over middleman/, scripts/ and tests/
make test            # 251 offline tests, no internet
make test-coverage   # the same, branch coverage of the engine gated at 95%
make test-live       # 6 tests against the real CoinMarketCap contract, keyless
make demo            # the judged capability, live, no key
make verify          # every committed receipt re-derived from its tape, offline
make bench           # deterministic p50/p95 over the committed tape β€” a replay, not the product
make bench-live      # p50/p95 over the real keyless fetch
make site            # re-render /, /evidence, /judge and /pitch from docs/proof/*.json
make serve           # site/ + api/ on http://localhost:8101, the way Vercel routes them
make audit           # pip-audit + gitleaks over the full history
make check           # refuse to ship a placeholder, a stale count, or a page that drifted
make ci              # lint + typecheck + test-coverage + audit + check
Layer Tool Status
Code quality ruff (check + format) Β· mypy βœ…
Unit testing pytest, 251 offline tests Β· engine + scripts branch coverage 100 %, gated at 100 % βœ…
Property testing hypothesis, 1,000 generated blocks βœ…
Live contract testing pytest -m live against real CMC, keyless βœ…
Permission boundary api/swaps.js driven under node with fetch stubbed βœ…
Judge surface /judge served and probed with no credentials β€” locally in the suite, in CI over HTTP βœ…
Security (SAST) CodeQL β€” Python and JavaScript βœ…
Security (SCA) Dependabot + pip-audit βœ…
Secret scanning gitleaks, full history, on every push βœ…
Release automation semver derived from Angular-convention commits βœ…

Six stages on every push: lint + mypy and the tests on Python 3.11 / 3.12 / 3.13 β†’ pip-audit and the three receipt gates β†’ the deterministic benchmark (a replay, labelled as such) β†’ the judged capability, live and keyless (exit 75 means CMC throttled the shared runner, not that the product failed) β†’ /judge, /, /evidence and /api/health probed over HTTP with no credentials β†’ the deploy gate on main, then the gated production deploy to Vercel (vercel build + vercel deploy --prebuilt --prod, only after every stage above is green). It is keyless, so it runs on forks and PRs too; if CMC changes the contract it breaks in CI rather than in front of a judge.


πŸ“ Project Structure

middleman/
β”œβ”€β”€ middleman/                    the engine β€” six stdlib modules
β”‚   β”œβ”€β”€ 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 Β· render Β· main β€” the table and the receipt
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ middleman.py              the door a judge walks through
β”‚   β”œβ”€β”€ 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 on every judge-facing surface
β”‚   β”œβ”€β”€ serve.js                  site/ + api/ locally, the way Vercel routes them
β”‚   └── site_templates/           index.html Β· evidence.html Β· judge.html Β· deck.html β€” {{slot}} templates
β”œβ”€β”€ site/                         generated: / Β· /evidence Β· /judge Β· /pitch Β· middleman.js (the engine, ported)
β”œβ”€β”€ api/                          swaps.js (keyless proxy) Β· health.js β€” the two Vercel functions
β”œβ”€β”€ tests/                        257 tests: the join, the fetch contract, the CLI, parity, the boundary, the surfaces
β”œβ”€β”€ data/                         tape_<sym>.json Γ—10 β€” rows verbatim with page hashes; nothing judged reads them
β”œβ”€β”€ docs/proof/                   <sym>.json Γ—10 Β· census.json Β· spike.json Β· live_run*.json Β· bench_*.json
β”œβ”€β”€ docs/METHOD.md                the definitions, the invariant, the exclusions
β”œβ”€β”€ JUDGE.md Β· DEMO.md Β· ARCHITECTURE.md Β· FEEDBACK.md
└── README.md                     you are here

πŸ—ΊοΈ Roadmap

  • Per-swap join on the keyless /v1/dex/tokens/transactions β€” round-trips, sandwiches, organic
  • Prices from a1 / a0, never the feed's rounded q; order from (int(h), int(lgid))
  • Pools recovered from (en, t0a, t1a) and labelled with address, liquidity and taxes
  • The routing rule and the slippage cap, stated on screen with the rule and the coverage
  • The hero chosen by a published rule from CMC's own ranking, on three chains
  • Ten-token census with every receipt committed and re-derivable offline
  • Backoff across both forms of the anonymous throttle; an exhausted quota exits 75 and explains itself
  • The page, the evidence page and the judge page, generated from the receipts and gated on drift
  • The engine ported to the browser, parity-tested, behind a keyless proxy whose boundary is tested
  • Per-fee-tier rows on Uniswap v3 β€” not possible from the feed: it carries no pool address (FEEDBACK.md Β§6). Merged tiers are marked Γ—n instead of guessed.
  • A 24 h window by default β€” eight keyless pages is 3–20 h depending on the token; a full day is --pages 24 on a busy pair, against a per-IP quota. The window's share of the day is printed instead of assumed.
  • Naming wallets β€” deliberately not built. A round-trip is a shape; the rows are printed and the reader decides.

πŸ“½οΈ Demo Materials

Demo video https://youtu.be/BwikjIf19hQ β€” 2 min 57 s: the committed page, a real keyless run from a fresh clone at real speed, LINK pasted live, the evidence page and the census; subtitles in the upload
For judges JUDGE.md Β· /judge β€” the claim, the 30-second path, the receipt, the real reproduce command.
Live page middleman.edycu.dev β€” the capture beside its raw rows, a dated snapshot that says so on every number, and a paste box that runs the engine live.
Evidence /evidence β€” every request, hashed.
Pitch deck /pitch β€” 12 slides, arrow keys, P for notes; the frozen slide is two raw rows and one division.
The receipts DEMO.md β€” four real transcripts, with docs/proof/ behind them.
Social card docs/assets/og-image.png β€” the mark: two blue prints, one orange hairpin.
Screenshots docs/screenshots/ β€” nine, of live execution: the table, the raw rows, the UNI sandwich, the paste box mid-fetch, /evidence, mobile.
How it differs docs/COMPARISON.md β€” the six nearest entries in this hackathon's gallery, by name, and the exact boundary with each.

The four transcripts disagree β€” 66.2 %, 27.5 %, 0 %, and then a different pair with 17 sandwiches β€” because the 800-print window and the rule's pick moved between the runs. Same rule, same engine. A number that moves with the market is the evidence it is live.


πŸ“„ License

MIT β€” see LICENSE. Β© 2026 Edy Cu Β· @edycutjong


πŸ™ Acknowledgments

Built for the Build with CMC: API Hackathon, Markets and Trading Tools track. Thank you to the CoinMarketCap team for publishing the maker address and the log index on every swap, keyless β€” those two fields are what make "who stood between" a count instead of a guess β€” and our feedback on the rest of the API is in FEEDBACK.md.

About

🀝 Who stands between your quote and your fill, per pool β€” round-trips, sandwiches and the price an organic fill pays, from real DEX prints on the CoinMarketCap API. Keyless.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages