DeFi

Uniswap V4 Hooks: The New DeFi Bot Primitive Every Developer Must Know

Uniswap V4 hooks let developers inject custom logic into every swap, add liquidity, and remove liquidity event. This changes everything for automated trading. Here's what hooks are, why they matter, and how to build your first one.

A
AI Agents Hubยท2026-04-09ยท5 min readยท875 words

Builder of AI agents, crypto trading bots, and open-source automation tools. Sharing practical guides on how to build, deploy, and profit from AI and DeFi technology.

What Uniswap V4 Hooks Change Forever

Uniswap V3 was a masterpiece of capital efficiency. But every pool behaved identically โ€” there was no way to add custom logic. V4 changes this entirely with hooks.

A hook is a smart contract that Uniswap V4 calls before and after core operations:

  • beforeSwap / afterSwap
  • beforeAddLiquidity / afterAddLiquidity
  • beforeRemoveLiquidity / afterRemoveLiquidity
  • beforeInitialize / afterInitialize

This means you can build pools that:

  • Apply dynamic fees based on volatility
  • Auto-compound LP fees back into the position
  • Implement limit orders natively within an AMM
  • Add oracle-based price protection against MEV
  • Create subscription-based fee structures

Hook Architecture

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {BaseHook} from "v4-periphery/BaseHook.sol";
import {Hooks} from "v4-core/src/libraries/Hooks.sol";
import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
import {PoolKey} from "v4-core/src/types/PoolKey.sol";
import {BalanceDelta} from "v4-core/src/types/BalanceDelta.sol";
import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "v4-core/src/types/BeforeSwapDelta.sol";

contract MyFirstHook is BaseHook {
    
    uint256 public swapCount;
    mapping(address => uint256) public userSwapCount;
    
    constructor(IPoolManager _poolManager) BaseHook(_poolManager) {}
    
    // Tell Uniswap which hooks this contract implements
    function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
        return Hooks.Permissions({
            beforeInitialize: false,
            afterInitialize: false,
            beforeAddLiquidity: false,
            afterAddLiquidity: false,
            beforeRemoveLiquidity: false,
            afterRemoveLiquidity: false,
            beforeSwap: true,   // We implement this
            afterSwap: true,    // We implement this
            beforeDonate: false,
            afterDonate: false,
            beforeSwapReturnDelta: false,
            afterSwapReturnDelta: false,
            afterAddLiquidityReturnDelta: false,
            afterRemoveLiquidityReturnDelta: false
        });
    }
    
    function beforeSwap(
        address sender,
        PoolKey calldata key,
        IPoolManager.SwapParams calldata params,
        bytes calldata hookData
    ) external override returns (bytes4, BeforeSwapDelta, uint24) {
        // Track swap count per user
        userSwapCount[sender]++;
        swapCount++;
        
        // Return selector to indicate success, no delta change, no fee override
        return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);
    }
    
    function afterSwap(
        address sender,
        PoolKey calldata key,
        IPoolManager.SwapParams calldata params,
        BalanceDelta delta,
        bytes calldata hookData
    ) external override returns (bytes4, int128) {
        // Post-swap logic here
        return (BaseHook.afterSwap.selector, 0);
    }
}

Dynamic Fee Hook โ€” Charge More During Volatility

This is the most practical hook for traders โ€” a pool that charges lower fees when markets are calm and higher fees when volatile (protecting LPs from impermanent loss):

