比特币价格·比特币行情
Ctrl+D收藏简单区块链

从代码角度上看懂DEX的聚合交易?

作者:

时间:2020/12/26 9:08:59

1inch发 币了,不知道各位老铁有没有领到。有的人暗中窃喜,有人还不了解 1inch,这篇文件就介绍了 1inch 的核心功能。

文章的主要步骤如下:

获得最大的收益兑换方案

授权1inch合约操作你的代币

利用第一步获得的兑换方案进行交易

去中心化交易所聚合器,即 DEX,以下都用 DEX 表示。DEX 聚合器是一个平台,它将搜索一组 DEX,以寻找在给定时间和数量下执行交易的最佳价格。

1inch 的一大特色就是聚合交易,它会在很多个 DEX 找到收益最大的成交方式。比如 100000dai 想买 x 个 eth,在 uniswap 成交 77%, 在 Bancor 成交 23% ,是最合算的,买到的 eth 最多。

1inch 是由 Anton Bukov 和 Sergej Kunz 开发的 DEX 聚合器,通过一次交易将订单在多个 DEX 之间拆分,给用户提供最好的兑换汇率。1inch 的智能合约是开源的。

在 1inch 执行交易,过程其实很简单:

根据输入的 token 或 ETH 数量,获得预期可兑换的 token 数量

授权(Approve)交易所使用你的 token

使用第一步的获取的 token 数量进行交易

我们首先仔细了解一下 1inch 的智能合约,让我们感兴趣的是这两个方法:

getExpectedReturn()

swap()

getExpectedReturn 可以随意调用,不需要消耗任何 gas。

这个函数需要传入兑换参数,返回兑换的期望结果,以及交易在各个 dex 之间的兑换比例。

这个方法接收 5 个参数:

fromToken:当前拥有的 token 的地址

toToken:要交换的 token 的地址

amount:想要交换的 token 数量

parts:卖出数量拆分成多少份进行最优分布的估算。查看distribution 可以了解更多细节,默认是 100

disableFlags:标记位,用于调整 1inch 的算法,例如可设置禁用某个特定的 DEX

这个方法有 2 个返回值:

returnAmount:执行交易后将收到的 token 数量。

distribution:一个 uint256 类型的数组,代表交易在不同 DEX 中的分布情况。例如,parts 设置为 100,成交额度的 25%在 Kyber 的,成交额度的 75%在 Uniswap,那么 distribution 看起来是这样的:[75, 25, 0, 0, …]。

目前 1inch 支持的交易所和排序(与 distribution 对应)如下:

注意:如果你想交易 Eth 而不是 ERC20 token,fromToken 需要设置为特殊的值 0x0或 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE。

getExpectedReturn函数的返回值非常重要,因为接下来需要利用它来执行实际的链上兑换操作。

要执行链上 token 兑换交易,就需要使用合约提供的另一个函数swap。调用swap时,需要传入我们之前从getExpectedReturn返回的数据,这个操作需要花费 gas。如果要卖出的是 ERC20 token,那么还需要先授权 1inch 合约可以操作你持有的待卖出 token。swap函数的定义如下:

swap 函数接收 6 个参数:

fromToken:待卖出 token 的地址

toToken:待买入 token 的地址

amount:待卖出 token 的数量

minReturn:期望得到的待买入 token 的最少数量

distribution:兑换交易拆分分布数组

parts:执行估算时的拆分数量,默认值是 100

disableFlags:标记位,例如可设置禁用某个特定的 DEX

我们将使用 ganache-cli 分叉(fork)当前的区块链状态,并提前在 1 个地址上充值了很多 DAI。在示例中,地址是 0x78bc49be7bae5e0eec08780c86f0e8278b8b035b。我们还将 gas limit 设置的非常高,因此在测试过程中不至于出现 out of gas 的问题,也不需要在每次交易前估算 gas。启动命令是:

分析完了 1inch 的关键方法,我们将进行第一笔兑换交易,代码已在 github 开源:https://github.com/liushooter/ethereumdevio-dex-tutorial/blob/master/part1/index.js。

(1)加载 ABI 以便实例化 1inch 合约实例

(2)该数组指定要使用的 DEX

(3)调用getExpectedReturn函数获取兑换方案

代码执行后返回结果类似下面这样:

此时卖出 1 个 eth,1inch 可以买到 148.47DAI,而 Coinbase 是 148.12。1inch 给出的最佳兑换方案是通过 Uniswap 兑换 96%,再通过 Bancor 兑换 4%,这样可以得到 148.47 DAI,这样比单独通过 Uniswap 或 Bancor 进行兑换都划算。

注意,这个价格不能作为智能合约的 Oracle 价格,由于各种各样的错误,DEX 可以提供非常低的价格,因此可能会严重操纵这个价格。

下面我们使用 1inch 聚合器将 1000 DAI 兑换为 ETH。首先定义一些变量,例如合约地址、ABI 等。

同时写一个辅助函数来等待交易确认:

我们在之前已经获得了兑换比率,现在把代码变的更可读,定义 1 个getQuote函数,返回一个包含所有参数的对象。

一旦我们得到了兑换 token 的比率,接下来需要授权 1inch 可以操作我们持有的 token,ERC20 token 标准不允许在一次交易中向合约发送 token 并触发下一个操作。我们写了一个简单的函数,调用approval函数,并使用 waitTransaction 等待交易确认。

注意,这里演示的时候 授权额度远远高于当前实际需要的数量,这样后续就不需要反复执行这个操作了。

接下来就可以调用 1inch 聚合器的 swap 函数了。在下面的代码中,我们在调用swap函数执行交易后,等待交易确认,并在交易确认后,显示转出账户的 eth 余额和 dai 余额。

最后的执行结果看起来是下面这样的:

我们用 1000 DAI 换回来 5.85 ETH。

1inch 提供了出色的链上 DEX 聚合实现,可以在一个交易内利用多个 DEX 实现最优的兑换策略。1inch 的 API 使用也很简单,只需要用 getExpectedReturn 估算兑换方案, 然后使用 swap 执行兑换方案,就可以得到最好的兑换结果。你不必总是用 eth 交易,也可以交换 2 个 ERC20 token,甚至可以用 weth 交易。

function getExpectedReturn(\n    IERC20 fromToken,\n    IERC20 toToken,\n    uint256 amount,\n    uint256 parts,\n    uint256 disableFlags\n) public view\nreturns(\n    uint256 returnAmount,\n    uint256[] memory distribution\n);这个方法接收 5 个参数:

[\n  "Uniswap", "Kyber", "Bancor", "Oasis",\n  "CurveCompound", "CurveUsdt", "CurveY",\n  "Binance", "Synthetix",\n  "UniswapCompound", "UniswapChai", "UniswapAave"\n]注意:如果你想交易 Eth 而不是 ERC20 token,fromToken 需要设置为特殊的值 0x0或 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE。

function swap(\n    IERC20 fromToken,\n    IERC20 toToken,\n    uint256 amount,\n    uint256 minReturn,\n    uint256[] memory distribution,\n    uint256 disableFlags\n ) public payable;swap 函数接收 6 个参数:

ganache-cli -f https://mainnet.infura.io/v3/[YOUR INFURA KEY] \\\n-d -i 66 \\\n--unlock 0x78bc49be7bae5e0eec08780c86f0e8278b8b035b \\\n-l 8000000实战 - 估算最佳兑换方案分析完了 1inch 的关键方法,我们将进行第一笔兑换交易,代码已在 github 开源:https://github.com/liushooter/ethereumdevio-dex-tutorial/blob/master/part1/index.js。

