Skip to main content
Create a V3 concentrated liquidity pool to enable trading between any two assets with custom fee structures.

Create a Pool

async function createConcentratedPool() {
  const response = await client.createConcentratedPool({
    assetAAddress: "asset-a-public-key",  // Base asset (e.g., BTC)
    assetBAddress: "asset-b-public-key",  // Quote asset (e.g., USDB)
    tickSpacing: 60,                       // Price granularity
    initialPrice: "0.00001",               // Price of A in terms of B
    lpFeeRateBps: 300,                     // 3% LP fee (300 basis points)
    hostFeeRateBps: 100,                   // 1% host fee
    hostNamespace: "my-exchange",          // Your integrator namespace
  });

  console.log('Pool created!');
  console.log('Pool ID:', response.poolId);
  console.log('Initial tick:', response.initialTick);

  return response;
}

Parameters

ParameterTypeDescription
assetAAddressstringPublic key of the base asset
assetBAddressstringPublic key of the quote asset
tickSpacingnumberMinimum tick increment (10, 60, or 200)
initialPricestringStarting price of asset A in terms of asset B
lpFeeRateBpsnumberFee to liquidity providers in basis points
hostFeeRateBpsnumberFee to host/integrator in basis points
hostNamespacestringOptional host namespace for fee sharing
poolOwnerPublicKeystringOptional pool owner (defaults to your wallet)

Tick Spacing

Tick spacing controls the granularity of liquidity positions. Lower spacing allows tighter ranges but increases swap complexity.
Tick SpacingPrice StepBest For
100.1%Stable pairs (USDB/USDC)
600.6%Standard pairs (BTC/USDB)
2002%Volatile pairs
Choose based on expected price movement and LP behavior:
// Stable pair - tight ranges work well
await client.createConcentratedPool({
  assetAAddress: usdbAddress,
  assetBAddress: usdcAddress,
  tickSpacing: 10,
  initialPrice: "1.0",
  lpFeeRateBps: 50,    // 0.5% - lower fee for stable pairs
  hostFeeRateBps: 10,
  hostNamespace: "my-exchange",
});

// Volatile pair - wider ranges needed
await client.createConcentratedPool({
  assetAAddress: memeTokenAddress,
  assetBAddress: btcAddress,
  tickSpacing: 200,
  initialPrice: "0.0000001",
  lpFeeRateBps: 1000,  // 10% - higher fee for volatile pairs
  hostFeeRateBps: 200,
  hostNamespace: "my-exchange",
});

Setting Initial Price

The initial price determines the starting tick of the pool. Express it as asset A’s price in terms of asset B:
// BTC/USDB pool: 1 BTC = 90,000 USDB
// But we need price in raw units (satoshis per USDB base unit)
// BTC has 8 decimals, USDB has 6 decimals
// 1 BTC = 100,000,000 sats, 90,000 USDB = 90,000,000,000 base units
// Price = 100,000,000 / 90,000,000,000 = 0.001111...

import { V3TickMath } from '@flashnet/sdk';

const initialPrice = V3TickMath.humanPriceToPoolPrice(
  90000,            // Human price: $90,000 per BTC
  8,                // Base decimals (BTC)
  6                 // Quote decimals (USDB)
);

await client.createConcentratedPool({
  assetAAddress: btcAddress,
  assetBAddress: usdbAddress,
  tickSpacing: 60,
  initialPrice,
  lpFeeRateBps: 300,
  hostFeeRateBps: 100,
  hostNamespace: "my-exchange",
});

Fee Configuration

Fees are expressed in basis points (1 bps = 0.01%):
Fee (bps)PercentageUse Case
500.5%Stable pairs
3003%Standard pairs
100010%Volatile/exotic pairs
The total fee is lpFeeRateBps + hostFeeRateBps. LP fees go to liquidity providers proportional to their share. Host fees go to the integrator.
// Total 4% fee: 3% to LPs, 1% to host
await client.createConcentratedPool({
  assetAAddress: tokenA,
  assetBAddress: tokenB,
  tickSpacing: 60,
  initialPrice: "1.0",
  lpFeeRateBps: 300,
  hostFeeRateBps: 100,
});

Host Namespace

Assign a host namespace to track integrator fees:
await client.createConcentratedPool({
  assetAAddress: tokenA,
  assetBAddress: tokenB,
  tickSpacing: 60,
  initialPrice: "1.0",
  lpFeeRateBps: 300,
  hostFeeRateBps: 100,
  hostNamespace: "my-exchange",
});
See Hosts for more on fee sharing.

After Creation

The pool starts empty. Add initial liquidity to enable trading:
async function createAndSeedPool() {
  // 1. Create pool
  const pool = await client.createConcentratedPool({
    assetAAddress: btcAddress,
    assetBAddress: usdbAddress,
    tickSpacing: 60,
    initialPrice: "0.001111",  // ~$90,000 per BTC
    lpFeeRateBps: 300,
    hostFeeRateBps: 100,
    hostNamespace: "my-exchange",
  });

  console.log('Pool created:', pool.poolId);

  // 2. Calculate a range around initial price
  const range = V3TickMath.tickRangeFromPrices({
    priceLower: "80000",   // $80,000
    priceUpper: "100000",  // $100,000
    baseDecimals: 8,
    quoteDecimals: 6,
    tickSpacing: 60,
  });

  // 3. Add initial liquidity
  await client.increaseLiquidity({
    poolId: pool.poolId,
    tickLower: range.tickLower,
    tickUpper: range.tickUpper,
    amountADesired: "100000000",    // 1 BTC
    amountBDesired: "90000000000",  // 90,000 USDB
    amountAMin: "0",
    amountBMin: "0",
  });

  console.log('Initial liquidity added');
  return pool.poolId;
}

Validation Rules

Pool creation will fail if:
  • assetAAddress equals assetBAddress
  • tickSpacing is not 10, 60, or 200
  • initialPrice is empty or invalid
  • hostFeeRateBps is below the host’s minimum (if namespace specified)
  • A pool with the same asset pair and tick spacing already exists

Response

FieldTypeDescription
poolIdstringPool identifier (LP identity public key)
initialTicknumberStarting tick derived from initial price
messagestringSuccess confirmation

Next Steps