How a cat prices a swap

HookCat is one Uniswap v4 hook. Every pool opened through it has a dynamic LP fee and one cat: a fee formula (its kind) with a packed set of parameters, stored in the hook. On every swap the hook asks the pool's cat for a fee and returns it to the PoolManager with the override flag, so the fee is set swap by swap.

The state the hook keeps

The cat never calls out. It reads state the hook keeps for each pool:

  • Volatility average: an exponential average of the absolute tick move per block, stored as 100 times the average move. 100 ticks are about a 1% price move.
  • Direction of the last move: down when the tick last fell (ETH was sold into the pool), up when it rose.
  • Swap size against active liquidity: the price impact of the specified amount, in parts per million, from the pool's price and in-range liquidity.
  • The UTC clock: whether NYSE regular hours (14:30 to 20:00 UTC, Monday to Friday) are open.

Block by block

On this chain block.number is the Ethereum block, about 12 seconds. The first swap of each block rolls the volatility forward before it is priced:

move = tickNow - lastTick              // the tick before this swap vs the last roll
ewma = ewma * 7/8 for each empty block   // at most 16 steps
ewma = (7 * ewma + 100 * |move|) / 8
lastTick = tickNow; lastDir = sign(move)

Every other swap in the same block pays a fee from the same average, so the fee moves one step per block. quoteFee(poolId, zeroForOne, amountSpecified) runs the same roll as a view and returns the fee the next swap will pay.

From fee to LP fee and cut

The cat's fee f is clamped between 0.01% and 3.00%. Against a fixed baseline b = 0.30%:

  • f ≤ b: the LP fee is f and nothing is cut.
  • f > b: with e = f - b, the LP fee is b + 0.88e and the hook takes 0.12e of the ETH side of the swap, 0.10e for the cat's builder and 0.02e for $HCAT stakers. The trader pays f in total, to rounding.

The cut is always in ETH because every pool pairs native ETH (currency0) with a token. It is taken in beforeSwap when ETH is the specified side and in afterSwap otherwise, held as PoolManager claims and turned into ETH when someone claims. Each swap emits Priced(poolId, catId, fee, lpFee, ethAmount, builderCut, stakerCut).

The fee at each swap is always computed onchain by the cat's formula. No model is called per swap or per block; the AI trainer only proposes parameters, offline.