LLMs.txt: agent-readable Markdown index of this site at /llms.txt

Just-in-Time Liquidity

Understand how DualPool deploys concentrated liquidity per swap and holds none between swaps.

A DualPool holds approximately zero resident liquidity in the Uniswap v4 PoolManager between swaps. Instead, the pool's capital rests in ERC-4626 vaults, and the hook deploys concentrated liquidity just-in-time (JIT), inside beforeSwap and for exactly one swap, then removes it again in afterSwap.

The lifecycle of every swap is:

  1. Rest state: the hook holds vault shares (plus any tracked ERC-20 and ERC-6909 claims). Capital earns lending yield continuously. The v4 pool itself holds no liquidity.
  2. beforeSwap: the hook computes how much of each token its configured tick ranges need at the current price, redeems any pending ERC-6909 claims, withdraws only the remaining shortfall from the vaults, and adds one v4 position per distribution bucket.
  3. Swap execution: the PoolManager executes ordinary v4 swap math against the temporary positions and charges the pool's static key.fee natively.
  4. afterSwap: the hook removes every position it added, settles its net deltas with the PoolManager, and deposits remaining balances back into the vaults.

All four steps happen atomically within the swap transaction. The swapper sees a normal swap and a normal BalanceDelta.

Because standing liquidity is ~0, onchain pool depth is not a routing signal for DualPool. Capacity is discovered through the hook's view functions; see Integrate as a Router or Aggregator.

Hook Permissions

DualPool declares five hook permissions:

PermissionPurpose
beforeInitializeBlocks direct PoolManager initialization, so pools must be created through initializePool
beforeAddLiquidityBlocks external LP positions; only the hook adds positions, during JIT deployment
beforeRemoveLiquidityBlocks external LP removals; only the hook removes its JIT positions
beforeSwapDeploys JIT liquidity
afterSwapRemoves JIT liquidity, settles deltas, and re-deposits into vaults

All return-delta flags are false. DualPool does not use BeforeSwapDelta or custom accounting to synthesize execution: it lets normal v4 swap math run against temporarily deployed liquidity while v4 applies key.fee natively.

Static Fee

DualPool pools use a static LP fee, fixed at pool creation via PoolKey.fee and never updated by the hook:

  • Dynamic-fee pools are rejected at initialization with DynamicFeeNotSupported
  • hookData is ignored by beforeSwap and by the quote views, so it cannot change pricing or fees
  • To change the fee, the operator deploys a new pool with a different PoolKey.fee

Any v4 protocol fee is composed with the LP fee automatically, so quotes already reflect both.

Liveness

A newly initialized pool is not live. Swaps revert with PoolNotLive until the owner calls bootstrap, which seeds the first deposit and flips the pool live. This closes the window between initialization and funding where a swap could move the pool price against zero liquidity.

The owner can pause and resume a live pool with setPoolLive. While paused, swaps revert PoolNotLive and the quote views return 0.

Transient State

The hook records each deployed position's liquidity in transient storage during beforeSwap so that afterSwap removes exactly what was added. A per-pool JIT lock and a global in-flight counter, both also transient, reject reentrant swaps and block LP or admin calls while any JIT cycle is active. Nothing survives past the transaction.

Where to Go Next