SUSHI的源码及方案解析二(智能合约部署补充)

我在上一篇说的是早期流动性挖矿部分。我们观看真正的sushi合约,发现不单单是那两个sol文件,因此本节就对剩下的文件重点部分做一下补充。

首先说一下。我的开发项目是智能设备自治,其中有一个很关键的部分就是,物联网设备之前如何进行有价的信息交换。 我在上一篇说的是早期流动性挖矿部分。我们观看真正的sushi合约,发现不单单是那两个sol文件,因此本节就对剩下的文件重点部分做一下补充。由于相关的笔记都是半年前做的,比较混乱,所以大家仅做参考即可。

智能合约内容补充

正式的sushi合约分两部分,一部分是sushi本体,一部分是uniswap部分。因为uniswap有很多人都讲过,所以这里只讲sushi本体。 GovernorAlpha.sol涉及到治理; Migrations.sol涉及到部署; MockERC20.sol涉及到新LP的发布; SushiToken.sol顾名思义就是sushi自身代币的发布。 这些都比较简单,我们对下面几个文件做一下描述。

Timelock.sol

高权限功能的实现。 重点是executeTransaction函数:

    function executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public payable returns (bytes memory) {
        require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");

        bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
        require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
        require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
        require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale.");

        queuedTransactions[txHash] = false;

        bytes memory callData;

        if (bytes(signature).length == 0) {//可以理解为是一个签名为0
            callData = data;
        } else {
            callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);//calldata为data地址
        }

        // solium-disable-next-line security/no-call-value
        (bool success, bytes memory returnData) = target.call.value(value)(callData);//call函数转账的使用方法是:地址.call.value(转账金额)()
        require(success, "Timelock::executeTransaction: Transaction execution reverted.");

        emit ExecuteTransaction(txHash, target, value, signature, data, eta);

        return returnData;
    }

Migrator.sol

迁移

contract Migrator {
    address public chef;
    address public oldFactory;//因此这里面的oldFactory应该就是uniswap的factory
    IUniswapV2Factory public factory;//应该是我们自己的新factory
    uint256 public notBeforeBlock;
    uint256 public desiredLiquidity = uint256(-1);
    constructor(
        address _chef,
        address _oldFactory,
        IUniswapV2Factory _factory,
        uint256 _notBeforeBlock
    ) public {
        chef = _chef;
        oldFactory = _oldFactory;
        factory = _factory;
        notBeforeBlock = _notBeforeBlock;
    }

    //chef里面的引用。
    //IERC20 newLpToken = migrator.migrate(lpToken);//完成转移,用的是lpToken的factory
    //简单说如果没有就是新建,如果有则chef获取所有的token0和1,然后挖出来的新的lp令牌
    //最后进行令牌的更换(对于挖矿的人来说,换了一个流动资金池,这个资金池里面可以取出来lp,同时还存储令牌对应的token0和1)
    //如果这个矿池被开发者控制则非常的危险。
    function migrate(IUniswapV2Pair orig) public returns (IUniswapV2Pair) {
        require(msg.sender == chef, "not from master chef");//发送者必须是chef
        require(block.number >= notBeforeBlock, "too early to migrate");//迁移时间必须是大于这个值才行
        require(orig.factory() == oldFactory, "not from old factory");//
        address token0 = orig.token0();//然后将token0和1分别授权
        address token1 = orig.token1();
        IUniswapV2Pair pair = IUniswapV2Pair(factory.getPair(token0, token1));//引入一个我们自己factory的地址
        if (pair == IUniswapV2Pair(address(0))) {
            pair = IUniswapV2Pair(factory.createPair(token0, token1));//如果没有则新建
        }
        uint256 lp = orig.balanceOf(msg.sender);//获得信息发送者,也就是chef的所有令牌数量
        if (lp == 0) return pair;//类似于新建
        desiredLiquidity = lp;
        orig.transferFrom(msg.sender, address(orig), lp);//将lp从chef里面发到旧的orig地址(意味着返回所有的token0和1到chef
        orig.burn(address(pair));//烧掉所有在orig(旧的)的pair的lp
        pair.mint(msg.sender);//挖新的lp到chef里面去
        desiredLiquidity = uint256(-1);
        return pair;//返回pair
    }
}

