Back to portfolioPortfolio

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.

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.

Fig. 1. Who prices this trade. Set up a trade and see which rule in the hook decides its fee. The branches and the back-run formula are the ones in DuelHook._rate and backrunRate; where the champion Guard decides, the exact rate depends on its live weights.

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

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

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

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

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

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

  6. 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
Duelis logo: two pixel robot heads facing each other, one black and one red
Logo: Guard against Shadow.
Duelis website: a black robot and a red robot facing each other on a 3D ring
Website: the arena, in 3D.
Duelis website panel How it works, step one: every trade enters the arena
Website: how it works, in four steps.
Duelis website How it works panel on a phone
On a phone.

How it works

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

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

  3. 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%.

  4. 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%.

  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.

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

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

55%Holders, as ETH that vests with the age of their tokens
20%The side pot, shared by the day’s winning side
20%Buyback and burn, at most 1% of pool depth an hour
5%Keepers, who run the daily duel

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.

For developers

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.

For developers

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.

For developers

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.

For developers

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.

For developers

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.

For developers

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.

For developers

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.

For developers

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.

For developers

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

26Foundry tests on a mainnet fork, all passing when re-run on 20 October 2026
4thSubmission approved by the launch platform’s review, which replays the launch on a fork
5%The most any trade can pay the hook, Guard or back-run rule alike

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.

test/SandwichGrid.t.sol on a mainnet fork, 20 October 2026: a pool about 6 ETH deep, a front-run half the victim’s size, the back-run charged 5%. Small victims are protected; large ones are not.
Victim’s buySandwich resultOutcome
0.02 ETH-0.00078 ETHLoses
0.05 ETH-0.00172 ETHLoses
0.10 ETH-0.00269 ETHLoses
0.20 ETH-0.00240 ETHLoses
0.50 ETH+0.01593 ETHProfits
1.00 ETH+0.10148 ETHProfits
2.00 ETH+0.45164 ETHProfits
Measured on a mainnet fork on 20 October 2026 (test/Duel.t.sol).
OperationGas
Buy through the Universal Router373,624
Sell of young tokens, Guard consulted390,252
Sell of tokens over six hours old266,310
scoreShadows()3,017,551
scoreGuard(g), each of six895,431
evolve()317,349

Limits, stated plainly

  1. 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.
  2. 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.
  3. 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.
  4. The economic model showed no consistent protection from the Shadows beyond what the Guards learn from real trades. The contracts say so.
  5. 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

Contracts on Ethereum mainnet
ContractAddressVerified source

DUELERC-20 token

0xD946fEFbdEdF6086afba72d91C9A10f0b3Fb8eBa

DuelHookUniswap v4 hook

0x2911623B872be9F44C81ce5FCd070AB1B56610cc

DuelSeederLaunch initializer

0x12A43efb4D6938dFEcdCA0dae3ba2cDDd0cFfA9F

DuelLockerLiquidity custody

0x539bAffe2CC41143b7a3e969fC5524F0f55f2140

Fee vaultPlatform fee, Programmable code

0x68b15402F2929CfA6760625b4b98010d2462BAdD

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)