阅读 822

使用代码实现uniswap和pancakeswap自动化交易

本文章主要分析通过代码调用 uniswap/pancakeswap 的接口实现自动化交易的流程

本文主要基于 pancakeswap 实现,其他的交易所 uniswap/mdex 等都基本类似

本文主要实现了 3 个功能

  • 用 BNB 换其他 TOKEN
  • 将其他 TOKEN 换成 BNB
  • 将某一种 TOKEN 换成另外一种 TOKEN

1、开发环境

  • 开发语言为 Javascript/Typescript
  • 通过 ethers.js 和链交互

2、构建 provider 和钱包对象

const provider = ethers.getDefaultProvider(
  "https://bsc-dataseed1.binance.org:443"
);
const wallet = new ethers.Wallet(config.privateKey, provider);
复制代码

这里首先基于 bsc 的官方节点构建了一个 provider 对象,然后使用私钥构建了一个钱包对象

3、构建 pancakeswap 的 Router 合约对象

const router = new ethers.Contract(
  "0x10ED43C718714eb63d5aA57B78B54704E256024E",
  [
    "function getAmountsOut(uint amountIn, address[] memory path) public view returns (uint[] memory amounts)",
    "function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts)",
    "function swapExactTokensForETHSupportingFeeOnTransferTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)",
    "function swapExactTokensForTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline) external returns (uint[] memory amounts)",
    "function swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline) external returns (uint[] memory amounts)",
  ],
  wallet
);
复制代码

首先上面这个地址是 pancakeswap 的 Router2 地址,所有的交易都是通过这个合约执行。

下面定义了 4 个函数,作用分别如下

  • getAmountsOut 估算代币转换的时候 基于 AmountIn 估算 AmountOut
  • swapExactETHForTokens 将 BNB 换成指定代币,注意这里有一个 payable 修饰符,表示这个接口需要支付 BNB
  • swapExactTokensForETHSupportingFeeOnTransferTokens 将指定代币换成 BNB
  • swapExactTokensForTokens/swapExactTokensForTokensSupportingFeeOnTransferTokens 这两个方法都是将某种 ERC20 协议 token 换成另外一种 ERC20 协议 token

4、将 BNB 换成其他 TOKEN

async function bnb2Token(bnbValue, tokenAddress) {
  const amountIn = bnbValue;
  // 估算能获得多少个token
  const amounts = await router.getAmountsOut(amountIn, [
    pancakeAddress.WBNB,
    tokenAddress,
  ]);
  // 滑点
  const amountOutMin = amounts[1].sub(amounts[1].div(config.slide));
  // 发起交易
  const path = [pancakeAddress.WBNB, tokenAddress];
  const gasPrice = await provider.getGasPrice();
  const tx = await router.swapExactETHForTokens(
    amountOutMin,
    path,
    wallet.address,
    Date.now() + 10 * 60_000,
    {
      gasPrice,
      gasLimit: config.gasLimit,
      value: amountIn,
    }
  );
  console.log(`交易已发起, https://bscscan.com/tx/${tx.hash}`);
  try {
    await tx.wait();
    console.log("交易成功");
  } catch (err) {
    console.log("交易失败");
    throw err;
  }
}
复制代码

注意这里有一个 path 变量,表示我们指定的交易路径,为了方便合约处理,Swap 构造了一个叫 WrappedBNB 的 ERC20 代币,也就是代码中的 WBNB,合约的处理流程是通过 payable 接收到我们的 BNB 之后,转换成 WBNB,然后使用 WBNB -> TOKEN 的交易路径来完成交易

5、将其他 TOKEN 换成 BNB

// 首先我们需要有一个构建 erc20 合约的构造器
function erc20Contract(tokenAddress) {
  return new ethers.Contract(
    tokenAddress,
    [
      "function symbol() public view returns (string)",
      "function decimals() public view returns (uint)",
      "function balanceOf(address user) public view returns (uint)",
      "function allowance(address a, address b) public view returns (uint)",
      "function approve(address a, uint amount) public",
    ],
    wallet
  );
}

