详解 SushiSwap

了解 SushiSwap 的实现细节

> * 原文:https://soliditydeveloper.com/sushi-swap > * 译文出自:[登链翻译计划](https://github.com/lbc-team/Pioneer) > * 译者:[翻译小组](https://learnblockchain.cn/people/412) > * 校对:[Tiny 熊](https://learnblockchain.cn/people/15) > * 本文永久链接:[learnblockchain.cn/article…](https://learnblockchain.cn/article/2740) 你现在可能已经听说过[SushiSwap](https://sushi.com/)了。其分叉自Uniswap,同时带来了新的功能,比如质押和治理。但它背后的合约究竟是如何运作的呢? 其实这并不难。详细了解这个工作原理将是学习Solidity和Defi的一个好方法。 ## 合约细节 我们现在将深入SushiSwap的实现细节。为了更容易理解,一些代码被修改了。你可以随时查看[Github](https://github.com/sushiswap/sushiswap)里面的完整代码。 ### 1. Uniswap v2 SushiSwap核心部分只是一个Uniswap v2的分叉。除了一些小的差别,合同代码是完全复制的。如果你对Uniswap v2的工作原理感到好奇,请看我以前的文章[这里](https://cdn0.scrvt.com/b095ee27d37b3d7b6b150adba9ac6ec8/a4cfca692816c48e/d1404155283d/v/3a019cf6ae1d/uniswap.png)。在不久的将来对[Uniswap v3](https://learnblockchain.cn/article/2302)的深入研究。 特别是SushiSwap正在利用流动性池代币(LP代币)。请查看https://uniswap.org/docs/v2/core-concepts/pools/ 在 LP 代币下的所有细节。从本质上讲,LP是用来接收在流动池中累积的[按比例](https://www.investopedia.com/terms/p/pro-rata.asp)费用。你在一个流动池里提供流动性,并得到LP代币的回报。当资金池现在随着交易的进行收取费用时,它们会在交易时平均分配给所有LP持有人。当你销毁LP代币时,则将收到对应流动池中的资金份额和收集的费用。 SushiSwap对Uniswap代码做了两处修改: 1. 在部署中调用了[setFeeTo](https://github.com/sushiswap/sushiswap/blob/64b758156da6f9bde1d8619f142946b005c1ba4a/contracts/uniswapv2/UniswapV2Factory.sol#L47-L50)函数,费用接受者被设置为SushiMaker合约(见下文)。一旦费用接受者被设置,由于当前交易导致的LP 发行量增长的1/6将以LP代币的形式被铸成协议费用。由于Uniswap的交易费是0.3%,因此每笔交易的0.05%的费用将流向SushiMaker。 2. 增加了一个迁移者功能(见下文SushiRoll)。 你可以看到Uniswap的核心合约和SushiSwap如何改变它们之间的全部差异[这里](https://github.com/sushiswap/sushiswap/commits/master/contracts/uniswapv2)。 ### 2. SushiMaker ![Sushi Maker](https://img.learnblockchain.cn/pics/20210714114715.jpeg) [SushiMaker](https://github.com/sushiswap/sushiswap/blob/master/contracts/SushiMaker.sol)将从SushiSwap上交易的人那里接收LP代币。它主要由一个 `convert` 函数组成,它的作用如下: 1. [销毁(Burn)](https://github.com/Uniswap/uniswap-v2-core/blob/4dd59067c76dea4a0e8e4bfdda41877a6b16dedc/contracts/UniswapV2Pair.sol#L134)掉提供的LP代币。其结果将是收到按比例的`token0`和`token1`的数量。 2. 在`convertStep`内将收到的两个代币兑换为SUSHI(代币)。如果没有直接的流动池来兑换为SUSHI,这可能需要额外的步骤。 ```javascript function convert(address token0, address token1) external { UniV2Pair pair = UniV2Pair(factory.getPair(token0, token1)); require(address(pair) != address(0), "Invalid pair"); IERC20(address(pair)).safeTransfer( address(pair), pair.balanceOf(address(this)) ); (uint256 amount0, uint256 amount1) = pair.burn(address(this)); if (token0 != pair.token0()) { (amount0, amount1) = (amount1, amount0); } _convertStep(token0, token1, amount0, amount1) } ``` 兑换本身也是通过SushiSwap 流动池来进行的。可以通过`_swap`函数来看看这是如何做到的。 ```javascript function _swap( address fromToken, address toToken, uint256 amountIn, address to ) internal returns (uint256 amountOut) { UniV2Pair pair = UniV2Pair(factory.getPair(fromToken, toToken)); require(address(pair) != address(0), "Cannot convert"); (uint256 reserve0, uint256 reserve1, ) = pair.getReserves(); uint256 amountInWithFee = amountIn.mul(997); if (fromToken == pair.token0()) { amountOut = amountInWithFee.mul(reserve1) / reserve0.mul(1000).add(amountInWithFee); IERC20(fromToken).safeTransfer(address(pair), amountIn); pair.swap(0, amountOut, to, new bytes(0)); } else { amountOut = amountInWithFee.mul(reserve0) / reserve1.mul(1000).add(amountInWithFee); IERC20(fromToken).safeTransfer(address(pair), amountIn); pair.swap(amountOut, 0, to, new bytes(0)); } } ``` 开始看到它使用了Uniswap低级别的 [swap](https://github.com/Uniswap/uniswap-v2-core/blob/4dd59067c76dea4a0e8e4bfdda41877a6b16dedc/contracts/UniswapV2Pair.sol#L159) 函数。 1. 获取池中两个代币的当前储备。 2. 从储备金和被兑换的代币金额中计算出接收金额,减去费用。计算是基于[x*y=k曲线](https://uniswap.org/docs/v1/frontend-integration/trade-tokens/)。 一旦计算出金额,我们就可以进行兑换了。转换为SUSHI的最后一步将总是通过传递SUSHI代币地址来调用`_swap`函数,并将其发送到bar: ```javascript _swap(token, sushi, amountIn, bar) ``` 就这样,把所有的LP代币转换成了SUSHI,所有转换后的SUSHI都被送到了SushiBar,见下一个合约: ### 3. SushiBar ![Sushi Bar](https://img.learnblockchain.cn/pics/20210714114711.jpg) 在[SushiBar](https://github.com/sushiswap/sushiswap/blob/master/contracts/SushiBar.sol)内,人们可以带着SUSHI进入,得到xSUSHI,然后带着更多的SUSHI离开。请记住,所有来自SushiMaker的SUSHI都被送到这里。所以随着时间的推移,SushiBar会积累越来越多的SUSHI。 谁会收到这个SUSHI? 任何进入SushiBar的人。用户进入后会收到xSUSHI,这有点像Uniswap的LP代币。它们代表了SushiBar中SUSHI代币的所有权。 你收到的xSUSHI数量是你转移的SUSHI * xSUSHI发行总量 / SUSHI的当前余额...

  • 原文:https://soliditydeveloper.com/sushi-swap
  • 译文出自:登链翻译计划
  • 译者:翻译小组
  • 校对:Tiny 熊
  • 本文永久链接:learnblockchain.cn/article…

