> ## Documentation Index
> Fetch the complete documentation index at: https://build.flashnet.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Concentrated Liquidity

> V3 liquidity within custom price ranges for higher capital efficiency

Concentrated liquidity (V3) allows you to provide liquidity within a specific price range instead of across all prices. Capital concentrated in active trading ranges earns more fees per dollar deployed.

## V3 and V2 Pools

V3 pools complement the existing V2 constant product and bonding curve pools. Both pool types coexist:

* **V2 Constant Product**: Liquidity spread across all prices, fungible LP tokens, simpler management
* **V2 Bonding Curve**: Single-sided pools that bond to constant product
* **V3 Concentrated**: Custom price ranges, non-fungible positions, higher capital efficiency

All pool types appear in `listPools()`. V3 pools have `curveType: "V3_CONCENTRATED"` and include additional fields like `currentTick`, `tickSpacing`, and `totalLiquidity`.

```typescript theme={null}
const pools = await client.listPools();

for (const pool of pools.pools) {
  if (pool.curveType === 'V3_CONCENTRATED') {
    console.log('V3 pool:', pool.lpPublicKey);
    console.log('  Current tick:', pool.currentTick);
    console.log('  Tick spacing:', pool.tickSpacing);
    console.log('  Positions:', pool.positionCount);
  } else {
    console.log('V2 pool:', pool.lpPublicKey, pool.curveType);
  }
}
```

V3 pools also have dedicated endpoints for detailed data: `getPoolLiquidity()` for depth chart visualization and `getPoolTicks()` for swap simulation.

## How V3 Works

In V2 constant product pools, liquidity spreads from zero to infinity. Most of it sits idle outside the trading range. V3 pools let you choose where your capital works.

The math uses **ticks** to represent prices. Each tick is a 0.01% price change from the previous tick:

```
price = 1.0001^tick
```

Your position is defined by a lower tick and upper tick. When the current price is within your range, your liquidity earns fees. When price moves outside, your position becomes single-sided and stops earning.

## Quick Comparison

| Aspect             | V2 Pools           | V3 Pools               |
| ------------------ | ------------------ | ---------------------- |
| Liquidity Range    | 0 to infinity      | Custom price range     |
| Capital Efficiency | \~50% active       | Up to 4000x better     |
| Position Type      | Fungible LP tokens | Non-fungible positions |
| Fee Tracking       | Global per pool    | Per-position           |

## Getting Started

### Create a Position

```typescript theme={null}
async function createPosition() {
  const response = await client.increaseLiquidity({
    poolId: "pool-public-key",
    tickLower: -23040,           // Lower price bound
    tickUpper: -22980,           // Upper price bound
    amountADesired: "100000000", // 1 BTC in satoshis
    amountBDesired: "100000000", // 100,000 USDB in base units
    amountAMin: "99000000",      // 1% slippage protection
    amountBMin: "99000000",
  });

  console.log('Liquidity added:', response.liquidityAdded);
  console.log('Asset A used:', response.amountAUsed);
  console.log('Asset B used:', response.amountBUsed);
}
```

### Convert Prices to Ticks

The SDK provides utilities to convert between human-readable prices and ticks:

```typescript theme={null}
import { V3TickMath } from '@flashnet/sdk';

// Convert a human price like "$90,000 per BTC" to a tick
const tick = V3TickMath.humanPriceToTick({
  humanPrice: "90000",
  baseDecimals: 8,   // BTC (base asset)
  quoteDecimals: 6,  // USDB (quote asset)
  tickSpacing: 60,
});

// Get a range around a target price
const range = V3TickMath.tickRangeFromPrices({
  priceLower: "85000",
  priceUpper: "95000",
  baseDecimals: 8,
  quoteDecimals: 6,
  tickSpacing: 60,
});

console.log('Tick range:', range.tickLower, 'to', range.tickUpper);
```

## Core Operations

### Add Liquidity

Creates a new position or adds to an existing one at the same tick range:

```typescript theme={null}
const response = await client.increaseLiquidity({
  poolId: "pool-public-key",
  tickLower: -23040,
  tickUpper: -22980,
  amountADesired: "100000000",
  amountBDesired: "100000000",
  amountAMin: "99000000",
  amountBMin: "99000000",
});
```

### Remove Liquidity

Withdraws liquidity and collects accumulated fees:

```typescript theme={null}
const response = await client.decreaseLiquidity({
  poolId: "pool-public-key",
  tickLower: -23040,
  tickUpper: -22980,
  liquidityToRemove: "123456789",  // V3 liquidity units, or "0" for all
  amountAMin: "0",
  amountBMin: "0",
});

console.log('Principal returned:', response.amountA, response.amountB);
console.log('Fees collected:', response.feesCollectedA, response.feesCollectedB);
```

