nest2.0智能合约架构解析四

2021-02-25 11:34:52 浏览数 (1)

继前文[1],我们在这里对 nest2.0 剩下的三个文件(NESTNODE,NEST_3_OrePoolLogic,NEST_3_OfferFactory)做简单的解析。NESTNODE.sol主要是守护者节点的内容,制作 1500 个 token,并提供高级权限,比如分红。里面生成了 4 个合约。其中最复杂也需要最后运行的 NEST_NodeAssignment。NEST_3_OrePoolLogic这个合约主要是设定各种报价出块的参数。这个合约不涉及执行任何 NEST_3_OfferFactory,仅仅验证运行者是不是 NEST_3_OfferFactory.NEST_3_OfferFactory主要讲的是报价工厂的成立,与各种价格的成交。orePoolLogic 提供各种报价功能。

1.注意事项

1.在 NEST_3_OrePoolLogic,和 NEST_3_OfferFactory,已经很确定是缺失了一个合约——NEST_2_Mapping,但通过函数形式,我怀疑和 IBMapping 内容基本一样。2.在 NEST_3_OrePoolLogic,还缺少一个合约——NEST_3_MiningSave,但通过函数形式,我怀疑和 NEST_MiningSave 内容基本一样,但应该是做了很多的修改。3.因为几个合约的缺少,因此我们无法完整的对 nest2.0 进行实现。不过大家也能注意到 nest2.0 明显是一个过渡品,因此我们只需要理解即可,不需要过度的深究。

2.NESTNODE

主要是守护节点的相关内容,与整个预言机关系并不大。简单说就是制作 1500 个 token,并提供高级权限。

2.1NEST_NodeSave

NEST_NodeSave 的功能就是从这个合约里面把 node 的币转出来。其中注意 turnOut,这里面的重点功能就在这里,限制为 nodeAssignment 必须是发送者。

代码语言:javascript复制
    function turnOut(uint256 amount, address to) public onlyMiningCalculation returns(uint256) {
        uint256 leftNum = nestContract.balanceOf(address(this));
        if (leftNum >= amount) {
            nestContract.transfer(to, amount);
            return amount;
        } else {
            return 0;
        }
    }

    modifier onlyOwner(){
        require(mappingContract.checkOwners(msg.sender) == true);
        _;
    }

    modifier onlyMiningCalculation(){
        require(address(mappingContract.checkAddress("nodeAssignment")) == msg.sender);
  //NEST_NodeAssignment,就是这个
        _;
    }

2.2NEST_NodeAssignmentData

这里面的最开始的那个大值好像是未提取的。如果按 18 位计算,是 9500 个,然后不断的累加。

代码语言:javascript复制
contract NEST_NodeAssignmentData {
    using SafeMath for uint256;
    IBMapping mappingContract;
    uint256 nodeAllAmount = 9546345842385995696603;
//目前需要提取的是9546个
    mapping(address => uint256) nodeLatestAmount;

    /**
    * @dev Initialization method
    * @param map Mapping contract address
    */
    constructor (address map) public {
        mappingContract = IBMapping(map);
    }

    /**
    * @dev Change mapping contract
    * @param map Mapping contract address
    */
    function changeMapping(address map) public onlyOwner{
        mappingContract = IBMapping(map);
    }

    //  Add nest
    function addNest(uint256 amount) public onlyNodeAssignment {
        nodeAllAmount = nodeAllAmount.add(amount);
  //总量里面增加
    }
 //总量里面增加相关的数量,需要权限

    //  View cumulative total
    function checkNodeAllAmount() public view returns (uint256) {
        return nodeAllAmount;
    }
 //查看总量

    //  Record last received quantity
    function addNodeLatestAmount(address add ,uint256 amount) public onlyNodeAssignment {
        nodeLatestAmount[add] = amount;
    }
 //将add地址里面充值。

    //  View last received quantity
    function checkNodeLatestAmount(address add) public view returns (uint256) {
        return nodeLatestAmount[address(add)];
    }

    modifier onlyOwner(){
        require(mappingContract.checkOwners(msg.sender) == true);
        _;
    }

    modifier onlyNodeAssignment(){
        require(address(msg.sender) == address(mappingContract.checkAddress("nodeAssignment")));
        _;
    }
}