你现在可能已经听说过SushiSwap了。其分叉自Uniswap,同时带来了新的功能,比如质押和治理。但它背后的合约究竟是如何运作的呢?

其实这并不难。详细了解这个工作原理将是学习Solidity和Defi的一个好方法。

合约细节

我们现在将深入SushiSwap的实现细节。为了更容易理解,一些代码被修改了。你可以随时查看Github里面的完整代码。

1. Uniswap v2

SushiSwap核心部分只是一个Uniswap v2的分叉。除了一些小的差别,合同代码是完全复制的。如果你对Uniswap v2的工作原理感到好奇,请看我以前的文章这里。在不久的将来对Uniswap v3的深入研究。

特别是SushiSwap正在利用流动性池代币(LP代币)。请查看https://uniswap.org/docs/v2/core-concepts/pools/ 在 LP 代币下的所有细节。从本质上讲,LP是用来接收在流动池中累积的按比例费用。你在一个流动池里提供流动性,并得到LP代币的回报。当资金池现在随着交易的进行收取费用时,它们会在交易时平均分配给所有LP持有人。当你销毁LP代币时,则将收到对应流动池中的资金份额和收集的费用。

SushiSwap对Uniswap代码做了两处修改:

  1. 在部署中调用了setFeeTo函数,费用接受者被设置为SushiMaker合约(见下文)。一旦费用接受者被设置,由于当前交易导致的LP 发行量增长的1/6将以LP代币的形式被铸成协议费用。由于Uniswap的交易费是0.3%,因此每笔交易的0.05%的费用将流向SushiMaker。
  2. 增加了一个迁移者功能(见下文SushiRoll)。