MasterChef.sol

    //这里主要涉及到一些基本的设定

    function poolLength() external view returns (uint256) {
        return poolInfo.length;
    }

    // Add a new lp to the pool. Can only be called by the owner.
    // XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do.
    //增加新币,管理员才可以
    function add(uint256 _allocPoint, IERC20 _lpToken, bool _withUpdate) public onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }
        uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;
        totalAllocPoint = totalAllocPoint.add(_allocPoint);
        //下面这个是建立新的信息
        poolInfo.push(PoolInfo({
            lpToken: _lpToken,
            allocPoint: _allocPoint,
            lastRewardBlock: lastRewardBlock,
            accSushiPerShare: 0
        }));
    }

    // Update the given pool's SUSHI allocation point. Can only be called by the owner.
    //设置池子的sushi点,管理员才行
    function set(uint256 _pid, uint256 _allocPoint, bool _withUpdate) public onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }
        totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);
        poolInfo[_pid].allocPoint = _allocPoint;
    }

    // Set the migrator contract. Can only be called by the owner.
    //设置迁移命令
    function setMigrator(IMigratorChef _migrator) public onlyOwner {
        migrator = _migrator;
    }

    // Migrate lp token to another lp contract. Can be called by anyone. We trust that migrator contract is good.
    //令牌的迁移,说白了就是把在uniswap里面存的币转到chef里面,同时分新的令牌
    //由于可以一个池子一个池子的转换,所以不用担心安全性,可以先测试一下再进行。
    function migrate(uint256 _pid) public {
        require(address(migrator) != address(0), "migrate: no migrator");
        PoolInfo storage pool = poolInfo[_pid];//获得池子的id
        IERC20 lpToken = pool.lpToken;//获得池子id对应的pl令牌地址
        uint256 bal = lpToken.balanceOf(address(this));//获得这个令牌地址里面在这次池子所有的币
        lpToken.safeApprove(address(migrator), bal);//对migrator进行一个安全的授权
        IERC20 newLpToken = migrator.migrate(lpToken);//完成转移,用的是lpToken的factory
        require(bal == newLpToken.balanceOf(address(this)), "migrate: bad");//这里需要做一个令牌数量的校验
        pool.lpToken = newLpToken;
    }

    // Return reward multiplier over the given _from to _to block.
    //得到每块的币量说明
    function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) {
        if (_to <= bonusEndBlock) {
            return _to.sub(_from).mul(BONUS_MULTIPLIER);
        } else if (_from >= bonusEndBlock) {
            return _to.sub(_from);
        } else {
            return bonusEndBlock.sub(_from).mul(BONUS_MULTIPLIER).add(
                _to.sub(bonusEndBlock)
            );
        }
    }

    // View function to see pending SUSHIs on frontend.
    //系统发送sushi到pool,谁都可以调用
    function pendingSushi(uint256 _pid, address _user) external view returns (uint256) {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];//地址
        uint256 accSushiPerShare = pool.accSushiPerShare;//地址的配额
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));//发送到这个地址的lp的总量(也就是分红池的lp)
        if (block.number > pool.lastRewardBlock && lpSupply != 0) {//如果是没有刷新,则意味着有币
            uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
            uint256 sushiReward = multiplier.mul(sushiPerBlock).mul(pool.allocPoint).div(totalAllocPoint);
            accSushiPerShare = accSushiPerShare.add(sushiReward.mul(1e12).div(lpSupply));
        }
        //上面是做一个判断
        return user.amount.mul(accSushiPerShare).div(1e12).sub(user.rewardDebt);
    }

    // Update reward vairables for all pools. Be careful of gas spending!
    //更新参数,被add调用
    function massUpdatePools() public {
        uint256 length = poolInfo.length;
        for (uint256 pid = 0; pid < length; ++pid) {
            updatePool(pid);
        }
    }

    // Update reward variables of the given pool to be up-to-date.
    //更新参数,间接被add调用
    function updatePool(uint256 _pid) public {
        PoolInfo storage pool = poolInfo[_pid];
        if (block.number <= pool.lastRewardBlock) {
            return;
        }
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));
        if (lpSupply == 0) {
            pool.lastRewardBlock = block.number;
            return;
        }
        uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
        uint256 sushiReward = multiplier.mul(sushiPerBlock).mul(pool.allocPoint).div(totalAllocPoint);
        sushi.mint(devaddr, sushiReward.div(10));
        sushi.mint(address(this), sushiReward);
        pool.accSushiPerShare = pool.accSushiPerShare.add(sushiReward.mul(1e12).div(lpSupply));
        pool.lastRewardBlock = block.number;
    }

    // Deposit LP tokens to MasterChef for SUSHI allocation.
    //放置lp,用户调用
    function deposit(uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);
        if (user.amount > 0) {
            uint256 pending = user.amount.mul(pool.accSushiPerShare).div(1e12).sub(user.rewardDebt);
            safeSushiTransfer(msg.sender, pending);
        }
        pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
        user.amount = user.amount.add(_amount);
        user.rewardDebt = user.amount.mul(pool.accSushiPerShare).div(1e12);
        emit Deposit(msg.sender, _pid, _amount);
    }

    // Withdraw LP tokens from MasterChef.
    //撤回lp,用户调用
    function withdraw(uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        require(user.amount >= _amount, "withdraw: not good");
        updatePool(_pid);
        uint256 pending = user.amount.mul(pool.accSushiPerShare).div(1e12).sub(user.rewardDebt);
        safeSushiTransfer(msg.sender, pending);
        user.amount = user.amount.sub(_amount);
        user.rewardDebt = user.amount.mul(pool.accSushiPerShare).div(1e12);
        pool.lpToken.safeTransfer(address(msg.sender), _amount);
        emit Withdraw(msg.sender, _pid, _amount);
    }

    // Withdraw without caring about rewards. EMERGENCY ONLY.
    //一次性返回
    function emergencyWithdraw(uint256 _pid) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        pool.lpToken.safeTransfer(address(msg.sender), user.amount);
        emit EmergencyWithdraw(msg.sender, _pid, user.amount);
        user.amount = 0;
        user.rewardDebt = 0;
    }

