Concentrated Liquidity Pools Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Concentrated Liquidity Pools Development
Complex
from 2 weeks to 3 months
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Concentrated Liquidity Pools Development

The team is launching a DEX and wants more than just copying Uniswap V2 with its x·y=k. A move to concentrated liquidity is the right decision, but implementation is significantly more complex: ticks, ranges, virtual reserves, piecewise curves. One math error — and LPs lose money without noticing, while arbitrageurs strip the protocol.

Why x·y=k no longer works for serious AMMs

The classic constant product formula effectively uses 5% to 20% of liquidity at realistic price ranges. The other 80% of LP capital "sleeps" on tails that never trade. Uniswap V3 solved this through concentration: an LP selects a range [tickLower, tickUpper], and their liquidity only works within it — but works orders of magnitude more intensely.

Architecturally this means moving from a single global curve to a piecewise-linear approximation. Each tick is a boundary of a range where a local formula applies. Crossing a tick recalculates active liquidity.

Math that breaks with wrong implementation

Tick math and Q64.96 fixed-point arithmetic

Uniswap V3 stores prices as sqrtPriceX96 — the square root of price in Q64.96 format. Not by accident: multiplying two Q64.96 numbers gives Q128.192, fitting in uint256. Any deviation from this scheme causes overflow or precision loss.

The TickMath.getSqrtRatioAtTick(int24 tick) function is critical: it converts a tick number to sqrtPrice via a table of precomputed constants with bit shifts. Implementing this independently without exactly matching these constants gives cumulative error, visible at boundary tick values (MIN_TICK = -887272, MAX_TICK = 887272).

Practical case: during Foundry fuzz testing with int24 parameters, we caught a 1 wei discrepancy at extreme ticks — seemed trivial. But in the liquidity burn function it caused uint256 underflow and revert. On mainnet this would block liquidity withdrawal.

Fee accumulation via global accumulators

Fee collection in concentrated pools works via global accumulators feeGrowthGlobal0X128 and feeGrowthGlobal1X128, plus per-tick feeGrowthOutside values. The formula for fees inside a range:

feeGrowthInside = feeGrowthGlobal - feeGrowthBelow(tickLower) - feeGrowthAbove(tickUpper)

Where feeGrowthBelow and feeGrowthAbove depend on whether the current tick is above or below the boundary. An error in the condition currentTick >= tickLower vs currentTick > tickLower gives wrong fee calculation at boundary ticks. This silent error — LPs get slightly less or more fees, protocol accumulates debt or surplus.

Reentrancy via callback in swap

The swap function in concentrated liquidity AMM uses a callback pattern: the pool contract first sends tokens, then calls uniswapV3SwapCallback on msg.sender, expecting to receive input tokens there. This opens a reentrancy vector: at the callback moment the pool state is already changed (price shifted), but the transaction isn't finished.

OpenZeppelin ReentrancyGuard doesn't help directly — callback is called by the pool contract itself within the same transaction. Protection: a lock-flag in pool storage, set at swap start, removed at end. Uniswap V3 uses an unlocked flag in slot0 for exactly this.

How we build concentrated liquidity pools

Architectural decisions

We develop based on Uniswap V3 Core as a reference, but don't fork directly — the BSL 1.1 license until 2023 had commercial restrictions (now expired, but auditors still ask). We use Uniswap V4's hooks architecture for extensions if custom fee logic is needed.

Stack: Foundry for all development and testing, Hardhat for deploy scripts with hardhat-deploy. Math libraries ported from @uniswap/v3-core/contracts/libraries: FullMath, TickMath, SqrtPriceMath, LiquidityMath. Tests include property-based fuzzing with invariant testing in Foundry:

  • Invariant 1: sum of all liquidity in active ranges always >= virtualReserves
  • Invariant 2: after any zero-slippage swap sqrtPrice doesn't exit stated range boundaries
  • Invariant 3: collected fees don't exceed accumulated feeGrowth * liquidity

Tick bitmap optimization

Finding the next initialized tick during cross-tick operations is a hot path. Uniswap V3 uses a bitmap: 256 ticks packed into one uint256. Finding the next set bit via BitMath.mostSignificantBit is O(1) instead of O(n) across all ticks.

Implementing bitmap for tickSpacing > 1 requires mapping from tickIndex to bitPosition: compressed = tick / tickSpacing, wordPos = compressed >> 8, bitPos = uint8(compressed). Shift errors give wrong tick lookup and miss cross-tick logic during swaps through multiple ranges.

Testing on real data

Fork tests on Ethereum mainnet via vm.createFork reproduce real pool states like USDC/ETH 0.05% fee tier with real liquidity distribution. We run historical swaps from Uniswap V3 subgraph through The Graph and compare results with reference implementation. Any discrepancy > 1 wei on any swap signals investigation needed.

Component Tool Coverage
TickMath Foundry fuzz, comparison with V3 core 100% boundary ticks
Fee accumulation Property-based invariant tests 50k iterations
Swap across multiple ticks Fork tests on mainnet data 1000+ historical swaps
Liquidity mint/burn Static analysis Slither + manual review All public functions

Periphery and integrations

The pool itself is just the core. For a full product you need NonfungiblePositionManager (or equivalent) to manage LP positions as NFTs (ERC-721), SwapRouter for route aggregation, and a quoter contract for off-chain swap simulation without gas.

Integration with Chainlink Price Feeds as sanity check: if pool price deviates from oracle by more than X%, circuit breaker pauses swaps. This protects against oracle manipulation via flash loans — an attack vector used in DeFi exploits built on AMM prices.

Frontend built on Uniswap SDK v3 + wagmi + viem. The SDK abstracts tick math and route finding, but for custom pools it needs extension — connecting your own pool factories and redefining computePoolAddress.

Work stages

Analytics (3-5 days). Define parameters: fee tiers (0.01% / 0.05% / 0.3% / 1%), tickSpacing, custom hooks needed (V4-style), multi-chain deployment (Ethereum + Arbitrum + Optimism typical). Check if pool needs upgradeability or immutable with admin functions only in periphery.

Design (5-7 days). Storage layout, interfaces, math libraries. Formal invariant verification on paper before coding.

Development (4-8 weeks). Core pool → math libraries → position manager → router → quoter. Order matters: each layer tested independently.

Audit. Concentrated liquidity is one of the most complex DeFi contract classes. External audit mandatory for any TVL volume. Internal audit via Slither + Echidna covers low/medium before sending out.

Deploy. Foundry forge script + Gnosis Safe multisig. Deploy to Sepolia/Arbitrum Goerli, load tests, then mainnet.

Timeline estimates

MVP with one fee tier and basic periphery — 6-8 weeks. Full multi-tier DEX with custom hooks and route aggregator — 2-3 months. Not counting external audit time (usually 3-6 weeks for protocol this complex).

Cost calculated individually after technical briefing.