主页 > imtoken用什么id下载 > 以太坊 DEX 的交易和套利:交易所(第 2 部分)

以太坊 DEX 的交易和套利:交易所(第 2 部分)

imtoken用什么id下载 2023-04-14 05:51:07

探索使用去中心化交易所 (DEX) 围绕以太坊构建的简单自动交易(套利)机器人的开发。

在本教程中,您将学习如何使用 1inch DEX 聚合器使用 web3.js 库以 Javascript 执行交易。 通过本教程,您将学习如何直接在以太坊区块链上兑换 ERC20 代币和以太币。

本文是第 2 部分(单击此处转到第 1 部分)。 在上一篇文章中,我向您介绍了如何获取交易报价:获取您要出售的代币所获得的代币数量。 在本文中,让我们看看如何使用 Javascript 执行事务。

img

要在 1inch 的 DEX 聚合器上完成交换,我们需要三样东西:

但在我们开始之前,我们需要定义所有常量变量和一些辅助实用函数以使代码更简单:

项目设置

在本教程中,我们将使用 ganache-cli 分叉当前的区块链状态,并使用它来解锁已经拥有大量 DAI 代币的账户。 在我们的示例中,帐号是 0x78bc49be7bae5e0eec08780c86f0e8278b8b035b。 我们还将 gas limit 设置得非常高,因此我们在测试期间不会出现 gas 不足的问题,而无需在每次交易前估算 gas 成本。 启动分叉区块链的命令行是:

ganache-cli  -f https://mainnet.infura.io/v3/[YOUR INFURA KEY] -d -i 66 --unlock 0x78bc49be7bae5e0eec08780c86f0e8278b8b035b -l 8000000

在本教程中,我们将尝试使用 1inch dex 聚合器将 1000 DAI 兑换成 ETH,首先,为了方便起见,让我们声明我们需要的所有变量,例如合约地址、ABI。

var Web3 = require('web3');
const BigNumber = require('bignumber.js');
const oneSplitABI = require('./abis/onesplit.json');
const onesplitAddress = "0xC586BeF4a0992C495Cf22e1aeEE4E446CECDee0E"; // 1plit contract address on Main net
const erc20ABI = require('./abis/erc20.json');
const daiAddress = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI ERC20 contract address on Main net
const fromAddress = "0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81";
const fromToken = daiAddress;
const fromTokenDecimals = 18;
const toToken = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; // ETH
const toTokenDecimals = 18;
const amountToExchange = new BigNumber(1000);
const web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545'));
const onesplitContract = new web3.eth.Contract(oneSplitABI, onesplitAddress);
const daiToken = new web3.eth.Contract(erc20ABI, fromToken);

我们还定义了一些辅助函数以太坊兑换,以方便等待交易被包含在区块链中:

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function waitTransaction(txHash) {
    let tx = null;
    while (tx == null) {
        tx = await web3.eth.getTransactionReceipt(txHash);
        await sleep(2000);
    }
    console.log("Transaction " + txHash + " was mined.");
    return (tx.status);
}

获取汇率

现在我们已设置好所有内容,让我们使用本教程系列第一部分中的代码来获取交易的预期汇率。 我们只是将代码转换为人类可读的函数。

函数 getQuote 返回一个包含所有参数的对象,以使用第一部分详述的交换函数。

async function getQuote(fromToken, toToken, amount, callback) {
    let quote = null;
    try {
        quote = await onesplitContract.methods.getExpectedReturn(fromToken, toToken, amount, 100, 0).call();
    } catch (error) {
        console.log('Impossible to get the quote', error)
    }
    console.log("Trade From: " + fromToken)
    console.log("Trade To: " + toToken);
    console.log("Trade Amount: " + amountToExchange);
    console.log(new BigNumber(quote.returnAmount).shiftedBy(-fromTokenDecimals).toString());
    console.log("Using Dexes:");
    for (let index = 0; index < quote.distribution.length; index++) {
        console.log(oneSplitDexes[index] + ": " + quote.distribution[index] + "%");
    }
    callback(quote);
}