contract DynamicFeeHook is BaseHook {
    using PoolIdLibrary for PoolKey;
    
    IPoolManager.SwapFee public constant BASE_FEE = IPoolManager.SwapFee.wrap(500);    // 0.05%
    IPoolManager.SwapFee public constant HIGH_VOL_FEE = IPoolManager.SwapFee.wrap(3000); // 0.30%
    IPoolManager.SwapFee public constant EXTREME_FEE = IPoolManager.SwapFee.wrap(10000); // 1.00%
    
    // Store last N prices for volatility calculation
    mapping(PoolId => uint256[10]) public priceHistory;
    mapping(PoolId => uint8) public priceIndex;
    
    function beforeSwap(
        address,
        PoolKey calldata key,
        IPoolManager.SwapParams calldata,
        bytes calldata
    ) external override returns (bytes4, BeforeSwapDelta, uint24) {
        
        PoolId poolId = key.toId();
        uint24 dynamicFee = calculateDynamicFee(poolId);
        
        // Return fee override โ€” this overrides the pool's base fee for this swap
        return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, dynamicFee);
    }
    
    function calculateDynamicFee(PoolId poolId) internal view returns (uint24) {
        uint256[] memory prices = priceHistory[poolId];
        
        // Calculate realized volatility from recent prices
        uint256 volatility = calculateVolatility(prices);
        
        if (volatility < 100) return 500;      // Low vol: 0.05%
        if (volatility < 500) return 3000;     // Med vol: 0.30%
        return 10000;                           // High vol: 1.00%
    }
    
    function calculateVolatility(uint256[] memory prices) internal pure returns (uint256) {
        if (prices.length < 2) return 0;
        
        uint256 sum = 0;
        for (uint256 i = 1; i < prices.length; i++) {
            uint256 change = prices[i] > prices[i-1] 
                ? ((prices[i] - prices[i-1]) * 10000) / prices[i-1]
                : ((prices[i-1] - prices[i]) * 10000) / prices[i-1];
            sum += change;
        }
        return sum / (prices.length - 1);
    }
}

Limit Order Hook โ€” On-Chain Limit Orders in an AMM

contract LimitOrderHook is BaseHook {
    
    struct LimitOrder {
        address owner;
        bool zeroForOne;     // Direction
        int24 tickLower;
        int24 tickUpper;
        uint128 liquidity;
        bool filled;
    }
    
    mapping(PoolId => mapping(bytes32 => LimitOrder)) public limitOrders;
    
    // Users call this to place a limit order
    function placeLimitOrder(
        PoolKey calldata key,
        int24 tickLower,
        int24 tickUpper,
        bool zeroForOne,
        uint128 liquidity
    ) external returns (bytes32 orderId) {
        orderId = keccak256(abi.encodePacked(msg.sender, block.timestamp, tickLower, tickUpper));
        
        PoolId poolId = key.toId();
        limitOrders[poolId][orderId] = LimitOrder({
            owner: msg.sender,
            zeroForOne: zeroForOne,
            tickLower: tickLower,
            tickUpper: tickUpper,
            liquidity: liquidity,
            filled: false
        });
        
        // Add liquidity at the specified range (the "limit order")
        // When price crosses this range, the liquidity gets consumed = order filled
    }
    
    function afterSwap(
        address,
        PoolKey calldata key,
        IPoolManager.SwapParams calldata params,
        BalanceDelta delta,
        bytes calldata
    ) external override returns (bytes4, int128) {
        // Check if any limit orders were filled by this swap
        // If so, remove the liquidity and send tokens to owner
        _checkAndFillOrders(key, params);
        return (BaseHook.afterSwap.selector, 0);
    }
}

Why Hooks Matter for Bot Builders

Before V4, bots had to work around AMM limitations:

  • Want a limit order? Use an external contract that watches and market-sells
  • Want dynamic fees? Can't โ€” fees are fixed at pool creation
  • Want MEV protection? Use private RPCs and hope for the best

With V4 hooks, these features live inside the pool itself. Bots can now:

  1. Interact with pools that have native limit orders
  2. Get better fills from dynamic-fee pools during calm markets
  3. Build their own hooks as a competitive moat

Getting Started With V4 Hooks

# Use Foundry for V4 hook development
forge init my-v4-hook
cd my-v4-hook

# Add V4 dependencies
forge install uniswap/v4-core
forge install uniswap/v4-periphery

# Run tests
forge test -vvv

The Uniswap V4 ecosystem is in early innings. Building hooks expertise now is like building Solidity expertise in 2020 โ€” the window of being early is still open.

Related Articles