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

82,816 MEME bot kept 0.97 WETH90,909 MEME bot kept nothing

Through the public mempool

—

Awaiting a run

Sealed with VeilSwap

—

Awaiting a run

Public mempool

Commit-reveal

01

Your order goes out

02

What a searcher can do with it

03

Your trade fills

04

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.

  1. 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

  2. Finding 02

    Settlement could be bought

    settleBatch is 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

The bot, run against the fixed contracts
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