你可以看到Uniswap的核心合约和SushiSwap如何改变它们之间的全部差异这里。

2. SushiMaker

SushiMaker将从SushiSwap上交易的人那里接收LP代币。它主要由一个 convert 函数组成,它的作用如下:

  1. 销毁(Burn)掉提供的LP代币。其结果将是收到按比例的token0token1的数量。
  2. convertStep内将收到的两个代币兑换为SUSHI(代币)。如果没有直接的流动池来兑换为SUSHI,这可能需要额外的步骤。
function convert(address token0, address token1) external {
  UniV2Pair pair = UniV2Pair(factory.getPair(token0, token1));
  require(address(pair) != address(0), "Invalid pair");

  IERC20(address(pair)).safeTransfer(
      address(pair),
      pair.balanceOf(address(this))
  );

  (uint256 amount0, uint256 amount1) = pair.burn(address(this));

  if (token0 != pair.token0()) {
      (amount0, amount1) = (amount1, amount0);
  }

  _convertStep(token0, token1, amount0, amount1)
}

兑换本身也是通过SushiSwap 流动池来进行的。可以通过_swap函数来看看这是如何做到的。

function _swap(
    address fromToken,
    address toToken,
    uint256 amountIn,
    address to
) internal returns (uint256 amountOut) {
    UniV2Pair pair =
        UniV2Pair(factory.getPair(fromToken, toToken));
    require(address(pair) != address(0), "Cannot convert");

    (uint256 reserve0, uint256 reserve1, ) = pair.getReserves();
    uint256 amountInWithFee = amountIn.mul(997);

    if (fromToken == pair.token0()) {
        amountOut =
            amountInWithFee.mul(reserve1) /
            reserve0.mul(1000).add(amountInWithFee);
        IERC20(fromToken).safeTransfer(address(pair), amountIn);
        pair.swap(0, amountOut, to, new bytes(0));
    } else {
        amountOut =
            amountInWithFee.mul(reserve0) /
            reserve1.mul(1000).add(amountInWithFee);
        IERC20(fromToken).safeTransfer(address(pair), amountIn);
        pair.swap(amountOut, 0, to, new bytes(0));
    }
}

开始看到它使用了Uniswap低级别的 swap 函数。

  1. 获取池中两个代币的当前储备。
  2. 从储备金和被兑换的代币金额中计算出接收金额,减去费用。计算是基于x*y=k曲线。

一旦计算出金额,我们就可以进行兑换了。转换为SUSHI的最后一步将总是通过传递SUSHI代币地址来调用_swap函数,并将其发送到bar:

_swap(token, sushi, amountIn, bar)

就这样,把所有的LP代币转换成了SUSHI,所有转换后的SUSHI都被送到了SushiBar,见下一个合约:

3. SushiBar

在SushiBar内,人们可以带着SUSHI进入,得到xSUSHI,然后带着更多的SUSHI离开。请记住,所有来自SushiMaker的SUSHI都被送到这里。所以随着时间的推移,SushiBar会积累越来越多的SUSHI。

谁会收到这个SUSHI?

任何进入SushiBar的人。用户进入后会收到xSUSHI,这有点像Uniswap的LP代币。它们代表了SushiBar中SUSHI代币的所有权。

你收到的xSUSHI数量是你转移的SUSHI * xSUSHI发行总量 / SUSHI的当前余额...

剩余50%的内容订阅专栏后可查看

  • 单篇购买 10学分
  • 永久订阅专栏 (30学分)
  • 发表于 2021-07-14 14:33
  • 阅读 ( 1941 )
  • 学分 ( 5 )
  • 分类:Solidity
  • 专栏:热门 DEFI 分析

评论