授权花费代币

一旦我们有了第 1 部分中描述的代币汇率,我们首先需要授权 1inch dex 聚合器智能合约使用我们的代币。 如您所知,ERC20代币标准不允许在一次交易中向智能合约发送代币和触发合约功能,我们写了一个简单的函数调用ERC20合约实例的approve函数,并使用之前的waitTransaction函数等待交易去完成:

function approveToken(tokenInstance, receiver, amount, callback) {
    tokenInstance.methods.approve(receiver, amount).send({ from: fromAddress }, async function(error, txHash) {
        if (error) {
            console.log("ERC20 could not be approved", error);
            return;
        }
        console.log("ERC20 token approved to " + receiver);
        const status = await waitTransaction(txHash);
        if (!status) {
            console.log("Approval transaction failed.");
            return;
        }
        callback();
    })
}

请注意,例如,您可以授权比计划交换更多的令牌,这样您就不需要在每次进行交换时都进行授权。

进行交换

现在所有的参数都已经拿到了,我们需要调用1inch聚合器的swap函数让合约访问我们的资金。 现在,我们将发起一个执行交换的交易,并等待交易被打包。

为了保证兑换成功,增加了对DAI代币balanceOf函数的调用,获取以太地址的余额,对比余额,确保DAI代币真正兑换成以太币.

let amountWithDecimals = new BigNumber(amountToExchange).shiftedBy(fromTokenDecimals).toFixed()
getQuote(fromToken, toToken, amountWithDecimals, function(quote) {
    approveToken(daiToken, onesplitAddress, amountWithDecimals, async function() {
        // We get the balance before the swap just for logging purpose
        let ethBalanceBefore = await web3.eth.getBalance(fromAddress);
        let daiBalanceBefore = await daiToken.methods.balanceOf(fromAddress).call();
        onesplitContract.methods.swap(fromToken, toToken, amountWithDecimals, quote.returnAmount, quote.distribution, 0).send({ from: fromAddress, gas: 8000000 }, async function(error, txHash) {
            if (error) {
                console.log("Could not complete the swap", error);
                return;
            }
            const status = await waitTransaction(txHash);
            // We check the final balances after the swap for logging purpose
            let ethBalanceAfter = await web3.eth.getBalance(fromAddress);
            let daiBalanceAfter = await daiToken.methods.balanceOf(fromAddress).call();
            console.log("Final balances:")
            console.log("Change in ETH balance", new BigNumber(ethBalanceAfter).minus(ethBalanceBefore).shiftedBy(-fromTokenDecimals).toFixed(2));
            console.log("Change in DAI balance", new BigNumber(daiBalanceAfter).minus(daiBalanceBefore).shiftedBy(-fromTokenDecimals).toFixed(2));
        });
    });
});

在撰写本文时,以太坊的价格约为 170 美元 (DAI),执行结果如下:

img

如您所见,当我们卖出 1000 个 DAI 代币时,兑换了 5.85 个 ETH。

为了方便起见,我们将源代码发布在了Github上,可以通过以下命令轻松获取:

git clone git@github.com:jdourlens/ethereumdevio-dex-tutorial.git && cd ethereumdevio-dex-tutorial/part2

并安装所需的依赖项 web3.js 和 bignumber.js:

npm install

执行代码:

node index.js

您可能遇到的问题是以下消息:“VM Exception while processing transaction: revert OneSplit: actual return amount is less than minReturn”。 这表明链上报价已更新。 如果您想避免这种情况发生,您可以通过将 minReturn 参数降低 1% 或 3% 来在代码中引入滑动。

这就是使用 1inch DEX 聚合器执行链上 ERC20 和 ETH 交换所需的全部。 当然也不一定非要和ETH兑换,也可以在2个ERC20 token之间兑换,甚至可以和Wrapped Ether兑换。

本翻译由 Cell Network 赞助。

本文参与登联社区写作激励计划,好文章好收益以太坊兑换,欢迎正在阅读的你加入。