You can create both single-sided and double-sided pools with the Flashnet SDK. The SDK handles all the complexity of intent generation, signing, and multi-step processes automatically.

Pool Types

Single-Sided Pools (Bonding Curve)

  • Goal: Gradually discover price as tokens sell, then “graduate” into a normal constant-product pool.
  • Mechanism: Single-sided pools are modeled using virtual reserves that move on a constant‑product curve. The creator deposits an initial supply of Asset A; price evolves as virtual reserves update with sales.
  • Graduation: Once a specified percentage of Asset A is sold, the accumulated Asset B and remaining Asset A are used to seed a new constant product pool.
  • Target-Based Creation: The SDK provides a helper to compute virtual reserves from your initial supply, graduation threshold, and target Asset B raised at graduation.

Double-Sided Pools (Constant Product)

  • Require initial deposits of both assets
  • Use actual reserves for pricing
  • Follow the constant product (x*y=k) formula
  • Better for liquid markets
In the Flashnet Stack, Bitcoin is always denominated as satoshis and bears the public key 020202020202020202020202020202020202020202020202020202020202020202.

Prerequisites

Before creating pools, make sure you have set up the Flashnet SDK client. This guide assumes you have already initialized the client instance, and that you have a wallet with either some BTC and LRC20 tokens, or two different LRC20 tokens. In our below examples we will use BTC and USDB, where BTC is denominated in sats (8 decimals) and USDB has 6 decimals.

Creating a Constant Product Pool

// Create a constant product pool with initial liquidity
async function createConstantProductPool() {
  try {
    const pool = await client.createConstantProductPool({
      assetAAddress: "bitcoin-pubkey",
      assetBAddress: "usdb-pubkey",
      lpFeeRateBps: 30,              // 0.3% LP fee
      totalHostFeeRateBps: 20,        // 0.2% host fee
      hostNamespace: "my-namespace", // Optional: for host attribution
      initialLiquidity: {             // Optional: add initial liquidity
        assetAAmount: 100000000n,     // 1 BTC
        assetBAmount: 100000000000n,  // 100,000 USDB
        assetAMinAmountIn: 100000000n,     // Min Asset A to deposit
        assetBMinAmountIn: 100000000000n,  // Min Asset B to deposit
      }
    });

    console.log('Pool created:', pool);
    console.log('Pool ID:', pool.poolId);
  } catch (error) {
    console.error('Failed to create pool:', error);
  }
}

// Create a pool without initial liquidity
async function createEmptyPool() {
  const pool = await client.createConstantProductPool({
    assetAAddress: "bitcoin-pubkey",
    assetBAddress: "usdb-pubkey",
    lpFeeRateBps: 30,
    totalHostFeeRateBps: 20,
  });

  console.log('Empty pool created. Add liquidity separately.');
}

Creating a Single-Sided Pool

The SDK automatically handles the deposit and confirmation process:
async function createSingleSidedPool() {
  try {
    // The SDK will automatically:
    // 1. Create the pool with a virtual-reserve price mechanism.
    // 2. Transfer the initial reserve of Asset A.
    // 3. Confirm the deposit.
    // 4. Graduate to a constant product pool when the threshold is met.

    // Compute virtual reserves from goals
    const { virtualReserveA, virtualReserveB, threshold } = FlashnetClient.calculateVirtualReserves({
      initialTokenSupply: "1000000000000", // 1,000,000 USDB initial supply
      graduationThresholdPct: 80,          // Graduate when 80% is sold (<= 95)
      targetRaise: "100000000",           // Target to raise 1 BTC (in sats)
    });

    const pool = await client.createSingleSidedPool({
      assetAAddress: "usdb-pubkey",      // The token being sold
      assetBAddress: "bitcoin-pubkey",   // The token being raised
      assetAInitialReserve: "1000000000000",
      virtualReserveA: virtualReserveA.toString(),
      virtualReserveB: virtualReserveB.toString(),
      threshold: threshold.toString(),    // Graduation threshold percentage
      lpFeeRateBps: 30,                   // 0.3% LP fee
      totalHostFeeRateBps: 50,            // 0.5% host fee
      hostNamespace: "my-exchange"       // Optional: for host attribution
    });

    console.log('Single-sided pool created:', pool);
    console.log('Pool ID:', pool.poolId);
  } catch (error) {
    console.error('Failed to create pool:', error);
  }
}

