详解 SushiSwap

了解 SushiSwap 的实现细节

你现在可能已经听说过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

Sushi Maker

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

Sushi Bar

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

谁会收到这个SUSHI?

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

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

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

1 条评论

请先 登录 后评论
翻译小组
翻译小组

首席翻译官

92 篇文章, 14105 学分