Skip to content
bibo.DocsOpen app
Protocol documentation
DocsDevelopers

DEX and aggregator integration

Discover, quote and route trades through BIBO's concentrated liquidity pools.

8 min read
On this page

Choose the pool adapter

Every BIBO pool is a concentrated liquidity (CL) pool; this deployment has no stable or volatile pools. A CL pool is identified by its factory, token pair and tick spacing, quoted through QuoterV2 and executed through SwapRouter.

Keep the factory and tick spacing in your routing data from discovery through transaction encoding.

Choose the pool adapter
StepContractKey or method
DiscoverCLFactoryToken pair + tickSpacing
QuoteQuoterV2quoteExactInput / quoteExactOutput through eth_call
ExecuteSwapRouterexactInput / exactOutput

Discover and identify pools

Index PoolCreated on CLFactory, or enumerate allPools entries up to allPoolsLength(). Resolve candidates with getPool(tokenA, tokenB, tickSpacing) and verify their token addresses. Include chain and factory in every pool key; one token pair can have several distinct pools, one per tick spacing.

To find every pool for a pair, enumerate tickSpacings() and query each spacing.

Read identities, current fees and liquidity state at one block. A zero getPool result means the pool does not exist; an existing address still needs liquidity and an executable quote. Use the configured factory to resolve pools rather than substituting a stock Uniswap address calculation.

For a graduated launch, use the recorded pool and clFactory after LaunchFactory.getLaunch(token).state becomes POOL_LIVE. Earlier trading belongs to BondingCurve, and a reserved pool address is not yet a live AMM market. The recorded pool is the only one that charges the launch's Creator Fee; other pools for the same token, opened through CLFactory.createPool or as platform-fee pools, charge none.

// CLFactory
event PoolCreated(
    address indexed token0,
    address indexed token1,
    int24 indexed tickSpacing,
    address pool
);

function tickSpacings() external view returns (int24[] memory);

function getPool(address tokenA, address tokenB, int24 tickSpacing)
    external view returns (address pool);

Encode concentrated liquidity paths

A CL path packs a 20-byte token address, a 3-byte int24 tickSpacing and the next 20-byte token address. Append one spacing and one token per additional pool. One pool takes 43 bytes; two take 66 bytes. The spacing identifies the pool and is not a fee tier.

Exact-input paths follow the trade direction. Exact-output paths reverse both the tokens and their corresponding spacings. For a Meme A → BIBOCAT → Meme B route, the encodings are:

// Addresses and spacings are resolved from confirmed pools.
bytes memory inputPath = abi.encodePacked(
    tokenA, spacingA, bibocat, spacingB, tokenB
);

bytes memory outputPath = abi.encodePacked(
    tokenB, spacingB, bibocat, spacingA, tokenA
);

Quote concentrated liquidity pools

Read token0(), token1(), tickSpacing(), slot0() and liquidity() from the pool. Call QuoterV2.quoteExactInput with the forward path or quoteExactOutput with the reversed path. Its first return value is the final output or required input, including the market fee and the pool's additional quote-side fee, if it has one.

Use RPC eth_call at the same block as the other candidate quotes. QuoterV2 methods are non-view because they simulate swaps; do not submit paid quote transactions or use an onchain staticcall. gasEstimate covers the quoted pool work, so estimate your complete executor transaction separately.

// QuoterV2
function quoteExactInput(bytes memory path, uint256 amountIn)
    external returns (
        uint256 amountOut,
        uint160[] memory sqrtPriceX96AfterList,
        uint32[] memory initializedTicksCrossedList,
        uint256 gasEstimate
    );

function quoteExactOutput(bytes memory path, uint256 amountOut)
    external returns (
        uint256 amountIn,
        uint160[] memory sqrtPriceX96AfterList,
        uint32[] memory initializedTicksCrossedList,
        uint256 gasEstimate
    );

Account for each pool's fees

Read the effective fee for the actual pool. Defaults can be overridden, and CL tick spacing does not determine the current fee. Keep the fee unit with its value when combining adapters.

A pool can also charge an additional fee on the quote-asset side. Read creatorFeePips() on each pool: zero means the swap pays only the market fee. Despite the name, a nonzero value is not all Creator Fee. In a launch's own pool it is the Creator Fee plus the platform fee, or the Creator Fee alone for a launch of LaunchFactory v1; in a platform-fee pool it is the 0.1% platform fee alone. Only the launch's own pool carries its Creator Fee. The pool charges the whole value, so quote with it, and read it from the pool rather than inferring it from the token pair.

Account for each pool's fees
FeeFee sourceUnit
CL market feeCLPool.fee()Pips: 1,000,000 = 100%
Additional quote-side feeCLPool.creatorFeePips(): any Creator Fee + any platform fee; 0 = nonePips: 1,000,000 = 100%
Its platform part, in a v2 launch's own poolLaunchFeeSplitter.platformFeePips(), out of totalFeePips(), at the pool's creatorFeeVault()Pips: 1,000,000 = 100%
Tell the pool kinds apart
A launch's own pool is getLaunch(token).pool, read on the factory that launched the token, and its creatorFeeVault() is the launch's LaunchFeeSplitter; for a launch of LaunchFactory v1, it is the launch's CreatorFeeVault, or zero when the launch has no Creator Fee. A platform-fee pool's creatorFeeVault() is LaunchFactory.platformFeeReceiver(). Any other pool has creatorFeePips() = 0 and a zero creatorFeeVault().
Respect the quote-asset side
When creatorFeePips() is nonzero, quoteIsToken0() identifies the quote asset. The additional fee reduces quote input before the market swap, or is deducted from quote output after it. It is enforced by the pool, including on direct swaps. Treating it as an additional input-token fee in every direction produces incorrect quotes.
Use fee-inclusive amounts
QuoterV2 already returns amounts after the applicable fees. Do not subtract them again. Local pricing must match QuoterV2 in both directions, including integer rounding and limited available depth.

