中文 / English
铁叔

天地不仁 以万物为刍狗


  • 首页

  • 归档

  • 关于我

  • 公益404

  • 搜索

uniswap 环回交易的手续费

时间: 2021-10-15   |   分类: Defi   uniswap   | 字数: 994 字 | 阅读: 2分钟 | 阅读次数:

uniswap 的交易费用,是通过 x * y = K 的恒等式中推导而来, 在特定的交易场景时,例如环回交易中,我们的交易成本可以做到远远低于额定手续费。

什么是环回交易

环回交易是在一个交易对 tokenA/tokenB 交易,先从 tokenA 兑换得到 tokenB, 然后立刻将得到的 tokenB 换回 tokenA 的交易。

uniswap v2 的标准费率是千分之三, 那么环回交易的成本就是千分之六, 这个成本相当之高. 如果我们仅仅是为了刷交易量, 我们需要一种有效的途径来降低手续费, 环回交易就是一种非常有效的途径。

uniswap formula

理论推导

下面是我们的详细推导过程. 假设如下:

  • dx: 输入的 tokenA 数量
  • r0: tokenA 的 reserve 数量
  • r1: tokenB 的 reserve 数量
  • dy: 第一次 tokenA -> tokenB swap 的数量
  • ex: 第二次 swap tokenB -> tokenA 的数量

根据uniswap的公式, 计算如下:

dy = dx*997*r1/(1000*r0 + dx*997)

R1 = r1 - dy
R0 = r0 + dx

ex = dy*997*R0/(1000*R1 + dy*997)

代入dy,化简后,可得:

ex = 997*997*dx*(r0+dx) / (1000*1000*r0 + 997*997*dx)

同时除以dx:

ex/dx = 997*997*(r0+dx) / (1000*1000*r0 + 997*997*dx)

由于ex是最终环回交易结束时我们的 tokenA 数量, dx 是我们 tokenA 的输入数量, 1-ex/dx 就是我们的手续费比例。

从上面的公式可知,最终环回交易的ex只跟输入dx和tokenA的reserve有关,而且,输入数量dx与tokenA reserve比值越高,最终得到的ex/dx 越接近1,也就是付出的手续费越少。

代码演算

测试代码如下:

Filename: loopbackSwap.js

const BigNumber = require('ethers').BigNumber

const e18 = BigNumber.from('1000000000000000000')

const getAmountOut = (amountIn, r0, r1) => {
    const amountInWithFee = amountIn.mul(997)
        , numerator = amountInWithFee.mul(r1)
        , denominator = r0.mul(1000).add(amountInWithFee);

    return numerator.div(denominator)
}

const getAmountBack = (amtIn, r0, r1, printable = false) => {
    const out = getAmountOut(amtIn, r0, r1)
    
    r0 = r0.add(amtIn)
    r1 = r1.sub(out)

    const dx = getAmountOut(out, r1, r0)
    if (printable) {
        console.info('swap x->y: out=%s r0=%s r1=%s backx=%s',
            out.toString(), r0.toString(), r1.toString(), dx.toString())
    }
    return { amtInter: out, amtOut: dx }
}

function loopbackSwap() {
    const reserveA = BigNumber.from(1000000).mul(e18)
    , reserveB = BigNumber.from(2000000).mul(e18)
    , ratio = (multor) => reserveA.mul(multor).div(1000)

    let amts = [
        1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 3000, 5000, 10000 ]

    for (let amt of amts) {
        let amtIn = ratio(amt)
        let { amtOut } = getAmountBack(amtIn, reserveA, reserveB)
        console.info('dx/reserve=%s amtOut/amtIn=%s', amt/1000, amtOut.mul(10000).div(amtIn).toNumber()/10000)
    }
}

loopbackSwap()

run the scripts:

npx hardhat run loopbackSwap.js
dx/reserve=0.001 amtOut/amtIn=0.994
dx/reserve=0.002 amtOut/amtIn=0.994
dx/reserve=0.005 amtOut/amtIn=0.994
dx/reserve=0.01 amtOut/amtIn=0.994
dx/reserve=0.02 amtOut/amtIn=0.9941
dx/reserve=0.05 amtOut/amtIn=0.9942
dx/reserve=0.1 amtOut/amtIn=0.9945
dx/reserve=0.2 amtOut/amtIn=0.995
dx/reserve=0.5 amtOut/amtIn=0.9959
dx/reserve=1 amtOut/amtIn=0.9969
dx/reserve=2 amtOut/amtIn=0.9979
dx/reserve=3 amtOut/amtIn=0.9984
dx/reserve=5 amtOut/amtIn=0.9989
dx/reserve=10 amtOut/amtIn=0.9994

从结果可以看出,当 dx/reserve 超过1后,手续费率极速降低,当dx/reserve=10,手续费仅为万分之6

由此,可以使用闪电贷,在较低的利率下,将大量资金从借贷池中借出,然后进行环回交易,在一些交易即挖矿的交易所中,可以通过这种方式降低挖矿手续费,套利获利。

实战

我们可以编写一个合约来执行我们的环回交易。

需要注意的是,我们不能直接将 uniswap router 的 path 参数设置为 [tokenA, tokenB, tokenA] 来进行环回交易,而必须分成两次swap, 首先是 [tokenA, tokenB], 然后是 [tokenB, tokenA].

示例代码如下:

    /// @dev swapLoopback swap tokenA to tokenB, then swap tokenB to tokenA
    /// @param _router uniswap-like router
    /// @param reward the reward token
    /// @param amountIn amount in
    /// @param amountOutMin just set to 0
    /// @param path [tokenA, tokenB]
    function swapLoopback(
                    address _router,      // router
                    address reward,       // reward token
                    uint amountIn,
                    uint amountOutMin,
                    address[] memory path
                )
                public
                onlyOwner {
        address tokenIn = path[0];
        uint tokenInitial = IERC20(tokenIn).balanceOf(address(this));

        _approve(IERC20(tokenIn), address(_router));
        // solhint-disable-next-line
        uint256 ts = block.timestamp + 60;
        uint[] memory amounts = IUniswapRouter(_router).swapExactTokensForTokens(amountIn, amountOutMin, path, address(this), ts);

        path[0] = path[1];
        path[1] = tokenIn;
        // console.log("amounts:", amounts[1]);
        _approve(IERC20(path[0]), address(_router));
        amounts = IUniswapRouter(_router).swapExactTokensForTokens(amounts[1], amountOutMin, path, address(this), ts);

        // other arbitrage code with reward..
        reward;
    }
#uniswap# #uniswap v2# #Defi# #Solidity# #套利# #arbitrage#

声明:uniswap 环回交易的手续费

链接:https://guotie.github.io/post/uniswap/uniswap-loopback-swap-fee/

作者:铁叔

声明: 本博客文章除特别声明外,均采用 CC BY-NC-SA 3.0许可协议,转载请注明出处!

创作实属不易,如有帮助,那就打赏博主些许茶钱吧 ^_^
WeChat Pay

微信打赏

Alipay

支付宝打赏

AAVE源代码分析 -- AAVE Proxy 体系
元交易及其实现
铁叔

铁叔

千里之行 始于足下

25 日志
14 分类
56 标签
GitHub twitter telegram email medium
标签云
  • Solidity
  • Defi
  • Aave
  • Compound
  • Abi
  • Dapp
  • Ethereum
  • Evm
  • Lend protocol
  • Lending
© 2010 - 2024 铁叔
Powered by - Hugo v0.119.0 / Theme by - NexT
/
0%