2.3NEST_NodeAssignment

主要函数 初始化 3 个合约都是本文件里面的 bookKeeping,往 nodesave 里面转币 nodeGet,从 nodesave 里面往 node 币持有者发币。nodeCount,在转账 node 币之前,清空分红计数器。主要是 bookKeeping 和 nodeGet,以及 nodeCount,这三个功能函数。

代码语言:javascript复制
function bookKeeping(uint256 amount) public {
        require(amount > 0);
        require(nestContract.balanceOf(address(msg.sender)) >= amount);
        require(nestContract.allowance(address(msg.sender), address(this)) >= amount);
        require(nestContract.transferFrom(address(msg.sender), address(nodeSave), amount));
        nodeAssignmentData.addNest(amount);
    }
 //往nodeSave里面转币,并对总量进行增加

    /**
    * @dev Guardian node collection
 //守护者节点的发币
    */
    function nodeGet() public {
        require(address(msg.sender) == address(tx.origin));
  //必须是普通地址
        require(supermanContract.balanceOf(address(msg.sender)) > 0);
  //必须是下面守护节点合约里面的用户
        uint256 allAmount = nodeAssignmentData.checkNodeAllAmount();
  //获得总量
        uint256 amount = allAmount.sub(nodeAssignmentData.checkNodeLatestAmount(address(msg.sender)));
  //获得发送者的币
        uint256 getAmount = amount.mul(supermanContract.balanceOf(address(msg.sender))).div(1500);
  //与币量乘,然后再除以1500
        require(nestContract.balanceOf(address(nodeSave)) >= getAmount);
        nodeSave.turnOut(getAmount,address(msg.sender));
  //把币传出去。
        nodeAssignmentData.addNodeLatestAmount(address(msg.sender),allAmount);
  //刷新量。
    }

    /**
    * @dev Transfer settlement
    * @param fromAdd Transfer out address
    * @param toAdd Transfer in address
 说白了就是在转账node币之前,清空分红计数器。
    */
    function nodeCount(address fromAdd, address toAdd) public {
        require(address(supermanContract) == address(msg.sender));
  //必须是超级节点里面的用户
        require(supermanContract.balanceOf(address(fromAdd)) > 0);
  //发送方必须有super币
        uint256 allAmount = nodeAssignmentData.checkNodeAllAmount();
  //获得nest累计值

        uint256 amountFrom = allAmount.sub(nodeAssignmentData.checkNodeLatestAmount(address(fromAdd)));
  //减去后的值
        uint256 getAmountFrom = amountFrom.mul(supermanContract.balanceOf(address(fromAdd))).div(1500);
  //获得应该有的nest分红
        require(nestContract.balanceOf(address(nodeSave)) >= getAmountFrom);
        nodeSave.turnOut(getAmountFrom,address(fromAdd));
  //转账应该有的分红过去
        nodeAssignmentData.addNodeLatestAmount(address(fromAdd),allAmount);
  //将最后发出的赋值。

        uint256 amountTo = allAmount.sub(nodeAssignmentData.checkNodeLatestAmount(address(toAdd)));
        uint256 getAmountTo = amountTo.mul(supermanContract.balanceOf(address(toAdd))).div(1500);
        require(nestContract.balanceOf(address(nodeSave)) >= getAmountTo);
        nodeSave.turnOut(getAmountTo,address(toAdd));
        nodeAssignmentData.addNodeLatestAmount(address(toAdd),allAmount);
    }

2.4SuperMan

守护者币的发行。这个里面特别有意思的一个地方是,decimals 为 0,意味着不可分割。这里面唯一注意的是一个隐藏函数_transfer,传输之前先清空 nest 分红。nodeAssignment.nodeCount(from, to); 其余的部分就和一般的 erc20 差不多了,就不贴分析的源码了。

3.NEST_3_OrePoolLogic

就一个合约函数,但比较复杂。1.合约刚开的部分主要是对挖矿和分红进行处理;2.首先是挖矿衰减问题;3.然后是挖矿的出块和产量问题;4.最后是分红问题。这个里面的重点是初始化部分,我就把这部分源码贴出来就行,剩下都比较好理解了。

