SocketDocs

Hooks

Hooks and permissions

What a Uniswap v4 hook is, how the 14 flags in its address decide which calls it gets, and which flags Socket's hooks use.

A hook is a contract that Uniswap v4's PoolManager calls at fixed points of a pool's life: when it is initialized, when liquidity is added or removed, around each swap and around donations. A pool names its hook in its key, so the hook is fixed for the pool's life. A pool with hook address(0) has no hook.

Flags in the address#

PoolManager doesn't ask a hook what it wants to be called for. It reads the lowest 14 bits of the hook's address. Each set bit turns on one call:

BitValueFlagCalled
138192beforeInitializeBefore the pool's state is set.
124096afterInitializeAfter the pool is initialized, with its starting price and tick.
112048beforeAddLiquidityBefore liquidity is added.
101024afterAddLiquidityAfter liquidity is added, with the caller's delta and feesAccrued.
9512beforeRemoveLiquidityBefore liquidity is removed, including a removal of zero liquidity.
8256afterRemoveLiquidityAfter liquidity is removed, with the caller's delta and feesAccrued.
7128beforeSwapBefore the swap runs against the pool.
664afterSwapAfter the swap, with its delta.
532beforeDonateBefore a donation to in-range LPs.
416afterDonateAfter a donation.
38beforeSwapReturnDeltabeforeSwap may return a delta.
24afterSwapReturnDeltaafterSwap may return a delta.
12afterAddLiquidityReturnDeltaafterAddLiquidity may return a delta.
01afterRemoveLiquidityReturnDeltaafterRemoveLiquidity may return a delta.

A hook gets its bits by being deployed with CREATE2 at a mined salt. Different permissions mean a different address, so a different contract. PoolManager checks the address when a pool is initialized and refuses it if:

  • a returns-delta flag is set without its call's flag;
  • the address is a hook with no flags at all, and the pool's fee isn't dynamic.

Every call must return the selector of the function PoolManager called. A wrong answer reverts with InvalidHookResponse. When a hook itself calls PoolManager for its pool, its own callbacks aren't called.

Try a combination:

0x…15C30b01010111000011

  • Dynamic feethis one
  • TWAP oraclethis one
  • Range order0x…1543

Returns-delta#

Without a returns-delta flag, a hook can read an operation, keep its own state or revert, but it can't move amounts between itself and the caller. In a dynamic-fee pool it can still set the LP fee. With a returns-delta flag, the call returns a delta: positive when the hook is owed or took currency, negative when it owes or sent currency.

FlagThe call returnsEffect
beforeSwapReturnDeltaBeforeSwapDelta, in the specified and unspecified currenciesChanges the amount that swaps against the pool. It can't turn an exact-input swap into an exact-output one, or back.
afterSwapReturnDeltaint128, in the unspecified currencyAdds to or takes from the side of the swap the swapper didn't fix.
afterAddLiquidityReturnDeltaBalanceDeltaSubtracted from the caller's delta for the addition, fees included.
afterRemoveLiquidityReturnDeltaBalanceDeltaSubtracted from the caller's delta for the removal, fees included.

A hook's delta is recorded against the hook. It settles in the same transaction as everyone else's, or the transaction reverts.

Dynamic fees#

A pool's fee is either a static LP fee or the dynamic-fee flag, 0x800000. LP fees are in hundredths of a basis point: 1,000,000 is 100%, and 3,000 is 0.30%.

In a pool whose fee is 0x800000, the hook sets the LP fee:

  • Per swap. beforeSwap returns a fee with the override flag 0x400000 set. That fee applies to this swap only.
  • Stored. The hook calls updateDynamicLPFee(key, fee) on PoolManager. A dynamic-fee pool starts with a stored fee of 0.

A fee above 1,000,000 reverts. A pool with no hook can't use the dynamic-fee flag.

Socket's hooks#

Each built-in hook is its own contract with its own flags. All three carry the four flags of the buyback share: afterAddLiquidity, afterRemoveLiquidity and both of their returns-delta flags.

HookFlagsLow bits
Dynamic feeafterInitialize, beforeSwap, afterSwap, plus the four buyback flags0x15C3
TWAP oracleafterInitialize, beforeSwap, afterSwap, plus the four buyback flags0x15C3
Range orderafterInitialize, afterSwap, plus the four buyback flags0x1543

A pool on the dynamic-fee hook has the dynamic-fee flag as its fee. None of the three has a swap returns-delta flag. Swappers pay the pool's fee and get what the pool's curve gives. The liquidity flags take the buyback share from the fees a position collects.

External hooks and the allowlist#

Any pool can name any hook. PoolManager enforces v4's rules for all of them, and a router can't tell a reviewed hook from one that isn't.

Socket keeps that list on chain. HookRegistry holds the hooks Socket's app routes through, and SOCKET holders vote it:

  • Socket's built-in hooks are on the list from the start.
  • Any other hook is added by a vote, with a stake from its author. See Write a hook.
  • Pools with no hook are always routable.

The app routes only through pools with no hook or a listed hook. Listed hooks outside the built-ins show as External hook in mustard.

Next: Limits lists what holds whatever a hook does.

Public documentation snapshot · 9 October 2026 · Original page ↗
LOCAL REPLICA · DOCUMENTATION SNAPSHOT