Uniswap v4 hook · $ETHERIS · nothing deployed yet

The token AI couldn't build.

Everyone pays 5% on every trade. Only verified humans can claim any of it back. Not a slogan bolted onto a tax token — the mechanism itself.

5%flat fee, buys & sells
8contracts
1,270lines of solidity
58tests, mainnet fork
0x20CChook flag word

The whole idea in one line

A whale gets one share. A bot running five hundred wallets gets one share, because it has one credential at best.

The inversion

Each epoch's dividend pot splits equally per verified human — not pro rata to holdings. Capital stops being the thing that earns.

Why the fee stays blind

A scanner sees one flat rate with no per-address logic anywhere in the hook. The fee never has to discriminate — the payout does.

The honest version

A copycat faked this with a tx.origin check that stops exactly zero bots. Personhood is proven at the claim, or not at all.

Where the 5% goes

Three buckets. No setter anywhere.

Flat and symmetric — a buy and a sell pay the same. FEE_BPS = 500 is a constant, and both splits are immutable. Move the slider to price any trade.

100ETH
Every swap
5.0000 ETH
Taken in
ETH
Treasury
3.0% · yours
3.0000 ETH

Withdrawable by the owner. The only share that funds the project.

Floor
1.0% · permanent bid
1.0000 ETH

Buys and burns everything it touches. No owner, no withdraw.

Humans
1.0% · claimable
1.0000 ETH

Split equally among verified humans. Verified only, forever.

FEE_BPS = 500 buy = sell no setter exists splits immutable

Why bots cannot game it

One human, one share.

The pot never looks at balances. Add humans, hand the bot a thousand wallets, make the whale richer — the arithmetic does not move.

5.00 ETH epoch pot · the 1% humans bucket
Verified humans 4 credentials
Each human claims 1.2500 ETH
The bot farm 100 wallets
The whole farm claims 0.0000 ETH
100 wallets, zero credentials, zero ETH. Wallets are free to make. The share is attached to the credential, and the bot has none — so every fee it paid on the way in is now funding the people it was trying to outrun.
from the test suite · equal-share assertion alice holds nothing     → received 1.0 ETH
whale holds a fortune  → received 1.0 ETH
bot 500 wallets      → one share at best, one credential at best

The surface area

Eight contracts, 1,270 lines.

Small enough to read in an afternoon. The token itself has no transfer logic, no mint and no owner — every moving part lives in the hook, and every bar below is deployed bytecode size.

01 Etheris.sol ERC-20, 1B fixed supply, EIP-2612 permit, self-burn. No owner, no mint, no transfer logic at all. 2,081 B
02 RatchetHook.sol Charges 5/5, splits three ways, holds the rescue. Mined to flag word 0x20CC. 6,074 B
03 FloorVault.sol Permanent bid. Burns everything it buys. No owner, no withdraw. 6,137 B
04 HumanRegistry.sol Append-only epoch merkle roots, irreversibly freezable. 1,548 B
05 HumanDividend.sol Equal-share-per-human claims. No owner, no withdraw. 3,570 B
06 lib/MerkleLib.sol Proof verification with double-hashed leaves. library
07 EtherisLens.sol Every claim readable in a single call. 4,644 B
08 HookDeployer.sol CREATE2 factory for the mined hook address. 597 B

58 tests, all against a mainnet fork

Checked to the wei.

Not a testnet, not a mock router. Every number below came out of a fork of Ethereum mainnet with real Uniswap v4 state.

Split on a 100 ETH buy 5 ETH → 1 floor / 1 humans / 3 treasury, to the wei
Whale vs. empty wallet identical — 1 ETH each
Dump through the floor 8 ETH bought 7 ETHERIS → supply 1,000,000,000 → 999,999,992
Rescue boundary took 7 ETH from the hook; the floor's 3 ETH untouched
Placing a bid costs ETH only — 0 tokens required

Plus 9 stateful invariants over 720 randomised calls each:

INV 1Supply never increases
INV 2Burn accounting stays exact
INV 3Fees reconcile across all three buckets
INV 4The hook never hides a balance
INV 5The dividend never overpays
INV 6The vault never sits on tokens

Liquidity is never trapped

0x20CC

All six liquidity bits are clear, so Uniswap v4 never calls the hook when anyone adds or removes liquidity.

Not "will not block you" — cannot be asked. The permission is missing from the address itself, and the address is mined, not configured.

Honest limit

You get your position back, not your value back. After a crash it is mostly tokens — ordinary AMM behaviour, unrelated to the hook.

permission on off liquidity bit — clear
  • Full exit after a crash
  • Mid-crash exit
  • Four repeated partial exits
  • Exit with the vault never touched

What the owner controls

The short list, and the shorter one.

Read the right-hand column first. It is the one that decides whether any of this is worth holding.

Can

four powers, all bounded

  • Take the 3% treasury share
  • Call rescue() on anything sitting in the hook
  • Call rescueToken() for stranded ERC-20s
  • Change the ops sink and the sweep threshold
  • Transfer or renounce ownership

Cannot

no setter is written anywhere

  • Change the fee or the splits
  • Mint, pause, or block a transfer
  • Block a liquidity withdrawal
  • Touch the FloorVault or HumanDividend
  • Reach any fee that has left the hook

The rescue's exact scope. Once a fee reaches its destination it can never be taken. The only window is the gap before it gets there, and that window is always visible on-chain as exactly pending(). Set sweepThreshold to 0 and it closes entirely.

The one trust assumption, not hidden

Somebody decides who counts as human.

That somebody holds curator. It is the single discretionary role in the system, and three things bound what it can do.

Past roots are immutable

Being dropped from a later roster cannot take away what you already earned. Old epochs stay claimable.

The curator never touches money

It can add an epoch and nothing else. No path from the roster to a balance.

freeze() is irreversible

The roster can be sealed permanently. Once frozen, no future epoch can ever be added.

Why a merkle root, not an oracle

World ID's Ethereum mainnet deployment is dormant — no events in roughly a month, since the project moved to World Chain. On-chain verification is not viable today, so the roster has to be committed as a merkle root. Said here in plain language rather than left for someone else to find.

What is left

Three things, in this order.

The order matters: two of these are free now and impossible after the salt is mined.

1

The personhood policy

Which sources count as proof of a person. The contract is source-agnostic, so this is published policy, not code — and it gets published before anything is deployed.

test default: World ID Gitcoin Passport ≥ 20 Coinbase verified
2

The decrease-only fee

Today the fee cannot be lowered at all. It can be made lowerable-but-never-raisable — a strictly one-way ratchet down.

free now impossible after the salt is mined
3

Deployment

Set the owner and the ops sink, publish the deployed addresses, mine 0x20CC, then produce the Etherscan verification files so every claim on this page can be checked against bytecode.

CREATE2 salt Etherscan verification