> ## 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.

# Liquidity

> Add and remove liquidity from constant product pools

The add and remove liquidity functions enable users to earn LP fees by providing liquidity to pools.

### Pool Types

Users can only add liquidity to constant product pools.

Single-sided pools only receive an initial deposit of asset A, at which point they trade against a bonding curve until they bond.

Constant product pools require an initial deposit of both assets.

### Simulate Before Adding

Adding liquidity has to be done in the right ratio to maintain the pool's price. Always simulate to check expected LP tokens and potential refunds:

```typescript theme={null}
async function simulateAddLiquidity() {
  try {
    const simulation = await client.simulateAddLiquidity({
      poolId: "pool-public-key",
      assetAAmount: "100000000",    // 1 BTC
      assetBAmount: "100000000000", // 100,000 USDB
    });

    console.log('LP tokens to mint:', simulation.lpTokensToMint);
    console.log('Pool share %:', simulation.poolSharePercentage);
    console.log('Asset A refund:', simulation.assetARefundAmount);
    console.log('Asset B refund:', simulation.assetBRefundAmount);
    
    return simulation;
  } catch (error) {
    console.error('Simulation failed:', error);
  }
}
```

If you add liquidity in the wrong ratio, you will be atomically refunded the excess asset.

### Execute Liquidity Addition

```typescript theme={null}
async function addLiquidity() {
  try {
    // First, check the pool to understand the current ratio
    const pool = await client.getPool("pool-id");
    console.log('Current pool ratio:', pool.assetAReserve, ':', pool.assetBReserve);

    // Add liquidity maintaining the pool ratio
    const response = await client.addLiquidity({
      poolId: "pool-id",
      assetAAmount: "100000000",          // 1 BTC
      assetBAmount: "100000000000",       // 100,000 USDB
      assetAMinAmountIn: "100000000",     // set minimums to protect from price moves
      assetBMinAmountIn: "100000000000",
    });

    console.log('Liquidity added successfully!');
    console.log('LP tokens received:', response.lpTokensMinted);
    
    // Check if any assets were refunded
    if (response.refund) {
      console.log('Asset A refunded:', response.refund.assetAAmount);
      console.log('Asset B refunded:', response.refund.assetBAmount);
    }
    
    return response;
  } catch (error) {
    console.error('Failed to add liquidity:', error);
  }
}
```

### Optimal Amount Calculation

Avoid refunds by calculating optimal amounts:

```typescript theme={null}
async function addLiquidityOptimal(poolId: string, assetADesired: bigint) {
  // Get current pool state
  const pool = await client.getPool(poolId);
  
  // Calculate optimal asset B amount based on current ratio
  const optimalAssetB = (assetADesired * BigInt(pool.assetBReserve)) / BigInt(pool.assetAReserve);
  
  console.log('Optimal amounts:', {
    assetA: assetADesired,
    assetB: optimalAssetB
  });
  
  // Add liquidity with optimal amounts
  const response = await client.addLiquidity({
    poolId: poolId,
    assetAAmount: assetADesired.toString(),
    assetBAmount: optimalAssetB.toString(),
    assetAMinAmountIn: assetADesired.toString(),
    assetBMinAmountIn: optimalAssetB.toString(),
  });
  
  console.log('Added liquidity with no refunds');
  return response;
}
```

## Tracking Your Position

### Monitor LP Holdings

```typescript theme={null}
async function trackPosition(poolId: string) {
  // Get your current position
  const position = await client.getLpPosition(poolId);
  
  console.log('LP tokens owned:', position.lpTokensOwned);
  console.log('Pool share:', position.sharePercentage, '%');
  console.log('Current value - Asset A:', position.valueAssetA);
  console.log('Current value - Asset B:', position.valueAssetB);
  
  // Check principal amounts if available
  if (position.principalAssetA !== undefined && position.principalAssetB !== undefined) {
    console.log('Initial deposit - Asset A:', position.principalAssetA);
    console.log('Initial deposit - Asset B:', position.principalAssetB);
  }
  
  // Check unrealized P&L if available
  if (position.unrealizedProfitLossAssetA && position.unrealizedProfitLossAssetB) {
    console.log('Unrealized P&L - Asset A:', position.unrealizedProfitLossAssetA);
    console.log('Unrealized P&L - Asset B:', position.unrealizedProfitLossAssetB);
  }
  
  return position;
}
```

### Calculate Returns

Track your investment performance:

```typescript theme={null}
async function calculateReturns(poolId: string) {
  const position = await client.getLpPosition(poolId);
  
  // If principal amounts are available, calculate returns
  if (position.principalAssetA !== undefined && position.principalAssetB !== undefined) {
    const returnsA = position.valueAssetA - position.principalAssetA;
    const returnsB = position.valueAssetB - position.principalAssetB;
    
    // Calculate percentage returns
    const percentReturnA = position.principalAssetA > 0 
      ? (returnsA / position.principalAssetA) * 100 
      : 0;
    const percentReturnB = position.principalAssetB > 0 
      ? (returnsB / position.principalAssetB) * 100 
      : 0;
    
    console.log('Returns:');
    console.log(`Asset A: ${returnsA} (${percentReturnA.toFixed(2)}%)`);
    console.log(`Asset B: ${returnsB} (${percentReturnB.toFixed(2)}%)`);
  }
  
  // The API provides unrealized P&L directly
  if (position.unrealizedProfitLossAssetA && position.unrealizedProfitLossAssetB) {
    console.log('Unrealized P&L from API:');
    console.log(`Asset A: ${position.unrealizedProfitLossAssetA}`);
    console.log(`Asset B: ${position.unrealizedProfitLossAssetB}`);
  }
  
  // Check for impermanent loss
  const pool = await client.getPool(poolId);
  const currentPrice = Number(pool.reserveB) / Number(pool.reserveA);
  
  if (position.principalAssetA && position.principalAssetB) {
    const initialPrice = position.principalAssetB / position.principalAssetA;
    const priceChange = ((currentPrice - initialPrice) / initialPrice) * 100;
    console.log(`Price change since deposit: ${priceChange.toFixed(2)}%`);
  }
}
```