### Collect Fees

Claim earned fees without removing liquidity:

```typescript theme={null}
const response = await client.collectFees({
  poolId: "pool-public-key",
  tickLower: -23040,
  tickUpper: -22980,
});

console.log('Fees collected:', response.feesCollectedA, response.feesCollectedB);
```

### Rebalance Position

Move your liquidity to a new price range in a single atomic operation:

```typescript theme={null}
const response = await client.rebalancePosition({
  poolId: "pool-public-key",
  oldTickLower: -23040,
  oldTickUpper: -22980,
  newTickLower: -23100,
  newTickUpper: -22920,
  liquidityToMove: "0",  // 0 = move all
});

console.log('Old liquidity:', response.oldLiquidity);
console.log('New liquidity:', response.newLiquidity);
console.log('Fees collected:', response.feesCollectedA, response.feesCollectedB);
```

## Pool Data

### Get Liquidity Distribution

For building depth charts and visualizations:

```typescript theme={null}
const liquidity = await client.getPoolLiquidity("pool-public-key");

console.log('Current price:', liquidity.currentPrice);
console.log('Active liquidity:', liquidity.activeLiquidity);

// Iterate through liquidity ranges
for (const range of liquidity.ranges) {
  console.log(`${range.priceLower} - ${range.priceUpper}: ${range.liquidity}`);
  console.log(`  Status: ${range.status}`);  // "in_range", "below_price", or "above_price"
}
```

### Get Pool Ticks

For swap simulation and slippage calculation:

```typescript theme={null}
const ticks = await client.getPoolTicks("pool-public-key");

console.log('Current tick:', ticks.currentTick);
console.log('Current liquidity:', ticks.currentLiquidity);
console.log('LP fee:', ticks.lpFeeBps, 'bps');

// Use ticks for simulation
for (const tick of ticks.ticks) {
  console.log(`Tick ${tick.tick}: net=${tick.liquidityNet}`);
}
```

## Tick Spacing

Pools have a tick spacing that determines valid tick boundaries. Common values:

| Tick Spacing | Price Step | Use Case                   |
| ------------ | ---------- | -------------------------- |
| 10           | 0.1%       | Stable pairs, tight ranges |
| 60           | 0.6%       | Most trading pairs         |
| 200          | 2%         | Volatile pairs             |

Ticks must be divisible by the pool's tick spacing:

```typescript theme={null}
// Round a tick to the nearest valid boundary
const validTick = V3TickMath.roundTick(arbitraryTick, tickSpacing);

// Round down (conservative for lower bounds)
const lower = V3TickMath.roundTickDown(tick, tickSpacing);

// Round up (conservative for upper bounds)
const upper = V3TickMath.roundTickUp(tick, tickSpacing);

// Raw price/tick conversions (without decimal adjustment)
const rawTick = V3TickMath.priceToTick(poolPrice);
const rawPrice = V3TickMath.tickToPrice(tick);

// Tick bounds
console.log('Valid tick range:', V3TickMath.MIN_TICK, 'to', V3TickMath.MAX_TICK);
```

## Position States

Your position's behavior depends on where the current price sits relative to your range:

| Price Location    | Asset A | Asset B | Earning Fees |
| ----------------- | ------- | ------- | ------------ |
| Below your range  | 100%    | 0%      | No           |
| Within your range | Mixed   | Mixed   | Yes          |
| Above your range  | 0%      | 100%    | No           |

When price moves outside your range, your position converts entirely to one asset and stops earning fees until price returns.

## Error Handling

```typescript theme={null}
import { isFlashnetError } from '@flashnet/sdk';

try {
  await client.increaseLiquidity({ ... });
} catch (error) {
  if (isFlashnetError(error)) {
    console.log('Error:', error.errorCode);
    console.log('Message:', error.userMessage);

    // Check for fund recovery
    if (error.wasClawbackAttempted()) {
      const summary = error.clawbackSummary!;
      console.log(`Recovered: ${summary.successCount}/${summary.totalTransfers}`);
    }
  }
  throw error;
}
```

## Next Steps

* [Managing Positions](/products/flashnet-amm/concentrated-positions) - List, monitor, and optimize your positions
* [Free Balances](/products/flashnet-amm/concentrated-balances) - Reduce transaction overhead for frequent operations
* [Create Pools](/products/flashnet-amm/concentrated-creation) - Launch new V3 pools
