Duelis
A Uniswap v4 pool defended by small on-chain networks that evolve every day. Guards price the trades that look like bots, Shadows paper-trade against them, and ordinary holders keep a 1% fee that no network can change.
Case study 02 of 04 · Ethereum mainnet · live since 14 October 2026 · block 26,193,597
- Role
- Design, economic model, Solidity, testing, launch and site integration
- Launched
- 14 October 2026, 22:01 UTCBlock 26,193,597, through Programmable’s Custom Launch
- Contracts
- Four of mine, all verifiedEtherscan, exact match; plus the platform’s fee vault
- Testing
- 26 Foundry tests on a mainnet forkAll 26 re-run and passing on 20 October 2026
- Stack
- Solidity 0.8.26 (via IR), Foundry, Uniswap v4, Permit2, JavaScript
- Hook permissions
- 0x10CC
The trade
Hook fee on the ETH side
1.00%
Fixed in code
A buy always pays the 1% floor unless the same wallet already traded in this block. No Guard can change it.
Every trade also pays the pool’s 1% LP fee and the 0.30% platform fee.
The project
Duelis is a token whose only pool is guarded by its hook. Six small neural networks, the Guards, decide the fee on trades that look like bots: quick flips, repeat trades in one block, and the back half of a sandwich. Four more, the Shadows, replay each day’s market as paper-trading bots and look for what the Guards fail to tax.
Once a day, anyone can run the duel. The weakest network on each side is replaced by a mutated copy of the strongest, and the best Guard takes over. Buyers who use the project’s site can back one side inside their buy, and the winning side shares a fifth of the day’s hook fees.
The problem
On a new token’s pool, ordinary buyers are the easiest target. Sandwich bots buy just before them and sell just after; flippers buy and sell within minutes and leave holders with the price impact. A higher flat fee would protect holders by taxing them too.
Duelis charges by behaviour instead, and puts a hard rule in the code: a buy, or a sell of tokens held for six hours or more, always pays the 1% floor, whatever the networks learn. Only trades with a bot’s shape reach the Guards, and a back-run pays at least the price it gained.
What I built
- An economic model, before any Solidity
Sixty-day markets in three volume shapes with three kinds of trader: holders, adaptive flippers and rational sandwichers. It found that holders need a hard guarantee, because under evolution alone their fees crept up, and that the Shadows add no consistent protection beyond what real trades teach. Both findings are written into the contracts.
- Four contracts
The token, with ETH rewards that vest by token age; the hook, with the Guards, the Shadows, the fee rules, the side game and the buyback; the seeder that opens the pool in the launch transaction; and the locker that holds the liquidity.
- 26 tests on a mainnet fork
Including the whole launch run through the platform’s real deployment factory, the platform fee on all four kinds of swap, and a sandwich grid that measures where the back-run rule stops working.
- The launch package for a reviewed platform
A public source repository at an exact commit, the compiler inputs split into three units after the combined one timed out on the platform’s compiler, and the launch configuration. Three rejections, each with a concrete request, were fixed one by one until the fourth submission was approved.
- Site integration
The project’s website wired to the contracts: buys that name a side in the hook data, sells through Permit2, reward claims and the duel steps.
- An owner dashboard
Reads the locked position’s uncollected fees exactly from the PoolManager’s storage, proven to the wei against a real collect on a fork.
Brand and website
Duelis is staged as a fight. Two corners, black for the Guards and red for the Shadows, run through the logo, the 3D arena and every panel of the website. Tall condensed headlines sit over plain body text, and short step-by-step panels explain the rules without leaving the arena.
- Palette
- Canvas
#F1EFEAGuard black#141414Shadow red#E1251B - Type
- Anton for headlinesInter for textJetBrains Mono for data
How it works
- 1Trade
Every swap pays a hook fee on its ETH side, on top of the pool’s 1% LP fee. The hook reads who signed the trade and how old that wallet’s tokens are.
- 2Floor
A buy, or a sell of tokens at least six hours old, pays exactly 1%, unless the same wallet already traded in this block.
- 3Guard
Everything else goes to the champion Guard: an int8 network of eight inputs, nine hidden units and one output, which charges between 1% and 5%.
- 4Back-run rule
The back half of a sandwich pays at least its own price gain since its first trade in the block, plus 1%, up to 5%.
- 5Purse
The hook fee is split: 55% to holders, 20% to the side pot, 20% to buying back and burning DUEL, and 5% to the keepers who run the duel.
- 6Duel
Once a day, three kinds of public call score both corners on the day’s recorded market and evolve them. Each caller is paid from the keepers’ share.
Guards set fees
Six networks. The champion prices every bot-shaped trade.
- scoreShadows()Each Shadow replays the day’s recorded price path and recorded buys as sandwich victims, and is scored on its paper profit against the champion.3,017,551 gas
- scoreGuard(g), six timesCredit for fees on recorded flips and back-runs, a penalty five times as heavy for fees on short holders, minus the best Shadow’s profit against it.895,431 gas each
- evolve()On each side the weakest network is replaced by a mutated copy of the strongest; the best Guard becomes champion and the side pot is settled.317,349 gas
Shadows hunt
Four paper-trading bots looking for what the Guards fail to tax.
Fig. 2. The daily duel, with the gas each step measured on a mainnet fork. Guard 0, the hand-designed starting network, is drawn as the champion.
Fig. 3. Where the hook fee goes. The platform’s 0.30% and the pool’s 1% LP fee are separate from this split.
The contracts, reviewed
Supply and minting
One billion DUEL were created once and placed in the pool in the launch transaction. No function can create more.
SUPPLY = 1_000_000_000 ether is minted to the seeder in the constructor. DuelSeeder.launch adds the whole supply as one DUEL-only band from the lowest usable tick up to the opening price.
Burning
Holders can burn their own tokens. A fifth of every hook fee is set aside to buy DUEL back from the pool and burn it, in small, capped steps that anyone can trigger.
burn(uint256). DuelHook.buyback() can be called by anyone at most once an hour, spends at most 1% of the pool’s ETH depth, and sets a price limit 50 ticks (0.5%) past spot. Bought DUEL is burned with TOKEN.burn.
Transfers and token age
Every balance carries an age: the average time its tokens were acquired, weighted by amount. Tokens bought from the pool start at zero. Tokens sent to another wallet keep their age, so a fresh wallet cannot make young tokens look old.
acquiredAt[address] is blended on every _move; ageOf(a) exposes it. The hook reads TOKEN.ageOf(tx.origin), the wallet that signed the trade.
Fees
Buys pay 1%. Sells of tokens at least six hours old pay 1%. Young sells, a second trade in one block and back-runs are priced by the champion Guard between 1% and 5%, and a back-run pays at least its price gain plus 1%. A swap that names an ETH amount must fill whole.
FLOOR = 10_000 and CAP = 50_000 pips. The Guard’s rate is FLOOR + max(o, 0) x (CAP - FLOOR) / 1e6 for its output o. The platform fee is ceil(gross x 0.30%), allocated first and minted as ERC-6909 claims to the fee vault.
if (!same && !sandwich && (buy || age >= HOLDER_AGE)) {
if (buy) _victim(ethSide, depth);
return FLOOR;
}Rewards and vesting
55% of every hook fee goes to holders as ETH. It accrues per token held and vests with the age of the tokens over 14 days. Sell early, and the part that has not vested goes back to the remaining holders. Until 1% of the supply is held, rewards are shared as if it were, so the first buyer cannot take the launch fees alone.
flush() sends the holders’ book to notify(); an accumulator accPerToken runs over eligibleSupply, which excludes the PoolManager, the hook, the token and 0x...dEaD. vested(a) is linear to AGE_FULL = 14 days; _handBack runs on every outgoing transfer; MIN_DENOM = SUPPLY / 100.
The side game
A buyer on the project’s site can back the Guards or the Shadows inside the buy. The Guards win the day if no Shadow made a paper profit against the champion; otherwise the Shadows win. The winning side shares the day’s side pot by ETH bought, and selling during the day drops your share.
Exact-input buys read abi.encode(uint8 side) from the hook data into backing[epoch][tx.origin]; one side per wallet per epoch. evolve() settles sidePot[e]; claimSide(e) pays it. An empty winning side rolls the pot over.
Owner and admin powers
There is no administrator. The owner wallet receives the locked position’s liquidity fees and nothing from the hook. It cannot change the code, the fees, the networks or anyone’s balance.
No owner function exists in DUEL, DuelHook or DuelLocker. bindHook runs once, from the seeder. afterInitialize accepts only the seeder, and only if TOKEN.HOOK() == this. Keeper pay is STEP_PAY = 0.002 ether a step, at most a quarter of the keeper book.
Liquidity
The launch liquidity is held by the DuelLocker, which has a single function: send the position’s fees to the owner. There is no code that could withdraw or move the position.
DuelLocker.collect(tokenId) runs DECREASE_LIQUIDITY with zero liquidity and TAKE_PAIR to OWNER. The seeder minted the position NFT straight to the locker in the launch transaction.
Randomness
Mutations and the sampling of recorded trades use the block’s own randomness. There is no oracle and no server.
evolve() seeds mutation from keccak256(prevrandao, blockhash(n - 1), epoch); each of the 91 weights moves by up to 24 with probability 1/12. Samples are kept by reservoir sampling on prevrandao.
Testing and security
The platform’s review rejected the first three submissions, each time with a concrete request: lock the liquidity and make a first buy, enforce the platform fee in the pool itself rather than in a router, refuse partial fills, and charge the hook’s own buybacks the platform fee on the same gross amount as every other trade. Each request became a change in the contracts and a test that proves it.
| Victim’s buy | Sandwich result | Outcome |
|---|---|---|
| 0.02 ETH | -0.00078 ETH | Loses |
| 0.05 ETH | -0.00172 ETH | Loses |
| 0.10 ETH | -0.00269 ETH | Loses |
| 0.20 ETH | -0.00240 ETH | Loses |
| 0.50 ETH | +0.01593 ETH | Profits |
| 1.00 ETH | +0.10148 ETH | Profits |
| 2.00 ETH | +0.45164 ETH | Profits |
| Operation | Gas |
|---|---|
| Buy through the Universal Router | 373,624 |
| Sell of young tokens, Guard consulted | 390,252 |
| Sell of tokens over six hours old | 266,310 |
| scoreShadows() | 3,017,551 |
| scoreGuard(g), each of six | 895,431 |
| evolve() | 317,349 |
Limits, stated plainly
- The Uniswap app cannot route this pool, because the hook settles its fee through return deltas. Trades go through the project’s site, other routers and bots.
- Token age is read from the wallet that signs the trade. Tokens kept in a smart-contract wallet look fresh and pay the Guard’s rate.
- Large sandwiches can still profit: on a pool about 6 ETH deep, victims of 0.5 ETH or more. The threshold grows with the pool’s depth.
- The economic model showed no consistent protection from the Shadows beyond what the Guards learn from real trades. The contracts say so.
- Mutation and sampling use block randomness, which a block proposer can bias slightly.
Status on chain
Read from Ethereum mainnet on 20 October 2026, block 26,234,176
- Three daily epochs had been scored and evolved on mainnet.
- The champion was still Guard 0, its weights unchanged from the hand-designed starting network.
Contracts
| Contract | Address | Verified source |
|---|---|---|
DUELERC-20 token |
0xD946fEFbdEdF6086afba72d91C9A10f0b3Fb8eBa | Etherscan (opens in a new tab) |
DuelHookUniswap v4 hook |
0x2911623B872be9F44C81ce5FCd070AB1B56610cc | Etherscan (opens in a new tab) |
DuelSeederLaunch initializer |
0x12A43efb4D6938dFEcdCA0dae3ba2cDDd0cFfA9F | Etherscan (opens in a new tab) |
DuelLockerLiquidity custody |
0x539bAffe2CC41143b7a3e969fC5524F0f55f2140 | Etherscan (opens in a new tab) |
Fee vaultPlatform fee, Programmable code |
0x68b15402F2929CfA6760625b4b98010d2462BAdD | Etherscan (opens in a new tab)Programmable’s unmodified EthereumNativeFeeVaultV3, deployed by the hook. Etherscan shows it as a similar match to the platform’s verified original. |
DUEL, DuelHook, DuelSeeder and DuelLocker are verified on Etherscan with an exact match. Compiler: solc 0.8.26, via IR, 2,000 optimizer runs, EVM target Cancun.
Source repository: github.com/DuelisAI/duelis-contracts (opens in a new tab)



