快速开始
入门指南
本指南将帮助您快速了解如何使用 JAMM DEX 进行代币交换和流动性管理。
前提条件
在开始之前,请确保您具备以下条件:
JuChain 钱包:支持 JuChain 网络的 Web3 钱包
JU 代币:用于支付交易费用的原生代币
测试代币:用于交换的 ERC-20 代币
合约地址
主网地址
JAMMFactory:
0x6b5d54E6F73e96Ca960DBA71D778b98221939aa6
JAMMRouter:
0x3F26fb54C28Eab026e908A9A9357a456F3c8Dc87
WJU:
0x4d1B49B424afd7075d3c063adDf97D5575E1c7E2
USDT:
0xc8e19C19479a866142B42fB390F2ea1Ff082E0D2
ETH:
0x80077F108Dd73B709C43A1a13F0EEF25e48f7D0e
BNB:
0x151b6F646Ac02Ed9877884ed9637A84f2FD8FaA6
测试网地址
JAMMFactory:
0xbddd716a9d6325700d1c29562c819987e5b1f6a8
JAMMRouter:
0x3f8b0038023393009712D0051D192a8825dd02B9
WJU:
0xb8cdb16bc2b64d42638e285a691973ff10078d8e
您的第一次代币交换
1. 设置
首先,您需要连接到 JuChain 网络并准备用于交换的代币。
// 连接到 JuChain 网络
const provider = new ethers.providers.JsonRpcProvider('https://rpc.juchain.org');
const signer = new ethers.Wallet(privateKey, provider);
// JAMMRouter 合约实例
const routerAddress = '0x3F26fb54C28Eab026e908A9A9357a456F3c8Dc87';
const routerABI = ['...']; // 添加路由器 ABI
const router = new ethers.Contract(routerAddress, routerABI, signer);
2. 执行交换
以下示例展示如何将 TokenA 交换为 TokenB:
// 交换参数
const amountIn = ethers.utils.parseEther("1.0"); // 1 个 TokenA
const amountOutMin = ethers.utils.parseEther("0.9"); // 最少接收 0.9 个 TokenB
const path = [tokenA_address, tokenB_address]; // 交换路径
const fees = [100]; // 交易对的费用层级(1%)
const to = signer.address; // 接收地址
const referrer = ethers.constants.AddressZero; // 无推荐人
const deadline = Math.floor(Date.now() / 1000) + 60 * 20; // 20 分钟后过期
// 执行交换
const tx = await router.swapExactTokensForTokens(
amountIn,
amountOutMin,
path,
fees,
to,
referrer,
deadline
);
await tx.wait();
console.log("交换完成!");
3. 使用 JU 进行交换
如果您想使用原生 JU 代币进行交换,请使用 ETH 相关函数:
// 用 JU 购买代币
const tx = await router.swapExactETHForTokens(
amountOutMin,
[WJU_address, tokenB_address], // 路径必须以 WJU 开始
[100], // 费用层级
to,
referrer,
deadline,
{ value: ethers.utils.parseEther("1.0") } // 发送 1 个 JU
);
首次提供流动性
1. 添加流动性
// 流动性参数
const tokenA = "TOKEN_A_ADDRESS";
const tokenB = "TOKEN_B_ADDRESS";
const fee = 100; // 1% 费用层级
const amountADesired = ethers.utils.parseEther("10");
const amountBDesired = ethers.utils.parseEther("10");
const amountAMin = ethers.utils.parseEther("9"); // TokenA 的最小数量
const amountBMin = ethers.utils.parseEther("9"); // TokenB 的最小数量
const to = signer.address;
const deadline = Math.floor(Date.now() / 1000) + 60 * 20;
// 首先批准路由器使用您的代币
const tokenAContract = new ethers.Contract(tokenA, erc20ABI, signer);
const tokenBContract = new ethers.Contract(tokenB, erc20ABI, signer);
await tokenAContract.approve(routerAddress, amountADesired);
await tokenBContract.approve(routerAddress, amountBDesired);
// 添加流动性
const tx = await router.addLiquidity(
tokenA,
tokenB,
fee,
amountADesired,
amountBDesired,
amountAMin,
amountBMin,
to,
deadline
);
const receipt = await tx.wait();
console.log("流动性添加成功!");
2. 移除流动性
// 获取 LP 代币余额
const pairAddress = await factory.getPair(tokenA, tokenB, fee);
const pairContract = new ethers.Contract(pairAddress, pairABI, signer);
const lpBalance = await pairContract.balanceOf(signer.address);
// 移除 50% 的流动性
const liquidity = lpBalance.div(2);
// 批准路由器使用您的 LP 代币
await pairContract.approve(routerAddress, liquidity);
// 计算 1% 滑点容忍度下的最小接收数量
const { reserve0, reserve1 } = await pairContract.getReserves();
const totalSupply = await pairContract.totalSupply();
const amount0 = liquidity.mul(reserve0).div(totalSupply);
const amount1 = liquidity.mul(reserve1).div(totalSupply);
const amount0Min = amount0.mul(99).div(100);
const amount1Min = amount1.mul(99).div(100);
// 移除流动性
const tx = await router.removeLiquidity(
tokenA,
tokenB,
fee,
liquidity,
amount0Min,
amount1Min,
to,
deadline
);
await tx.wait();
console.log("流动性移除成功!");
使用 permit
功能
permit
功能JAMM DEX 支持 EIP-2612 permit
,允许您通过签名批准代币使用,避免单独的 approve
交易。
// 生成 permit 签名
const domain = {
name: 'JAMM LPs',
version: '1',
chainId: await signer.getChainId(),
verifyingContract: pairAddress
};
const types = {
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' }
]
};
const nonce = await pairContract.nonces(signer.address);
const value = {
owner: signer.address,
spender: routerAddress,
value: liquidity,
nonce: nonce,
deadline: deadline
};
const signature = await signer._signTypedData(domain, types, value);
const { v, r, s } = ethers.utils.splitSignature(signature);
// 使用 permit 移除流动性
const tx = await router.removeLiquidityWithPermit(
tokenA,
tokenB,
fee,
liquidity,
amount0Min, // 使用计算的最小数量
amount1Min,
to,
deadline,
false, // approveMax
v,
r,
s
);
查询信息
获取交易对信息
// 检查交易对是否存在
const pairAddress = await factory.getPair(tokenA, tokenB, fee);
if (pairAddress === ethers.constants.AddressZero) {
console.log("交易对不存在。");
} else {
console.log("交易对地址:", pairAddress);
}
// 获取储备量
const pair = new ethers.Contract(pairAddress, pairABI, provider);
const { reserve0, reserve1 } = await pair.getReserves();
console.log("储备量:", reserve0.toString(), reserve1.toString());
计算交换数量
// 计算输出数量
const amountOut = await router.getAmountOut(
amountIn,
reserves.reserve0,
reserves.reserve1,
fee
);
console.log("预期输出:", ethers.utils.formatEther(amountOut));
// 计算多跳交换的数量
const amounts = await router.getAmountsOut(amountIn, path, fees);
console.log("多跳交换路径数量:", amounts.map(a => ethers.utils.formatEther(a)));
常见错误处理
交易失败的常见原因
"JAMMRouter: EXPIRED":交易截止时间已过
"JAMMRouter: INSUFFICIENT_OUTPUT_AMOUNT":滑点过高,输出数量少于所需最小值
"JAMM: INSUFFICIENT_LIQUIDITY":交易对中没有足够的流动性进行交换
"JAMMFactory: INVALID_FEE":使用了不支持的费用层级
错误处理示例
try {
const tx = await router.swapExactTokensForTokens(...);
await tx.wait();
} catch (error) {
if (error.message.includes("INSUFFICIENT_OUTPUT_AMOUNT")) {
console.log("滑点过高。请增加您的滑点容忍度。");
} else if (error.message.includes("EXPIRED")) {
console.log("交易已过期。请重试。");
} else {
console.log("交易失败:", error.message);
}
}
下一步
现在您已经了解了使用 JAMM DEX 的基础知识,可以继续学习:
费用结构 - 了解费用系统
推荐系统 - 学习如何赚取推荐奖励
集成指南 - 将 JAMM DEX 集成到您的应用程序中
Last updated