SushiBar.sol

// Enter the bar. Pay some SUSHIs. Earn some shares.
//本质上是一个分红池,但没有看到分红去哪里了(eth)
function enter(uint256 _amount) public {
    uint256 totalSushi = sushi.balanceOf(address(this));//本地址里面的sushi总量
    uint256 totalShares = totalSupply();//已经做出来的总量
    if (totalShares == 0 || totalSushi == 0) {
        _mint(msg.sender, _amount);
    } else {
        uint256 what = _amount.mul(totalShares).div(totalSushi);//简单说就是,放进去的越少,给的越多
        _mint(msg.sender, what);
    }
    sushi.transferFrom(msg.sender, address(this), _amount);//挖出来发送过去
}

// Leave the bar. Claim back your SUSHIs.
function leave(uint256 _share) public {
    uint256 totalShares = totalSupply();
    uint256 what = _share.mul(sushi.balanceOf(address(this))).div(totalShares);
    _burn(msg.sender, _share);
    sushi.transfer(msg.sender, what);//撤出的份额
}

SushiMaker.sol

contract SushiMaker {
    using SafeMath for uint256;

    IUniswapV2Factory public factory;
    address public bar;
    address public sushi;
    address public weth;

    //新的工厂地址,sushibar地址,sushi地址,weth地址
    constructor(IUniswapV2Factory _factory, address _bar, address _sushi, address _weth) public {
        factory = _factory;
        sushi = _sushi;
        bar = _bar;
        weth = _weth;
    }

        //一直没太理解,但应该就是分红。
    function convert(address token0, address token1) public {
        // At least we try to make front-running harder to do.
        require(msg.sender == tx.origin, "do not convert from contract");
        //首先发送者必须是地址而不是合约
        IUniswapV2Pair pair = IUniswapV2Pair(factory.getPair(token0, token1));
        //获得两个token的pair
        pair.transfer(address(pair), pair.balanceOf(address(this)));
        //按照数量从本地址到pair地址
        pair.burn(address(this));//将本地烧毁
        uint256 wethAmount = _toWETH(token0) + _toWETH(token1);//简单说就是将一对pair换算为eth,如果是sushi,则直接传输到bar并返回0,并且下面也是0
        //如果是weth,则传输到eth/sushi交易对,并返回数量。下面执行该数量。
        //如果都不是,则看看下面有没有对应的pair,那么多?都是为了解决对应pair的ab问题。
        //简单说如果是sushi/eth,则sushi传输到bar,eth传输到sushi/eth交易对。
        _toSUSHI(wethAmount);//然后再转换为sushi。
    }

    function _toWETH(address token) internal returns (uint256) {
        if (token == sushi) {//如何这个币是sushi,则直接传输到bar里面,
            uint amount = IERC20(token).balanceOf(address(this));
            IERC20(token).transfer(bar, amount);
            return 0;
        }//如果是sushi则返回
        if (token == weth) {
            uint amount = IERC20(token).balanceOf(address(this));
            IERC20(token).transfer(factory.getPair(weth, sushi), amount);//如果是eth,则直接传输到eth/sushi交易对里面去。
            return amount;
        }//也就是说,如果是eth/sushi,则默认处理。
        IUniswapV2Pair pair = IUniswapV2Pair(factory.getPair(token, weth));
        if (address(pair) == address(0)) {//如果不存在这个交易对则返回。
            return 0;
        }
        //如果都不是则继续往下走
        (uint reserve0, uint reserve1,) = pair.getReserves();
        address token0 = pair.token0();
        (uint reserveIn, uint reserveOut) = token0 == token ? (reserve0, reserve1) : (reserve1, reserve0);
        uint amountIn = IERC20(token).balanceOf(address(this));
        uint amountInWithFee = amountIn.mul(997);
        uint numerator = amountInWithFee.mul(reserveOut);
        uint denominator = reserveIn.mul(1000).add(amountInWithFee);
        uint amountOut = numerator / denominator;
        (uint amount0Out, uint amount1Out) = token0 == token ? (uint(0), amountOut) : (amountOut, uint(0));
        IERC20(token).transfer(address(pair), amountIn);//这个就是核心语句,将执行convert的人的token币传输到pair里面去。
        pair.swap(amount0Out, amount1Out, factory.getPair(weth, sushi), new bytes(0));//完成一次token的交换
        return amountOut;
    }

    function _toSUSHI(uint256 amountIn) internal {
        IUniswapV2Pair pair = IUniswapV2Pair(factory.getPair(weth, sushi));
        //获取eth/sushi交易对信息
        (uint reserve0, uint reserve1,) = pair.getReserves();
        address token0 = pair.token0();
        (uint reserveIn, uint reserveOut) = token0 == weth ? (reserve0, reserve1) : (reserve1, reserve0);//获得两个token的价格比
        uint amountInWithFee = amountIn.mul(997);//去掉税后的钱
        uint numerator = amountInWithFee.mul(reserveOut);//乘以价格比
        uint denominator = reserveIn.mul(1000).add(amountInWithFee);//另一个价格比
        uint amountOut = numerator / denominator;
        (uint amount0Out, uint amount1Out) = token0 == weth ? (uint(0), amountOut) : (amountOut, uint(0));
        pair.swap(amount0Out, amount1Out, bar, new bytes(0));//交换bar里面的两个值。
    }
}

