SimHook on Ethereum

Uniswap v4 Hook Contract 0xbf9828455cdc5f02771536e3ecb3c0f931eabec4 on Ethereum · Dynamic fee · Verified contract

Volume (24h)
$12.94K
Value locked (TVL)
$45.19K
All-time volume
$299.05K
Pools using this hook
10
Swaps (24h)
336 (172 traders)
Total swaps
5,926

What it does

A dynamic-fee hook that prices each swap from the pool's cumulative directional price impact, raising fees when a swap pushes the pool further out of balance and lowering them when it moves back, while taking a small protocol fee share from swaps.

How it works step by step

  • Requires the pool to use the dynamic fee flag and a minimum initial sqrt price, and only accepts pools that the owner or pool deployer has configured
  • Simulates each swap in beforeSwap to estimate the resulting price impact before the trade executes
  • Tracks a cumulative signed price impact per pool that decays over time between swaps and is updated again in afterSwap
  • Computes the swap fee from the estimated impact and cumulative imbalance, using separate K and C coefficients depending on whether the trade increases imbalance, reduces it, or crosses zero, clamped between a time-ramped minimum fee and a configured maximum fee
  • Splits the dynamic fee between LPs and a protocol treasury cut (capped at 20% of the fee via governance) and takes that cut as an unspecified-currency delta in afterSwap
  • Enforces an optional just-in-time liquidity lock so a position cannot remove liquidity within a set number of blocks after adding it
Swap behaviour
beforeSwap simulates the swap, computes the dynamic fee and overrides the LP fee with a split of it; afterSwap updates the cumulative price impact, emits events, and may take a protocol fee in the unspecified currency via a return delta.
Liquidity behaviour
afterAddLiquidity records the block of the last add for each position when the JIT lock is enabled. beforeRemoveLiquidity reverts removals of liquidity that are still inside the JIT lock window. Adds are blocked when governance has paused add-liquidity globally or for this hook.
Fee behaviour
The swap fee is dynamic, derived from price impact and cumulative imbalance, bounded by a time-decaying minimum and a per-pool maximum (hook-wide ceiling 50%). A governance-set protocol fee share (max 20% of the swap fee) is carved out and sent to the treasury.
Owner & admin controls
Owner or the configured pool deployer can configure each pool once (fee bounds, decay length, JIT lock length, K and C). Governance (owner plus timelock) sets the treasury, the protocol fee rate (up to 20%), subscriber removal, and the pool deployer. The owner or a pauser can pause add-liquidity globally or per hook. The governance timelock can remove a hook as a subscriber, which pauses its add-liquidity. The hook itself has no upgrade mechanism in the source shown; it is not a proxy.

Enabled Uniswap v4 callbacks

beforeInitialize, afterInitialize, beforeAddLiquidity, afterAddLiquidity, beforeRemoveLiquidity, beforeSwap, afterSwap, afterSwapReturnDelta

beforeInitialize enforces dynamic fee and minimum price and owner/deployer access; afterInitialize emits a pool event; beforeAddLiquidity enforces the pause; afterAddLiquidity records JIT timestamps; beforeRemoveLiquidity enforces the JIT lock; beforeSwap computes and overrides the dynamic fee; afterSwap updates imbalance state and takes the protocol fee via afterSwapReturnDelta.

Security & trust assessment

Risk level: medium (verified source, high confidence).

Who controls it: Governance (owner and timelock) controls the protocol fee rate up to 20%, the treasury destination, subscriber removal and the pool deployer. The owner or a pauser can block liquidity additions. The owner or deployer can set each pool's fee caps and decay parameters once. None of these can move existing pool funds directly; the hook's direct fund access is limited to the protocol fee take.

Risk to user funds: The hook does not custody user funds. It can take a protocol fee share from swap outputs through afterSwapReturnDelta and can block new liquidity adds or LP removals within the JIT window. It cannot seize liquidity already in the pool, but a pause or JIT lock can prevent LPs from entering or exiting.

Security findings

  • [info] Hook takes a share of swap fees via return delta: afterSwap returns a positive hook delta in the unspecified currency, transferring a protocol fee to the governance treasury on every swap while the fee is nonzero. Users pay this through their swap output, so it is a material economic cost to traders.
  • [medium] Governance can change the protocol fee and treasury: The timelock can set protocolFeeBps up to 20% and change the treasury address, which is pushed to subscribed hooks. A malicious or compromised governance could redirect the fee share to an arbitrary address or raise it within the 20% cap, affecting all swaps through these hooks.
  • [medium] Owner or pauser can halt liquidity adds: The owner or pauser can set addLiquidityPaused or hookAddLiquidityPaused, which causes beforeAddLiquidity to revert for all or specific hooks. Existing liquidity can still be removed (subject to JIT locks), but new LPs cannot enter while paused.
  • [low] Owner-controlled pool configuration and deployer role: configurePool is callable by the owner or poolDeployer and sets the maximum fee (up to 50%), the decay and JIT parameters. Since it can be called only once per pool, a bad configuration cannot be corrected through the hook, and the deployer role is set by the timelock.
  • [low] JIT lock restricts LP withdrawals: When jitLockBlocks is set, positions cannot remove liquidity for up to 50,400 blocks after their last add. This is an intentional anti-JIT mechanism but it can lock LP capital for about a week.

Contract details

Verified source
Yes (SimHook)
Compiler
solc 0.8.26+commit.8a97fa7a
Bytecode size
14,903 bytes
Upgradeable proxy
No
Detected patterns
dynamic-fee, moves-currency, return-delta

Pools using SimHook