代码语言:javascript复制
    using address_make_payable for address;
    using SafeMath for uint256;
    uint256 blockAttenuation = 2400000;                         //  Block attenuation interval
 //每隔 2400000 个区块(约为 1 年)衰减一次,每个区块产出量衰减为原来的 80%;
 ////  区块衰减间隔
    uint256 attenuationTop = 90;                                //  Attenuation coefficient
 //衰减系数

    uint256 attenuationBottom = 100;                            //  Attenuation coefficient
 //衰减系数
    mapping(uint256 => mapping(address => uint256)) blockEth;   //  Total service charge of quotation block. block No. = > token address = > total service charge
 //报价块服务费合计。区块号=>令牌地址=>总服务费
 //内嵌的映射是一个地址,类型是uint256
 // mapping(uint256 => address),映射是一个uint256,类型是地址,
 //建立 一个这样的blockEth,用于服务费
    mapping(uint256 => uint256) blockTokenNum;                  //  Block currency quantity. block number = > currency quantity
 //每块的token数量,映射的是数量到数量
   mapping(uint256 => uint256) blockMining;                    //  Ore yield of quotation block. Block No. = > ore yield
 //报价区块链的挖矿数量,映射的是从数量到数量
   uint256 latestMining;                                       //  Latest quotation block
   //最后一个报价的区块
    NEST_2_Mapping mappingContract;                             //  Mapping contract
 //mapping合约,看后面的结构,应该和IBMapping一样
    NEST_3_MiningSave miningSave;                               //  Ore pool contract
 //矿池合约

    address abonusAddress;                                      //  Address of dividend pool
    //分红池地址
 address offerFactoryAddress;                                //  Offer factory contract address
 //工厂红利合约
    mapping(uint256 => uint256) blockAmountList;                //  Attenuation list. block number = > attenuation coefficient
    //衰减列表,从区块数到衰减系数
 uint256 latestBlock;                                        //  Latest attenuation block
 //最后一个衰减的区块
    //  Current block, last quoted block, current block ore yield, current handling fee, token address
 //当前块,最后报价的块,当前块矿石产量,当前处理费,令牌地址
    event oreDrawingLog(uint256 nowBlock, uint256 frontBlock, uint256 blockAmount, uint256 miningEth, address tokenAddress);
    //  Quotation block, token address, all handling charges of token, my handling charges, number of tokens
    //报价块,令牌地址,令牌的所有处理费用,我的处理费用,令牌的数量
 event miningLog(uint256 blockNum, address tokenAddress, uint256 miningEthAll, uint256 miningEthSelf, uint256 tokenNum);

    /**
    * @dev Initialization method
    * @param map Mapping contract address
    */
 //初始化map Mapping合约地址
    constructor(address map) public {
        mappingContract = NEST_2_Mapping(address(map));
  //这个是NEST_2_Mapping对应的合约地址(没有源码)
        miningSave = NEST_3_MiningSave(mappingContract.checkAddress("miningSave"));
  //这个貌似是NEST_3_MiningSave对应的合约地址(没有源码)
        abonusAddress = address(mappingContract.checkAddress("abonus"));
  //这个对应的是NESTAbonus合约地址
        offerFactoryAddress = address(mappingContract.checkAddress("offerFactory"));
  //这个对应的是offerFactory的地址
        latestBlock = block.number.sub(388888);
        latestMining = block.number;
        blockAmountList[block.number.sub(2788888)] = 400 ether;
        blockAmountList[block.number.sub(388888)] = blockAmountList[block.number.sub(2788888)].mul(attenuationTop).div(attenuationBottom);
    }

4.NEST_3_OfferFactory

这里是预言机最关键的地方,如何实现的部分。一共四个合约。

4.1NEST_3_OfferData

这个理解起来比较简单,就是做报价合约审查的。值得注意是,在构造器里面,需要新的 NEST_2_Mapping。此外 addContractAddress(必须由超级用户执行)和 checkContract 分别是用来增加合约地址和检测合约地址。

4.2NEST_3_OfferFactory

