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

# Provide Liquidity (V3)

> Create, manage, and close V3 concentrated liquidity positions

# Provide Liquidity (V3)

V3 allows LPs to concentrate liquidity within a specific price range for higher capital efficiency. Each position is an NFT with unique parameters.

## Create a Position

### 1. Choose Pool Parameters

* **Token pair**: The two tokens
* **Fee tier**: 0.01%, 0.05%, 0.3%, or 1% (see [Fee Tiers](/concepts/v3/fee-tiers))
* **Price range**: `[tickLower, tickUpper]` (see [Ticks and Ranges](/concepts/v3/ticks-and-ranges))

### 2. Get Current Pool State

```typescript theme={null}
const V3_FACTORY = '0x0dfb1Bb755d872EA1fa4d95E4ad0c2E6317Ce9B9'; // Mainnet
const V3_POSITION_MANAGER = '0x343b244bEDF133D57C61b241557bF29AA32ea4F9';

const FACTORY_ABI = ['function getPool(address,address,uint24) view returns (address)'];
const POOL_ABI = [
  'function slot0() view returns (uint160, int24, uint16, uint16, uint16, uint8, bool)',
  'function tickSpacing() view returns (int24)'
];

const factory = new ethers.Contract(V3_FACTORY, FACTORY_ABI, provider);
const poolAddress = await factory.getPool(token0, token1, 3000);
const pool = new ethers.Contract(poolAddress, POOL_ABI, provider);

const [sqrtPriceX96, currentTick] = await pool.slot0();
const tickSpacing = await pool.tickSpacing();
```

### 3. Calculate Tick Range

Align your desired price range to the pool's tick spacing:

```typescript theme={null}
function nearestUsableTick(tick, tickSpacing) {
  return Math.round(tick / tickSpacing) * tickSpacing;
}

// Example: ±10% around current price
const tickLower = nearestUsableTick(currentTick - 2000, tickSpacing);
const tickUpper = nearestUsableTick(currentTick + 2000, tickSpacing);
```

### 4. Approve Tokens

```typescript theme={null}
await token0Contract.approve(V3_POSITION_MANAGER, amount0Desired);
await token1Contract.approve(V3_POSITION_MANAGER, amount1Desired);
```

### 5. Mint Position

```typescript theme={null}
const PM_ABI = [
  'function mint((address,address,uint24,int24,int24,uint256,uint256,uint256,uint256,address,uint256)) payable returns (uint256,uint128,uint256,uint256)'
];

const pm = new ethers.Contract(V3_POSITION_MANAGER, PM_ABI, signer);
const deadline = Math.floor(Date.now() / 1000) + 1200;

const tx = await pm.mint({
  token0,
  token1,
  fee: 3000,
  tickLower,
  tickUpper,
  amount0Desired,
  amount1Desired,
  amount0Min: amount0Desired * 99n / 100n,
  amount1Min: amount1Desired * 99n / 100n,
  recipient: userAddress,
  deadline
});

const receipt = await tx.wait();
// Parse the tokenId from the receipt events
```

<Note>
  `token0` must have a lower address than `token1`. Sort them before calling.
</Note>

### With Native KAS

If one token is KAS, send it as `value` instead of approving WKAS:

```typescript theme={null}
const tx = await pm.mint(params, { value: kasAmount });
```

## Increase Liquidity

Add more tokens to an existing position (same tick range):

```typescript theme={null}
const tx = await pm.increaseLiquidity({
  tokenId,
  amount0Desired,
  amount1Desired,
  amount0Min: 0,
  amount1Min: 0,
  deadline
});
```

## Collect Fees

Claim accumulated trading fees:

```typescript theme={null}
const tx = await pm.collect({
  tokenId,
  recipient: userAddress,
  amount0Max: ethers.MaxUint128,
  amount1Max: ethers.MaxUint128
});
```

## Remove Liquidity

### 1. Decrease Liquidity

```typescript theme={null}
const tx = await pm.decreaseLiquidity({
  tokenId,
  liquidity: liquidityToRemove,  // Use full liquidity for complete removal
  amount0Min: 0,
  amount1Min: 0,
  deadline
});
```

### 2. Collect the Tokens

After decreasing, tokens are held by the Position Manager. Call `collect()` to withdraw:

```typescript theme={null}
const tx = await pm.collect({
  tokenId,
  recipient: userAddress,
  amount0Max: ethers.MaxUint128,
  amount1Max: ethers.MaxUint128
});
```

### 3. Burn the NFT (Optional)

After removing all liquidity and collecting all tokens/fees:

```typescript theme={null}
const tx = await pm.burn(tokenId);
```

## Single-Sided Positions

You can create positions entirely above or below the current price:

| Range                                           | Deposit     | Behavior                   |
| ----------------------------------------------- | ----------- | -------------------------- |
| Above current price (`tickLower > currentTick`) | Token0 only | Acts as a limit sell order |
| Below current price (`tickUpper < currentTick`) | Token1 only | Acts as a limit buy order  |

```typescript theme={null}
// Limit sell: provide only token0, range above current price
const tx = await pm.mint({
  token0,
  token1,
  fee: 3000,
  tickLower: currentTick + 60,    // Above current
  tickUpper: currentTick + 600,
  amount0Desired: sellAmount,
  amount1Desired: 0,              // No token1 needed
  amount0Min: 0,
  amount1Min: 0,
  recipient: userAddress,
  deadline
});
```

## Key Points

* Each position is an NFT — track `tokenId` for future operations
* Fees must be explicitly collected (they don't auto-compound)
* Out-of-range positions earn no fees until price re-enters the range
* Narrower ranges = higher capital efficiency but more active management needed
* Use `multicall()` on the Position Manager to batch decrease + collect in one transaction