Fee Configuration

Fee Constraints

  • LP Fee: Trading fee that goes to liquidity providers (typically 0.1-1%)
  • Host Fee: Additional fee for the host/integrator (0-1%)
  • Total Fee: Sum of LP and host fees charged to traders
Important: If creating a pool with a hostNamespace, the totalHostFeeRateBps must be greater than or equal to the host’s registered minFeeBps.
  • If you do NOT provide a hostNamespace, the minimum totalHostFeeRateBps is 10 BPS.
For single-sided pools, the graduation threshold (percentage sold) is capped at 95% by the underlying AMM math.
// Check host requirements before creating pool
async function createPoolWithHost(hostNamespace: string) {
  // Get host info to check minimum fee
  const hostInfo = await client.getHost(hostNamespace);
  console.log(`Host ${hostNamespace} requires minimum ${hostInfo.minFeeBps} BPS`);

  // Create pool meeting host requirements
  const pool = await client.createConstantProductPool({
    assetAAddress: "bitcoin-pubkey",
    assetBAddress: "usdb-pubkey",
    lpFeeRateBps: 30,
    totalHostFeeRateBps: Math.max(50, hostInfo.minFeeBps), // Meet minimum
    hostNamespace: hostNamespace,
  });

  console.log('Pool created with host:', pool);
}

Complete Example: Pool Creation and Management

async function completePoolFlow() {
  // 1. Create a pool
  const pool = await client.createConstantProductPool({
    assetAAddress: "bitcoin-pubkey",
    assetBAddress: "usdb-pubkey",
    lpFeeRateBps: 30,
    totalHostFeeRateBps: 20,
    initialLiquidity: {
      assetAAmount: 100000000n,           // 1 BTC
      assetBAmount: 100000000000n,        // 100,000 USDB
      assetAMinAmountIn: 100000000n,      // Min Asset A to deposit
      assetBMinAmountIn: 100000000000n,   // Min Asset B to deposit
    }
  });

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

  // 2. Get pool details
  const poolInfo = await client.getPool(poolId);
    console.log('Pool info:', poolInfo);

  // 3. Check your LP position
  const position = await client.getLpPosition(poolId);
  console.log('Your LP tokens:', position.lpTokensOwned);
  console.log('Your share:', position.sharePercentage, '%');

  // 4. Add more liquidity
  await client.addLiquidity({
    poolId: poolId,
    assetAAmount: "50000000",            // 0.5 BTC
    assetBAmount: "50000000000",         // 50,000 USDB
    assetAMinAmountIn: "50000000",       // set your acceptable minimums
    assetBMinAmountIn: "50000000000",
  });

  console.log('Additional liquidity added');

  // 5. List all pools (with filters)
  const pools = await client.listPools({
    assetAAddress: "bitcoin-pubkey",
    assetBAddress: "usdb-pubkey",
    curveTypes: ["CONSTANT_PRODUCT"],
  });

  console.log('Active BTC pools:', pools.pools.length);
}

Error Handling

async function createPoolWithErrorHandling() {
  try {
    const pool = await client.createSingleSidedPool({
      assetAAddress: "usdb-pubkey",
      assetBAddress: "bitcoin-pubkey",
      assetAInitialReserve: "1000000000000",
      assetAPctSoldAtGraduation: 80,
      targetBRaisedAtGraduation: "100000000",
      lpFeeRateBps: 30,
      totalHostFeeRateBps: 50,
    });
    
    console.log('Success:', pool);
  } catch (error) {
    if (error.message.includes('Insufficient balance')) {
      console.error('Not enough funds to create pool');
    } else if (error.message.includes('initial deposit failed')) {
      console.error('Pool created but deposit failed:', error.message);
      // The pool ID is included in the error message
    } else {
      console.error('Pool creation failed:', error);
    }
  }
}

Next Steps

After creating a pool, you can:
  1. Execute swaps against your pool
  2. Add more liquidity to earn LP fees
  3. Remove liquidity when needed