Build1 publisher3 min readPublished Updated
Three wallets round-tripped two thirds of the volume in CoinMarketCap's top Uniswap v2 pair
A keyless detector built to catch sandwich attacks on public DEX data found two in 8,000 swaps. In the pool CoinMarketCap ranked first by 24-hour transactions, three wallets were buying back what they had just sold inside single transactions.
The Engineer · Build desk

What happened
- A keyless CoinMarketCap swap feed returns the maker wallet, the block and the log index on every row, so a detector can count a wallet standing on both sides of one block instead of inferring it.
- Twenty-nine round-trips by three wallets accounted for 66.2 per cent of that pool's volume across the 800 prints the run covered.
- Each of those round-trips closed inside a single transaction, the same wallet buying back what it had just sold, usually with no other print between the legs.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision The maker-address join is a few fields wide, so anyone feeding a pool's public activity numbers into a routing, listing or research decision can run it on the same free feed first.
- contradiction The two-thirds figure is a volume share. The #1 placing came from a transaction count, so how far the ranking itself was moved is a different number from the widely quotable one.
- capability Reproducing the count needs no node, no mempool feed and no API key, so a single engineer with an afternoon can run this class of audit.
- constraint At 0.025 per cent of prints, same-block sandwiches were too rare in this sample to explain what a trader pays, so a tool that instruments only front-running measures a minor component of trading cost.
The two fields that place a swap in the chain come back from the endpoint as quoted strings, while the amounts on the same row come back as numbers [3]. Sort them as text and "99" lands after "1000", so every "between" the detector computes is wrong and nothing raises an error [3]. The first function in the engine does one thing: cast block and log index to integers, and return None when a row cannot be placed [4]. Rows that cannot be placed are dropped and counted, and every receipt states the count [4]. The regression test that pins the trap is named test_block_and_log_index_are_sorted_as_integers_not_as_the_strings_they_arrive_as [5].
The ranking and the volume share measure different fields. MOTO/WETH was first among Ethereum's Uniswap v2 pairs by 24-hour transactions at capture time [13]. The share is of volume: $127,254 of $192,275 [15]. Each round-trip prints two legs, so the 29 round-trips are 58 of the window's 800 prints, which is 7.25 per cent of the count the ranking sorts on [20]. Everything else trading in that pool over the 3.6 hours came to $65,021 [21].
Whether the volume share transfers to another pool depends on what the join accepts. Two prints count as one middleman when the maker address is the same, the block is the same, the sides are opposite and the sizes match within 5 per cent [7]. Five per cent is generous; the first receipt in the evidence matched at 1.58 per cent, in block 26006339, log index 209 out and 218 back, inside one transaction [16]. The feed does not include a pool address, so prints are grouped by venue plus the two token contracts [6]. The join wants one address on both legs, so a wallet that splits its two legs across two addresses falls outside it, and loosening the size tolerance leaves that where it is.
The sandwich hunt itself came up close to empty. A first run over 1,200 prints from the busiest pair found zero same-block sandwiches [10]. The ten-token census across Ethereum, BSC and Solana found two in 8,000 prints, the same wallet front-running a seller on UNI and on LINK, for $0.95 and $2.19 [11]. That is a rate of 0.025 per cent [12], and $3.14 of extracted value in total [22].
The detector gets there without a mempool subscription. "You get no mempool and no MEV labels, and you don't need them: a middleman has to print," the author wrote [17]. The round-trip count covers one pair over 3.6 hours [14]; the wider census was run to count sandwiches [11]. "So I changed the headline before building the page, not in a caveat after," he wrote [18]. The code is MIT-licensed, stdlib-only Python with no key required, and the live judge page puts every request behind it on /evidence [19].
What to watch
- Whether a round-trip census across the other nine census tokens shows the MOTO/WETH pattern is pool-specific or common.
- Whether CoinMarketCap changes what its 24-hour transactions field counts, or filters legs that close inside one transaction.
- Whether the single maker-address join holds once a wallet splits its two legs across two addresses.