## Removing Liquidity

### Simulate Removal

Check what you'll receive before removing:

```typescript theme={null}
async function simulateRemoveLiquidity(poolId: string, lpTokensToRemove: string) {
  try {
    const simulation = await client.simulateRemoveLiquidity({
      poolId: poolId,
      providerPublicKey: (await client.wallet.getIdentityPublicKey()),
      lpTokensToRemove: lpTokensToRemove,
    });
    
    console.log('Expected asset A:', simulation.assetAAmount);
    console.log('Expected asset B:', simulation.assetBAmount);
    
    return simulation;
  } catch (error) {
    console.error('Simulation failed:', error);
  }
}
```

### Execute Removal

Remove liquidity and claim accumulated fees:

```typescript theme={null}
async function removeLiquidity(poolId: string, lpTokensToRemove: string) {
  try {
    // Check your LP balance first
    const position = await client.getLpPosition(poolId);
    const lpTokensOwned = BigInt(position.lpTokensOwned);
    const tokensToRemove = BigInt(lpTokensToRemove);
    
    if (lpTokensOwned < tokensToRemove) {
      throw new Error(`Insufficient LP tokens. Owned: ${lpTokensOwned}, Requested: ${tokensToRemove}`);
    }
    
    // Remove liquidity
    const response = await client.removeLiquidity({
      poolId: poolId,
      lpTokensToRemove: lpTokensToRemove,
    });
    
    console.log('Liquidity removed successfully!');
    console.log('Asset A received:', response.assetAWithdrawn);
    console.log('Asset B received:', response.assetBWithdrawn);
    console.log('Transfers:', response.assetATransferId, response.assetBTransferId);
    
    return response;
  } catch (error) {
    console.error('Failed to remove liquidity:', error);
  }
}
```

### Partial vs Full Removal

```typescript theme={null}
// Remove all liquidity
async function removeAllLiquidity(poolId: string) {
  const position = await client.getLpPosition(poolId);
  return await removeLiquidity(poolId, position.lpTokensOwned);
}

// Remove partial liquidity (e.g., 50%)
async function removePartialLiquidity(poolId: string, percentage: number) {
  const position = await client.getLpPosition(poolId);
  const lpTokensToRemove = (BigInt(position.lpTokensOwned) * BigInt(percentage)) / 100n;
  return await removeLiquidity(poolId, lpTokensToRemove.toString());
}
```

## Complete Liquidity Lifecycle

```typescript theme={null}
async function completeLiquidityLifecycle() {
  const poolId = "pool-id";
  
  // 1. Add liquidity
  console.log('=== Adding Liquidity ===');
  const addResponse = await client.addLiquidity({
    poolId: poolId,
    assetAAmount: 100000000n,    // 1 BTC
    assetBAmount: 100000000000n, // 100,000 USDB
  });
  
  const initialInvestment = {
    assetA: 100000000n,
    assetB: 100000000000n,
  };
  
  // 2. Monitor position over time
  console.log('\n=== Monitoring Position ===');
  await new Promise(resolve => setTimeout(resolve, 60000)); // Wait 1 minute
  await trackPosition(poolId);
  
  // 3. Calculate returns
  console.log('\n=== Calculating Returns ===');
  await calculateReturns(poolId);
  
  // 4. Remove 50% of liquidity
  console.log('\n=== Removing 50% Liquidity ===');
  await removePartialLiquidity(poolId, 50);
  
  // 5. Check remaining position
  console.log('\n=== Remaining Position ===');
  await trackPosition(poolId);
}
```

## Error Handling

The SDK provides typed errors with automatic fund recovery. See [Error Handling](/products/flashnet-amm/errors) for the complete guide.

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

async function safeAddLiquidity(poolId: string, assetAAmount: string, assetBAmount: string) {
  try {
    return await client.addLiquidity({
      poolId,
      assetAAmount,
      assetBAmount,
      assetAMinAmountIn: "0",
      assetBMinAmountIn: "0",
    });
  } catch (error) {
    if (isFlashnetError(error)) {
      console.log('Error:', error.errorCode, '-', error.userMessage);
      
      // Check fund recovery (add liquidity sends 2 transfers)
      if (error.wasClawbackAttempted()) {
        const summary = error.clawbackSummary!;
        console.log(`Recovery: ${summary.successCount}/${summary.totalTransfers} transfers`);
        
        if (error.wereAllTransfersRecovered()) {
          console.log('✅ All funds recovered');
        } else {
          console.log('⚠️ Unrecovered:', summary.unrecoveredTransferIds);
        }
      } else if (error.willAutoRefund()) {
        console.log('Funds will be auto-refunded');
      }
      
      if (error.isRetryable) {
        console.log('This error is retryable');
      }
    }
    throw error;
  }
}
```

## Best Practices

1. **Always simulate first**: Check expected outcomes before executing
2. **Track impermanent loss**: Understand the risks of providing liquidity
3. **Regular monitoring**: Check your position and fees periodically

## Next Steps

* [Create new pools](/products/flashnet-amm/creation) to earn from the start
* [Execute swaps](/products/flashnet-amm/swaps) to generate fees
* [Become a host](/products/flashnet-amm/hosts) to earn additional fees
* [Lock a position](/products/flashnet-amm/lock-overview) to commit liquidity without transferring it
