本文主要基于 pancakeswap 实现,其余的交易所 uniswap/mdex 等都基本相似javascript
本文主要实现了 3 个功能java
const provider = ethers.getDefaultProvider(
"https://bsc-dataseed1.binance.org:443"
);
const wallet = new ethers.Wallet(config.privateKey, provider);
复制代码
这里首先基于 bsc 的官方节点构建了一个 provider 对象,而后使用私钥构建了一个钱包对象微信
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 地址,全部的交易都是经过这个合约执行。markdown
下面定义了 4 个函数,做用分别以下app
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 的交易路径来完成交易async
// 首先咱们须要有一个构建 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;
}
}
复制代码
// 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;
}
}
复制代码
咱们使用一个简单的命令行来将上面的程序包裹起来ide
下一篇文章咱们会结合 telegram 机器人实现一个交互式命令交易机器人函数
之前的文章oop
经过程序监控 pancakeswap 中 babydoge 的交易数据测试
微信:czz5242199
telegram: @zhuozhongcao