分析每一个通过外部可以调用的函数以及函数之间的组合在合约内部不同状态的情况下,可能带来的风险。

Oracle控制流程安全性综合分析

包括函数: //取消 function cancelOracle(uint256 oracleID) external lock //加入 function joinOracleAndSetGoteRawStg( uint256 oracleID, uint256 stg, uint256 goteOracleBid ) external payable PCO_ACC(Oracles[oracleID].joinFee, 80) // 仲裁 function makeAnneal( uint256 oracleID, string calldata entropy, uint256 InitiativeStg, uint256 initOracleBid, address opponent ) public lock PCO_ACC(Oracles[oracleID].joinFee, 120) // 超时 function finishExpiredOracle(uint256 oracleID) public lock PCO_ACC(Oracles[oracleID].joinFee, 120) 下图中对应关系: 取消——cancelOracle、加入——joinOracleAndSetGoteRawStg、 仲裁——makeAnneal、超时——finishExpiredOracle 图中 revert 表示合约预言机不会执行,而是驳回发起的尝试攻击的交易。 先手 = 通过传入加密参数创建预言机的玩家 后手 = 使用明文参数加入预言机的玩家 陌生人 = 与交互的预言机无关,非创建人也非加入者的玩家

image.png

Openzeppelin 安全合约

  1. Approve:任何人可调用,安全性由openzeppelin保证
  2. decreaseAllowance:任何人可调用,安全性由openzeppelin保证
  3. increaseAllowance:任何人可调用,安全性由openzeppelin保证
  4. renounceOwnership:onlyOwner 调用,释放合约所有权,安全性由openzeppelin保证
  5. transferOwnership:onlyOwner 调用,转移所有权,安全性由openzeppelin保证
  6. ERC20系列函数:balanceOf、totalSupply、transfer、allowance、approve、transferFrom 的安全性均由openzeppelin保证。

其他函数安全性分析

  1. callLoopss(bytes calldata _data) external onlyOwner returns (bool, bytes memory):onlyowner可调用,通过调用Loopss来获得Loopss对该地址发行的内部地址Token以及修正该地址对其他地址的信任关系,为后续对接Loopss做准备。
    1. 通过call调用而非delegatecall,不会通过复制外部代码到该合约内修改该合约内部的变量。
      1. call: 最常用的调用方式,调用后内置变量 msg 的值会修改为调用者,执行环境为被调用者的运行环境(合约的 storage)。
      2. delegatecall: 调用后内置变量 msg 的值不会修改为调用者,但执行环境为调用者的运行环境。
  2. setLoopss(address _LOOPSSMEaddress) external onlyOwner:onlyowner可调用,设置Loopss合约地址
  3. deposit(uint256 amount) external lock returns (bool) :任何人。充值LPToken到该合约参与分红。
    1. 处理分支
      1. 新质押:私人分红变量 = 公共分红变量。消灭差值。
      2. 增加质押:
        1. 将收益保存到收益缓存变量中,即:私人内部缓存
        2. 私人分红变量 = 公共分红变量。消灭差值。
    2. 执行划转资金与入账
      1. _transfer内部转账,从调用者处转入数量amount到该地址
        1. 因为余额不足等调用失败的情况下,合约会revert拒绝执行整个交易
      2. 增加用户的挖矿NAP数量
      3. 增加总的挖矿NAP数量
  4. claim() public lock returns (bool):任何人。获取NAP挖矿挖到的本链原生代币NativeToken简称NT(以太坊上就是ETH,BSC上就是BNB)的分红。
    1. 通过lock加锁,防止payable调用外部进行重入
    2. 首先通过unClaimedETHsOf获得未领取的总的NT数量,由未实现的和已实现的两部分整合而来。
      1. 未实现的由公共分红变量和私人分红变量之差得到。
      2. 已经实现的由存入和取出挖矿代币时的交易进行结算私人内部缓存。
    3. 将NT数量保存到内存的_totalProfits变量中,并更新个人账户的私人分红变量和私人内部缓存。
      1. 将私人分红变量 设置为公共分红变量的值,使得插值为0
      2. 将私人内部缓存设置为0
    4. 通过transfer转账NT给调用者,transfer函数只拥有2300gas。
      1. 2300 gas 的限制,也就是传递给 fallback 的只有 2300 gas,这个 gas 只能用于记录日志,因为其他操作都将超过 2300 gas。
    5. lock解锁
  5. withdraw(uint256 amount) external lock returns (bool):任何人。提取交易发送者之前质押于此处参与分红的LPToken。
    1. 加锁lock,防止reEntry
    2. 首先保存分红到分红变量:toRealizeETH(); //save to player Realized ETHs_
    3. 之后减掉要提取的LPToken的个人和总体数量=amount
    4. 调用交易对合约,执行转账
    5. 解锁lock
  6. setPairAddress(address _swapPair_NAP_ETH_address) 设置挖矿用LPToken的$NAP交易对地址
  7. setPCORatio(uint256 _PCORatio) external onlyOwner 设置PCO的流量兑换比例,
    1. 仅在合约部署的60w区块之后才可调用才生效,之前调用执行无效
    2. 调用执行设置PCO流量兑换比例也不能大于之前60w区块兑换比例的最小值
  8. PCO_claimNAP() external:任何人可调用。兑换当前PCO流量为$NAP
    1. 获取当前赠送可兑换的$NAP数量x
    2. 将个人流量记录清零
    3. 使用openzeppelin的_mint 为函数调用地址铸币x个$NAP
    4. 整体上是先缓存,再更新状态,最后执行,即便有办法重入,也没有办法攻击。因为更新先于执行。
  9. setRevenueFeeMilli(uint256 _newRevenueFeeMilli) external onlyOwner 设置税收比例,最大为10%,仅在PCO完成也就是总供应量超过最大量之后可用。
    1. 总供应量超过最大量之后,PCO就被自动停止了,使得总供应量不会再继续增加。
    2. _newRevenueFeeMilli 为千分值,例如10就是千分之10。最大为10%,就是最大只能为 100,是为千分之100。

已废弃:

  1. _function_ transferAnyERC20Token(address tokenAddress, uint256 tokens)
  2. external
  3. onlyOwner
  4. returns (bool success)
  5. {
  6. if (tokenAddress **==** address(swapPair_NAP_ETH)) revert("Forbid");
  7. return IERC20(tokenAddress).transfer(owner(), tokens);
  8. }

因为此函数可以配合setPairAddress 来绕开 if (tokenAddress == address(swapPair_NAP_ETH)) revert(“Forbid”);从而使得owner获得质押挖矿的LPToken。