V2 添加流动性示例
一个向 V2 恒定乘积池添加流动性的完整示例。完整代码
复制
询问AI
import { ethers } from 'ethers';
// === 配置(主网)===
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)',
];
/**
* 向 V2 池添加流动性。
*
* @param signer - 已连接的 ethers Signer
* @param tokenA - 第一个代币地址
* @param tokenB - 第二个代币地址
* @param amountA - 期望的 tokenA 数量(原始单位)
* @param slippage - 滑点容差(例如 1 表示 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)) ; // 例如 1% 时为 9900
// --- 检查池是否存在并计算 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) {
// 新池 — 用户设定初始价格
console.log('池不存在。你正在设定初始价格。');
console.log('请手动输入 amountB(此示例使用 1:1 比率):');
amountB = amountA; // 修改为你期望的比率
} else {
// 已有池 — 按比例计算 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(`池已存在。计算得出的 amountB:${amountB}`);
}
// --- 授权两个代币给 V2 Router ---
console.log('正在授权代币...');
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();
}
// --- 添加流动性 ---
console.log('正在添加流动性...');
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(`流动性已添加。交易哈希:${receipt!.hash}`);
// --- 检查 LP 余额 ---
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 代币余额:${lpBalance}`);
}
// === 使用示例 ===
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 个代币
1 // 1% 滑点
);
}
main().catch(console.error);
使用原生 KAS
要使用原生 KAS 添加流动性,请使用addLiquidityETH:
复制
询问AI
const tx = await router.addLiquidityETH(
tokenAddress, // ERC-20 代币(不是 WKAS)
amountTokenDesired,
amountTokenMin,
amountKASMin,
userAddress,
deadline,
{ value: amountKASDesired } // 将 KAS 作为 value 发送
);
移除流动性
复制
询问AI
const REMOVE_ABI = [
'function removeLiquidity(address,address,uint256,uint256,uint256,address,uint256) returns (uint256,uint256)',
];
// 授权 LP 代币给 router
const lpToken = new ethers.Contract(pairAddress, ERC20_ABI, signer);
await (await lpToken.approve(V2_ROUTER, lpAmount)).wait();
// 移除
const router = new ethers.Contract(V2_ROUTER, REMOVE_ABI, signer);
const tx = await router.removeLiquidity(
tokenA,
tokenB,
lpAmount,
0, // amountAMin
0, // amountBMin
userAddress,
deadline
);