Skip to content

GuidesLiquidity management

Pools and liquidity

Liquidity management

Status: live for Uniswap V3 and V4 pools on testnet. Aero Slipstream and Nest CL (Algebra) are built and tested on other chains; they switch on on Elysium as soon as their position managers are published and added to positionManagers (Nov 9).

How it works

Lane deploys no liquidity contract and never holds LP funds. For each action the API builds the call to the venue's own position manager, lists the approvals to send first, and simulates the call from the user's address. The user signs it; the position is an NFT in their wallet, visible in the venue's own UI too. Same model as /quote.

VenuePosition managerHow tokens are pulled
Uniswap V3NonfungiblePositionManagerERC20 approve to the manager; the WHYPE side can be paid in native HYPE (native=true)
Aero SlipstreamSlipstream NonfungiblePositionManager (pools by tick spacing)ERC20 approve; WETH side payable natively
Nest CL (Algebra Integral), Algebra 1.9Algebra NonfungiblePositionManager (no fee tier; Integral adds deployer)ERC20 approve; native via WNativeToken / refundNativeToken
Uniswap V4PositionManagerPermit2: approve the token to Permit2 once, then a Permit2 allowance for the manager (30 days); native HYPE pools take HYPE directly

Only the managers listed in positionManagers in the network file are used (to read positions and to build transactions). Discovery (the owner of V3 Mint events, the sender of v4 ModifyLiquidity events, checked with factory() / poolManager()) only suggests candidates, listed with trusted: false and logged for the operator: anyone can call a pool's mint and answer factory() with the real factory, so trusting discovery would let a fake manager collect token approvals. GET /v1/liquidity/managers lists both. On testnet: V3 0x28580f9b… (factory B) and V4 0xbE6E9E09… (PoolManager 0x0088BEA…).

The screens

FigmaRouteNotes
Choose a range, see the other amountGET /v1/liquidity/quoteTicks aligned to the pool's spacing; give amount0 or amount1, the other is computed from the price; also returns the share of the last 30 days the price spent in that range
Add liquidity (new position)GET /v1/liquidity/addReturns approvals (send in order), then tx
Add to an existing positionGET /v1/liquidity/add?tokenId=Same range as the position
My positionsGET /v1/liquidity/positions?owner=Value, uncollected fees, in range, time in range, APR estimate
Remove (25/50/75/100%)GET /v1/liquidity/remove?percent=Collects the removed tokens and the fees; 100% also burns the NFT
Collect feesGET /v1/liquidity/collectFees only, liquidity untouched

Ranges

Give either tickLower + tickUpper, or priceLower + priceUpper (human units: token1 per token0, e.g. WHYPE per USDC when USDC is token0), or fullRange=true. Without any, the range is ±10% around the current price. Ticks are rounded outward to the pool's tick spacing. A range entirely above the price takes only token0; entirely below, only token1.

Position fields

FieldMeaning
amountsTokens the position holds now (at the current price)
valueUsdTheir USD value
feesUncollected fees, per token and in USD. V3: what collect() returns when simulated from the owner (exact). V4: computed from the PoolManager's fee growth, which matched real collects to the wei on live testnet positions
inRangeThe price is inside the range now (the position earns fees)
inRangePct30d"In range 94% of the time": share of the last 30 days' hourly price samples inside the range
aprEstimatePctPool's 24h fees × the position's share of in-range liquidity × 365 ÷ position value. 0 when out of range. An estimate, not a promise

Example: add then remove

API=https://api.lane.markets/v1; POOL=0x591Aa2eC4275135C640D07F9eAa35CDad52ab05c; ME=0xYou

# 1 HYPE on the WHYPE side, paid natively, ±10% around the price
curl "$API/liquidity/add?pool=$POOL&amount1=1000000000000000000&native=true&owner=$ME"
#  -> approvals: [{ to: USDC, data: approve(...) , description }]   send them, then call again
#  -> tx: { to: NonfungiblePositionManager, data: multicall(mint, refundETH), value }, simulation: { ok: true }

curl "$API/liquidity/positions?owner=$ME"
curl "$API/liquidity/remove?manager=0x28580f9b…&tokenId=42&percent=50&owner=$ME"

With the SDK: addLiquidity, positions, removeLiquidity, collectFees (SDK).

Limits (phase 1)

  • On Elysium: Uniswap V3 and V4 until Aero's and Nest's position managers are published; the code for them is in place. The manager's dialect (Uniswap, Slipstream, Algebra 1.9, Algebra Integral) is detected from its bytecode.
  • A wallet's positions: the newest 50 per manager.
  • V4 positions are found by scanning ownerOf over the newest 200,000 ids of each manager (the V4 NFT isn't enumerable); the scan is cached a minute and shared by concurrent requests.
  • openPositions in /v1/pools/stats stays null: counting every position on the chain needs position indexing.
  • V4 pools whose hook restricts who can add liquidity will fail in simulation: the API shows the revert.

How it was verified

  • V4 fees: the collect call the API builds was run, by a probe placed at each owner's address in a simulation, on 4 live testnet positions: what it received equals the fees the API computed, to the wei (liquidity-live-check.ts).
  • Remove, collect, V4 add: simulated from real owners' addresses on live testnet.
  • Aero Slipstream (Base) and Algebra 1.9 (Camelot, Arbitrum): mint calldata from the same encoders, replayed on forks (LiquidityL2.fork.t.sol): exact range and liquidity, native ETH with refund. Algebra Integral (Nest's version) isn't exercised on-chain yet: no test deployment known.
  • V3 add: the exact calldata the API produced was replayed on a fork (LiquidityV3.fork.t.sol): the NFT is minted with exactly the liquidity and range the API computed, and unused native HYPE is refunded.

Next: Live prices

lane.markets · Make the most of every move.