智能合约部署补充

UniswapV2Factory.solo:p 可以设置一个迁移权限,这个需要管理员完成 feeToSetter,migrator。 Uniswap最终就布置一个文件即可,UniswapV2Factory.sol,但估计中间会生成很多中间合约,记得保存。 这个需要布置,在后面的Factory用 代币合同,带有COMP / YAM投票功能。 (需要部署,第一级)SushiToken.sol里面没有constructoro:p (需要部署,第一级)Timelock.sol,构造函数:管理员(地址),延伸时间(2到30天) 来自Compound的治理功能 (需要部署,第二级)GovernorAlpha.sol构造函数:时间锁地址,SushiToken地址(SushiToken public sushi),guardian(监护人地址,可以跟管理员不是一个人) 存入LP代币以进行SUSHI farm (需要部署,第二级)[MasterChef]().sol构造函数:SushiToken地址,devaddr地址(治理员,跟前两个不是一个),sushiPerBlock(每块创建的寿司代币数量,40暂定)bonusEndBlock(块结束时间,区块号,在这个后面就开始变少了8643000),startBlock(开始块的时间,开始才出口,8623000) Ps:用户调用的就是这块,也就意味着收益都存在MasterChef里面。同时也就意味着,分红需要调用这个地址。o:p (需要部署,第二级)SushiBar:抵押SUSHI可获得更多SUSHI。 是一个erc20币,sushibar,简称xSUSHI,ERC20("SushiBar", "xSUSHI") 构造的时候需要填入SUSHI的地址。 (需要部署,第三级)SushiMaker:收集交易费,转换为SUSHI,然后发送到SushiBar。 构架器:bar是sushibar的地址,factory是uniswap的factory地址(因为在sol里面一直强调的是getpair,0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f),sushi是token的地址,-web:0xc778417E063141139Fce010982780140Aa0cD5Ab 我先是add(没有用true),默认0参数。

点赞 1
收藏 2
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
问答区块链
问答区块链
致力于人工智能与区块链相结合下的智能设备自治