ChangingFeeHook on Base
Uniswap v4 Hook Contract 0x5eab65856c5fe9d8a06c40b412390660f5d3e080 on Base · Dynamic fee · Verified contract
- Volume (24h)
- $0
- Value locked (TVL)
- $0
- All-time volume
- $0
- Pools using this hook
- 1
- Swaps (24h)
- 0 (0 traders)
- Total swaps
- 0
What it does
This hook charges swappers a dynamic fee of up to 2% per side: swaps that trade back against a price move since the block's start pay a backrun fee that captures part of the move for LPs, while swaps that continue the move pay a capped fade on top of the baseline, and buys of token0 pay the ask-side fee while sells pay the bid-side fee.
This read was written for the hook 0x3a96…6080 on Polygon and reused here: the two share a code shape, so constants such as fees, owners and addresses can differ.
How it works step by step
- Sets a dynamic LP fee per swap direction using the price move since the top of the current block, via Uniswap's volatile fee math (VolatileFeeCalculation and RelativeMoveLibrary) with a 2% per-side cap.
- Stores a fixed per-pool fee config written once at creation by the owner through initializePool; there is no setter to change it afterward.
- Tracks a permanent skew per pool that steps toward the side the price moved to each block, bounded by maxSkewStep and maxSkew.
- Adds a one-block temporary fee on the backrun side when the price moves past backrunThreshold or restoreThreshold, capturing part of the backrun profit for LPs.
- Blocks direct pool initialization on the PoolManager (beforeInitialize reverts) so pools can only be created through initializePool with validated parameters.
- Holds no tokens, makes no external calls except to the PoolManager, and does not use the sender identity when computing fees.
- Swap behaviour
- On each swap beforeSwap loads the block's top-of-block state, advances it on the first swap of a new block, and returns a per-direction fee with the override flag. A buy of token0 (zeroForOne false) pays the ask-side fee and a sell of token0 (zeroForOne true) pays the bid-side fee; each is the baseline plus a displacement-based backrun or continuation component, clamped to 2%. Swaps against the in-block price displacement (backruns) pay more; swaps continuing in the displacement direction pay the baseline plus a capped fade. Swap size does not affect the fee.
- Liquidity behaviour
- No liquidity callbacks.
- Fee behaviour
- The LP fee is set dynamically per swap direction by beforeSwap. Baseline fees come from a permanent skew (restingSpread split between ask and bid, leaning toward the side the price moved toward) plus a one-block temporary fee on the backrun side. Continuation swaps add a fade capped by maxMidFade, backrun swaps add a capture term proportional to displacement, and both are clamped to MAX_CHARGED_FEE_E6 (20,000 pips, 2%) per side. The stored slot0 LP fee is set to restingSpread/2 at creation. Protocol fees are not accounted for here.
- Owner & admin controls
- A single immutable owner, set in the constructor, is the only address that can call initializePool, which creates a pool and writes its fee config once. There is no config update function, no pause, no upgrade mechanism, no proxy, and no role system. The owner cannot change fees for existing pools.
Enabled Uniswap v4 callbacks
beforeInitialize, beforeSwap
beforeInitialize rejects any direct PoolManager initialization so pool creation goes through initializePool. beforeSwap computes and returns the dynamic per-direction fee with the override flag. No other hook callbacks are enabled, and no return-delta flags are set.
Security & trust assessment
Risk level: low (verified source, high confidence).
Who controls it: The owner can only create new pools through initializePool and cannot alter fee parameters, pause swaps, move funds, or upgrade the contract. Its only power is choosing which configs get created.
Risk to user funds: The hook holds no tokens and performs no token transfers. It cannot take or move user or pool funds, and it cannot block exits from pools.
Security findings
- [info] Owner can create pools with arbitrary validated parameters: initializePool is owner-gated and accepts any config that passes the validation checks (resting spread up to 4%, maxSkew up to 2%, side fee floor of 1 bp). Traders in a pool created by the owner face whatever config was chosen, but cannot have it changed afterward.
- [info] Fee capped at 2% per side: _chargedFee clamps every direction's fee to MAX_CHARGED_FEE_E6 (20,000 pips), so a swap can never be charged more than 2% by this hook regardless of price movement or config.
- [info] Hook cannot block swaps or liquidity actions: Only beforeSwap and beforeInitialize are enabled. No liquidity callbacks and no return-delta flags are claimed, so the hook cannot revert liquidity removal or alter swap amounts. beforeSwap reverts only on the PoolNotInitialized guard, which is unreachable through the PoolManager.
- [info] Temporary fee pricing depends on in-block price history: The fee is driven by price displacement since the block's first swap, so a swapper who manipulates the price within a block can influence which side's fee is high. The design caps this at 2% per side, which limits the impact on LPs.
Contract details
- Verified source
- Yes (ChangingFeeHook)
- Compiler
- solc 0.8.26+commit.8a97fa7a
- Bytecode size
- 12,619 bytes
- Upgradeable proxy
- No
- Detected patterns
- dynamic-fee, ownable
Pools using ChangingFeeHook
- CADC / USDC (Dynamic fee) — 24h volume $0, TVL $0, 0 swaps today