AdaptivePoolPolicyHookV1
EthereumAdaptivePoolPolicyHookV1 is a Uniswap v4 hook on Ethereum. Runs code on every swap. Can take tokens from swaps. Sets its own swap fee. Gates who can add liquidity.
- Contract name
- AdaptivePoolPolicyHookV1
How this hook works
Managed poolA hook for pools run by a manager. The hook developer sets a different trading fee for each direction, limits the price range the pool may trade in, decides who can add liquidity, and can take an extra fee for themselves.
A separate fee for each direction
Buying and selling can have different trading fees. The hook developer or an approved manager can change them at any time, and the code sets no upper limit below 100%.
The code that does this (_beforeSwap):function _beforeSwap(address, PoolKey calldata, SwapParams calldata params, bytes calldata) internal virtual override returns (bytes4, BeforeSwapDelta, uint24) { uint24 fee = params.zeroForOne ? fee0For1 : fee1For0; uint24 feeWithFlag = LPFeeLibrary.OVERRIDE_FEE_FLAG | fee; return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, feeWithFlag); }Price limits
A lowest and a highest price can be set. A trade that would push the price past either limit is refused.
The code that does this (_afterSwap):function _afterSwap(address, PoolKey calldata key, SwapParams calldata params, BalanceDelta delta, bytes calldata) internal virtual override returns (bytes4, int128) { (uint160 sqrtPriceX96,,,) = poolManager.getSlot0(key.toId()); if (params.zeroForOne) { if (sqrtPriceX96 < minSqrtPriceX96) revert PriceOutOfBounds(sqrtPriceX96); } else { if (sqrtPriceX96 > maxSqrtPriceX96) revert PriceOutOfBounds(sqrtPriceX96); } int128 hookDeltaUnspecified = 0; uint24 fee = hookFee; if (fee > 0) { // The unspecified side is output for exact-input swaps and input for // exact-output swaps. (Currency unspecified, int128 unspecifiedAmount) = (params.amountSpecified < 0) == params.zeroForOne ? (key.currency1, delta.amount1()) : (key.currency0, delta.amount0()); // Widen before negation so int128.min cannot overflow. uint256 absAmount = unspecifiedAmount < 0 ? uint256(-int256(unspecifiedAmount)) : uint256(int256(unspecifiedAmount)); uint256 feeAmount = (absAmount * fee) / MAX_FEE; if (feeAmount > 0) { // afterSwap's returned delta is int128. The only value that may // …An extra fee for the hook developer
On top of the trading fee, the hook can take a share of what each trade pays out. It collects in the hook until the hook developer claims it. This fee also has no upper limit below 100%.
The code that does this (_afterSwap):function _afterSwap(address, PoolKey calldata key, SwapParams calldata params, BalanceDelta delta, bytes calldata) internal virtual override returns (bytes4, int128) { (uint160 sqrtPriceX96,,,) = poolManager.getSlot0(key.toId()); if (params.zeroForOne) { if (sqrtPriceX96 < minSqrtPriceX96) revert PriceOutOfBounds(sqrtPriceX96); } else { if (sqrtPriceX96 > maxSqrtPriceX96) revert PriceOutOfBounds(sqrtPriceX96); } int128 hookDeltaUnspecified = 0; uint24 fee = hookFee; if (fee > 0) { // The unspecified side is output for exact-input swaps and input for // exact-output swaps. (Currency unspecified, int128 unspecifiedAmount) = (params.amountSpecified < 0) == params.zeroForOne ? (key.currency1, delta.amount1()) : (key.currency0, delta.amount0()); // Widen before negation so int128.min cannot overflow. uint256 absAmount = unspecifiedAmount < 0 ? uint256(-int256(unspecifiedAmount)) : uint256(int256(unspecifiedAmount)); uint256 feeAmount = (absAmount * fee) / MAX_FEE; if (feeAmount > 0) { // afterSwap's returned delta is int128. The only value that may // …Liquidity by approval
Only approved addresses can add liquidity, unless the hook developer opens it to everyone. Taking liquidity out is never blocked.
The code that does this (_beforeAddLiquidity):function _beforeAddLiquidity(address sender, PoolKey calldata, ModifyLiquidityParams calldata, bytes calldata) internal virtual override returns (bytes4) { if (publicLiquidityEnabled) return this.beforeAddLiquidity.selector; address caller; if (allowedRouters[sender]) { // The trusted router reports the authenticated original caller. caller = IMsgSender(sender).msgSender(); } else { // Direct calls are checked using PoolManager's immediate sender. caller = sender; } if (caller != owner() && !allowedProviders[caller]) { revert NotAuthorized(caller); } return this.beforeAddLiquidity.selector; }
Who controls it: The hook developer can set the extra fee, claim it, open liquidity to everyone and choose the managers. Managers can change the trading fees, the price limits and the list of approved liquidity providers. Control can be passed on but not given up. The code cannot be changed.
Based on the hook’s published code.
What it can do
Its code executes inside each trade in pools that use it.
It can take or add tokens during a swap. Fees, burns and custom curves work this way, and so do scams.
Pools using it let the hook choose the swap fee instead of a fixed tier.
It checks every deposit before it goes in.
Hook history
Nothing recorded yet.
Counted from what the hook recorded on-chain across all its pools, for every action in its published code, plus token transfers in and out of it.