这里面啰里啰嗦一大堆,但都是非常重要的内容,简单说就是如何的报价,如何的转账,还有各种报价的参数。

代码语言:javascript复制
    using SafeMath for uint256;
    using address_make_payable for address;
    mapping(address => bool) tokenAllow;                //  Insured mining token
 //保险挖矿token
    NEST_2_Mapping mappingContract;                     //  Mapping contract
 //mapping
    NEST_3_OfferData dataContract;                      //  Data contract
 //前面的报价地址
    NEST_2_OfferPrice offerPrice;                       //  Price contract
 //报价价格
    NEST_3_OrePoolLogic orePoolLogic;                   //  Mining contract
 //挖矿逻辑
    NEST_NodeAssignment NNcontract;                     //  NestNode contract
 //守护者节点里面的工作分配
    ERC20 nestToken;                                    //  nestToken
 //token
    address abonusAddress;                              //  Dividend pool
    address coderAddress;                               //  Developer address
    uint256 miningETH = 10;                             //  Quotation mining service charge mining proportion, 10 thousandths
    //最少eth为10
 uint256 tranEth = 2;                                //  Service charge proportion of the bill of lading, 2 ‰
    //提单手续费2%
 uint256 blockLimit = 25;                            //  Block interval upper limit
    //25个块
 uint256 tranAddition = 2;                           //  Transaction bonus
    //交易奖金
 uint256 coderAmount = 5;                            //  Developer ratio
    //开发者比例
 uint256 NNAmount = 15;                              //  Guardian node proportion
    //node比例
 uint256 otherAmount = 80;                           //  Distributable proportion
    //上传者比例
 uint256 leastEth = 1 ether;                         //  Minimum offer eth

 uint256 offerSpan = 1 ether;                        //  Quotation eth span

    //  log Personal asset contract
    event offerTokenContractAddress(address contractAddress);
    //  log Quotation contract, token address, ETH quantity, erc20 quantity
    event offerContractAddress(address contractAddress, address tokenAddress, uint256 ethAmount, uint256 erc20Amount);
    //  log Transaction, transaction initiator, transaction token address, transaction token quantity, purchase token address, purchase token quantity, traded quotation contract address, traded user address
    event offerTran(address tranSender, address tranToken, uint256 tranAmount,address otherToken, uint256 otherAmount, address tradedContract, address tradedOwner);

    /**
    * @dev Initialization method
    * @param map Mapping contract address
    */
    constructor (address map) public {
        mappingContract = NEST_2_Mapping(map);
        offerPrice = NEST_2_OfferPrice(address(mappingContract.checkAddress("offerPrice")));
        orePoolLogic = NEST_3_OrePoolLogic(address(mappingContract.checkAddress("miningCalculation")));
        abonusAddress = mappingContract.checkAddress("abonus");
        nestToken = ERC20(mappingContract.checkAddress("nest"));
        NNcontract = NEST_NodeAssignment(address(mappingContract.checkAddress("nodeAssignment")));
        coderAddress = mappingContract.checkAddress("coder");
        dataContract = NEST_3_OfferData(address(mappingContract.checkAddress("offerData")));
    }
 //几乎填满了构造函数,但有几个nest2的没有。

    /**
    * @dev Change mapping contract
    * @param map Mapping contract address
    */
    function changeMapping(address map) public onlyOwner {
        mappingContract = NEST_2_Mapping(map);
        offerPrice = NEST_2_OfferPrice(address(mappingContract.checkAddress("offerPrice")));
        orePoolLogic = NEST_3_OrePoolLogic(address(mappingContract.checkAddress("miningCalculation")));
        abonusAddress = mappingContract.checkAddress("abonus");
        nestToken = ERC20(mappingContract.checkAddress("nest"));
        NNcontract = NEST_NodeAssignment(address(mappingContract.checkAddress("nodeAssignment")));
        coderAddress = mappingContract.checkAddress("coder");
        dataContract = NEST_3_OfferData(address(mappingContract.checkAddress("offerData")));
    }

    /**
    * @dev Quotation mining
    * @param ethAmount ETH amount
    * @param erc20Amount erc20 amount
    * @param erc20Address erc20Token address
 提交比例的函数
    */
    function offer(uint256 ethAmount, uint256 erc20Amount, address erc20Address) public payable {
        require(address(msg.sender) == address(tx.origin));
        uint256 ethMining = ethAmount.mul(miningETH).div(1000);
        require(msg.value == ethAmount.add(ethMining));
        require(tokenAllow[erc20Address]);
        createOffer(ethAmount,erc20Amount,erc20Address,ethMining);
        orePoolLogic.oreDrawing.value(ethMining)(erc20Address);
    }

    /**
    * @dev Generate quote
    * @param ethAmount ETH amount
    * @param erc20Amount erc20 amount
    * @param erc20Address erc20Token address
    * @param mining Mining Commission
 建立一个全新的token提交
    */
    function createOffer(uint256 ethAmount, uint256 erc20Amount, address erc20Address, uint256 mining) private {
        require(ethAmount >= leastEth);
        require(ethAmount % offerSpan == 0);
        require(erc20Amount % (ethAmount.div(offerSpan)) == 0);
        ERC20 token = ERC20(erc20Address);
        require(token.balanceOf(address(msg.sender)) >= erc20Amount);
        require(token.allowance(address(msg.sender), address(this)) >= erc20Amount);
        NEST_3_OfferContract newContract = new NEST_3_OfferContract(ethAmount,erc20Amount,erc20Address,mining,address(mappingContract));
        dataContract.addContractAddress(address(newContract));
        emit offerContractAddress(address(newContract), address(erc20Address), ethAmount, erc20Amount);
        token.transferFrom(address(msg.sender), address(newContract), erc20Amount);
        newContract.offerAssets.value(ethAmount)();
        offerPrice.addPrice(ethAmount,erc20Amount,erc20Address);
    }

    /**
    * @dev Take out quoted assets
    * @param contractAddress Address of quotation contract
 删除上链的资产
    */
    function turnOut(address contractAddress) public {
        require(address(msg.sender) == address(tx.origin));
        require(dataContract.checkContract(contractAddress));
        NEST_3_OfferContract offerContract = NEST_3_OfferContract(contractAddress);
        offerContract.turnOut();
        uint256 miningEth = offerContract.checkServiceCharge();
        uint256 blockNum = offerContract.checkBlockNum();
        address tokenAddress = offerContract.checkTokenAddress();
        if (miningEth > 0) {
            uint256 miningAmount = orePoolLogic.mining(miningEth, blockNum, address(this),tokenAddress);
            uint256 coder = miningAmount.mul(coderAmount).div(100);
            uint256 NN = miningAmount.mul(NNAmount).div(100);
            uint256 other = miningAmount.mul(otherAmount).div(100);
            nestToken.transfer(address(tx.origin), other);
            require(nestToken.approve(address(NNcontract), NN));
            NNcontract.bookKeeping(NN);
            nestToken.transfer(coderAddress, coder);
        }
    }

    /**
    * @dev Transfer erc20 to buy eth
    * @param ethAmount Offer ETH amount
    * @param tokenAmount Offer erc20 amount
    * @param contractAddress Address of quotation contract
    * @param tranEthAmount ETH amount of transaction
    * @param tranTokenAmount erc20 amount of transaction
    * @param tranTokenAddress erc20Token address
 传输erc20代币用来买eth
    */
    function ethTran(uint256 ethAmount, uint256 tokenAmount, address contractAddress, uint256 tranEthAmount, uint256 tranTokenAmount, address tranTokenAddress) public payable {
        require(address(msg.sender) == address(tx.origin));
        require(dataContract.checkContract(contractAddress));
        require(ethAmount >= tranEthAmount.mul(tranAddition));
        uint256 serviceCharge = tranEthAmount.mul(tranEth).div(1000);
        require(msg.value == ethAmount.add(tranEthAmount).add(serviceCharge));
        require(tranEthAmount % offerSpan == 0);
        createOffer(ethAmount,tokenAmount,tranTokenAddress,0);
        NEST_3_OfferContract offerContract = NEST_3_OfferContract(contractAddress);
        offerContract.changeOfferEth.value(tranEthAmount)(tranTokenAmount, tranTokenAddress);
        offerPrice.changePrice(tranEthAmount,tranTokenAmount,tranTokenAddress,offerContract.checkBlockNum());
        emit offerTran(address(tx.origin), address(0x0), tranEthAmount,address(tranTokenAddress),tranTokenAmount,contractAddress,offerContract.checkOwner());
        repayEth(abonusAddress,serviceCharge);
    }

    /**
    * @dev Transfer eth to buy erc20
    * @param ethAmount Offer ETH amount
    * @param tokenAmount Offer erc20 amount
    * @param contractAddress Address of quotation contract
    * @param tranEthAmount ETH amount of transaction
    * @param tranTokenAmount erc20 amount of transaction
    * @param tranTokenAddress erc20Token address
 传输erc20代币用来买eth
    */
    function ercTran(uint256 ethAmount, uint256 tokenAmount, address contractAddress, uint256 tranEthAmount, uint256 tranTokenAmount, address tranTokenAddress) public payable {
        require(address(msg.sender) == address(tx.origin));
        require(dataContract.checkContract(contractAddress));
        require(ethAmount >= tranEthAmount.mul(tranAddition));
        uint256 serviceCharge = tranEthAmount.mul(tranEth).div(1000);
        require(msg.value == ethAmount.add(serviceCharge));
        require(tranEthAmount % offerSpan == 0);
        createOffer(ethAmount,tokenAmount,tranTokenAddress,0);
        NEST_3_OfferContract offerContract = NEST_3_OfferContract(contractAddress);
        ERC20 token = ERC20(tranTokenAddress);
        require(token.balanceOf(address(msg.sender)) >= tranTokenAmount);
        require(token.allowance(address(msg.sender), address(this)) >= tranTokenAmount);
        token.transferFrom(address(msg.sender), address(offerContract), tranTokenAmount);
        offerContract.changeOfferErc(tranEthAmount,tranTokenAmount, tranTokenAddress);
        offerPrice.changePrice(tranEthAmount,tranTokenAmount,tranTokenAddress,offerContract.checkBlockNum());
        emit offerTran(address(tx.origin),address(tranTokenAddress),tranTokenAmount, address(0x0), tranEthAmount,contractAddress,offerContract.checkOwner());
        repayEth(abonusAddress,serviceCharge);
    }

