Plate 01 · Module registry

Three entries, catalogued, and no room for a fourth.

A module is a rule about what happens during a swap. Each one is a small contract deployed alongside the hook and recorded in it at construction. This plate is the whole set.

A module never touches the pool and never holds anything. When a swap arrives, the hook asks the active module one question — what should happen to this trade? — and the module returns an answer: an optional fee override, and an optional share of the trade's input leg. The hook then performs every action itself.

That shape is why rotating modules is safe. A module has no balance to strand, no storage to migrate and no privileged caller. Changing which one is active changes a single number.

MOD-00

BurnOnSell

deflation on exit
Elevation drawing of form 00 Form 00 · elevation
Registry index
0
Constant
BURN_BPS = 300  (3.00%)
Applies to
sells — MORPH paid into the pool
Buys
no effect
Fee override
none
Destination
burned; total supply falls

On a sell, the hook claims 3% of the MORPH the seller pays in and burns it. Nobody receives it: the tokens leave the supply permanently, and the pool keeps the rest of the input as usual.

Both trade shapes are covered. If you specify how much MORPH you are selling, the cut is taken as the trade begins; if you specify how much ETH you want out, the input is only known once the trade has run, so the cut is taken immediately afterwards. There is no shape of sell that escapes it.

The claim is held against the pool manager and converted into real tokens by redeemBurnAndBurn(), which anyone may call. Until someone calls it the burn is owed rather than done; the only action the call can take is the burn.

MOD-01

DynamicFee

asymmetric toll
Elevation drawing of form 01 Form 01 · elevation
Registry index
1
Buy fee
10,000 pips  (1.00%)
Sell fee
30,000 pips  (3.00%)
Hook's share
none
Destination
the pool's liquidity position
Supply effect
none

The pool is deployed with a dynamic fee, which means the hook may set the fee for each individual swap. This module sets it by direction: buying costs 1%, selling costs 3%.

Nothing is diverted to the hook here. The fee is an ordinary Uniswap fee and accrues to whoever provides liquidity, which makes this the module that pays the pool rather than the token. It is the module the deployment starts on.

Unlike the two collecting modules, a fee override applies to every shape of trade by construction — it is the pool's own fee, not a deduction the hook performs.

MOD-02

BuybackSkim

reserve accretion
Elevation drawing of form 02 Form 02 · elevation
Registry index
2
Constant
SKIM_BPS = 100  (1.00%)
Applies to
buys — ETH paid into the pool
Sells
no effect
Fee override
none
Destination
held as ETH by the hook contract

On a buy, the hook claims 1% of the ETH the buyer pays in. As with the burn, the claim is converted to real ETH off the trading path by redeemSkimmedEth(), which anyone may call, and the ETH then sits in the hook contract.

Be clear about what that is and is not. The contract does not buy anything back on its own — there is no automatic bid, no schedule and no promise encoded anywhere. The balance is withdrawable by a keeper address fixed at deployment, and what happens to it after that is a human decision. Read it as a discretionary reserve funded by buy-side volume, and price that discretion into what you think the module is worth.

§ 01.1What the three have in common

  • They are stateless. Nothing carries over from one swap, or one epoch, to the next.
  • They hold no balance, and no function on them can move a token.
  • They have no privileged caller — every one of their functions is a pure read.
  • The hook reads the active module once at the top of a swap, so the rule cannot change halfway through a trade.

The corollary is that switching modules has no migration and no wind-down. The old rule simply stops being asked.

§ 01.2Why the catalogue is closed

The registry is copied into the hook once, in its constructor, and the array is private and never written again. No function accepts module code, a module address, or an implementation pointer. There is no proxy, no delegatecall, and no administrative function that can reach the registry.

The reason is that the set of modules is the thing you are asked to trust. Three small rules can be read end to end in a sitting. A registry that could grow would mean the honest answer to "what can this pool do to a trade?" is "we will find out", which is the answer this design exists to avoid.

A fourth behaviour would require a new hook, a new pool and a new token. It could not be added to this one.