我在上一篇说的是早期流动性挖矿部分。我们观看真正的sushi合约,发现不单单是那两个sol文件,因此本节就对剩下的文件重点部分做一下补充。
首先说一下。我的开发项目是智能设备自治,其中有一个很关键的部分就是,物联网设备之前如何进行有价的信息交换。 我在上一篇说的是早期流动性挖矿部分。我们观看真正的sushi合约,发现不单单是那两个sol文件,因此本节就对剩下的文件重点部分做一下补充。由于相关的笔记都是半年前做的,比较混乱,所以大家仅做参考即可。
正式的sushi合约分两部分,一部分是sushi本体,一部分是uniswap部分。因为uniswap有很多人都讲过,所以这里只讲sushi本体。 GovernorAlpha.sol涉及到治理; Migrations.sol涉及到部署; MockERC20.sol涉及到新LP的发布; SushiToken.sol顾名思义就是sushi自身代币的发布。 这些都比较简单,我们对下面几个文件做一下描述。
高权限功能的实现。 重点是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;
}
迁移
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
}
}
//这里主要涉及到一些基本的设定
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;
}
// 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);//撤出的份额
}
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参数。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!