4.3NEST_3_OfferContract

这个是报价合约,只有 NEST_3_OfferFactory 才能调用。相对来说比较简单,就不做解析了。

4.4NEST_2_OfferPrice

这个主要是做价格的更新。这个部分最主要的函数是 addPrice。由于在后面的 nest3.0 做了升级和修改,所以在这里不专门的讲述了。

5.总结

我们在这里对 nest2.0 做了简单的总结。整个 nest2.0 的代码是不同的时期开发的,总共包含了 7 个文件和 15 个合约,使得整体架构显得有些凌乱。我们在这里简单梳理一下。1.nest2.0 的 token 使用的还是 2019 年的,为了方便灵活升级,采用了 mapping 来进行对应。这就是头两个文件和合约,包括IBNESTIBMapping。2.然后为了激励 nest2.0 的参与者使用,开发了矿池合约和分红底层合约,这就是NEST_MiningSave.和NESTAbonus。3.为了方便对整个项目进行高效管理,开发了守护者节点和守护者合约,这就是NESTNODE。4.为了实现分红逻辑,实现了NEST_3_OrePoolLogic。5.为了实现预言机,开发了NEST_3_OfferFactory,这也是整个预言机最最重要的地方。因为整个 nest2.0 的实现并不完整,甚至有些混乱,所以我们只需要了解大致的结构就行。简单说就是,先有 nest 的 token,然后在此基础上实现了两个内容,一个是预言机,用于报价和获得 nesttoken;另一个是分红池,获得预言机的利润。接下来为了方便管理,还做了守护者节点。整篇文章因为制作的时间比较久,问题比较多,有问题可以给我留言。我们在下一篇文章,将对 nest3.0 和如何克隆一个 nest 预言机做继续的描述。

参考资料

[1]

前文: https://learnblockchain.cn/article/2066

0 人点赞