Skip to main content

Provide Liquidity (V2)

V2 pools use a simple constant-product model. LPs deposit both tokens in the current reserve ratio and receive fungible LP tokens representing their share.

Add Liquidity

1. Approve Tokens

Approve both tokens to the V2 Router:
const V2_ROUTER = '0xC7ca845B8302346e1C7227f03bb9EFb35ecD51fe'; // Mainnet

await tokenA.approve(V2_ROUTER, amountA);
await tokenB.approve(V2_ROUTER, amountB);

2. Call addLiquidity

const V2_ROUTER_ABI = [
  'function addLiquidity(address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity)',
  'function addLiquidityETH(address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline) payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity)'
];

const router = new ethers.Contract(V2_ROUTER, V2_ROUTER_ABI, signer);
const deadline = Math.floor(Date.now() / 1000) + 1200; // 20 minutes

// ERC-20 / ERC-20 pair
const tx = await router.addLiquidity(
  tokenA.target,
  tokenB.target,
  amountADesired,
  amountBDesired,
  amountADesired * 99n / 100n,  // 1% slippage
  amountBDesired * 99n / 100n,
  userAddress,
  deadline
);

3. With Native KAS

Use addLiquidityETH and send KAS as value:
const tx = await router.addLiquidityETH(
  tokenAddress,
  amountTokenDesired,
  amountTokenDesired * 99n / 100n,
  amountKASDesired * 99n / 100n,
  userAddress,
  deadline,
  { value: amountKASDesired }
);

Remove Liquidity

1. Approve LP Token

const lpToken = new ethers.Contract(pairAddress, ERC20_ABI, signer);
await lpToken.approve(V2_ROUTER, lpAmount);

2. Call removeLiquidity

const REMOVE_ABI = [
  'function removeLiquidity(address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) returns (uint256 amountA, uint256 amountB)',
  'function removeLiquidityETH(address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline) returns (uint256 amountToken, uint256 amountETH)'
];

const router = new ethers.Contract(V2_ROUTER, REMOVE_ABI, signer);

// ERC-20 / ERC-20 pair
const tx = await router.removeLiquidity(
  tokenA,
  tokenB,
  lpAmount,
  0,  // amountAMin (set to 0 for simplicity; use slippage in production)
  0,  // amountBMin
  userAddress,
  deadline
);

3. With Native KAS

Use removeLiquidityETH to receive KAS instead of WKAS:
const tx = await router.removeLiquidityETH(
  tokenAddress,
  lpAmount,
  0,
  0,
  userAddress,
  deadline
);

Calculating Amounts

To add liquidity at the current price ratio, first query the pool reserves:
const PAIR_ABI = [
  'function getReserves() view returns (uint112, uint112, uint32)',
  'function token0() view returns (address)'
];

const pair = new ethers.Contract(pairAddress, PAIR_ABI, provider);
const [reserve0, reserve1] = await pair.getReserves();
const token0 = await pair.token0();

// If you want to deposit amountA of tokenA:
const isToken0 = tokenA.toLowerCase() === token0.toLowerCase();
const amountB = isToken0
  ? (amountA * reserve1) / reserve0
  : (amountA * reserve0) / reserve1;

Key Points

  • First LP sets the initial price — deposit tokens at the desired ratio
  • Subsequent LPs must match the current reserve ratio
  • LP tokens are ERC-20 and can be transferred or used in other protocols
  • Fees (0.3%) auto-compound into the pool — no claiming needed
  • Subject to impermanent loss