Concentrated Liquidity Management System Development
Uniswap V3 launched in May 2021 with idea that seemed obvious only after explanation: why spread liquidity across entire price range from 0 to infinity when actual trading happens in ±10–20% range from current price? Concentrated liquidity lets LP choose range [tickLower, tickUpper] and provide 100–1000x more depth at same capital.
Problem: position earns fee only while price in range. Once price exits boundaries — LP fully converts to one asset and stops earning. Managing such positions manually — job for bots, not people.
Why passive position in V3 unprofitable on volatile pairs
Classical backtesting on Uniswap V3 data (2021–2024) shows unpleasant picture: LP in wide range on ETH/USDC earns from fee less than loses on impermanent loss at typical ETH volatility. Hold of both assets outperforms passive LP.
Why? On 20% price move from center, position experiences accelerated IL — amplified impermanent loss compared to V2. Same capital concentrated in 5x narrower range earns 5x more fee, but far more often out-of-range.
Only way to go positive — active management: move range following price, balancing between fee APR and gas costs for rebalancing. This is management system task.
System architecture: three components
1. On-chain Vault contract
Vault — smart contract that owns Uniswap V3 position NFTs (ERC-721) on behalf of users. Users deposit tokenA and tokenB, receive vault shares. Vault independently manages position.
interface IConcentratedLiquidityVault {
function deposit(uint256 amount0, uint256 amount1, address recipient)
external returns (uint256 shares);
function withdraw(uint256 shares, address recipient)
external returns (uint256 amount0, uint256 amount1);
function rebalance(
int24 newTickLower,
int24 newTickUpper,
uint256 swapAmount,
bool zeroForOne
) external;
function collectFees() external returns (uint256 fees0, uint256 fees1);
}
Key: rebalance function called by external keeper bot. It:
- Removes all liquidity from current position (
decreaseLiquidity+collectvia NonfungiblePositionManager) - Optionally swaps portion of assets for balance to needed ratio
- Creates new position in new range (
mint)
Authorization for rebalance call — via whitelist of keeper addresses with Gnosis Safe multisig for list management.
2. Off-chain strategic engine
Node.js (or Python) service that continuously monitors position state and makes rebalancing decisions.
Input data:
- Current price (from Uniswap V3 pool
slot0.sqrtPriceX96) - Current position range (
tickLower,tickUpper) - Accumulated fees (not yet collected)
- Historical volatility data (for optimal range width calculation)
Rebalancing strategies:
Threshold strategy — rebalance when price deviates from range center by X%. Simple, predictable, but doesn't account for volatility regime.
Volatility-based strategy — range width = N * σ (standard deviation of price over last K days). At high volatility — wider range (fewer rebalances, better IL protection), at low — narrower (more fee). N usually 1.5 to 3 standard deviations.
Backtesting on historical data mandatory before deployment. Take historical prices from The Graph or Uniswap V3 subgraph, simulate strategy, calculate:
- Total fee over period
- Total IL
- Gas costs for rebalances
- Net P&L vs hold
interface RebalanceSignal {
shouldRebalance: boolean
newTickLower: number
newTickUpper: number
swapAmount: bigint
zeroForOne: boolean
reason: 'out_of_range' | 'drift' | 'volatility_change'
}
function evaluateRebalance(
currentTick: number,
tickLower: number,
tickUpper: number,
volatility30d: number
): RebalanceSignal {
const rangeWidth = tickUpper - tickLower
const center = (tickLower + tickUpper) / 2
const drift = Math.abs(currentTick - center) / (rangeWidth / 2)
// Rebalance if price moved to 80% from center to edge of range
if (drift > 0.8 || currentTick < tickLower || currentTick > tickUpper) {
const optimalRange = Math.round(volatility30d * 2 * 10000) // ticks
return {
shouldRebalance: true,
newTickLower: Math.round(currentTick - optimalRange / 2),
newTickUpper: Math.round(currentTick + optimalRange / 2),
// ... swap calculation
}
}
return { shouldRebalance: false, ... }
}
3. Keeper infrastructure
Keeper — service that calls rebalance() on-chain upon receiving signal from strategic engine. Requirements for keeper:
- High uptime (99.9%+). Out-of-range position brings no fee.
- Atomicity: rebalance must complete or entirely rollback. Can't end up in "liquidity removed, new position not created" state.
- MEV protection: rebalancing includes swap visible in mempool. Must use Flashbots private relay to avoid sandwich attack.
Sending via Flashbots:
const bundle = await flashbotsProvider.sendBundle(
[{ signer: keeperWallet, transaction: rebalanceTx }],
targetBlock,
{ minTimestamp: 0, maxTimestamp: Math.floor(Date.now() / 1000) + 30 }
)
Rebalancing gas optimization
One rebalance on Ethereum mainnet costs 300,000–500,000 gas (removal + fee collection + swap + new position). At 20 gwei gas price and ETH $3000 — $18–30 per operation.
For small positions (<$10,000) this makes frequent rebalancing unprofitable. Solution: L2 support (Arbitrum, Optimism, Base) with Uniswap V3 deployment. On Arbitrum gas 100x cheaper — rebalance costs $0.20–0.50.
Additional optimization: compounding collected fees back into position within same rebalance transaction — instead of separate collect + mint.
Uniswap V4 and Hooks
Uniswap V4 introduces Hooks — ability to embed management logic directly in pool via hook contract. beforeSwap and afterSwap hooks allow dynamically changing fee based on conditions. For liquidity management system this opens possibility to implement dynamic fee range: wider range with lower fee at high volatility, narrow with high — at low volatility.
Architecturally cleaner than off-chain keeper, but requires writing hook contract and separate audit from vault logic.
Development process
Strategy research (3–5 days). Backtest several strategies on historical data of chosen pair. Evaluate optimal parameters for range width and rebalancing triggers.
Vault contract development (2–3 weeks). Solidity, Foundry tests with fork-testing on real Uniswap V3 mainnet data.
Off-chain engine (1–2 weeks). Strategic engine, keeper bot, monitoring.
Frontend (1–2 weeks). Deposit/withdraw UI, current position display and historical returns.
Audit. Recommend external audit of vault contract before attracting significant liquidity.
Timeline estimates
Basic vault for one pair with threshold strategy — 4–6 weeks. Multi-pair vault with volatility-based strategy, Arbitrum/Optimism support and analytics dashboard — 10–14 weeks.







