Liquidity integration
Resolve pools, quote and execute swaps, and manage concentrated liquidity positions.
On this page
The liquidity model
Every BIBO pool is a concentrated liquidity (CL) pool. A position supplies liquidity between tickLower and tickUpper and is an NFT minted by NonfungiblePositionManager. This deployment has no stable or volatile pools and no ERC-20 LP tokens.
Resolve a concentrated liquidity pool
Graduated launches use concentrated liquidity pools. Once getLaunch(token).state is POOL_LIVE, use the recorded pool address. For general pool discovery, call CLFactory.getPool(tokenA, tokenB, tickSpacing); the zero address means that pool does not exist.
Read token0(), token1(), tickSpacing(), slot0(), liquidity(), fee() and creatorFeePips() from the pool. Pool identity uses tickSpacing, not a fee-tier argument. Read the market fee from fee() rather than deriving it from spacing. Both fees use pips, with 1,000,000 representing 100%. creatorFeePips() is the pool's additional fee on the quote-asset side: in a launch's own pool the Creator Fee plus the platform fee (the Creator Fee alone for a launch of LaunchFactory v1), in a platform-fee pool the platform fee alone. Zero means the pool charges none; otherwise, use quoteIsToken0() to identify the side on which it applies.
// CLFactory
function getPool(
address tokenA,
address tokenB,
int24 tickSpacing
) external view returns (address pool);Quote a CL swap
Use QuoterV2 through an RPC simulation / eth_call. Its quote functions are not marked view, but they are intended for offchain quoting; do not submit them as paid transactions. The result is an execution estimate at the simulated block, including the pool's market fee and its additional quote-side fee, if any. Do not subtract those fees a second time.
Set sqrtPriceLimitX96 to zero when you are not imposing an additional pool price limit. Derive an amountOutMinimum from the quote and the user's chosen slippage tolerance for the actual swap.
struct QuoteExactInputSingleParams {
address tokenIn;
address tokenOut;
uint256 amountIn;
int24 tickSpacing;
uint160 sqrtPriceLimitX96;
}
function quoteExactInputSingle(
QuoteExactInputSingleParams memory params
) external returns (
uint256 amountOut,
uint160 sqrtPriceX96After,
uint32 initializedTicksCrossed,
uint256 gasEstimate
);Execute a CL swap
Approve tokenIn to SwapRouter, then simulate and submit exactInputSingle. Use the same token pair, amount and tick spacing as the quote, along with the recipient, a deadline in seconds and amountOutMinimum. When both tokens are paid and received as ERC-20s, send no native transaction value.
For a direct wallet payout, set recipient to that wallet explicitly. A zero recipient directs the swap output to SwapRouter itself.
To pay with ETH where tokenIn is WETH, send the ETH as the transaction value and call multicall with exactInputSingle followed by refundETH(), which returns any unspent ETH; no approval is needed. To receive ETH where tokenOut is WETH, set the swap's recipient to the SwapRouter address, so the output stays in the router, and follow it in the same multicall with unwrapWETH9(amountOutMinimum, recipient).
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
int24 tickSpacing;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
function exactInputSingle(ExactInputSingleParams calldata params)
external payable returns (uint256 amountOut);- Keep the tuple order
- The router and quoter structs have different field orders. Encode each call against its own ABI; neither takes a Uniswap-style fee field in place of tickSpacing.
- Follow execution
- After confirmation, refresh balances and pool state. Use actual receipt logs and token transfers for trade history; the earlier quote is not the executed amount.
Create a CL position
Use NonfungiblePositionManager for a user's own liquidity position. Read the pool's token ordering and choose tickLower and tickUpper aligned to tickSpacing. Approve both assets to the position manager, then simulate mint. For an existing initialized pool, set sqrtPriceX96 to zero.
mint creates a position NFT for recipient. This is separate from launch.positionId, the graduation position held by LaunchLiquidityLocker, whose fees are collected through the locker; a LaunchFactory v1 launch's locker cannot collect them.
struct MintParams {
address token0;
address token1;
int24 tickSpacing;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
uint160 sqrtPriceX96;
}
function mint(MintParams calldata params)
external payable returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);- Use the actual deposit
- Desired amounts are maximum inputs. Simulation shows the amounts the selected range can use; receipt events record the resulting liquidity and deposits.
- Read an existing position
- positions(tokenId) returns token0, token1, tickSpacing, tickLower, tickUpper, liquidity and accounting fields. ownerOf(tokenId) identifies the NFT owner. Use the ERC-721 Transfer event to discover the newly minted tokenId.
Manage and collect a CL position
The position manager accepts the following parameter tuples. Amounts labelled 0 and 1 always follow the position's token0/token1 order.
Removing liquidity, collecting and burning require NFT ownership or approval. Check the caller's authorization before enabling an action. For a direct collection payout, set recipient to the intended wallet; zero directs the tokens to NonfungiblePositionManager itself.
The tokensOwed0 and tokensOwed1 fields in positions(tokenId) are stored accounting values, not a live quote of collectible fees. Simulate collect from an authorized account with the intended recipient and collection limits to preview its current output. After a liquidity decrease, that output can include withdrawn principal as well as fees. Use events to follow position changes and token transfers to confirm actual payouts.
struct IncreaseLiquidityParams {
uint256 tokenId;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
struct DecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}| Call | Result |
|---|---|
| increaseLiquidity(params) | Pulls additional approved assets from the caller. Returns liquidity added, amount0 and amount1. |
| decreaseLiquidity(params) | Removes liquidity and credits the position's owed token balances. Returns amount0 and amount1; this call alone does not transfer the tokens out. |
| collect(params) | Transfers owed tokens up to each maximum to recipient. Returns the collected amount0 and amount1. Also used to collect position fees without removing liquidity. |
| burn(tokenId) | Closes an empty NFT after all liquidity and owed tokens have been removed. |