Development of Custom Pools on Uniswap v4
Uniswap v4 is not just another DEX version with improved AMM. It's a platform for creating custom pools. The key change: the hooks architecture enables arbitrary code execution before and after any pool operation—initialization, swap, adding/removing liquidity, donation. This opens a space that didn't exist in v2 and v3: dynamic fees, on-chain order books, automatic rebalancing, MEV protection directly in the pool.
The price of this flexibility is complexity. The hook contract operates in the PoolManager context through an unlock callback mechanism with a BalanceDelta accounting system. A mistake in the hook can block the entire pool or drain liquidity.
Uniswap v4 Architecture: What Changed Fundamentally
Singleton PoolManager and BalanceDelta
In v2/v3, each pool is a separate contract. In v4, all pools live inside a single PoolManager. This reduces pool creation gas from ~500k to ~150k and makes multihop swaps significantly cheaper (no transfers between contracts).
Token accounting uses BalanceDelta—not real transfers, but deltas accumulated and settled at the end of an unlock callback. While PoolManager is locked (within one unlock), tokens don't physically move. This enables flash accounting: swap, use received tokens for anything, repay the debt—all in one transaction without a separate flash loan.
Hook System: Address as Bitmask
Hook contracts register at pool creation through PoolKey. The hook's address encodes allowed callbacks via leading bits: specific bits must be set to activate corresponding hooks. beforeSwap is bit 7, afterSwap is bit 6, beforeAddLiquidity is bit 5, and so on.
This means the hook contract address cannot be chosen arbitrarily—you must mine a vanity address with the required bits via CREATE2. Tools: v4-template from Uniswap, scripts for address mining via Foundry.
There are 14 possible hooks total:
| Hook | Description |
|---|---|
beforeInitialize / afterInitialize |
Before/after pool creation |
beforeAddLiquidity / afterAddLiquidity |
Before/after adding liquidity |
beforeRemoveLiquidity / afterRemoveLiquidity |
Before/after removal |
beforeSwap / afterSwap |
Before/after swap |
beforeDonate / afterDonate |
Before/after donation to pool |
beforeSwapReturnDelta |
Modify swap output |
afterSwapReturnDelta |
Capture fee after swap |
afterAddLiquidityReturnDelta |
Modify LP balance |
afterRemoveLiquidityReturnDelta |
Modify balance on removal |
Most Interesting Custom Mechanics
Dynamic Fees via BeforeSwap
In v3, fee tier is fixed at pool creation (0.05%, 0.3%, 1%). In v4, a hook can return lpFeeOverride directly from beforeSwap, changing the fee for each swap dynamically. This enables:
Volatility-dependent fees: Chainlink price feed + sliding window volatility → at high volatility, 1% fee; at low, 0.05%. LPs get fair rewards for impermanent loss risk.
TWAP-based fees: if the current price deviates from TWAP by more than X%, increase fee—protection against oracle-driven arbitrage that enriches arbitrageurs at LP expense.
Limit Orders via Tick-Based Hooks
With afterSwap, the hook knows a swap occurred and price changed. If the current tick crossed a pre-set level, you can execute a limit order: buy/sell liquidity from an orders mapping. This is a full on-chain limit order book without a centralized sequencer.
Implementation complexity: gas for iterating orders in a hook. With 50+ orders on one tick, swaps become expensive. Solution—lazy execution: orders execute on the next tick access, not blocking the current swap.
TWAMM (Time-Weighted Average Market Maker)
Large orders split into small virtual orders, executed continuously over time. The hook accumulates TWAMM order tokens and gradually executes them through the pool, preventing slippage. This solves whale orders—a $10M swap in one block moves price and creates sandwich opportunities. TWAMM spreads it over 1000 blocks.
Math: virtual orders compute via exponential distribution formula, allowing state recalculation for any point in time without step-by-step iteration.
Common Hook Development Mistakes
Reentrancy through PoolManager. PoolManager has its own lock, but a hook can call external contracts which re-enter the pool. NoSuchPool, AlreadyUnlocked are common debug-time errors. Rule: don't make external calls in hooks except read-only queries.
Incorrect BalanceDelta. The hook returns a token delta. If calculated wrongly, PoolManager reverts with DeltaNotSettled. Common case: afterSwapReturnDelta tries to take more than exists in the swap delta.
Address mining for production. CREATE2 mining takes minutes to hours depending on required bits. For 14 hooks (all active), you need 14 specific bits—roughly 16,384 attempts on average. On GPU it's fast; on CPU with Foundry, 10 minutes to an hour.
Stack and Development Process
Development uses the official v4-template repository. Foundry is the only adequate tool for v4 hook testing: PoolSwapTest, PoolModifyLiquidityTest helpers from v4-periphery; fork tests on mainnet/testnet state.
Design (2–3 days): define pool mechanics, hook set, BalanceDelta logic. At this stage, formal specification of invariants: "pool never has negative balance," "fee never exceeds X% under any condition."
Hook contract development (3–5 days): implement callbacks, mine address, support contracts (oracle, order book, rebalancer).
Testing (3–5 days): unit tests for each hook in isolation, integration tests via PoolSwapTest, fuzz-tests on critical parameters (swap size, tick range, fee multiplier), fork tests on real state.
Audit and deployment (2–3 days): Slither, manual review focusing on reentrancy and BalanceDelta correctness, deployment via Foundry forge script with verification.
Total: 1–2 weeks for a hook with one mechanic. Complex systems (TWAMM, full order book): 4–6 weeks. Cost calculated after describing mechanics and throughput requirements.