// 需要有一个授权函数,将 TOKEN 授权给 pancakeswap 处理
async function approve(tokenContract) {
  const amount = await tokenContract.allowance(
    wallet.address,
    pancakeAddress.router
  );
  // 已经授权过
  if (amount.gt(0)) {
    return;
  }
  const gasPrice = await provider.getGasPrice();
  // 重新调用授权
  const tx = await tokenContract.approve(
    pancakeAddress.router,
    ethers.constants.MaxUint256,
    {
      gasPrice,
      gasLimit: config.gasLimit,
    }
  );
  console.log("代币授权已发起", tx.hash);
  await tx.wait();
  console.log("授权成功");
}

// 将 TOKEN 换成 BNB 的主函数
// amount 如果不指定就卖出全部
async function token2Bnb(tokenAddress, amount) {
  // 拿到token的erc20合约
  const tokenContract = erc20Contract(tokenAddress);

  // 先授权给pancakeswap
  await approve(tokenContract);

  // 检查剩余数量
  let amountIn = await tokenContract.balanceOf(wallet.address);
  if (Number(amountIn) === 0) {
    console.log("数量为0,无法卖出");
    return;
  }
  // 指定数量
  if (amount) {
    amount = ethers.BigNumber.from(amount);
    // 指定了卖出数量
    if (amountIn.lt(amount)) {
      console.log("余额不足");
      return;
    }
    amountIn = amount;
  }
  // 卖出
  const gasPrice = await provider.getGasPrice();
  const tx = await router.swapExactTokensForETHSupportingFeeOnTransferTokens(
    amountIn,
    0,
    [tokenAddress, pancakeAddress.WBNB],
    wallet.address,
    Date.now() + 10 * 60_000,
    {
      gasPrice,
      gasLimit: config.gasLimit,
    }
  );
  console.log(`交易已发起, https://bscscan.com/tx/${tx.hash}`);
  try {
    await tx.wait();
    console.log("交易成功");
  } catch (err) {
    console.log("交易失败");
    throw err;
  }
}
复制代码

6、将某种 TOKEN 换成另外一种 TOKEN

// token换token,第三个参数表示数量,如果all表示所有
async function token2Token(token1, token2, amount) {
  // 拿到token的erc20合约
  const token1Contract = erc20Contract(token1);

  // 先授权给pancakeswap
  await approve(token1Contract);

  // 计算销售数量
  let amountIn;
  if (amount === "all") {
    amountIn = await token1Contract.balanceOf(wallet.address);
  } else {
    amountIn = amount;
  }

  // 交易路径,用bnb中转
  const path = [token1, pancakeAddress.WBNB, token2];
  // 发起交易
  const args = [amountIn, 0, path, wallet.address, Date.now() + 10 * 60_000];
  // 检查交易是否能成功
  try {
    await router.callStatic.swapExactTokensForTokensSupportingFeeOnTransferTokens(
      ...args
    );
  } catch (e) {
    console.log("测试发起交易出现错误");
    throw e;
  }

  const gasPrice = await provider.getGasPrice();
  const tx = await router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
    ...args,
    {
      gasPrice,
      gasLimit: config.gasLimit,
    }
  );
  console.log(`交易已发起, https://bscscan.com/tx/${tx.hash}`);
  try {
    await tx.wait();
    console.log("交易成功");
  } catch (err) {
    console.log("交易失败");
    throw err;
  }
}
复制代码

7、实现效果

我们使用一个简单的命令行来将上面的程序包裹起来

下一篇文章我们会结合 telegram 机器人实现一个交互式命令交易机器人

以前的文章

通过程序监控 pancakeswap 中 babydoge 的交易数据

联系作者

微信:czz5242199

telegram: @zhuozhongcao

文章分类
代码人生
文章标签