Road to Devcon · NITK Surathkal
VeilSwap
- Pool
- — WETH
- — MEME
One 10 WETH trade, routed two ways against the same live pool. Everything below executes on chain while you watch.
Not a recording — a bot actually did this
Same trade. Same pool.
One version pays a bot 8,093 MEME.
Route a 10 WETH swap through the public mempool and an independent searcher brackets it before it fills. Seal the same order behind a commit and there is nothing for it to read.
Pool price during the trade
Through the public mempool
—
Awaiting a run
Sealed with VeilSwap
—
Awaiting a run
Public mempool
Commit-reveal
Your order goes out
What a searcher can do with it
Your trade fills
What happens after
The second gap
A settled trade is still a public trade
Sealing the order removes the front-run, but once it executes the swap is on chain at full size, tagged with your address. Batching closes that: orders revealed together are matched against each other first, and only the leftover imbalance reaches the pool.
Run the batch to see how little of it reaches the pool.
The adversary
We pointed a real searcher at it
scripts/searcher-bot.mjs is a separate process. It polls the pending pool, decodes whatever it finds and decides for itself what is worth attacking — nothing about the demo is scripted into it. Left on the unprotected path it takes 0.970654627539503385 WETH, the Session 1 figure to the last wei, arrived at on its own. It also broke two versions of this project.
Finding 01
The reveal gave the order away
The commit is genuinely opaque — the bot logged it and moved on. But
reveal()has to spell the order out in calldata, and that transaction sits in the mempool like any other. The bot read it and bracketed the reveal itself.Bot took 0.6977 WETH
Execution is now anchored to the price recorded at commit time, so shifting the pool enough to be worth attacking makes the reveal revert instead.
test_frontRunningTheRevealMakesItRevert
Finding 02
Settlement could be bought
settleBatchis permissionless and read the pool’s live price to decide how much of the batch matched internally. So whoever called it could move the pool and settle in one transaction, choosing the rate their own order cleared at.Settler took 2.965 WETH · honest buyer lost 30% of their fill
The batch now matches at the price recorded when its first order was revealed, and refuses to settle if the pool has drifted more than 1% since.
test_settlerCannotBuyTheSettlementPrice
SEENa sealed commit — hash only, no size or direction. Nothing to act on.TARGETveil reveal: 10 WETH in, min out 81818.18 (9090.90 MEME of slippage room)FRONT-RUNbuying with 5 WETH at 3600000024 wei gasLOSS-0.000000000000000001 WETH — the attack did not pay