快速开始

入门指南

本指南将帮助您快速了解如何使用 JAMM DEX 进行代币交换和流动性管理。

前提条件

在开始之前,请确保您具备以下条件:

  1. JuChain 钱包:支持 JuChain 网络的 Web3 钱包

  2. JU 代币:用于支付交易费用的原生代币

  3. 测试代币:用于交换的 ERC-20 代币

合约地址

主网地址

  • JAMMFactory0x6b5d54E6F73e96Ca960DBA71D778b98221939aa6

  • JAMMRouter0x3F26fb54C28Eab026e908A9A9357a456F3c8Dc87

  • WJU0x4d1B49B424afd7075d3c063adDf97D5575E1c7E2

  • USDT0xc8e19C19479a866142B42fB390F2ea1Ff082E0D2

  • ETH0x80077F108Dd73B709C43A1a13F0EEF25e48f7D0e

  • BNB0x151b6F646Ac02Ed9877884ed9637A84f2FD8FaA6

测试网地址

  • JAMMFactory0xbddd716a9d6325700d1c29562c819987e5b1f6a8

  • JAMMRouter0x3f8b0038023393009712D0051D192a8825dd02B9

  • WJU0xb8cdb16bc2b64d42638e285a691973ff10078d8e

您的第一次代币交换

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 功能

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)));

常见错误处理

交易失败的常见原因

  1. "JAMMRouter: EXPIRED":交易截止时间已过

  2. "JAMMRouter: INSUFFICIENT_OUTPUT_AMOUNT":滑点过高,输出数量少于所需最小值

  3. "JAMM: INSUFFICIENT_LIQUIDITY":交易对中没有足够的流动性进行交换

  4. "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