Build routes across pools

Discover pools around both request tokens and your supported intermediate assets. Launches pair Meme tokens with an approved quote asset — BIBOCAT, WETH or BGB — so include those when searching for routes between launched tokens. Allow enough discovery capacity for the added pairs, and use only pools that exist and can absorb the trade.

Compare final amounts for the same input and snapshot. If a split route reuses a pool, simulate its legs in execution order: separate quotes against the original state do not include the price movement caused by earlier legs.

A packed CL path can combine several BIBO pools. To cross to another DEX, compose separate router calls in your executor. For example, A → B through SwapRouter can feed B → C through another venue's router. Receive B in the executor and use the balance actually credited to fund the next call.

A generic executor can call SwapRouter through its approved-call mechanism. Encode the router's own ABI, bind its deployment to the expected factory, and give that caller the required allowance. A generic V3 fork identifier does not by itself identify a BIBO CL pool.

Execute and settle the trade

For exact input, call SwapRouter.exactInput with the forward path and amountOutMinimum. For exact output, call exactOutput with the reversed path and amountInMaximum. All amounts are integer token units; deadlines are Unix seconds.

// SwapRouter — concentrated liquidity pools
struct ExactInputParams {
    bytes path;
    address recipient;
    uint256 deadline;
    uint256 amountIn;
    uint256 amountOutMinimum;
}

function exactInput(ExactInputParams calldata params)
    external payable returns (uint256 amountOut);

struct ExactOutputParams {
    bytes path;
    address recipient;
    uint256 deadline;
    uint256 amountOut;
    uint256 amountInMaximum;
}

function exactOutput(ExactOutputParams calldata params)
    external payable returns (uint256 amountIn);
Fund the router's caller
A wallet calling a router approves that router. When an executor calls it, the executor must hold the input and grant the router its own allowance. A user's approval to an aggregator does not authorize the router to spend the aggregator's balance. For quote/Meme ERC-20 routes, send zero native value.
Choose recipients explicitly
Direct a final payout to the user's chosen recipient, or direct intermediate output to your executor. In SwapRouter, a zero recipient leaves output in the router. Preserve the intended recipient throughout a composed transaction.
Enforce the complete trade limit
Derive limits from the quote and the user's slippage tolerance. A composed executor must check the final output after all venue legs and any aggregator fees. Simulate the complete call with its actual sender, approvals, deadlines and limits before submission.
Return unused assets
CL exact-output trades can spend less than the maximum, and CL exact-input trades can stop at a price boundary. Reconcile actual transfers and return remaining input and intermediate balances held by the executor to an explicit refund recipient. If native input was wrapped, define whether its refund is native or wrapped. Confirm the output from receipt transfers rather than the earlier quote.

Calling pools directly

Router calls handle the pool interaction for you. A direct adapter calls CLPool.swap(recipient, zeroForOne, amountSpecified, sqrtPriceLimitX96, data) and must pay the positive deltas in uniswapV3SwapCallback. The deltas include the pool's additional quote-side fee whenever creatorFeePips() is nonzero.

// CLPool callback — implemented by the swap caller
function uniswapV3SwapCallback(
    int256 amount0Delta,
    int256 amount1Delta,
    bytes calldata data
) external;
CL direction and limits
zeroForOne means input is token0. A positive amountSpecified requests exact input; a negative value requests exact output. Direct pool calls need a valid directional sqrtPriceLimitX96. Zero is a router/quoter convenience and is not a valid CLPool limit.
Authorize callback payments
Accept callbacks only for the active swap and the expected pool resolved from a trusted factory. Bind the token pair, spacing, payer and spending limit to that call. Pool authenticity alone does not authorize a payment. Direct adapters must also enforce final output and return unused assets.

Validate the complete integration

Keep discovery, quoting and execution on the same deployment configuration. The contract directory lists this deployment's shared addresses; discover each pool from CLFactory. Validate the selected ABIs and complete transactions against the intended network before enabling routes.

Validate the complete integration
CaseExpected result
Pool selectionPools for one pair remain distinct by tick spacing and factory.
Local pricingOutputs match QuoterV2 in both directions, with different decimals, small inputs, current fees and limited liquidity. Cover pools with creatorFeePips() = 0, launch pools of both LaunchFactory versions with and without a Creator Fee, and platform-fee pools.
Route compositionTick spacings identify the intended pools; shared pools are simulated sequentially and intermediate balances fund the next call.
Limits and payoutsExpired deadlines and breached slippage limits revert; output reaches the requested recipient and unused assets follow the refund policy.
Direct callbacks, if usedUnexpected pools, unsolicited callbacks and payments outside the active swap's authorization are rejected.
BIBO ProtocolLaunchpad + ve(3,3) liquidity.