var Web3 = require(\'web3\');\nconst BigNumber = require(\'bignumber.js\');\nconst oneSplitABI = require(\'./abis/onesplit.json\');\nconst onesplitAddress = "0xC586BeF4a0992C495Cf22e1aeEE4E446CECDee0E"; // 1plit contract address on Main net\nconst fromToken = \'0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\'; // ETHEREUM\nconst fromTokenDecimals = 18;\nconst toToken = \'0x6b175474e89094c44da98b954eedeac495271d0f\'; // DAI Token\nconst toTokenDecimals = 18;\nconst amountToExchange = 1\nconst web3 = new Web3(\'http://127.0.0.1:8545\');\nconst onesplitContract = new web3.eth.Contract(oneSplitABI, onesplitAddress); // (1)\nconst oneSplitDexes = ["Uniswap", "Kyber", "Bancor", // (2)\n  "Oasis", "CurveCompound", "CurveUsdt", "CurveY", "Binance", "Synthetix", "UniswapCompound", "UniswapChai", "UniswapAave"\n]\nonesplitContract.methods.getExpectedReturn(fromToken, toToken, // (3)\n  new BigNumber(amountToExchange).shiftedBy(fromTokenDecimals).toString(), 100, 0).call({\n  from: \'0x9759A6Ac90977b93B58547b4A71c78317f391A28\'\n}, function(error, result) {\n  if (error) {\n    console.log(error)\n    return;\n  }\n  console.log("Trade From: " + fromToken)\n  console.log("Trade To: " + toToken);\n  console.log("Trade Amount: " + amountToExchange);\n  console.log(new BigNumber(result.returnAmount).shiftedBy(-fromTokenDecimals).toString());\n  console.log("Using Dexes:");\n  for (let index = 0; index < result.distribution.length; index++) {\n    console.log(oneSplitDexes[index] + ": " + result.distribution[index] + "%");\n  }\n});(1)加载 ABI 以便实例化 1inch 合约实例

var Web3 = require(\'web3\');\nconst BigNumber = require(\'bignumber.js\');\nconst oneSplitABI = require(\'./abis/onesplit.json\');\nconst onesplitAddress = "0xC586BeF4a0992C495Cf22e1aeEE4E446CECDee0E"; // 1plit contract address on Main net\nconst erc20ABI = require(\'./abis/erc20.json\');\nconst daiAddress = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI ERC20 contract address on Main net\nconst fromAddress = "0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81";\nconst fromToken = daiAddress;\nconst fromTokenDecimals = 18;\nconst toToken = \'0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\'; // ETH\nconst toTokenDecimals = 18;\nconst amountToExchange = new BigNumber(1000);\nconst web3 = new Web3(new Web3.providers.HttpProvider(\'http://127.0.0.1:8545\'));\nconst onesplitContract = new web3.eth.Contract(oneSplitABI, onesplitAddress);\nconst daiToken = new web3.eth.Contract(erc20ABI, fromToken);同时写一个辅助函数来等待交易确认:

function sleep(ms) {\n    return new Promise(resolve => setTimeout(resolve, ms));\n}\nasync function waitTransaction(txHash) {\n    let tx = null;\n    while (tx == null) {\n        tx = await web3.eth.getTransactionReceipt(txHash);\n        await sleep(2000);\n    }\n    console.log("Transaction " + txHash + " was mined.");\n    return (tx.status);\n}我们在之前已经获得了兑换比率,现在把代码变的更可读,定义 1 个getQuote函数,返回一个包含所有参数的对象。

async function getQuote(fromToken, toToken, amount, callback) {\n    let quote = null;\n    try {\n        quote = await onesplitContract.methods.getExpectedReturn(fromToken, toToken, amount, 100, 0).call();\n    } catch (error) {\n        console.log(\'Impossible to get the quote\', error)\n    }\n    console.log("Trade From: " + fromToken)\n    console.log("Trade To: " + toToken);\n    console.log("Trade Amount: " + amountToExchange);\n    console.log(new BigNumber(quote.returnAmount).shiftedBy(-fromTokenDecimals).toString());\n    console.log("Using Dexes:");\n    for (let index = 0; index < quote.distribution.length; index++) {\n        console.log(oneSplitDexes[index] + ": " + quote.distribution[index] + "%");\n    }\n    callback(quote);\n}一旦我们得到了兑换 token 的比率,接下来需要授权 1inch 可以操作我们持有的 token,ERC20 token 标准不允许在一次交易中向合约发送 token 并触发下一个操作。我们写了一个简单的函数,调用approval函数,并使用 waitTransaction 等待交易确认。

function approveToken(tokenInstance, receiver, amount, callback) {\n    tokenInstance.methods.approve(receiver, amount).send({ from: fromAddress }, async function(error, txHash) {\n        if (error) {\n            console.log("ERC20 could not be approved", error);\n            return;\n        }\n        console.log("ERC20 token approved to " + receiver);\n        const status = await waitTransaction(txHash);\n        if (!status) {\n            console.log("Approval transaction failed.");\n            return;\n        }\n        callback();\n    })\n}注意,这里演示的时候 授权额度远远高于当前实际需要的数量,这样后续就不需要反复执行这个操作了。

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

IPFS微信指数日环比上升65.35%:金色财经报道,微信指数显示,11月11日,区块链微信指数为1716801,日环比上升5.27%;比特币微信指数为868904,日环比下降0.35%;以太坊微信指数为228716,日环比下降6.11%;IPFS微信指数为558029,日环比上升65.35%。[2020/11/12 14:07:36]

美国电信巨头AT&T希望法院再次撤销SIM卡交换诉讼案:7月6日消息,美国电信巨头AT&T再次因SIM交换导致加密货币被盗而被起诉。原告Seth Shapiro表示,AT&T错误地转移了他的电话号码,导致他损失了高达190万美元的加密货币基金。AT&T敦促法院驳回诉讼,在最新的文件中,该公司跟进了先前的文件(先前的文件被加利福尼亚法院驳回)。该文件声称Shapiro的主张存在不足之处,AT&T并未违反有关保护敏感数据的规范。(coingeek)[2020/7/6]

芝加哥DeFi联盟启动 旨在连接DeFi项目与芝加哥加密交易公司:一批总部位于美国芝加哥的加密货币交易商已经成立了芝加哥DeFi联盟(CDA),这是一个旨在将芝加哥的交易公司和DeFi项目联系在一起的组织。为了引导流动性,许多DeFi初创公司正在寻求交易公司和专业交易员。然而,他们并不一定了解这些贸易公司在DeFi方面的需求和关注。CDA希望填补这一缺口,并帮助DeFi初创公司找到资源,开发能够利用这些交易公司所代表的流动性的产品。(The Block)[2020/4/8]

标签:

区块链热门资讯
行情分析丨比特币再创新高, 现在请忘记技术分析, 享受这份惊喜

世界对加密货币的热情和认可正到达无以复加的地步,BTC一夜醒来再创新高,这是我们对昨日BTC分析的不好的预判,挑战24000以上的中枢顶部是必然的走势,再创新高却让我惴惴不安。这种不安来自于,主力继续加仓,市场可以流通的比特币越来越少,他成为一个有价无市的市场,这个市场是否健康?未来是否像海天味业、茅台一样成为越来越高的定位,很难明断。

2020/12/26 11:55:03
12.26 过节期间的模式 缓涨急跌的吓人模式还会反复上演

基本面| Fundamental Analysis   消息面| News1.Bitstamp将暂停所有美国客户的XRP交易及存款2.Zilliqa:已引入临时修复程序 网络恢复正常交易3.报告:尽管币价飙升。

2020/12/26 11:33:22
金色说明书|如何在1inch进行流动性挖矿?

金色财经报道,12月26日上午,去中心化交易聚合平台1inch官方发推更新初始1INCH代币流动性挖矿计划。流动性挖矿将于UTC时间12月26日12时(北京时间12月26日20时)开始。

2020/12/26 10:29:09
陈楚初:比特币多头量能充足 日内或者上攻25000关口

今日快讯: Bitstamp刚刚发推宣布,“鉴于美国SEC最近的文件指控XRP是一种证券,我们将于2021年1月8日暂停所有美国客户的XRP交易和存款。其他国家不受影响。”Bitstamp将密切关注不断发展的局势,并继续作出相应的调整。

2020/12/26 8:41:59
金色早报|1INCH空投最大获利者获得2750万美元代币

头条 ▌一名DeFi交易者在1INCH空投中获得超2700万美元代币 Dune Analytics数据显示,一名DeFi交易者在1INCH空投中获得了9749686.003枚代币,根据2.8美元的价格计算,其获得的代币总价值约2750万美元,占1INCH供应量的约11%。

2020/12/26 7:53:42
12.25晚间行情:BTC 新高 则强而上

文章系金色财经专栏作者币圈北冥供稿,发表言论仅代表其个人观点,仅供学习交流!金色盘面不会主动提供任何交易指导,亦不会收取任何费用指导交易,请读者仔细甄别防上当。 圣诞节庄家突然袭击,XRP 这个搅屎棍先前带崩盘面,BTC 今天再度创出新高,盘面怎一个乱字了得。BTC 震荡上行为主,得留意洗盘风险,其他币种修复需要时间。

2020/12/25 23:59:55