V2 Add Liquidity Example
A complete example of adding liquidity to a V2 constant-product pool.Full Code
Copy
Ask AI
import { ethers } from 'ethers';
// === Configuration (Mainnet) ===
const RPC_URL = 'https://evmrpc.kasplex.org';
const V2_FACTORY = '0x4373b7Fcf5059A785843cD224129e01d243Aef71';
const V2_ROUTER = '0xC7ca845B8302346e1C7227f03bb9EFb35ecD51fe';
// === ABIs ===
const ERC20_ABI = [
'function approve(address,uint256) returns (bool)',
'function allowance(address,address) view returns (uint256)',
'function balanceOf(address) view returns (uint256)',
'function symbol() view returns (string)',
'function decimals() view returns (uint8)',
];
const FACTORY_ABI = [
'function getPair(address,address) view returns (address)',
];
const PAIR_ABI = [
'function getReserves() view returns (uint112, uint112, uint32)',
'function token0() view returns (address)',
'function totalSupply() view returns (uint256)',
'function balanceOf(address) view returns (uint256)',
];
const ROUTER_ABI = [
'function addLiquidity(address,address,uint256,uint256,uint256,uint256,address,uint256) returns (uint256,uint256,uint256)',
'function addLiquidityETH(address,uint256,uint256,uint256,address,uint256) payable returns (uint256,uint256,uint256)',
];
/**
* Add liquidity to a V2 pool.
*
* @param signer - Connected ethers Signer
* @param tokenA - First token address
* @param tokenB - Second token address
* @param amountA - Desired amount of tokenA (raw units)
* @param slippage - Slippage tolerance (e.g., 1 for 1%)
*/
async function addLiquidityV2(
signer: ethers.Signer,
tokenA: string,
tokenB: string,
amountA: bigint,
slippage: number = 1
) {
const provider = signer.provider!;
const userAddress = await signer.getAddress();
const deadline = Math.floor(Date.now() / 1000) + 1200;
const slippageFactor = BigInt(Math.floor((100 - slippage) * 100)) ; // e.g., 9900 for 1%
// --- Check if pool exists and calculate amountB ---
const factory = new ethers.Contract(V2_FACTORY, FACTORY_ABI, provider);
const pairAddress = await factory.getPair(tokenA, tokenB);
let amountB: bigint;
if (pairAddress === ethers.ZeroAddress) {
// New pool — user sets the initial price
console.log('Pool does not exist. You are setting the initial price.');
console.log('Enter amountB manually (this example uses 1:1 ratio):');
amountB = amountA; // Change this to your desired ratio
} else {
// Existing pool — calculate proportional amountB
const pair = new ethers.Contract(pairAddress, PAIR_ABI, provider);
const [reserve0, reserve1] = await pair.getReserves();
const token0 = await pair.token0();
const isAToken0 = tokenA.toLowerCase() === token0.toLowerCase();
const reserveA = isAToken0 ? reserve0 : reserve1;
const reserveB = isAToken0 ? reserve1 : reserve0;
amountB = (amountA * reserveB) / reserveA;
console.log(`Pool exists. Calculated amountB: ${amountB}`);
}
// --- Approve both tokens to V2 Router ---
console.log('Approving tokens...');
const tokenAContract = new ethers.Contract(tokenA, ERC20_ABI, signer);
const tokenBContract = new ethers.Contract(tokenB, ERC20_ABI, signer);
const allowanceA = await tokenAContract.allowance(userAddress, V2_ROUTER);
const allowanceB = await tokenBContract.allowance(userAddress, V2_ROUTER);
if (allowanceA < amountA) {
const tx = await tokenAContract.approve(V2_ROUTER, ethers.MaxUint256);
await tx.wait();
}
if (allowanceB < amountB) {
const tx = await tokenBContract.approve(V2_ROUTER, ethers.MaxUint256);
await tx.wait();
}
// --- Add liquidity ---
console.log('Adding liquidity...');
const router = new ethers.Contract(V2_ROUTER, ROUTER_ABI, signer);
const tx = await router.addLiquidity(
tokenA,
tokenB,
amountA,
amountB,
(amountA * slippageFactor) / 10000n,
(amountB * slippageFactor) / 10000n,
userAddress,
deadline
);
const receipt = await tx.wait();
console.log(`Liquidity added. Tx: ${receipt!.hash}`);
// --- Check LP balance ---
const newPairAddress = pairAddress === ethers.ZeroAddress
? await factory.getPair(tokenA, tokenB)
: pairAddress;
const pair = new ethers.Contract(newPairAddress, PAIR_ABI, provider);
const lpBalance = await pair.balanceOf(userAddress);
console.log(`LP token balance: ${lpBalance}`);
}
// === Usage ===
async function main() {
const provider = new ethers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet('YOUR_PRIVATE_KEY', provider);
const TOKEN_A = '0xB190a6A7fC2873f1Abf145279eD664348d5Ef630';
const TOKEN_B = '0x3Ac3B30b7f18AEFD4590D7FE4d9C5944aaeB7220';
await addLiquidityV2(
signer,
TOKEN_A,
TOKEN_B,
ethers.parseEther('10'), // 10 tokens
1 // 1% slippage
);
}
main().catch(console.error);
With Native KAS
To add liquidity with native KAS, useaddLiquidityETH:
Copy
Ask AI
const tx = await router.addLiquidityETH(
tokenAddress, // The ERC-20 token (not WKAS)
amountTokenDesired,
amountTokenMin,
amountKASMin,
userAddress,
deadline,
{ value: amountKASDesired } // Send KAS as value
);
Removing Liquidity
Copy
Ask AI
const REMOVE_ABI = [
'function removeLiquidity(address,address,uint256,uint256,uint256,address,uint256) returns (uint256,uint256)',
];
// Approve LP token to router
const lpToken = new ethers.Contract(pairAddress, ERC20_ABI, signer);
await (await lpToken.approve(V2_ROUTER, lpAmount)).wait();
// Remove
const router = new ethers.Contract(V2_ROUTER, REMOVE_ABI, signer);
const tx = await router.removeLiquidity(
tokenA,
tokenB,
lpAmount,
0, // amountAMin
0, // amountBMin
userAddress,
deadline
);