ETH Price: $3,122.55 (-0.56%)

Contract

0xcafea7934490EF8B9d2572EaeFeb9d48162ea5D8
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Sell NXM204519152024-08-04 1:31:35106 days ago1722735095IN
0xcafea793...8162ea5D8
0 ETH0.000117931
Sell NXM Tokens128379142021-07-16 12:31:271220 days ago1626438687IN
0xcafea793...8162ea5D8
0 ETH0.0045404937
Sell NXM Tokens128236772021-07-14 7:01:221223 days ago1626246082IN
0xcafea793...8162ea5D8
0 ETH00
Make Cover Begin125086742021-05-26 7:38:491272 days ago1622014729IN
0xcafea793...8162ea5D8
3.12640657 ETH0.0168349233.1
Make Cover Begin125085412021-05-26 7:07:001272 days ago1622012820IN
0xcafea793...8162ea5D8
0.02562628 ETH0.0157403829
Buy NXM125079412021-05-26 4:57:371272 days ago1622005057IN
0xcafea793...8162ea5D8
1.48 ETH0.0060785742
Make Cover Begin125075252021-05-26 3:28:441272 days ago1621999724IN
0xcafea793...8162ea5D8
3.20328542 ETH0.0249223849
Make Cover Begin125068522021-05-26 1:01:431272 days ago1621990903IN
0xcafea793...8162ea5D8
0.09467488 ETH0.0019288429
Make Cover Using...125065812021-05-25 23:57:261272 days ago1621987046IN
0xcafea793...8162ea5D8
0 ETH0.0188158334
Make Cover Using...125065752021-05-25 23:56:261272 days ago1621986986IN
0xcafea793...8162ea5D8
0 ETH0.0182576332
Make Cover Using...125064712021-05-25 23:35:051272 days ago1621985705IN
0xcafea793...8162ea5D8
0 ETH0.0193696635
Buy NXM125062342021-05-25 22:44:241272 days ago1621982664IN
0xcafea793...8162ea5D8
0.5 ETH0.0056439239
Make Cover Begin125062162021-05-25 22:40:531272 days ago1621982453IN
0xcafea793...8162ea5D8
0.34221072 ETH0.0299621657
Make Cover Using...125057532021-05-25 20:55:171272 days ago1621976117IN
0xcafea793...8162ea5D8
0 ETH0.0214791137.4
Make Cover Begin125056682021-05-25 20:34:381272 days ago1621974878IN
0xcafea793...8162ea5D8
0.07830253 ETH0.0185093434.1
Make Cover Begin125056532021-05-25 20:31:391272 days ago1621974699IN
0xcafea793...8162ea5D8
0.12813141 ETH0.0189907235
Buy NXM125053842021-05-25 19:31:301272 days ago1621971090IN
0xcafea793...8162ea5D8
2 ETH0.0067967742
Make Cover Begin125049562021-05-25 17:54:261272 days ago1621965266IN
0xcafea793...8162ea5D8
0.12813141 ETH0.0287675553
Buy NXM125048712021-05-25 17:31:271272 days ago1621963887IN
0xcafea793...8162ea5D8
0.1 ETH0.007119944.00000134
Make Cover Using...125047272021-05-25 16:58:301272 days ago1621961910IN
0xcafea793...8162ea5D8
0 ETH0.0330782758
Make Cover Using...125043112021-05-25 15:28:411272 days ago1621956521IN
0xcafea793...8162ea5D8
0 ETH0.0327239257
Make Cover Using...125041362021-05-25 14:53:231272 days ago1621954403IN
0xcafea793...8162ea5D8
0 ETH0.0252084947
Make Cover Begin125038702021-05-25 13:49:471272 days ago1621950587IN
0xcafea793...8162ea5D8
0.1089117 ETH0.0172922634
Make Cover Begin125036162021-05-25 12:57:141272 days ago1621947434IN
0xcafea793...8162ea5D8
0.03843942 ETH0.0162944631
Make Cover Using...125035972021-05-25 12:53:581272 days ago1621947238IN
0xcafea793...8162ea5D8
0 ETH0.0171485431
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
125099632021-05-26 12:34:511271 days ago1622032491
0xcafea793...8162ea5D8
161,873.19324096 ETH
125079312021-05-26 4:55:371272 days ago1622004937
0xcafea793...8162ea5D8
0.07687885 ETH
124917332021-05-23 16:43:461274 days ago1621788226
0xcafea793...8162ea5D8
5.67372791 ETH
124917022021-05-23 16:37:471274 days ago1621787867
0xcafea793...8162ea5D8
5.71105506 ETH
124916672021-05-23 16:31:021274 days ago1621787462
0xcafea793...8162ea5D8
5.37511065 ETH
124916612021-05-23 16:29:351274 days ago1621787375
0xcafea793...8162ea5D8
0.30489021 ETH
124916602021-05-23 16:29:161274 days ago1621787356
0xcafea793...8162ea5D8
5.22580202 ETH
124916092021-05-23 16:20:371274 days ago1621786837
0xcafea793...8162ea5D8
0.36575173 ETH
124916042021-05-23 16:19:081274 days ago1621786748
0xcafea793...8162ea5D8
6.94285126 ETH
124915752021-05-23 16:13:521274 days ago1621786432
0xcafea793...8162ea5D8
5.44976496 ETH
124915682021-05-23 16:12:201274 days ago1621786340
0xcafea793...8162ea5D8
0.34909666 ETH
124915672021-05-23 16:12:141274 days ago1621786334
0xcafea793...8162ea5D8
6.56957968 ETH
124907162021-05-23 13:10:381274 days ago1621775438
0xcafea793...8162ea5D8
0.31873057 ETH
124907152021-05-23 13:10:311274 days ago1621775431
0xcafea793...8162ea5D8
5.78570938 ETH
124907002021-05-23 13:08:081274 days ago1621775288
0xcafea793...8162ea5D8
5.23204182 ETH
124906422021-05-23 12:55:321274 days ago1621774532
0xcafea793...8162ea5D8
4.68776275 ETH
124906062021-05-23 12:47:581274 days ago1621774078
0xcafea793...8162ea5D8
0.30927431 ETH
124906012021-05-23 12:47:231274 days ago1621774043
0xcafea793...8162ea5D8
5.41243781 ETH
124905972021-05-23 12:46:191274 days ago1621773979
0xcafea793...8162ea5D8
5.07135064 ETH
124905902021-05-23 12:44:311274 days ago1621773871
0xcafea793...8162ea5D8
0.31960621 ETH
124905892021-05-23 12:44:151274 days ago1621773855
0xcafea793...8162ea5D8
5.56174643 ETH
124905742021-05-23 12:41:141274 days ago1621773674
0xcafea793...8162ea5D8
0.90070633 ETH
124896982021-05-23 9:13:491275 days ago1621761229
0xcafea793...8162ea5D8
0.4597753 ETH
124896332021-05-23 8:57:501275 days ago1621760270
0xcafea793...8162ea5D8
5.83713533 ETH
124896062021-05-23 8:51:331275 days ago1621759893
0xcafea793...8162ea5D8
0.33379222 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Pool

Compiler Version
v0.5.17+commit.d19bba13

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 38 : Pool.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import "../../abstract/MasterAware.sol";
import "../cover/Quotation.sol";
import "../oracles/PriceFeedOracle.sol";
import "../token/NXMToken.sol";
import "../token/TokenController.sol";
import "./MCR.sol";
import "./SwapAgent.sol";

contract Pool is MasterAware, ReentrancyGuard {
  using Address for address;
  using SafeMath for uint;
  using SafeERC20 for IERC20;

  /* storage */
  address[] public assets;
  mapping(address => SwapAgent.AssetData) public assetData;

  // contracts
  Quotation public quotation;
  NXMToken public nxmToken;
  TokenController public tokenController;
  MCR public mcr;

  // parameters
  address public twapOracle;
  address public swapController;
  uint public minPoolEth;
  PriceFeedOracle public priceFeedOracle;

  /* constants */
  address constant public ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

  uint public constant MCR_RATIO_DECIMALS = 4;
  uint public constant MAX_MCR_RATIO = 40000; // 400%
  uint public constant MAX_BUY_SELL_MCR_ETH_FRACTION = 500; // 5%. 4 decimal points

  uint internal constant CONSTANT_C = 5800000;
  uint internal constant CONSTANT_A = 1028 * 1e13;
  uint internal constant TOKEN_EXPONENT = 4;

  /* events */
  event Payout(address indexed to, address indexed asset, uint amount);
  event NXMSold (address indexed member, uint nxmIn, uint ethOut);
  event NXMBought (address indexed member, uint ethIn, uint nxmOut);
  event Swapped(address indexed fromAsset, address indexed toAsset, uint amountIn, uint amountOut);

  /* logic */
  modifier onlySwapController {
    require(msg.sender == swapController, "Pool: not swapController");
    _;
  }

  constructor (
    address[] memory _assets,
    uint112[] memory _minAmounts,
    uint112[] memory _maxAmounts,
    uint[] memory _maxSlippageRatios,
    address _master,
    address _priceOracle,
    address _twapOracle,
    address _swapController
  ) public {

    require(_assets.length == _minAmounts.length, "Pool: length mismatch");
    require(_assets.length == _maxAmounts.length, "Pool: length mismatch");
    require(_assets.length == _maxSlippageRatios.length, "Pool: length mismatch");

    for (uint i = 0; i < _assets.length; i++) {

      address asset = _assets[i];
      require(asset != address(0), "Pool: asset is zero address");
      require(_maxAmounts[i] >= _minAmounts[i], "Pool: max < min");
      require(_maxSlippageRatios[i] <= 1 ether, "Pool: max < min");

      assets.push(asset);
      assetData[asset].minAmount = _minAmounts[i];
      assetData[asset].maxAmount = _maxAmounts[i];
      assetData[asset].maxSlippageRatio = _maxSlippageRatios[i];
    }

    master = INXMMaster(_master);
    priceFeedOracle = PriceFeedOracle(_priceOracle);
    twapOracle = _twapOracle;
    swapController = _swapController;
  }

  // fallback function
  function() external payable {}

  // for legacy Pool1 upgrade compatibility
  function sendEther() external payable {}

  /**
   * @dev Calculates total value of all pool assets in ether
   */
  function getPoolValueInEth() public view returns (uint) {

    uint total = address(this).balance;

    for (uint i = 0; i < assets.length; i++) {

      address assetAddress = assets[i];
      IERC20 token = IERC20(assetAddress);

      uint rate = priceFeedOracle.getAssetToEthRate(assetAddress);
      require(rate > 0, "Pool: zero rate");

      uint assetBalance = token.balanceOf(address(this));
      uint assetValue = assetBalance.mul(rate).div(1e18);

      total = total.add(assetValue);
    }

    return total;
  }

  /* asset related functions */

  function getAssets() external view returns (address[] memory) {
    return assets;
  }

  function getAssetDetails(address _asset) external view returns (
    uint balance,
    uint112 min,
    uint112 max,
    uint32 lastAssetSwapTime,
    uint maxSlippageRatio
  ) {

    IERC20 token = IERC20(_asset);
    balance = token.balanceOf(address(this));
    SwapAgent.AssetData memory data = assetData[_asset];

    return (balance, data.minAmount, data.maxAmount, data.lastSwapTime, data.maxSlippageRatio);
  }

  function addAsset(
    address _asset,
    uint112 _min,
    uint112 _max,
    uint _maxSlippageRatio
  ) external onlyGovernance {

    require(_asset != address(0), "Pool: asset is zero address");
    require(_max >= _min, "Pool: max < min");
    require(_maxSlippageRatio <= 1 ether, "Pool: max slippage ratio > 1");

    for (uint i = 0; i < assets.length; i++) {
      require(_asset != assets[i], "Pool: asset exists");
    }

    assets.push(_asset);
    assetData[_asset] = SwapAgent.AssetData(_min, _max, 0, _maxSlippageRatio);
  }

  function removeAsset(address _asset) external onlyGovernance {

    for (uint i = 0; i < assets.length; i++) {

      if (_asset != assets[i]) {
        continue;
      }

      delete assetData[_asset];
      assets[i] = assets[assets.length - 1];
      assets.pop();

      return;
    }

    revert("Pool: asset not found");
  }

  function setAssetDetails(
    address _asset,
    uint112 _min,
    uint112 _max,
    uint _maxSlippageRatio
  ) external onlyGovernance {

    require(_min <= _max, "Pool: min > max");
    require(_maxSlippageRatio <= 1 ether, "Pool: max slippage ratio > 1");

    for (uint i = 0; i < assets.length; i++) {

      if (_asset != assets[i]) {
        continue;
      }

      assetData[_asset].minAmount = _min;
      assetData[_asset].maxAmount = _max;
      assetData[_asset].maxSlippageRatio = _maxSlippageRatio;

      return;
    }

    revert("Pool: asset not found");
  }

  /* swap functions */

  function getSwapQuote(
    uint tokenAmountIn,
    IERC20 fromToken,
    IERC20 toToken
  ) public view returns (uint tokenAmountOut) {

    return SwapAgent.getSwapQuote(
      tokenAmountIn,
      fromToken,
      toToken
    );
  }

  function swapETHForAsset(
    address toTokenAddress,
    uint amountIn,
    uint amountOutMin
  ) external whenNotPaused onlySwapController nonReentrant {

    SwapAgent.AssetData storage assetDetails = assetData[toTokenAddress];

    uint amountOut = SwapAgent.swapETHForAsset(
      twapOracle,
      assetDetails,
      toTokenAddress,
      amountIn,
      amountOutMin,
      minPoolEth
    );

    emit Swapped(ETH, toTokenAddress, amountIn, amountOut);
  }

  function swapAssetForETH(
    address fromTokenAddress,
    uint amountIn,
    uint amountOutMin
  ) external whenNotPaused onlySwapController nonReentrant {

    uint amountOut = SwapAgent.swapAssetForETH(
      twapOracle,
      assetData[fromTokenAddress],
      fromTokenAddress,
      amountIn,
      amountOutMin
    );

    emit Swapped(fromTokenAddress, ETH, amountIn, amountOut);
  }

  /* claim related functions */

  /**
   * @dev Execute the payout in case a claim is accepted
   * @param asset token address or 0xEee...EEeE for ether
   * @param payoutAddress send funds to this address
   * @param amount amount to send
   */
  function sendClaimPayout (
    address asset,
    address payable payoutAddress,
    uint amount
  ) external onlyInternal nonReentrant returns (bool success) {

    bool ok;

    if (asset == ETH) {
      // solhint-disable-next-line avoid-low-level-calls
      (ok, /* data */) = payoutAddress.call.value(amount)("");
    } else {
      ok =  _safeTokenTransfer(asset, payoutAddress, amount);
    }

    if (ok) {
      emit Payout(payoutAddress, asset, amount);
    }

    return ok;
  }

  /**
   * @dev safeTransfer implementation that does not revert
   * @param tokenAddress ERC20 address
   * @param to destination
   * @param value amount to send
   * @return success true if the transfer was successfull
   */
  function _safeTokenTransfer (
    address tokenAddress,
    address to,
    uint256 value
  ) internal returns (bool) {

    // token address is not a contract
    if (!tokenAddress.isContract()) {
      return false;
    }

    IERC20 token = IERC20(tokenAddress);
    bytes memory data = abi.encodeWithSelector(token.transfer.selector, to, value);
    // solhint-disable-next-line avoid-low-level-calls
    (bool success, bytes memory returndata) = tokenAddress.call(data);

    // low-level call failed/reverted
    if (!success) {
      return false;
    }

    // tokens that don't have return data
    if (returndata.length == 0) {
      return true;
    }

    // tokens that have return data will return a bool
    return abi.decode(returndata, (bool));
  }

  /* pool lifecycle functions */

  function transferAsset(
    address asset,
    address payable destination,
    uint amount
  ) external onlyGovernance nonReentrant {

    require(assetData[asset].maxAmount == 0, "Pool: max not zero");
    require(destination != address(0), "Pool: dest zero");

    IERC20 token = IERC20(asset);
    uint balance = token.balanceOf(address(this));
    uint transferableAmount = amount > balance ? balance : amount;

    token.safeTransfer(destination, transferableAmount);
  }

  function upgradeCapitalPool(address payable newPoolAddress) external onlyMaster nonReentrant {

    // transfer ether
    uint ethBalance = address(this).balance;
    (bool ok, /* data */) = newPoolAddress.call.value(ethBalance)("");
    require(ok, "Pool: transfer failed");

    // transfer assets
    for (uint i = 0; i < assets.length; i++) {
      IERC20 token = IERC20(assets[i]);
      uint tokenBalance = token.balanceOf(address(this));
      token.safeTransfer(newPoolAddress, tokenBalance);
    }

  }

  /**
   * @dev Update dependent contract address
   * @dev Implements MasterAware interface function
   */
  function changeDependentContractAddress() public {
    nxmToken = NXMToken(master.tokenAddress());
    tokenController = TokenController(master.getLatestAddress("TC"));
    quotation = Quotation(master.getLatestAddress("QT"));
    mcr = MCR(master.getLatestAddress("MC"));
  }

  /* cover purchase functions */

  /// @dev Enables user to purchase cover with funding in ETH.
  /// @param smartCAdd Smart Contract Address
  function makeCoverBegin(
    address smartCAdd,
    bytes4 coverCurr,
    uint[] memory coverDetails,
    uint16 coverPeriod,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) public payable onlyMember whenNotPaused {

    require(coverCurr == "ETH", "Pool: Unexpected asset type");
    require(msg.value == coverDetails[1], "Pool: ETH amount does not match premium");

    quotation.verifyCoverDetails(msg.sender, smartCAdd, coverCurr, coverDetails, coverPeriod, _v, _r, _s);
  }

  /**
   * @dev Enables user to purchase cover via currency asset eg DAI
   */
  function makeCoverUsingCA(
    address smartCAdd,
    bytes4 coverCurr,
    uint[] memory coverDetails,
    uint16 coverPeriod,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) public onlyMember whenNotPaused {
    require(coverCurr != "ETH", "Pool: Unexpected asset type");
    quotation.verifyCoverDetails(msg.sender, smartCAdd, coverCurr, coverDetails, coverPeriod, _v, _r, _s);
  }

  function transferAssetFrom (address asset, address from, uint amount) public onlyInternal whenNotPaused {
    IERC20 token = IERC20(asset);
    token.safeTransferFrom(from, address(this), amount);
  }

  /* token sale functions */

  /**
   * @dev (DEPRECATED, use sellTokens function instead) Allows selling of NXM for ether.
   * Seller first needs to give this contract allowance to
   * transfer/burn tokens in the NXMToken contract
   * @param  _amount Amount of NXM to sell
   * @return success returns true on successfull sale
   */
  function sellNXMTokens(uint _amount) public onlyMember whenNotPaused returns (bool success) {
    sellNXM(_amount, 0);
    return true;
  }

  /**
   * @dev (DEPRECATED, use calculateNXMForEth function instead) Returns the amount of wei a seller will get for selling NXM
   * @param amount Amount of NXM to sell
   * @return weiToPay Amount of wei the seller will get
   */
  function getWei(uint amount) external view returns (uint weiToPay) {
    return getEthForNXM(amount);
  }

  /**
   * @dev Buys NXM tokens with ETH.
   * @param  minTokensOut Minimum amount of tokens to be bought. Revert if boughtTokens falls below this number.
   * @return boughtTokens number of bought tokens.
   */
  function buyNXM(uint minTokensOut) public payable onlyMember whenNotPaused {

    uint ethIn = msg.value;
    require(ethIn > 0, "Pool: ethIn > 0");

    uint totalAssetValue = getPoolValueInEth().sub(ethIn);
    uint mcrEth = mcr.getLastMCREther();
    uint mcrRatio = calculateMCRRatio(totalAssetValue, mcrEth);
    require(mcrRatio <= MAX_MCR_RATIO, "Pool: Cannot purchase if MCR% > 400%");
    uint tokensOut = calculateNXMForEth(ethIn, totalAssetValue, mcrEth);
    require(tokensOut >= minTokensOut, "Pool: tokensOut is less than minTokensOut");
    tokenController.mint(msg.sender, tokensOut);

    emit NXMBought(msg.sender, ethIn, tokensOut);
  }

  /**
   * @dev Sell NXM tokens and receive ETH.
   * @param tokenAmount Amount of tokens to sell.
   * @param  minEthOut Minimum amount of ETH to be received. Revert if ethOut falls below this number.
   * @return ethOut amount of ETH received in exchange for the tokens.
   */
  function sellNXM(uint tokenAmount, uint minEthOut) public onlyMember nonReentrant whenNotPaused {

    require(nxmToken.balanceOf(msg.sender) >= tokenAmount, "Pool: Not enough balance");
    require(nxmToken.isLockedForMV(msg.sender) <= now, "Pool: NXM tokens are locked for voting");

    uint currentTotalAssetValue = getPoolValueInEth();
    uint mcrEth = mcr.getLastMCREther();
    uint ethOut = calculateEthForNXM(tokenAmount, currentTotalAssetValue, mcrEth);
    require(currentTotalAssetValue.sub(ethOut) >= mcrEth, "Pool: MCR% cannot fall below 100%");
    require(ethOut >= minEthOut, "Pool: ethOut < minEthOut");

    tokenController.burnFrom(msg.sender, tokenAmount);
    (bool ok, /* data */) = msg.sender.call.value(ethOut)("");
    require(ok, "Pool: Sell transfer failed");

    emit NXMSold(msg.sender, tokenAmount, ethOut);
  }

  /**
   * @dev Get value in tokens for an ethAmount purchase.
   * @param ethAmount amount of ETH used for buying.
   * @return tokenValue tokens obtained by buying worth of ethAmount
   */
  function getNXMForEth(
    uint ethAmount
  ) public view returns (uint) {
    uint totalAssetValue = getPoolValueInEth();
    uint mcrEth = mcr.getLastMCREther();
    return calculateNXMForEth(ethAmount, totalAssetValue, mcrEth);
  }

  function calculateNXMForEth(
    uint ethAmount,
    uint currentTotalAssetValue,
    uint mcrEth
  ) public pure returns (uint) {

    require(
      ethAmount <= mcrEth.mul(MAX_BUY_SELL_MCR_ETH_FRACTION).div(10 ** MCR_RATIO_DECIMALS),
      "Pool: Purchases worth higher than 5% of MCReth are not allowed"
    );

    /*
      The price formula is:
      P(V) = A + MCReth / C *  MCR% ^ 4
      where MCR% = V / MCReth
      P(V) = A + 1 / (C * MCReth ^ 3) *  V ^ 4

      To compute the number of tokens issued we can integrate with respect to V the following:
        ΔT = ΔV / P(V)
        which assumes that for an infinitesimally small change in locked value V price is constant and we
        get an infinitesimally change in token supply ΔT.
      This is not computable on-chain, below we use an approximation that works well assuming
       * MCR% stays within [100%, 400%]
       * ethAmount <= 5% * MCReth

      Use a simplified formula excluding the constant A price offset to compute the amount of tokens to be minted.
      AdjustedP(V) = 1 / (C * MCReth ^ 3) *  V ^ 4
      AdjustedP(V) = 1 / (C * MCReth ^ 3) *  V ^ 4

      For a very small variation in tokens ΔT, we have,  ΔT = ΔV / P(V), to get total T we integrate with respect to V.
      adjustedTokenAmount = ∫ (dV / AdjustedP(V)) from V0 (currentTotalAssetValue) to V1 (nextTotalAssetValue)
      adjustedTokenAmount = ∫ ((C * MCReth ^ 3) / V ^ 4 * dV) from V0 to V1
      Evaluating the above using the antiderivative of the function we get:
      adjustedTokenAmount = - MCReth ^ 3 * C / (3 * V1 ^3) + MCReth * C /(3 * V0 ^ 3)
    */

    if (currentTotalAssetValue == 0 || mcrEth.div(currentTotalAssetValue) > 1e12) {
      /*
       If the currentTotalAssetValue = 0, adjustedTokenPrice approaches 0. Therefore we can assume the price is A.
       If currentTotalAssetValue is far smaller than mcrEth, MCR% approaches 0, let the price be A (baseline price).
       This avoids overflow in the calculateIntegralAtPoint computation.
       This approximation is safe from arbitrage since at MCR% < 100% no sells are possible.
      */
      uint tokenPrice = CONSTANT_A;
      return ethAmount.mul(1e18).div(tokenPrice);
    }

    // MCReth * C /(3 * V0 ^ 3)
    uint point0 = calculateIntegralAtPoint(currentTotalAssetValue, mcrEth);
    // MCReth * C / (3 * V1 ^3)
    uint nextTotalAssetValue = currentTotalAssetValue.add(ethAmount);
    uint point1 = calculateIntegralAtPoint(nextTotalAssetValue, mcrEth);
    uint adjustedTokenAmount = point0.sub(point1);
    /*
      Compute a preliminary adjustedTokenPrice for the minted tokens based on the adjustedTokenAmount above,
      and to that add the A constant (the price offset previously removed in the adjusted Price formula)
      to obtain the finalPrice and ultimately the tokenValue based on the finalPrice.

      adjustedPrice = ethAmount / adjustedTokenAmount
      finalPrice = adjustedPrice + A
      tokenValue = ethAmount  / finalPrice
    */
    // ethAmount is multiplied by 1e18 to cancel out the multiplication factor of 1e18 of the adjustedTokenAmount
    uint adjustedTokenPrice = ethAmount.mul(1e18).div(adjustedTokenAmount);
    uint tokenPrice = adjustedTokenPrice.add(CONSTANT_A);

    return ethAmount.mul(1e18).div(tokenPrice);
  }

  /**
   * @dev integral(V) =  MCReth ^ 3 * C / (3 * V ^ 3) * 1e18
   * computation result is multiplied by 1e18 to allow for a precision of 18 decimals.
   * NOTE: omits the minus sign of the correct integral to use a uint result type for simplicity
   * WARNING: this low-level function should be called from a contract which checks that
   * mcrEth / assetValue < 1e17 (no overflow) and assetValue != 0
   */
  function calculateIntegralAtPoint(
    uint assetValue,
    uint mcrEth
  ) internal pure returns (uint) {

    return CONSTANT_C
      .mul(1e18)
      .div(3)
      .mul(mcrEth).div(assetValue)
      .mul(mcrEth).div(assetValue)
      .mul(mcrEth).div(assetValue);
  }

  function getEthForNXM(uint nxmAmount) public view returns (uint ethAmount) {
    uint currentTotalAssetValue = getPoolValueInEth();
    uint mcrEth = mcr.getLastMCREther();
    return calculateEthForNXM(nxmAmount, currentTotalAssetValue, mcrEth);
  }

  /**
   * @dev Computes token sell value for a tokenAmount in ETH with a sell spread of 2.5%.
   * for values in ETH of the sale <= 1% * MCReth the sell spread is very close to the exact value of 2.5%.
   * for values higher than that sell spread may exceed 2.5%
   * (The higher amount being sold at any given time the higher the spread)
   */
  function calculateEthForNXM(
    uint nxmAmount,
    uint currentTotalAssetValue,
    uint mcrEth
  ) public pure returns (uint) {

    // Step 1. Calculate spot price at current values and amount of ETH if tokens are sold at that price
    uint spotPrice0 = calculateTokenSpotPrice(currentTotalAssetValue, mcrEth);
    uint spotEthAmount = nxmAmount.mul(spotPrice0).div(1e18);

    //  Step 2. Calculate spot price using V = currentTotalAssetValue - spotEthAmount from step 1
    uint totalValuePostSpotPriceSell = currentTotalAssetValue.sub(spotEthAmount);
    uint spotPrice1 = calculateTokenSpotPrice(totalValuePostSpotPriceSell, mcrEth);

    // Step 3. Min [average[Price(0), Price(1)] x ( 1 - Sell Spread), Price(1) ]
    // Sell Spread = 2.5%
    uint averagePriceWithSpread = spotPrice0.add(spotPrice1).div(2).mul(975).div(1000);
    uint finalPrice = averagePriceWithSpread < spotPrice1 ? averagePriceWithSpread : spotPrice1;
    uint ethAmount = finalPrice.mul(nxmAmount).div(1e18);

    require(
      ethAmount <= mcrEth.mul(MAX_BUY_SELL_MCR_ETH_FRACTION).div(10 ** MCR_RATIO_DECIMALS),
      "Pool: Sales worth more than 5% of MCReth are not allowed"
    );

    return ethAmount;
  }

  function calculateMCRRatio(uint totalAssetValue, uint mcrEth) public pure returns (uint) {
    return totalAssetValue.mul(10 ** MCR_RATIO_DECIMALS).div(mcrEth);
  }

  /**
  * @dev Calculates token price in ETH 1 NXM token. TokenPrice = A + (MCReth / C) * MCR%^4
  */
  function calculateTokenSpotPrice(uint totalAssetValue, uint mcrEth) public pure returns (uint tokenPrice) {

    uint mcrRatio = calculateMCRRatio(totalAssetValue, mcrEth);
    uint precisionDecimals = 10 ** TOKEN_EXPONENT.mul(MCR_RATIO_DECIMALS);

    return mcrEth
      .mul(mcrRatio ** TOKEN_EXPONENT)
      .div(CONSTANT_C)
      .div(precisionDecimals)
      .add(CONSTANT_A);
  }

  /**
   * @dev Returns the NXM price in a given asset
   * @param asset Asset name.
   */
  function getTokenPrice(address asset) public view returns (uint tokenPrice) {

    uint totalAssetValue = getPoolValueInEth();
    uint mcrEth = mcr.getLastMCREther();
    uint tokenSpotPriceEth = calculateTokenSpotPrice(totalAssetValue, mcrEth);

    return priceFeedOracle.getAssetForEth(asset, tokenSpotPriceEth);
  }

  function getMCRRatio() public view returns (uint) {
    uint totalAssetValue = getPoolValueInEth();
    uint mcrEth = mcr.getLastMCREther();
    return calculateMCRRatio(totalAssetValue, mcrEth);
  }

  function updateUintParameters(bytes8 code, uint value) external onlyGovernance {

    if (code == "MIN_ETH") {
      minPoolEth = value;
      return;
    }

    revert("Pool: unknown parameter");
  }

  function updateAddressParameters(bytes8 code, address value) external onlyGovernance {

    if (code == "TWAP") {
      twapOracle = value;
      return;
    }

    if (code == "SWAP") {
      swapController = value;
      return;
    }

    if (code == "PRC_FEED") {
      priceFeedOracle = PriceFeedOracle(value);
      return;
    }

    revert("Pool: unknown parameter");
  }
}

File 2 of 38 : SafeMath.sol
pragma solidity ^0.5.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * _Available since v2.4.0._
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

File 3 of 38 : IERC20.sol
pragma solidity ^0.5.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see {ERC20Detailed}.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 4 of 38 : SafeERC20.sol
pragma solidity ^0.5.0;

import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves.

        // A Solidity high level call has three parts:
        //  1. The target address is checked to verify it contains contract code
        //  2. The call itself is made, and success asserted
        //  3. The return value is decoded, which in turn checks the size of the returned data.
        // solhint-disable-next-line max-line-length
        require(address(token).isContract(), "SafeERC20: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success, "SafeERC20: low-level call failed");

        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 5 of 38 : ReentrancyGuard.sol
pragma solidity ^0.5.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 *
 * _Since v2.5.0:_ this module is now much more gas efficient, given net gas
 * metering changes introduced in the Istanbul hardfork.
 */
contract ReentrancyGuard {
    bool private _notEntered;

    constructor () internal {
        // Storing an initial non-zero value makes deployment a bit more
        // expensive, but in exchange the refund on every call to nonReentrant
        // will be lower in amount. Since refunds are capped to a percetange of
        // the total transaction's gas, it is best to keep them low in cases
        // like this one, to increase the likelihood of the full refund coming
        // into effect.
        _notEntered = true;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_notEntered, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _notEntered = false;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _notEntered = true;
    }
}

File 6 of 38 : Address.sol
pragma solidity ^0.5.5;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following 
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly { codehash := extcodehash(account) }
        return (codehash != accountHash && codehash != 0x0);
    }

    /**
     * @dev Converts an `address` into `address payable`. Note that this is
     * simply a type cast: the actual underlying value is not changed.
     *
     * _Available since v2.4.0._
     */
    function toPayable(address account) internal pure returns (address payable) {
        return address(uint160(account));
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     *
     * _Available since v2.4.0._
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-call-value
        (bool success, ) = recipient.call.value(amount)("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }
}

File 7 of 38 : IUniswapV2Pair.sol
pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

File 8 of 38 : MasterAware.sol
/*
    Copyright (C) 2020 NexusMutual.io

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/
*/

pragma solidity ^0.5.0;

import "./INXMMaster.sol";

contract MasterAware {

  INXMMaster public master;

  modifier onlyMember {
    require(master.isMember(msg.sender), "Caller is not a member");
    _;
  }

  modifier onlyInternal {
    require(master.isInternal(msg.sender), "Caller is not an internal contract");
    _;
  }

  modifier onlyMaster {
    if (address(master) != address(0)) {
      require(address(master) == msg.sender, "Not master");
    }
    _;
  }

  modifier onlyGovernance {
    require(
      master.checkIsAuthToGoverned(msg.sender),
      "Caller is not authorized to govern"
    );
    _;
  }

  modifier whenPaused {
    require(master.isPause(), "System is not paused");
    _;
  }

  modifier whenNotPaused {
    require(!master.isPause(), "System is paused");
    _;
  }

  function changeMasterAddress(address masterAddress) public onlyMaster {
    master = INXMMaster(masterAddress);
  }
}

File 9 of 38 : Quotation.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../capital/MCR.sol";
import "../capital/PoolData.sol";
import "../claims/ClaimsReward.sol";
import "../governance/MemberRoles.sol";
import "../token/TokenController.sol";
import "../token/TokenData.sol";
import "../token/TokenFunctions.sol";
import "./QuotationData.sol";

contract Quotation is Iupgradable {
  using SafeMath for uint;

  TokenFunctions public tf;
  TokenController public tc;
  TokenData public td;
  Pool public pool;
  PoolData public pd;
  QuotationData public qd;
  MCR public m1;
  MemberRoles public mr;
  ClaimsReward public cr;

  bool internal locked;

  event RefundEvent(address indexed user, bool indexed status, uint holdedCoverID, bytes32 reason);

  modifier noReentrancy() {
    require(!locked, "Reentrant call.");
    locked = true;
    _;
    locked = false;
  }

  /**
   * @dev Iupgradable Interface to update dependent contract address
   */
  function changeDependentContractAddress() public onlyInternal {
    m1 = MCR(ms.getLatestAddress("MC"));
    tf = TokenFunctions(ms.getLatestAddress("TF"));
    tc = TokenController(ms.getLatestAddress("TC"));
    td = TokenData(ms.getLatestAddress("TD"));
    qd = QuotationData(ms.getLatestAddress("QD"));
    pd = PoolData(ms.getLatestAddress("PD"));
    mr = MemberRoles(ms.getLatestAddress("MR"));
    cr = ClaimsReward(ms.getLatestAddress("CR"));
    pool = Pool(ms.getLatestAddress("P1"));
  }

  function sendEther() public payable {

  }

  /**
   * @dev Expires a cover after a set period of time.
   * Changes the status of the Cover and reduces the current
   * sum assured of all areas in which the quotation lies
   * Unlocks the CN tokens of the cover. Updates the Total Sum Assured value.
   * @param _cid Cover Id.
   */
  function expireCover(uint _cid) public {
    require(checkCoverExpired(_cid) && qd.getCoverStatusNo(_cid) != uint(QuotationData.CoverStatus.CoverExpired));

    tf.unlockCN(_cid);
    bytes4 curr;
    address scAddress;
    uint sumAssured;
    (,, scAddress, curr, sumAssured,) = qd.getCoverDetailsByCoverID1(_cid);
    if (qd.getCoverStatusNo(_cid) != uint(QuotationData.CoverStatus.ClaimAccepted))
      _removeSAFromCSA(_cid, sumAssured);
    qd.changeCoverStatusNo(_cid, uint8(QuotationData.CoverStatus.CoverExpired));
  }

  /**
   * @dev Checks if a cover should get expired/closed or not.
   * @param _cid Cover Index.
   * @return expire true if the Cover's time has expired, false otherwise.
   */
  function checkCoverExpired(uint _cid) public view returns (bool expire) {

    expire = qd.getValidityOfCover(_cid) < uint64(now);

  }

  /**
   * @dev Updates the Sum Assured Amount of all the quotation.
   * @param _cid Cover id
   * @param _amount that will get subtracted Current Sum Assured
   * amount that comes under a quotation.
   */
  function removeSAFromCSA(uint _cid, uint _amount) public onlyInternal {
    _removeSAFromCSA(_cid, _amount);
  }

  /**
   * @dev Makes Cover funded via NXM tokens.
   * @param smartCAdd Smart Contract Address
   */
  function makeCoverUsingNXMTokens(
    uint[] memory coverDetails,
    uint16 coverPeriod,
    bytes4 coverCurr,
    address smartCAdd,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  )
  public
  isMemberAndcheckPause
  {

    tc.burnFrom(msg.sender, coverDetails[2]); // need burn allowance
    _verifyCoverDetails(msg.sender, smartCAdd, coverCurr, coverDetails, coverPeriod, _v, _r, _s, true);
  }

  /**
   * @dev Verifies cover details signed off chain.
   * @param from address of funder.
   * @param scAddress Smart Contract Address
   */
  function verifyCoverDetails(
    address payable from,
    address scAddress,
    bytes4 coverCurr,
    uint[] memory coverDetails,
    uint16 coverPeriod,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  )
  public
  onlyInternal
  {
    _verifyCoverDetails(
      from,
      scAddress,
      coverCurr,
      coverDetails,
      coverPeriod,
      _v,
      _r,
      _s,
      false
    );
  }

  /**
   * @dev Verifies signature.
   * @param coverDetails details related to cover.
   * @param coverPeriod validity of cover.
   * @param smaratCA smarat contract address.
   * @param _v argument from vrs hash.
   * @param _r argument from vrs hash.
   * @param _s argument from vrs hash.
   */
  function verifySign(
    uint[] memory coverDetails,
    uint16 coverPeriod,
    bytes4 curr,
    address smaratCA,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  )
  public
  view
  returns (bool)
  {
    require(smaratCA != address(0));
    require(pd.capReached() == 1, "Can not buy cover until cap reached for 1st time");
    bytes32 hash = getOrderHash(coverDetails, coverPeriod, curr, smaratCA);
    return isValidSignature(hash, _v, _r, _s);
  }

  /**
   * @dev Gets order hash for given cover details.
   * @param coverDetails details realted to cover.
   * @param coverPeriod validity of cover.
   * @param smaratCA smarat contract address.
   */
  function getOrderHash(
    uint[] memory coverDetails,
    uint16 coverPeriod,
    bytes4 curr,
    address smaratCA
  )
  public
  view
  returns (bytes32)
  {
    return keccak256(
      abi.encodePacked(
        coverDetails[0],
        curr, coverPeriod,
        smaratCA,
        coverDetails[1],
        coverDetails[2],
        coverDetails[3],
        coverDetails[4],
        address(this)
      )
    );
  }

  /**
   * @dev Verifies signature.
   * @param hash order hash
   * @param v argument from vrs hash.
   * @param r argument from vrs hash.
   * @param s argument from vrs hash.
   */
  function isValidSignature(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public view returns (bool) {
    bytes memory prefix = "\x19Ethereum Signed Message:\n32";
    bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, hash));
    address a = ecrecover(prefixedHash, v, r, s);
    return (a == qd.getAuthQuoteEngine());
  }

  /**
   * @dev to get the status of recently holded coverID
   * @param userAdd is the user address in concern
   * @return the status of the concerned coverId
   */
  function getRecentHoldedCoverIdStatus(address userAdd) public view returns (int) {

    uint holdedCoverLen = qd.getUserHoldedCoverLength(userAdd);
    if (holdedCoverLen == 0) {
      return - 1;
    } else {
      uint holdedCoverID = qd.getUserHoldedCoverByIndex(userAdd, holdedCoverLen.sub(1));
      return int(qd.holdedCoverIDStatus(holdedCoverID));
    }
  }

  /**
   * @dev to get the verdict of kyc process
   * @param status is the kyc status
   * @param _add is the address of member
   */
  function kycVerdict(address _add, bool status) public checkPause noReentrancy {
    require(msg.sender == qd.kycAuthAddress());
    _kycTrigger(status, _add);
  }

  /**
   * @dev transfering Ethers to newly created quotation contract.
   */
  function transferAssetsToNewContract(address newAdd) public onlyInternal noReentrancy {
    uint amount = address(this).balance;
    IERC20 erc20;
    if (amount > 0) {
      // newAdd.transfer(amount);
      Quotation newQT = Quotation(newAdd);
      newQT.sendEther.value(amount)();
    }
    uint currAssetLen = pd.getAllCurrenciesLen();
    for (uint64 i = 1; i < currAssetLen; i++) {
      bytes4 currName = pd.getCurrenciesByIndex(i);
      address currAddr = pd.getCurrencyAssetAddress(currName);
      erc20 = IERC20(currAddr); // solhint-disable-line
      if (erc20.balanceOf(address(this)) > 0) {
        require(erc20.transfer(newAdd, erc20.balanceOf(address(this))));
      }
    }
  }


  /**
   * @dev Creates cover of the quotation, changes the status of the quotation ,
   * updates the total sum assured and locks the tokens of the cover against a quote.
   * @param from Quote member Ethereum address.
   */

  function _makeCover(//solhint-disable-line
    address payable from,
    address scAddress,
    bytes4 coverCurr,
    uint[] memory coverDetails,
    uint16 coverPeriod
  )
  internal
  {
    uint cid = qd.getCoverLength();

    qd.addCover(
      coverPeriod,
      coverDetails[0],
      from,
      coverCurr,
      scAddress,
      coverDetails[1],
      coverDetails[2]
    );

    uint coverNoteAmount = (coverDetails[2].mul(qd.tokensRetained())).div(100);
    tc.mint(from, coverNoteAmount);
    tf.lockCN(coverNoteAmount, coverPeriod, cid, from);
    qd.addInTotalSumAssured(coverCurr, coverDetails[0]);
    qd.addInTotalSumAssuredSC(scAddress, coverCurr, coverDetails[0]);

    tf.pushStakerRewards(scAddress, coverDetails[2]);
  }

  /**
   * @dev Makes a cover.
   * @param from address of funder.
   * @param scAddress Smart Contract Address
   */
  function _verifyCoverDetails(
    address payable from,
    address scAddress,
    bytes4 coverCurr,
    uint[] memory coverDetails,
    uint16 coverPeriod,
    uint8 _v,
    bytes32 _r,
    bytes32 _s,
    bool isNXM
  ) internal {
    require(coverDetails[3] > now);
    require(!qd.timestampRepeated(coverDetails[4]));
    qd.setTimestampRepeated(coverDetails[4]);
    require(coverPeriod >= 30 && coverPeriod <= 365, "Quotation: Cover period out of bounds");

    address asset = cr.getCurrencyAssetAddress(coverCurr);
    if (coverCurr != "ETH" && !isNXM) {
      pool.transferAssetFrom(asset, from, coverDetails[1]);
    }

    require(verifySign(coverDetails, coverPeriod, coverCurr, scAddress, _v, _r, _s));
    _makeCover(from, scAddress, coverCurr, coverDetails, coverPeriod);
  }

  /**
   * @dev Updates the Sum Assured Amount of all the quotation.
   * @param _cid Cover id
   * @param _amount that will get subtracted Current Sum Assured
   * amount that comes under a quotation.
   */
  function _removeSAFromCSA(uint _cid, uint _amount) internal checkPause {
    address _add;
    bytes4 coverCurr;
    (,, _add, coverCurr,,) = qd.getCoverDetailsByCoverID1(_cid);
    qd.subFromTotalSumAssured(coverCurr, _amount);
    qd.subFromTotalSumAssuredSC(_add, coverCurr, _amount);
  }

  /**
   * @dev to trigger the kyc process
   * @param status is the kyc status
   * @param _add is the address of member
   */
  function _kycTrigger(bool status, address _add) internal {

    uint holdedCoverLen = qd.getUserHoldedCoverLength(_add).sub(1);
    uint holdedCoverID = qd.getUserHoldedCoverByIndex(_add, holdedCoverLen);
    address payable userAdd;
    address scAddress;
    bytes4 coverCurr;
    uint16 coverPeriod;
    uint[]  memory coverDetails = new uint[](4);
    IERC20 erc20;

    (, userAdd, coverDetails) = qd.getHoldedCoverDetailsByID2(holdedCoverID);
    (, scAddress, coverCurr, coverPeriod) = qd.getHoldedCoverDetailsByID1(holdedCoverID);
    require(qd.refundEligible(userAdd));
    qd.setRefundEligible(userAdd, false);
    require(qd.holdedCoverIDStatus(holdedCoverID) == uint(QuotationData.HCIDStatus.kycPending));
    uint joinFee = td.joiningFee();
    if (status) {
      mr.payJoiningFee.value(joinFee)(userAdd);
      if (coverDetails[3] > now) {
        qd.setHoldedCoverIDStatus(holdedCoverID, uint(QuotationData.HCIDStatus.kycPass));
        if (coverCurr == "ETH") {
          (bool ok,) = address(pool).call.value(coverDetails[1])("");
          require(ok, "Quotation: ether transfer to pool failed");
        } else {
          erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr)); // solhint-disable-line
          require(erc20.transfer(address(pool), coverDetails[1]));
        }
        emit RefundEvent(userAdd, status, holdedCoverID, "KYC Passed");
        _makeCover(userAdd, scAddress, coverCurr, coverDetails, coverPeriod);

      } else {
        qd.setHoldedCoverIDStatus(holdedCoverID, uint(QuotationData.HCIDStatus.kycPassNoCover));
        if (coverCurr == "ETH") {
          userAdd.transfer(coverDetails[1]);
        } else {
          erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr)); // solhint-disable-line
          require(erc20.transfer(userAdd, coverDetails[1]));
        }
        emit RefundEvent(userAdd, status, holdedCoverID, "Cover Failed");
      }
    } else {
      qd.setHoldedCoverIDStatus(holdedCoverID, uint(QuotationData.HCIDStatus.kycFailedOrRefunded));
      uint totalRefund = joinFee;
      if (coverCurr == "ETH") {
        totalRefund = coverDetails[1].add(joinFee);
      } else {
        erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr)); // solhint-disable-line
        require(erc20.transfer(userAdd, coverDetails[1]));
      }
      userAdd.transfer(totalRefund);
      emit RefundEvent(userAdd, status, holdedCoverID, "KYC Failed");
    }

  }
}

File 10 of 38 : PriceFeedOracle.sol
/* Copyright (C) 2020 NexusMutual.io
  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/math/SafeMath.sol";

contract Aggregator {
  function latestAnswer() public view returns (int);
}

contract PriceFeedOracle {
  using SafeMath for uint;

  mapping (address => address) public aggregators;
  address public daiAddress;
  address constant public ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

  constructor (address[] memory assets, address[] memory _aggregators, address _daiAddress) public {
    require(assets.length == _aggregators.length, "PriceFeedOracle: assets and _aggregators need to have same length");
    for (uint i = 0; i < assets.length; i++) {
      aggregators[assets[i]] = _aggregators[i];
    }
    daiAddress = _daiAddress;
  }

  /**
   * @dev Returns the amount of ether in wei that are equivalent to 1 unit (10 ** decimals) of asset
   * @param asset quoted currency
   * @return price in ether
   */
  function getAssetToEthRate(address asset) public view returns (uint) {

    if (asset == ETH) {
      return 1 ether;
    }

    address aggregatorAddress = aggregators[asset];

    if (aggregatorAddress == address(0)) {
      revert("PriceFeedOracle: Oracle asset not found");
    }

    int rate = Aggregator(aggregatorAddress).latestAnswer();
    require(rate > 0, "PriceFeedOracle: Rate must be > 0");

    return uint(rate);
  }

  /**
  * @dev Returns the amount of currency that is equivalent to ethIn amount of ether.
  * @param asset quoted  Supported values: ["DAI", "ETH"]
  * @param ethIn amount of ether to be converted to the currency
  * @return price in ether
  */
  function getAssetForEth(address asset, uint ethIn) external view returns (uint) {

    if (asset == daiAddress) {
      return ethIn.mul(1e18).div(getAssetToEthRate(daiAddress));
    }

    if (asset == ETH) {
      return ethIn;
    }

    revert("PriceFeedOracle: Unknown asset");
  }

}

File 11 of 38 : NXMToken.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "./external/OZIERC20.sol";
import "./external/OZSafeMath.sol";

contract NXMToken is OZIERC20 {
  using OZSafeMath for uint256;

  event WhiteListed(address indexed member);

  event BlackListed(address indexed member);

  mapping(address => uint256) private _balances;

  mapping(address => mapping(address => uint256)) private _allowed;

  mapping(address => bool) public whiteListed;

  mapping(address => uint) public isLockedForMV;

  uint256 private _totalSupply;

  string public name = "NXM";
  string public symbol = "NXM";
  uint8 public decimals = 18;
  address public operator;

  modifier canTransfer(address _to) {
    require(whiteListed[_to]);
    _;
  }

  modifier onlyOperator() {
    if (operator != address(0))
      require(msg.sender == operator);
    _;
  }

  constructor(address _founderAddress, uint _initialSupply) public {
    _mint(_founderAddress, _initialSupply);
  }

  /**
  * @dev Total number of tokens in existence
  */
  function totalSupply() public view returns (uint256) {
    return _totalSupply;
  }

  /**
  * @dev Gets the balance of the specified address.
  * @param owner The address to query the balance of.
  * @return An uint256 representing the amount owned by the passed address.
  */
  function balanceOf(address owner) public view returns (uint256) {
    return _balances[owner];
  }

  /**
  * @dev Function to check the amount of tokens that an owner allowed to a spender.
  * @param owner address The address which owns the funds.
  * @param spender address The address which will spend the funds.
  * @return A uint256 specifying the amount of tokens still available for the spender.
  */
  function allowance(
    address owner,
    address spender
  )
  public
  view
  returns (uint256)
  {
    return _allowed[owner][spender];
  }

  /**
  * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
  * Beware that changing an allowance with this method brings the risk that someone may use both the old
  * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
  * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
  * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
  * @param spender The address which will spend the funds.
  * @param value The amount of tokens to be spent.
  */
  function approve(address spender, uint256 value) public returns (bool) {
    require(spender != address(0));

    _allowed[msg.sender][spender] = value;
    emit Approval(msg.sender, spender, value);
    return true;
  }

  /**
  * @dev Increase the amount of tokens that an owner allowed to a spender.
  * approve should be called when allowed_[_spender] == 0. To increment
  * allowed value is better to use this function to avoid 2 calls (and wait until
  * the first transaction is mined)
  * From MonolithDAO Token.sol
  * @param spender The address which will spend the funds.
  * @param addedValue The amount of tokens to increase the allowance by.
  */
  function increaseAllowance(
    address spender,
    uint256 addedValue
  )
  public
  returns (bool)
  {
    require(spender != address(0));

    _allowed[msg.sender][spender] = (
    _allowed[msg.sender][spender].add(addedValue));
    emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
    return true;
  }

  /**
  * @dev Decrease the amount of tokens that an owner allowed to a spender.
  * approve should be called when allowed_[_spender] == 0. To decrement
  * allowed value is better to use this function to avoid 2 calls (and wait until
  * the first transaction is mined)
  * From MonolithDAO Token.sol
  * @param spender The address which will spend the funds.
  * @param subtractedValue The amount of tokens to decrease the allowance by.
  */
  function decreaseAllowance(
    address spender,
    uint256 subtractedValue
  )
  public
  returns (bool)
  {
    require(spender != address(0));

    _allowed[msg.sender][spender] = (
    _allowed[msg.sender][spender].sub(subtractedValue));
    emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
    return true;
  }

  /**
  * @dev Adds a user to whitelist
  * @param _member address to add to whitelist
  */
  function addToWhiteList(address _member) public onlyOperator returns (bool) {
    whiteListed[_member] = true;
    emit WhiteListed(_member);
    return true;
  }

  /**
  * @dev removes a user from whitelist
  * @param _member address to remove from whitelist
  */
  function removeFromWhiteList(address _member) public onlyOperator returns (bool) {
    whiteListed[_member] = false;
    emit BlackListed(_member);
    return true;
  }

  /**
  * @dev change operator address
  * @param _newOperator address of new operator
  */
  function changeOperator(address _newOperator) public onlyOperator returns (bool) {
    operator = _newOperator;
    return true;
  }

  /**
  * @dev burns an amount of the tokens of the message sender
  * account.
  * @param amount The amount that will be burnt.
  */
  function burn(uint256 amount) public returns (bool) {
    _burn(msg.sender, amount);
    return true;
  }

  /**
  * @dev Burns a specific amount of tokens from the target address and decrements allowance
  * @param from address The address which you want to send tokens from
  * @param value uint256 The amount of token to be burned
  */
  function burnFrom(address from, uint256 value) public returns (bool) {
    _burnFrom(from, value);
    return true;
  }

  /**
  * @dev function that mints an amount of the token and assigns it to
  * an account.
  * @param account The account that will receive the created tokens.
  * @param amount The amount that will be created.
  */
  function mint(address account, uint256 amount) public onlyOperator {
    _mint(account, amount);
  }

  /**
  * @dev Transfer token for a specified address
  * @param to The address to transfer to.
  * @param value The amount to be transferred.
  */
  function transfer(address to, uint256 value) public canTransfer(to) returns (bool) {

    require(isLockedForMV[msg.sender] < now); // if not voted under governance
    require(value <= _balances[msg.sender]);
    _transfer(to, value);
    return true;
  }

  /**
  * @dev Transfer tokens to the operator from the specified address
  * @param from The address to transfer from.
  * @param value The amount to be transferred.
  */
  function operatorTransfer(address from, uint256 value) public onlyOperator returns (bool) {
    require(value <= _balances[from]);
    _transferFrom(from, operator, value);
    return true;
  }

  /**
  * @dev Transfer tokens from one address to another
  * @param from address The address which you want to send tokens from
  * @param to address The address which you want to transfer to
  * @param value uint256 the amount of tokens to be transferred
  */
  function transferFrom(
    address from,
    address to,
    uint256 value
  )
  public
  canTransfer(to)
  returns (bool)
  {
    require(isLockedForMV[from] < now); // if not voted under governance
    require(value <= _balances[from]);
    require(value <= _allowed[from][msg.sender]);
    _transferFrom(from, to, value);
    return true;
  }

  /**
   * @dev Lock the user's tokens
   * @param _of user's address.
   */
  function lockForMemberVote(address _of, uint _days) public onlyOperator {
    if (_days.add(now) > isLockedForMV[_of])
      isLockedForMV[_of] = _days.add(now);
  }

  /**
  * @dev Transfer token for a specified address
  * @param to The address to transfer to.
  * @param value The amount to be transferred.
  */
  function _transfer(address to, uint256 value) internal {
    _balances[msg.sender] = _balances[msg.sender].sub(value);
    _balances[to] = _balances[to].add(value);
    emit Transfer(msg.sender, to, value);
  }

  /**
  * @dev Transfer tokens from one address to another
  * @param from address The address which you want to send tokens from
  * @param to address The address which you want to transfer to
  * @param value uint256 the amount of tokens to be transferred
  */
  function _transferFrom(
    address from,
    address to,
    uint256 value
  )
  internal
  {
    _balances[from] = _balances[from].sub(value);
    _balances[to] = _balances[to].add(value);
    _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);
    emit Transfer(from, to, value);
  }

  /**
  * @dev Internal function that mints an amount of the token and assigns it to
  * an account. This encapsulates the modification of balances such that the
  * proper events are emitted.
  * @param account The account that will receive the created tokens.
  * @param amount The amount that will be created.
  */
  function _mint(address account, uint256 amount) internal {
    require(account != address(0));
    _totalSupply = _totalSupply.add(amount);
    _balances[account] = _balances[account].add(amount);
    emit Transfer(address(0), account, amount);
  }

  /**
  * @dev Internal function that burns an amount of the token of a given
  * account.
  * @param account The account whose tokens will be burnt.
  * @param amount The amount that will be burnt.
  */
  function _burn(address account, uint256 amount) internal {
    require(amount <= _balances[account]);

    _totalSupply = _totalSupply.sub(amount);
    _balances[account] = _balances[account].sub(amount);
    emit Transfer(account, address(0), amount);
  }

  /**
  * @dev Internal function that burns an amount of the token of a given
  * account, deducting from the sender's allowance for said account. Uses the
  * internal burn function.
  * @param account The account whose tokens will be burnt.
  * @param value The amount that will be burnt.
  */
  function _burnFrom(address account, uint256 value) internal {
    require(value <= _allowed[account][msg.sender]);

    // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted,
    // this function needs to emit an event with the updated approval.
    _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(
      value);
    _burn(account, value);
  }
}

File 12 of 38 : TokenController.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../abstract/Iupgradable.sol";
import "../../interfaces/IPooledStaking.sol";
import "./NXMToken.sol";
import "./external/IERC1132.sol";

contract TokenController is IERC1132, Iupgradable {
  using SafeMath for uint256;

  event Burned(address indexed member, bytes32 lockedUnder, uint256 amount);

  NXMToken public token;
  IPooledStaking public pooledStaking;
  uint public minCALockTime = uint(30).mul(1 days);
  bytes32 private constant CLA = bytes32("CLA");

  /**
  * @dev Just for interface
  */
  function changeDependentContractAddress() public {
    token = NXMToken(ms.tokenAddress());
    pooledStaking = IPooledStaking(ms.getLatestAddress("PS"));
  }

  /**
   * @dev to change the operator address
   * @param _newOperator is the new address of operator
   */
  function changeOperator(address _newOperator) public onlyInternal {
    token.changeOperator(_newOperator);
  }

  /**
   * @dev Proxies token transfer through this contract to allow staking when members are locked for voting
   * @param _from   Source address
   * @param _to     Destination address
   * @param _value  Amount to transfer
   */
  function operatorTransfer(address _from, address _to, uint _value) onlyInternal external returns (bool) {
    require(msg.sender == address(pooledStaking), "Call is only allowed from PooledStaking address");
    require(token.operatorTransfer(_from, _value), "Operator transfer failed");
    require(token.transfer(_to, _value), "Internal transfer failed");
    return true;
  }

  /**
  * @dev Locks a specified amount of tokens,
  *    for CLA reason and for a specified time
  * @param _reason The reason to lock tokens, currently restricted to CLA
  * @param _amount Number of tokens to be locked
  * @param _time Lock time in seconds
  */
  function lock(bytes32 _reason, uint256 _amount, uint256 _time) public checkPause returns (bool)
  {
    require(_reason == CLA, "Restricted to reason CLA");
    require(minCALockTime <= _time, "Should lock for minimum time");
    // If tokens are already locked, then functions extendLock or
    // increaseLockAmount should be used to make any changes
    _lock(msg.sender, _reason, _amount, _time);
    return true;
  }

  /**
  * @dev Locks a specified amount of tokens against an address,
  *    for a specified reason and time
  * @param _reason The reason to lock tokens
  * @param _amount Number of tokens to be locked
  * @param _time Lock time in seconds
  * @param _of address whose tokens are to be locked
  */
  function lockOf(address _of, bytes32 _reason, uint256 _amount, uint256 _time)
  public
  onlyInternal
  returns (bool)
  {
    // If tokens are already locked, then functions extendLock or
    // increaseLockAmount should be used to make any changes
    _lock(_of, _reason, _amount, _time);
    return true;
  }

  /**
  * @dev Extends lock for reason CLA for a specified time
  * @param _reason The reason to lock tokens, currently restricted to CLA
  * @param _time Lock extension time in seconds
  */
  function extendLock(bytes32 _reason, uint256 _time)
  public
  checkPause
  returns (bool)
  {
    require(_reason == CLA, "Restricted to reason CLA");
    _extendLock(msg.sender, _reason, _time);
    return true;
  }

  /**
  * @dev Extends lock for a specified reason and time
  * @param _reason The reason to lock tokens
  * @param _time Lock extension time in seconds
  */
  function extendLockOf(address _of, bytes32 _reason, uint256 _time)
  public
  onlyInternal
  returns (bool)
  {
    _extendLock(_of, _reason, _time);
    return true;
  }

  /**
  * @dev Increase number of tokens locked for a CLA reason
  * @param _reason The reason to lock tokens, currently restricted to CLA
  * @param _amount Number of tokens to be increased
  */
  function increaseLockAmount(bytes32 _reason, uint256 _amount)
  public
  checkPause
  returns (bool)
  {
    require(_reason == CLA, "Restricted to reason CLA");
    require(_tokensLocked(msg.sender, _reason) > 0);
    token.operatorTransfer(msg.sender, _amount);

    locked[msg.sender][_reason].amount = locked[msg.sender][_reason].amount.add(_amount);
    emit Locked(msg.sender, _reason, _amount, locked[msg.sender][_reason].validity);
    return true;
  }

  /**
   * @dev burns tokens of an address
   * @param _of is the address to burn tokens of
   * @param amount is the amount to burn
   * @return the boolean status of the burning process
   */
  function burnFrom(address _of, uint amount) public onlyInternal returns (bool) {
    return token.burnFrom(_of, amount);
  }

  /**
  * @dev Burns locked tokens of a user
  * @param _of address whose tokens are to be burned
  * @param _reason lock reason for which tokens are to be burned
  * @param _amount amount of tokens to burn
  */
  function burnLockedTokens(address _of, bytes32 _reason, uint256 _amount) public onlyInternal {
    _burnLockedTokens(_of, _reason, _amount);
  }

  /**
  * @dev reduce lock duration for a specified reason and time
  * @param _of The address whose tokens are locked
  * @param _reason The reason to lock tokens
  * @param _time Lock reduction time in seconds
  */
  function reduceLock(address _of, bytes32 _reason, uint256 _time) public onlyInternal {
    _reduceLock(_of, _reason, _time);
  }

  /**
  * @dev Released locked tokens of an address locked for a specific reason
  * @param _of address whose tokens are to be released from lock
  * @param _reason reason of the lock
  * @param _amount amount of tokens to release
  */
  function releaseLockedTokens(address _of, bytes32 _reason, uint256 _amount)
  public
  onlyInternal
  {
    _releaseLockedTokens(_of, _reason, _amount);
  }

  /**
  * @dev Adds an address to whitelist maintained in the contract
  * @param _member address to add to whitelist
  */
  function addToWhitelist(address _member) public onlyInternal {
    token.addToWhiteList(_member);
  }

  /**
  * @dev Removes an address from the whitelist in the token
  * @param _member address to remove
  */
  function removeFromWhitelist(address _member) public onlyInternal {
    token.removeFromWhiteList(_member);
  }

  /**
  * @dev Mints new token for an address
  * @param _member address to reward the minted tokens
  * @param _amount number of tokens to mint
  */
  function mint(address _member, uint _amount) public onlyInternal {
    token.mint(_member, _amount);
  }

  /**
   * @dev Lock the user's tokens
   * @param _of user's address.
   */
  function lockForMemberVote(address _of, uint _days) public onlyInternal {
    token.lockForMemberVote(_of, _days);
  }

  /**
  * @dev Unlocks the unlockable tokens against CLA of a specified address
  * @param _of Address of user, claiming back unlockable tokens against CLA
  */
  function unlock(address _of)
  public
  checkPause
  returns (uint256 unlockableTokens)
  {
    unlockableTokens = _tokensUnlockable(_of, CLA);
    if (unlockableTokens > 0) {
      locked[_of][CLA].claimed = true;
      emit Unlocked(_of, CLA, unlockableTokens);
      require(token.transfer(_of, unlockableTokens));
    }
  }

  /**
   * @dev Updates Uint Parameters of a code
   * @param code whose details we want to update
   * @param val value to set
   */
  function updateUintParameters(bytes8 code, uint val) public {
    require(ms.checkIsAuthToGoverned(msg.sender));
    if (code == "MNCLT") {
      minCALockTime = val.mul(1 days);
    } else {
      revert("Invalid param code");
    }
  }

  /**
  * @dev Gets the validity of locked tokens of a specified address
  * @param _of The address to query the validity
  * @param reason reason for which tokens were locked
  */
  function getLockedTokensValidity(address _of, bytes32 reason)
  public
  view
  returns (uint256 validity)
  {
    validity = locked[_of][reason].validity;
  }

  /**
  * @dev Gets the unlockable tokens of a specified address
  * @param _of The address to query the the unlockable token count of
  */
  function getUnlockableTokens(address _of)
  public
  view
  returns (uint256 unlockableTokens)
  {
    for (uint256 i = 0; i < lockReason[_of].length; i++) {
      unlockableTokens = unlockableTokens.add(_tokensUnlockable(_of, lockReason[_of][i]));
    }
  }

  /**
  * @dev Returns tokens locked for a specified address for a
  *    specified reason
  *
  * @param _of The address whose tokens are locked
  * @param _reason The reason to query the lock tokens for
  */
  function tokensLocked(address _of, bytes32 _reason)
  public
  view
  returns (uint256 amount)
  {
    return _tokensLocked(_of, _reason);
  }

  /**
  * @dev Returns unlockable tokens for a specified address for a specified reason
  * @param _of The address to query the the unlockable token count of
  * @param _reason The reason to query the unlockable tokens for
  */
  function tokensUnlockable(address _of, bytes32 _reason)
  public
  view
  returns (uint256 amount)
  {
    return _tokensUnlockable(_of, _reason);
  }

  function totalSupply() public view returns (uint256)
  {
    return token.totalSupply();
  }

  /**
  * @dev Returns tokens locked for a specified address for a
  *    specified reason at a specific time
  *
  * @param _of The address whose tokens are locked
  * @param _reason The reason to query the lock tokens for
  * @param _time The timestamp to query the lock tokens for
  */
  function tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time)
  public
  view
  returns (uint256 amount)
  {
    return _tokensLockedAtTime(_of, _reason, _time);
  }

  /**
  * @dev Returns the total amount of tokens held by an address:
  *   transferable + locked + staked for pooled staking - pending burns.
  *   Used by Claims and Governance in member voting to calculate the user's vote weight.
  *
  * @param _of The address to query the total balance of
  * @param _of The address to query the total balance of
  */
  function totalBalanceOf(address _of) public view returns (uint256 amount) {

    amount = token.balanceOf(_of);

    for (uint256 i = 0; i < lockReason[_of].length; i++) {
      amount = amount.add(_tokensLocked(_of, lockReason[_of][i]));
    }

    uint stakerReward = pooledStaking.stakerReward(_of);
    uint stakerDeposit = pooledStaking.stakerDeposit(_of);

    amount = amount.add(stakerDeposit).add(stakerReward);
  }

  /**
  * @dev Returns the total locked tokens at time
  *   Returns the total amount of locked and staked tokens at a given time. Used by MemberRoles to check eligibility
  *   for withdraw / switch membership. Includes tokens locked for Claim Assessment and staked for Risk Assessment.
  *   Does not take into account pending burns.
  *
  * @param _of member whose locked tokens are to be calculate
  * @param _time timestamp when the tokens should be locked
  */
  function totalLockedBalance(address _of, uint256 _time) public view returns (uint256 amount) {

    for (uint256 i = 0; i < lockReason[_of].length; i++) {
      amount = amount.add(_tokensLockedAtTime(_of, lockReason[_of][i], _time));
    }

    amount = amount.add(pooledStaking.stakerDeposit(_of));
  }

  /**
  * @dev Locks a specified amount of tokens against an address,
  *    for a specified reason and time
  * @param _of address whose tokens are to be locked
  * @param _reason The reason to lock tokens
  * @param _amount Number of tokens to be locked
  * @param _time Lock time in seconds
  */
  function _lock(address _of, bytes32 _reason, uint256 _amount, uint256 _time) internal {
    require(_tokensLocked(_of, _reason) == 0);
    require(_amount != 0);

    if (locked[_of][_reason].amount == 0) {
      lockReason[_of].push(_reason);
    }

    require(token.operatorTransfer(_of, _amount));

    uint256 validUntil = now.add(_time); // solhint-disable-line
    locked[_of][_reason] = LockToken(_amount, validUntil, false);
    emit Locked(_of, _reason, _amount, validUntil);
  }

  /**
  * @dev Returns tokens locked for a specified address for a
  *    specified reason
  *
  * @param _of The address whose tokens are locked
  * @param _reason The reason to query the lock tokens for
  */
  function _tokensLocked(address _of, bytes32 _reason)
  internal
  view
  returns (uint256 amount)
  {
    if (!locked[_of][_reason].claimed) {
      amount = locked[_of][_reason].amount;
    }
  }

  /**
  * @dev Returns tokens locked for a specified address for a
  *    specified reason at a specific time
  *
  * @param _of The address whose tokens are locked
  * @param _reason The reason to query the lock tokens for
  * @param _time The timestamp to query the lock tokens for
  */
  function _tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time)
  internal
  view
  returns (uint256 amount)
  {
    if (locked[_of][_reason].validity > _time) {
      amount = locked[_of][_reason].amount;
    }
  }

  /**
  * @dev Extends lock for a specified reason and time
  * @param _of The address whose tokens are locked
  * @param _reason The reason to lock tokens
  * @param _time Lock extension time in seconds
  */
  function _extendLock(address _of, bytes32 _reason, uint256 _time) internal {
    require(_tokensLocked(_of, _reason) > 0);
    emit Unlocked(_of, _reason, locked[_of][_reason].amount);
    locked[_of][_reason].validity = locked[_of][_reason].validity.add(_time);
    emit Locked(_of, _reason, locked[_of][_reason].amount, locked[_of][_reason].validity);
  }

  /**
  * @dev reduce lock duration for a specified reason and time
  * @param _of The address whose tokens are locked
  * @param _reason The reason to lock tokens
  * @param _time Lock reduction time in seconds
  */
  function _reduceLock(address _of, bytes32 _reason, uint256 _time) internal {
    require(_tokensLocked(_of, _reason) > 0);
    emit Unlocked(_of, _reason, locked[_of][_reason].amount);
    locked[_of][_reason].validity = locked[_of][_reason].validity.sub(_time);
    emit Locked(_of, _reason, locked[_of][_reason].amount, locked[_of][_reason].validity);
  }

  /**
  * @dev Returns unlockable tokens for a specified address for a specified reason
  * @param _of The address to query the the unlockable token count of
  * @param _reason The reason to query the unlockable tokens for
  */
  function _tokensUnlockable(address _of, bytes32 _reason) internal view returns (uint256 amount)
  {
    if (locked[_of][_reason].validity <= now && !locked[_of][_reason].claimed) {
      amount = locked[_of][_reason].amount;
    }
  }

  /**
  * @dev Burns locked tokens of a user
  * @param _of address whose tokens are to be burned
  * @param _reason lock reason for which tokens are to be burned
  * @param _amount amount of tokens to burn
  */
  function _burnLockedTokens(address _of, bytes32 _reason, uint256 _amount) internal {
    uint256 amount = _tokensLocked(_of, _reason);
    require(amount >= _amount);

    if (amount == _amount) {
      locked[_of][_reason].claimed = true;
    }

    locked[_of][_reason].amount = locked[_of][_reason].amount.sub(_amount);
    if (locked[_of][_reason].amount == 0) {
      _removeReason(_of, _reason);
    }
    token.burn(_amount);
    emit Burned(_of, _reason, _amount);
  }

  /**
  * @dev Released locked tokens of an address locked for a specific reason
  * @param _of address whose tokens are to be released from lock
  * @param _reason reason of the lock
  * @param _amount amount of tokens to release
  */
  function _releaseLockedTokens(address _of, bytes32 _reason, uint256 _amount) internal
  {
    uint256 amount = _tokensLocked(_of, _reason);
    require(amount >= _amount);

    if (amount == _amount) {
      locked[_of][_reason].claimed = true;
    }

    locked[_of][_reason].amount = locked[_of][_reason].amount.sub(_amount);
    if (locked[_of][_reason].amount == 0) {
      _removeReason(_of, _reason);
    }
    require(token.transfer(_of, _amount));
    emit Unlocked(_of, _reason, _amount);
  }

  function _removeReason(address _of, bytes32 _reason) internal {
    uint len = lockReason[_of].length;
    for (uint i = 0; i < len; i++) {
      if (lockReason[_of][i] == _reason) {
        lockReason[_of][i] = lockReason[_of][len.sub(1)];
        lockReason[_of].pop();
        break;
      }
    }
  }
}

File 13 of 38 : MCR.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../capital/Pool.sol";
import "../cover/QuotationData.sol";
import "../governance/MemberRoles.sol";
import "../governance/ProposalCategory.sol";
import "../oracles/PriceFeedOracle.sol";
import "../token/NXMToken.sol";
import "../token/TokenData.sol";
import "./PoolData.sol";

contract MCR is Iupgradable {
  using SafeMath for uint;

  Pool public pool;
  PoolData public pd;
  NXMToken public tk;
  QuotationData public qd;
  MemberRoles public mr;
  TokenData public td;
  ProposalCategory public proposalCategory;

  uint private constant minCapFactor = uint(10) ** 21;

  uint public variableMincap;
  uint public dynamicMincapThresholdx100 = 13000;
  uint public dynamicMincapIncrementx100 = 100;

  event MCREvent(
    uint indexed date,
    uint blockNumber,
    bytes4[] allCurr,
    uint[] allCurrRates,
    uint mcrEtherx100,
    uint mcrPercx100,
    uint vFull
  );

  constructor (address masterAddress) public {

    changeMasterAddress(masterAddress);

    // we'll pass the zero address on the first deploy
    // due to missing previous MCR contract
    if (masterAddress == address(0)) {
      return;
    }

    address mcrAddress = ms.getLatestAddress("MC");
    MCR previousMCR = MCR(mcrAddress);

    // fetch MCR parameters from previous contract
    variableMincap = previousMCR.variableMincap();
    dynamicMincapThresholdx100 = previousMCR.dynamicMincapThresholdx100();
    dynamicMincapIncrementx100 = previousMCR.dynamicMincapIncrementx100();
  }

  /**
   * @dev Adds new MCR data.
   * @param mcrP  Minimum Capital Requirement in percentage.
   * @param vF Pool fund value in Ether used in the last full daily calculation of the Capital model.
   * @param onlyDate  Date(yyyymmdd) at which MCR details are getting added.
   */
  function addMCRData(
    uint mcrP,
    uint mcrE,
    uint vF,
    bytes4[] calldata curr,
    uint[] calldata _threeDayAvg,
    uint64 onlyDate
  )
  external
  checkPause
  {
    require(proposalCategory.constructorCheck());
    require(pd.isnotarise(msg.sender));
    if (mr.launched() && pd.capReached() != 1) {

      if (mcrP >= 10000)
        pd.setCapReached(1);

    }
    uint len = pd.getMCRDataLength();
    _addMCRData(len, onlyDate, curr, mcrE, mcrP, vF, _threeDayAvg);
  }

  // proxying this call through mcr contract to get rid of pd from pool
  function getLastMCREther() external view returns (uint) {
    return pd.getLastMCREther();
  }

  /**
   * @dev Iupgradable Interface to update dependent contract address
   */
  function changeDependentContractAddress() public {
    qd = QuotationData(ms.getLatestAddress("QD"));
    pool = Pool(ms.getLatestAddress("P1"));
    pd = PoolData(ms.getLatestAddress("PD"));
    tk = NXMToken(ms.tokenAddress());
    mr = MemberRoles(ms.getLatestAddress("MR"));
    td = TokenData(ms.getLatestAddress("TD"));
    proposalCategory = ProposalCategory(ms.getLatestAddress("PC"));
  }

  /**
   * @dev Gets total sum assured (in ETH).
   * @return amount of sum assured
   */
  function getAllSumAssurance() public view returns (uint) {

    PriceFeedOracle priceFeed = pool.priceFeedOracle();
    address daiAddress = priceFeed.daiAddress();

    uint ethAmount = qd.getTotalSumAssured("ETH").mul(1e18);
    uint daiAmount = qd.getTotalSumAssured("DAI").mul(1e18);

    uint daiRate = priceFeed.getAssetToEthRate(daiAddress);
    uint daiAmountInEth = daiAmount.mul(daiRate).div(1e18);

    return ethAmount.add(daiAmountInEth);
  }

  function getThresholdValues(uint vtp, uint vF, uint totalSA, uint minCap) public view returns (uint lowerThreshold, uint upperThreshold)
  {
    minCap = (minCap.mul(minCapFactor)).add(variableMincap);
    uint lower = 0;
    if (vtp >= vF) {
      // Max Threshold = [MAX(Vtp, Vfull) x 120] / mcrMinCap
      upperThreshold = vtp.mul(120).mul(100).div((minCap));
    } else {
      upperThreshold = vF.mul(120).mul(100).div((minCap));
    }

    if (vtp > 0) {
      lower = totalSA.mul(pd.shockParameter()).div(100);
      if (lower < minCap.mul(11).div(10))
        lower = minCap.mul(11).div(10);
    }
    if (lower > 0) {
      // Min Threshold = [Vtp / MAX(TotalActiveSA x ShockParameter, mcrMinCap x 1.1)] x 100
      lowerThreshold = vtp.mul(100).mul(100).div(lower);
    }
  }

  /**
   * @dev Gets Uint Parameters of a code
   * @param code whose details we want
   * @return string value of the code
   * @return associated amount (time or perc or value) to the code
   */
  function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {
    codeVal = code;
    if (code == "DMCT") {
      val = dynamicMincapThresholdx100;

    } else if (code == "DMCI") {

      val = dynamicMincapIncrementx100;

    }

  }

  /**
   * @dev Updates Uint Parameters of a code
   * @param code whose details we want to update
   * @param val value to set
   */
  function updateUintParameters(bytes8 code, uint val) public {
    require(ms.checkIsAuthToGoverned(msg.sender));
    if (code == "DMCT") {
      dynamicMincapThresholdx100 = val;

    } else if (code == "DMCI") {

      dynamicMincapIncrementx100 = val;

    }
    else {
      revert("Invalid param code");
    }

  }

  /**
   * @dev Adds MCR Data. Checks if MCR is within valid
   * thresholds in order to rule out any incorrect calculations
   */
  function _addMCRData(
    uint len,
    uint64 newMCRDate,
    bytes4[] memory curr,
    uint mcrE,
    uint mcrP,
    uint vF,
    uint[] memory _threeDayAvg
  )
  internal
  {
    uint lowerThreshold = 0;
    uint upperThreshold = 0;

    if (len > 1) {
      uint vtp = pool.getPoolValueInEth();
      (lowerThreshold, upperThreshold) = getThresholdValues(vtp, vF, getAllSumAssurance(), pd.minCap());
    }

    if (mcrP > dynamicMincapThresholdx100) {
      variableMincap = (variableMincap.mul(dynamicMincapIncrementx100.add(10000)).add(minCapFactor.mul(pd.minCap().mul(dynamicMincapIncrementx100)))).div(10000);
    }

    // Explanation for above formula :-
    // actual formula -> variableMinCap =  variableMinCap + (variableMinCap+minCap)*dynamicMincapIncrement/100
    // Implemented formula is simplified form of actual formula.
    // Let consider above formula as b = b + (a+b)*c/100
    // here, dynamicMincapIncrement is in x100 format.
    // so b+(a+b)*cx100/10000 can be written as => (10000.b + b.cx100 + a.cx100)/10000.
    // It can further simplify to (b.(10000+cx100) + a.cx100)/10000.
    if (len == 1 || (mcrP) >= lowerThreshold && (mcrP) <= upperThreshold) {

      pd.pushMCRData(mcrP, mcrE, vF, newMCRDate);

      for (uint i = 0; i < curr.length; i++) {
        pd.updateCAAvgRate(curr[i], _threeDayAvg[i]);
      }

      emit MCREvent(newMCRDate, block.number, curr, _threeDayAvg, mcrE, mcrP, vF);
      return;
    }

    revert("MCR: Failed");
  }

}

File 14 of 38 : SwapAgent.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../../external/uniswap/IUniswapV2Router02.sol";
import "../oracles/TwapOracle.sol";

library SwapAgent {
  using SafeMath for uint;

  struct AssetData {
    uint112 minAmount;
    uint112 maxAmount;
    uint32 lastSwapTime;
    // 18 decimals of precision. 0.01% -> 0.0001 -> 1e14
    uint maxSlippageRatio;
  }

  IUniswapV2Router02 constant public router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
  uint constant public MAX_LIQUIDITY_RATIO = 3 * 1e15;

  function getSwapQuote(
    uint tokenAmountIn,
    IERC20 fromToken,
    IERC20 toToken
  ) public view returns (uint tokenAmountOut) {

    address[] memory path = new address[](2);
    path[0] = address(fromToken);
    path[1] = address(toToken);
    uint[] memory amountsOut = router.getAmountsOut(tokenAmountIn, path);

    return amountsOut[1];
  }

  function swapETHForAsset(
    address _oracle,
    AssetData storage assetData,
    address toTokenAddress,
    uint amountIn,
    uint amountOutMin,
    uint minLeftETH
  ) external returns (uint) {

    uint balanceBefore = IERC20(toTokenAddress).balanceOf(address(this));
    address WETH = router.WETH();

    {
      // scope for swap frequency check
      uint timeSinceLastTrade = block.timestamp.sub(uint(assetData.lastSwapTime));
      require(timeSinceLastTrade > TwapOracle(_oracle).periodSize(), "SwapAgent: too fast");
    }

    {
      // scope for liquidity check
      address pairAddress = TwapOracle(_oracle).pairFor(WETH, toTokenAddress);
      IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
      (uint112 reserve0, uint112 reserve1, /* time */) = pair.getReserves();

      uint ethReserve = WETH < toTokenAddress ? reserve0 : reserve1;
      uint maxTradable = ethReserve.mul(MAX_LIQUIDITY_RATIO).div(1e18);

      require(amountIn <= maxTradable, "SwapAgent: exceeds max tradable amount");
    }

    {
      // scope for ether checks
      uint ethBalanceBefore = address(this).balance;
      uint ethBalanceAfter = ethBalanceBefore.sub(amountIn);
      require(ethBalanceAfter >= minLeftETH, "SwapAgent: insufficient ether left");
    }

    {
      // scope for token checks
      uint avgAmountOut = TwapOracle(_oracle).consult(WETH, amountIn, toTokenAddress);
      uint maxSlippageAmount = avgAmountOut.mul(assetData.maxSlippageRatio).div(1e18);
      uint minOutOnMaxSlippage = avgAmountOut.sub(maxSlippageAmount);

      // gas optimisation: reads both values using a single SLOAD
      (uint minAssetAmount, uint maxAssetAmount) = (assetData.minAmount, assetData.maxAmount);

      require(amountOutMin >= minOutOnMaxSlippage, "SwapAgent: amountOutMin < minOutOnMaxSlippage");
      require(balanceBefore < minAssetAmount, "SwapAgent: balanceBefore >= min");
      require(balanceBefore.add(amountOutMin) <= maxAssetAmount, "SwapAgent: balanceAfter > max");
    }

    address[] memory path = new address[](2);
    path[0] = WETH;
    path[1] = toTokenAddress;
    router.swapExactETHForTokens.value(amountIn)(amountOutMin, path, address(this), block.timestamp);

    assetData.lastSwapTime = uint32(block.timestamp);

    uint balanceAfter = IERC20(toTokenAddress).balanceOf(address(this));
    uint amountOut = balanceAfter.sub(balanceBefore);

    return amountOut;
  }

  function swapAssetForETH(
    address _oracle,
    AssetData storage assetData,
    address fromTokenAddress,
    uint amountIn,
    uint amountOutMin
  ) external returns (uint) {

    uint tokenBalanceBefore = IERC20(fromTokenAddress).balanceOf(address(this));
    uint balanceBefore = address(this).balance;
    address WETH = router.WETH();

    {
      // scope for swap frequency check
      uint timeSinceLastTrade = block.timestamp.sub(uint(assetData.lastSwapTime));
      require(timeSinceLastTrade > TwapOracle(_oracle).periodSize(), "SwapAgent: too fast");
    }

    {
      // scope for liquidity check
      address pairAddress = TwapOracle(_oracle).pairFor(fromTokenAddress, WETH);
      IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
      (uint112 reserve0, uint112 reserve1, /* time */) = pair.getReserves();

      uint tokenReserve = fromTokenAddress < WETH ? reserve0 : reserve1;
      uint maxTradable = tokenReserve.mul(MAX_LIQUIDITY_RATIO).div(1e18);

      require(amountIn <= maxTradable, "SwapAgent: exceeds max tradable amount");
    }

    {
      // scope for token checks
      uint avgAmountOut = TwapOracle(_oracle).consult(fromTokenAddress, amountIn, WETH);
      uint maxSlippageAmount = avgAmountOut.mul(assetData.maxSlippageRatio).div(1e18);
      uint minOutOnMaxSlippage = avgAmountOut.sub(maxSlippageAmount);

      // gas optimisation: reads both values using a single SLOAD
      (uint minAssetAmount, uint maxAssetAmount) = (assetData.minAmount, assetData.maxAmount);

      require(amountOutMin >= minOutOnMaxSlippage, "SwapAgent: amountOutMin < minOutOnMaxSlippage");
      require(tokenBalanceBefore > maxAssetAmount, "SwapAgent: tokenBalanceBefore <= max");
      require(tokenBalanceBefore.sub(amountIn) >= minAssetAmount, "SwapAgent: tokenBalanceAfter < min");
    }

    address[] memory path = new address[](2);
    path[0] = fromTokenAddress;
    path[1] = router.WETH();
    IERC20(fromTokenAddress).approve(address(router), amountIn);
    router.swapExactTokensForETH(amountIn, amountOutMin, path, address(this), block.timestamp);

    assetData.lastSwapTime = uint32(block.timestamp);

    uint balanceAfter = address(this).balance;
    uint amountOut = balanceAfter.sub(balanceBefore);

    return amountOut;
  }

}

File 15 of 38 : INXMMaster.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

contract INXMMaster {

  address public tokenAddress;

  address public owner;

  uint public pauseTime;

  function delegateCallBack(bytes32 myid) external;

  function masterInitialized() public view returns (bool);

  function isInternal(address _add) public view returns (bool);

  function isPause() public view returns (bool check);

  function isOwner(address _add) public view returns (bool);

  function isMember(address _add) public view returns (bool);

  function checkIsAuthToGoverned(address _add) public view returns (bool);

  function updatePauseTime(uint _time) public;

  function dAppLocker() public view returns (address _add);

  function dAppToken() public view returns (address _add);

  function getLatestAddress(bytes2 _contractName) public view returns (address payable contractAddress);
}

File 16 of 38 : PoolData.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../abstract/Iupgradable.sol";

contract DSValue {
  function peek() public view returns (bytes32, bool);

  function read() public view returns (bytes32);
}

contract PoolData is Iupgradable {
  using SafeMath for uint;

  struct ApiId {
    bytes4 typeOf;
    bytes4 currency;
    uint id;
    uint64 dateAdd;
    uint64 dateUpd;
  }

  struct CurrencyAssets {
    address currAddress;
    uint baseMin;
    uint varMin;
  }

  struct InvestmentAssets {
    address currAddress;
    bool status;
    uint64 minHoldingPercX100;
    uint64 maxHoldingPercX100;
    uint8 decimals;
  }

  struct IARankDetails {
    bytes4 maxIACurr;
    uint64 maxRate;
    bytes4 minIACurr;
    uint64 minRate;
  }

  struct McrData {
    uint mcrPercx100;
    uint mcrEther;
    uint vFull; //Pool funds
    uint64 date;
  }

  IARankDetails[] internal allIARankDetails;
  McrData[] public allMCRData;

  bytes4[] internal allInvestmentCurrencies;
  bytes4[] internal allCurrencies;
  bytes32[] public allAPIcall;
  mapping(bytes32 => ApiId) public allAPIid;
  mapping(uint64 => uint) internal datewiseId;
  mapping(bytes16 => uint) internal currencyLastIndex;
  mapping(bytes4 => CurrencyAssets) internal allCurrencyAssets;
  mapping(bytes4 => InvestmentAssets) internal allInvestmentAssets;
  mapping(bytes4 => uint) internal caAvgRate;
  mapping(bytes4 => uint) internal iaAvgRate;

  address public notariseMCR;
  address public daiFeedAddress;
  uint private constant DECIMAL1E18 = uint(10) ** 18;
  uint public uniswapDeadline;
  uint public liquidityTradeCallbackTime;
  uint public lastLiquidityTradeTrigger;
  uint64 internal lastDate;
  uint public variationPercX100;
  uint public iaRatesTime;
  uint public minCap;
  uint public mcrTime;
  uint public a;
  uint public shockParameter;
  uint public c;
  uint public mcrFailTime;
  uint public ethVolumeLimit;
  uint public capReached;
  uint public capacityLimit;

  constructor(address _notariseAdd, address _daiFeedAdd, address _daiAdd) public {
    notariseMCR = _notariseAdd;
    daiFeedAddress = _daiFeedAdd;
    c = 5800000;
    a = 1028;
    mcrTime = 24 hours;
    mcrFailTime = 6 hours;
    allMCRData.push(McrData(0, 0, 0, 0));
    minCap = 12000 * DECIMAL1E18;
    shockParameter = 50;
    variationPercX100 = 100; // 1%
    iaRatesTime = 24 hours; // 24 hours in seconds
    uniswapDeadline = 20 minutes;
    liquidityTradeCallbackTime = 4 hours;
    ethVolumeLimit = 4;
    capacityLimit = 10;
    allCurrencies.push("ETH");
    allCurrencyAssets["ETH"] = CurrencyAssets(address(0), 1000 * DECIMAL1E18, 0);
    allCurrencies.push("DAI");
    allCurrencyAssets["DAI"] = CurrencyAssets(_daiAdd, 50000 * DECIMAL1E18, 0);
    allInvestmentCurrencies.push("ETH");
    allInvestmentAssets["ETH"] = InvestmentAssets(address(0), true, 2500, 10000, 18);
    allInvestmentCurrencies.push("DAI");
    allInvestmentAssets["DAI"] = InvestmentAssets(_daiAdd, true, 250, 1500, 18);
  }

  /**
   * @dev to set the maximum cap allowed
   * @param val is the new value
   */
  function setCapReached(uint val) external onlyInternal {
    capReached = val;
  }

  /// @dev Updates the 3 day average rate of a IA currency.
  /// To be replaced by MakerDao's on chain rates
  /// @param curr IA Currency Name.
  /// @param rate Average exchange rate X 100 (of last 3 days).
  function updateIAAvgRate(bytes4 curr, uint rate) external onlyInternal {
    iaAvgRate[curr] = rate;
  }

  /// @dev Updates the 3 day average rate of a CA currency.
  /// To be replaced by MakerDao's on chain rates
  /// @param curr Currency Name.
  /// @param rate Average exchange rate X 100 (of last 3 days).
  function updateCAAvgRate(bytes4 curr, uint rate) external onlyInternal {
    caAvgRate[curr] = rate;
  }

  /// @dev Adds details of (Minimum Capital Requirement)MCR.
  /// @param mcrp Minimum Capital Requirement percentage (MCR% * 100 ,Ex:for 54.56% ,given 5456)
  /// @param vf Pool fund value in Ether used in the last full daily calculation from the Capital model.
  function pushMCRData(uint mcrp, uint mcre, uint vf, uint64 time) external onlyInternal {
    allMCRData.push(McrData(mcrp, mcre, vf, time));
  }

  /**
   * @dev Updates the Timestamp at which result of oracalize call is received.
   */
  function updateDateUpdOfAPI(bytes32 myid) external onlyInternal {
    allAPIid[myid].dateUpd = uint64(now);
  }

  /**
   * @dev Saves the details of the Oraclize API.
   * @param myid Id return by the oraclize query.
   * @param _typeof type of the query for which oraclize call is made.
   * @param id ID of the proposal,quote,cover etc. for which oraclize call is made
   */
  function saveApiDetails(bytes32 myid, bytes4 _typeof, uint id) external onlyInternal {
    allAPIid[myid] = ApiId(_typeof, "", id, uint64(now), uint64(now));
  }

  /**
   * @dev Stores the id return by the oraclize query.
   * Maintains record of all the Ids return by oraclize query.
   * @param myid Id return by the oraclize query.
   */
  function addInAllApiCall(bytes32 myid) external onlyInternal {
    allAPIcall.push(myid);
  }

  /**
   * @dev Saves investment asset rank details.
   * @param maxIACurr Maximum ranked investment asset currency.
   * @param maxRate Maximum ranked investment asset rate.
   * @param minIACurr Minimum ranked investment asset currency.
   * @param minRate Minimum ranked investment asset rate.
   * @param date in yyyymmdd.
   */
  function saveIARankDetails(
    bytes4 maxIACurr,
    uint64 maxRate,
    bytes4 minIACurr,
    uint64 minRate,
    uint64 date
  )
  external
  onlyInternal
  {
    allIARankDetails.push(IARankDetails(maxIACurr, maxRate, minIACurr, minRate));
    datewiseId[date] = allIARankDetails.length.sub(1);
  }

  /**
   * @dev to get the time for the laste liquidity trade trigger
   */
  function setLastLiquidityTradeTrigger() external onlyInternal {
    lastLiquidityTradeTrigger = now;
  }

  /**
   * @dev Updates Last Date.
   */
  function updatelastDate(uint64 newDate) external onlyInternal {
    lastDate = newDate;
  }

  /**
   * @dev Adds currency asset currency.
   * @param curr currency of the asset
   * @param currAddress address of the currency
   * @param baseMin base minimum in 10^18.
   */
  function addCurrencyAssetCurrency(
    bytes4 curr,
    address currAddress,
    uint baseMin
  )
  external
  {
    require(ms.checkIsAuthToGoverned(msg.sender));
    allCurrencies.push(curr);
    allCurrencyAssets[curr] = CurrencyAssets(currAddress, baseMin, 0);
  }

  /**
   * @dev Adds investment asset.
   */
  function addInvestmentAssetCurrency(
    bytes4 curr,
    address currAddress,
    bool status,
    uint64 minHoldingPercX100,
    uint64 maxHoldingPercX100,
    uint8 decimals
  )
  external
  {
    require(ms.checkIsAuthToGoverned(msg.sender));
    allInvestmentCurrencies.push(curr);
    allInvestmentAssets[curr] = InvestmentAssets(currAddress, status,
      minHoldingPercX100, maxHoldingPercX100, decimals);
  }

  /**
   * @dev Changes base minimum of a given currency asset.
   */
  function changeCurrencyAssetBaseMin(bytes4 curr, uint baseMin) external {
    require(ms.checkIsAuthToGoverned(msg.sender));
    allCurrencyAssets[curr].baseMin = baseMin;
  }

  /**
   * @dev changes variable minimum of a given currency asset.
   */
  function changeCurrencyAssetVarMin(bytes4 curr, uint varMin) external onlyInternal {
    allCurrencyAssets[curr].varMin = varMin;
  }

  /**
   * @dev Changes the investment asset status.
   */
  function changeInvestmentAssetStatus(bytes4 curr, bool status) external {
    require(ms.checkIsAuthToGoverned(msg.sender));
    allInvestmentAssets[curr].status = status;
  }

  /**
   * @dev Changes the investment asset Holding percentage of a given currency.
   */
  function changeInvestmentAssetHoldingPerc(
    bytes4 curr,
    uint64 minPercX100,
    uint64 maxPercX100
  )
  external
  {
    require(ms.checkIsAuthToGoverned(msg.sender));
    allInvestmentAssets[curr].minHoldingPercX100 = minPercX100;
    allInvestmentAssets[curr].maxHoldingPercX100 = maxPercX100;
  }

  /**
   * @dev Gets Currency asset token address.
   */
  function changeCurrencyAssetAddress(bytes4 curr, address currAdd) external {
    require(ms.checkIsAuthToGoverned(msg.sender));
    allCurrencyAssets[curr].currAddress = currAdd;
  }

  /**
   * @dev Changes Investment asset token address.
   */
  function changeInvestmentAssetAddressAndDecimal(
    bytes4 curr,
    address currAdd,
    uint8 newDecimal
  )
  external
  {
    require(ms.checkIsAuthToGoverned(msg.sender));
    allInvestmentAssets[curr].currAddress = currAdd;
    allInvestmentAssets[curr].decimals = newDecimal;
  }

  /// @dev Changes address allowed to post MCR.
  function changeNotariseAddress(address _add) external onlyInternal {
    notariseMCR = _add;
  }

  /// @dev updates daiFeedAddress address.
  /// @param _add address of DAI feed.
  function changeDAIfeedAddress(address _add) external onlyInternal {
    daiFeedAddress = _add;
  }

  /**
   * @dev Gets Uint Parameters of a code
   * @param code whose details we want
   * @return string value of the code
   * @return associated amount (time or perc or value) to the code
   */
  function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {
    codeVal = code;
    if (code == "MCRTIM") {
      val = mcrTime / (1 hours);

    } else if (code == "MCRFTIM") {

      val = mcrFailTime / (1 hours);

    } else if (code == "MCRMIN") {

      val = minCap;

    } else if (code == "MCRSHOCK") {

      val = shockParameter;

    } else if (code == "MCRCAPL") {

      val = capacityLimit;

    } else if (code == "IMZ") {

      val = variationPercX100;

    } else if (code == "IMRATET") {

      val = iaRatesTime / (1 hours);

    } else if (code == "IMUNIDL") {

      val = uniswapDeadline / (1 minutes);

    } else if (code == "IMLIQT") {

      val = liquidityTradeCallbackTime / (1 hours);

    } else if (code == "IMETHVL") {

      val = ethVolumeLimit;

    } else if (code == "C") {
      val = c;

    } else if (code == "A") {

      val = a;

    }

  }

  /// @dev Checks whether a given address can notaise MCR data or not.
  /// @param _add Address.
  /// @return res Returns 0 if address is not authorized, else 1.
  function isnotarise(address _add) external view returns (bool res) {
    res = false;
    if (_add == notariseMCR)
      res = true;
  }

  /// @dev Gets the details of last added MCR.
  /// @return mcrPercx100 Total Minimum Capital Requirement percentage of that month of year(multiplied by 100).
  /// @return vFull Total Pool fund value in Ether used in the last full daily calculation.
  function getLastMCR() external view returns (uint mcrPercx100, uint mcrEtherx1E18, uint vFull, uint64 date) {
    uint index = allMCRData.length.sub(1);
    return (
    allMCRData[index].mcrPercx100,
    allMCRData[index].mcrEther,
    allMCRData[index].vFull,
    allMCRData[index].date
    );
  }

  /// @dev Gets last Minimum Capital Requirement percentage of Capital Model
  /// @return val MCR% value,multiplied by 100.
  function getLastMCRPerc() external view returns (uint) {
    return allMCRData[allMCRData.length.sub(1)].mcrPercx100;
  }

  /// @dev Gets last Ether price of Capital Model
  /// @return val ether value,multiplied by 100.
  function getLastMCREther() external view returns (uint) {
    return allMCRData[allMCRData.length.sub(1)].mcrEther;
  }

  /// @dev Gets Pool fund value in Ether used in the last full daily calculation from the Capital model.
  function getLastVfull() external view returns (uint) {
    return allMCRData[allMCRData.length.sub(1)].vFull;
  }

  /// @dev Gets last Minimum Capital Requirement in Ether.
  /// @return date of MCR.
  function getLastMCRDate() external view returns (uint64 date) {
    date = allMCRData[allMCRData.length.sub(1)].date;
  }

  /// @dev Gets details for token price calculation.
  function getTokenPriceDetails(bytes4 curr) external view returns (uint _a, uint _c, uint rate) {
    _a = a;
    _c = c;
    rate = _getAvgRate(curr, false);
  }

  /// @dev Gets the total number of times MCR calculation has been made.
  function getMCRDataLength() external view returns (uint len) {
    len = allMCRData.length;
  }

  /**
   * @dev Gets investment asset rank details by given date.
   */
  function getIARankDetailsByDate(
    uint64 date
  )
  external
  view
  returns (
    bytes4 maxIACurr,
    uint64 maxRate,
    bytes4 minIACurr,
    uint64 minRate
  )
  {
    uint index = datewiseId[date];
    return (
    allIARankDetails[index].maxIACurr,
    allIARankDetails[index].maxRate,
    allIARankDetails[index].minIACurr,
    allIARankDetails[index].minRate
    );
  }

  /**
   * @dev Gets Last Date.
   */
  function getLastDate() external view returns (uint64 date) {
    return lastDate;
  }

  /**
   * @dev Gets investment currency for a given index.
   */
  function getInvestmentCurrencyByIndex(uint index) external view returns (bytes4 currName) {
    return allInvestmentCurrencies[index];
  }

  /**
   * @dev Gets count of investment currency.
   */
  function getInvestmentCurrencyLen() external view returns (uint len) {
    return allInvestmentCurrencies.length;
  }

  /**
   * @dev Gets all the investment currencies.
   */
  function getAllInvestmentCurrencies() external view returns (bytes4[] memory currencies) {
    return allInvestmentCurrencies;
  }

  /**
   * @dev Gets All currency for a given index.
   */
  function getCurrenciesByIndex(uint index) external view returns (bytes4 currName) {
    return allCurrencies[index];
  }

  /**
   * @dev Gets count of All currency.
   */
  function getAllCurrenciesLen() external view returns (uint len) {
    return allCurrencies.length;
  }

  /**
   * @dev Gets all currencies
   */
  function getAllCurrencies() external view returns (bytes4[] memory currencies) {
    return allCurrencies;
  }

  /**
   * @dev Gets currency asset details for a given currency.
   */
  function getCurrencyAssetVarBase(
    bytes4 curr
  )
  external
  view
  returns (
    bytes4 currency,
    uint baseMin,
    uint varMin
  )
  {
    return (
    curr,
    allCurrencyAssets[curr].baseMin,
    allCurrencyAssets[curr].varMin
    );
  }

  /**
   * @dev Gets minimum variable value for currency asset.
   */
  function getCurrencyAssetVarMin(bytes4 curr) external view returns (uint varMin) {
    return allCurrencyAssets[curr].varMin;
  }

  /**
   * @dev Gets base minimum of  a given currency asset.
   */
  function getCurrencyAssetBaseMin(bytes4 curr) external view returns (uint baseMin) {
    return allCurrencyAssets[curr].baseMin;
  }

  /**
   * @dev Gets investment asset maximum and minimum holding percentage of a given currency.
   */
  function getInvestmentAssetHoldingPerc(
    bytes4 curr
  )
  external
  view
  returns (
    uint64 minHoldingPercX100,
    uint64 maxHoldingPercX100
  )
  {
    return (
    allInvestmentAssets[curr].minHoldingPercX100,
    allInvestmentAssets[curr].maxHoldingPercX100
    );
  }

  /**
   * @dev Gets investment asset decimals.
   */
  function getInvestmentAssetDecimals(bytes4 curr) external view returns (uint8 decimal) {
    return allInvestmentAssets[curr].decimals;
  }

  /**
   * @dev Gets investment asset maximum holding percentage of a given currency.
   */
  function getInvestmentAssetMaxHoldingPerc(bytes4 curr) external view returns (uint64 maxHoldingPercX100) {
    return allInvestmentAssets[curr].maxHoldingPercX100;
  }

  /**
   * @dev Gets investment asset minimum holding percentage of a given currency.
   */
  function getInvestmentAssetMinHoldingPerc(bytes4 curr) external view returns (uint64 minHoldingPercX100) {
    return allInvestmentAssets[curr].minHoldingPercX100;
  }

  /**
   * @dev Gets investment asset details of a given currency
   */
  function getInvestmentAssetDetails(
    bytes4 curr
  )
  external
  view
  returns (
    bytes4 currency,
    address currAddress,
    bool status,
    uint64 minHoldingPerc,
    uint64 maxHoldingPerc,
    uint8 decimals
  )
  {
    return (
    curr,
    allInvestmentAssets[curr].currAddress,
    allInvestmentAssets[curr].status,
    allInvestmentAssets[curr].minHoldingPercX100,
    allInvestmentAssets[curr].maxHoldingPercX100,
    allInvestmentAssets[curr].decimals
    );
  }

  /**
   * @dev Gets Currency asset token address.
   */
  function getCurrencyAssetAddress(bytes4 curr) external view returns (address) {
    return allCurrencyAssets[curr].currAddress;
  }

  /**
   * @dev Gets investment asset token address.
   */
  function getInvestmentAssetAddress(bytes4 curr) external view returns (address) {
    return allInvestmentAssets[curr].currAddress;
  }

  /**
   * @dev Gets investment asset active Status of a given currency.
   */
  function getInvestmentAssetStatus(bytes4 curr) external view returns (bool status) {
    return allInvestmentAssets[curr].status;
  }

  /**
   * @dev Gets type of oraclize query for a given Oraclize Query ID.
   * @param myid Oraclize Query ID identifying the query for which the result is being received.
   * @return _typeof It could be of type "quote","quotation","cover","claim" etc.
   */
  function getApiIdTypeOf(bytes32 myid) external view returns (bytes4) {
    return allAPIid[myid].typeOf;
  }

  /**
   * @dev Gets ID associated to oraclize query for a given Oraclize Query ID.
   * @param myid Oraclize Query ID identifying the query for which the result is being received.
   * @return id1 It could be the ID of "proposal","quotation","cover","claim" etc.
   */
  function getIdOfApiId(bytes32 myid) external view returns (uint) {
    return allAPIid[myid].id;
  }

  /**
   * @dev Gets the Timestamp of a oracalize call.
   */
  function getDateAddOfAPI(bytes32 myid) external view returns (uint64) {
    return allAPIid[myid].dateAdd;
  }

  /**
   * @dev Gets the Timestamp at which result of oracalize call is received.
   */
  function getDateUpdOfAPI(bytes32 myid) external view returns (uint64) {
    return allAPIid[myid].dateUpd;
  }

  /**
   * @dev Gets currency by oracalize id.
   */
  function getCurrOfApiId(bytes32 myid) external view returns (bytes4) {
    return allAPIid[myid].currency;
  }

  /**
   * @dev Gets ID return by the oraclize query of a given index.
   * @param index Index.
   * @return myid ID return by the oraclize query.
   */
  function getApiCallIndex(uint index) external view returns (bytes32 myid) {
    myid = allAPIcall[index];
  }

  /**
   * @dev Gets Length of API call.
   */
  function getApilCallLength() external view returns (uint) {
    return allAPIcall.length;
  }

  /**
   * @dev Get Details of Oraclize API when given Oraclize Id.
   * @param myid ID return by the oraclize query.
   * @return _typeof ype of the query for which oraclize
   * call is made.("proposal","quote","quotation" etc.)
   */
  function getApiCallDetails(
    bytes32 myid
  )
  external
  view
  returns (
    bytes4 _typeof,
    bytes4 curr,
    uint id,
    uint64 dateAdd,
    uint64 dateUpd
  )
  {
    return (
    allAPIid[myid].typeOf,
    allAPIid[myid].currency,
    allAPIid[myid].id,
    allAPIid[myid].dateAdd,
    allAPIid[myid].dateUpd
    );
  }

  /**
   * @dev Updates Uint Parameters of a code
   * @param code whose details we want to update
   * @param val value to set
   */
  function updateUintParameters(bytes8 code, uint val) public {
    require(ms.checkIsAuthToGoverned(msg.sender));
    if (code == "MCRTIM") {
      _changeMCRTime(val * 1 hours);

    } else if (code == "MCRFTIM") {

      _changeMCRFailTime(val * 1 hours);

    } else if (code == "MCRMIN") {

      _changeMinCap(val);

    } else if (code == "MCRSHOCK") {

      _changeShockParameter(val);

    } else if (code == "MCRCAPL") {

      _changeCapacityLimit(val);

    } else if (code == "IMZ") {

      _changeVariationPercX100(val);

    } else if (code == "IMRATET") {

      _changeIARatesTime(val * 1 hours);

    } else if (code == "IMUNIDL") {

      _changeUniswapDeadlineTime(val * 1 minutes);

    } else if (code == "IMLIQT") {

      _changeliquidityTradeCallbackTime(val * 1 hours);

    } else if (code == "IMETHVL") {

      _setEthVolumeLimit(val);

    } else if (code == "C") {
      _changeC(val);

    } else if (code == "A") {

      _changeA(val);

    } else {
      revert("Invalid param code");
    }

  }

  /**
   * @dev to get the average rate of currency rate
   * @param curr is the currency in concern
   * @return required rate
   */
  function getCAAvgRate(bytes4 curr) public view returns (uint rate) {
    return _getAvgRate(curr, false);
  }

  /**
   * @dev to get the average rate of investment rate
   * @param curr is the investment in concern
   * @return required rate
   */
  function getIAAvgRate(bytes4 curr) public view returns (uint rate) {
    return _getAvgRate(curr, true);
  }

  function changeDependentContractAddress() public onlyInternal {}

  /// @dev Gets the average rate of a CA currency.
  /// @param curr Currency Name.
  /// @return rate Average rate X 100(of last 3 days).
  function _getAvgRate(bytes4 curr, bool isIA) internal view returns (uint rate) {
    if (curr == "DAI") {
      DSValue ds = DSValue(daiFeedAddress);
      rate = uint(ds.read()).div(uint(10) ** 16);
    } else if (isIA) {
      rate = iaAvgRate[curr];
    } else {
      rate = caAvgRate[curr];
    }
  }

  /**
   * @dev to set the ethereum volume limit
   * @param val is the new limit value
   */
  function _setEthVolumeLimit(uint val) internal {
    ethVolumeLimit = val;
  }

  /// @dev Sets minimum Cap.
  function _changeMinCap(uint newCap) internal {
    minCap = newCap;
  }

  /// @dev Sets Shock Parameter.
  function _changeShockParameter(uint newParam) internal {
    shockParameter = newParam;
  }

  /// @dev Changes time period for obtaining new MCR data from external oracle query.
  function _changeMCRTime(uint _time) internal {
    mcrTime = _time;
  }

  /// @dev Sets MCR Fail time.
  function _changeMCRFailTime(uint _time) internal {
    mcrFailTime = _time;
  }

  /**
   * @dev to change the uniswap deadline time
   * @param newDeadline is the value
   */
  function _changeUniswapDeadlineTime(uint newDeadline) internal {
    uniswapDeadline = newDeadline;
  }

  /**
   * @dev to change the liquidity trade call back time
   * @param newTime is the new value to be set
   */
  function _changeliquidityTradeCallbackTime(uint newTime) internal {
    liquidityTradeCallbackTime = newTime;
  }

  /**
   * @dev Changes time after which investment asset rates need to be fed.
   */
  function _changeIARatesTime(uint _newTime) internal {
    iaRatesTime = _newTime;
  }

  /**
   * @dev Changes the variation range percentage.
   */
  function _changeVariationPercX100(uint newPercX100) internal {
    variationPercX100 = newPercX100;
  }

  /// @dev Changes Growth Step
  function _changeC(uint newC) internal {
    c = newC;
  }

  /// @dev Changes scaling factor.
  function _changeA(uint val) internal {
    a = val;
  }

  /**
   * @dev to change the capacity limit
   * @param val is the new value
   */
  function _changeCapacityLimit(uint val) internal {
    capacityLimit = val;
  }
}

File 17 of 38 : ClaimsReward.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

//Claims Reward Contract contains the functions for calculating number of tokens
// that will get rewarded, unlocked or burned depending upon the status of claim.

pragma solidity ^0.5.0;

import "../../interfaces/IPooledStaking.sol";
import "../capital/Pool.sol";
import "../cover/QuotationData.sol";
import "../governance/Governance.sol";
import "../token/TokenData.sol";
import "../token/TokenFunctions.sol";
import "./Claims.sol";
import "./ClaimsData.sol";

contract ClaimsReward is Iupgradable {
  using SafeMath for uint;

  NXMToken internal tk;
  TokenController internal tc;
  TokenFunctions internal tf;
  TokenData internal td;
  QuotationData internal qd;
  Claims internal c1;
  ClaimsData internal cd;
  Pool internal pool;
  Governance internal gv;
  IPooledStaking internal pooledStaking;
  MemberRoles internal memberRoles;

  // assigned in constructor
  address public DAI;

  // constants
  address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
  uint private constant DECIMAL1E18 = uint(10) ** 18;

  constructor (address masterAddress, address _daiAddress) public {
    changeMasterAddress(masterAddress);
    DAI = _daiAddress;
  }

  function changeDependentContractAddress() public onlyInternal {
    c1 = Claims(ms.getLatestAddress("CL"));
    cd = ClaimsData(ms.getLatestAddress("CD"));
    tk = NXMToken(ms.tokenAddress());
    tc = TokenController(ms.getLatestAddress("TC"));
    td = TokenData(ms.getLatestAddress("TD"));
    tf = TokenFunctions(ms.getLatestAddress("TF"));
    qd = QuotationData(ms.getLatestAddress("QD"));
    gv = Governance(ms.getLatestAddress("GV"));
    pooledStaking = IPooledStaking(ms.getLatestAddress("PS"));
    memberRoles = MemberRoles(ms.getLatestAddress("MR"));
    pool = Pool(ms.getLatestAddress("P1"));
  }

  /// @dev Decides the next course of action for a given claim.
  function changeClaimStatus(uint claimid) public checkPause onlyInternal {

    (, uint coverid) = cd.getClaimCoverId(claimid);
    (, uint status) = cd.getClaimStatusNumber(claimid);

    // when current status is "Pending-Claim Assessor Vote"
    if (status == 0) {
      _changeClaimStatusCA(claimid, coverid, status);
    } else if (status >= 1 && status <= 5) {
      _changeClaimStatusMV(claimid, coverid, status);
    } else if (status == 12) {// when current status is "Claim Accepted Payout Pending"

      bool payoutSucceeded = attemptClaimPayout(coverid);

      if (payoutSucceeded) {
        c1.setClaimStatus(claimid, 14);
      } else {
        c1.setClaimStatus(claimid, 12);
      }
    }

    c1.changePendingClaimStart();
  }

  function getCurrencyAssetAddress(bytes4 currency) public view returns (address) {

    if (currency == "ETH") {
      return ETH;
    }

    if (currency == "DAI") {
      return DAI;
    }

    revert("ClaimsReward: unknown asset");
  }

  function attemptClaimPayout(uint coverId) internal returns (bool success) {

    uint sumAssured = qd.getCoverSumAssured(coverId);
    // TODO: when adding new cover currencies, fetch the correct decimals for this multiplication
    uint sumAssuredWei = sumAssured.mul(1e18);

    // get asset address
    bytes4 coverCurrency = qd.getCurrencyOfCover(coverId);
    address asset = getCurrencyAssetAddress(coverCurrency);

    // get payout address
    address payable coverHolder = qd.getCoverMemberAddress(coverId);
    address payable payoutAddress = memberRoles.getClaimPayoutAddress(coverHolder);

    // execute the payout
    bool payoutSucceeded = pool.sendClaimPayout(asset, payoutAddress, sumAssuredWei);

    if (payoutSucceeded) {

      // burn staked tokens
      (, address scAddress) = qd.getscAddressOfCover(coverId);
      uint tokenPrice = pool.getTokenPrice(asset);

      // note: for new assets "18" needs to be replaced with target asset decimals
      uint burnNXMAmount = sumAssuredWei.mul(1e18).div(tokenPrice);
      pooledStaking.pushBurn(scAddress, burnNXMAmount);

      // adjust total sum assured
      (, address coverContract) = qd.getscAddressOfCover(coverId);
      qd.subFromTotalSumAssured(coverCurrency, sumAssured);
      qd.subFromTotalSumAssuredSC(coverContract, coverCurrency, sumAssured);

      return true;
    }

    return false;
  }

  /// @dev Amount of tokens to be rewarded to a user for a particular vote id.
  /// @param check 1 -> CA vote, else member vote
  /// @param voteid vote id for which reward has to be Calculated
  /// @param flag if 1 calculate even if claimed,else don't calculate if already claimed
  /// @return tokenCalculated reward to be given for vote id
  /// @return lastClaimedCheck true if final verdict is still pending for that voteid
  /// @return tokens number of tokens locked under that voteid
  /// @return perc percentage of reward to be given.
  function getRewardToBeGiven(
    uint check,
    uint voteid,
    uint flag
  )
  public
  view
  returns (
    uint tokenCalculated,
    bool lastClaimedCheck,
    uint tokens,
    uint perc
  )

  {
    uint claimId;
    int8 verdict;
    bool claimed;
    uint tokensToBeDist;
    uint totalTokens;
    (tokens, claimId, verdict, claimed) = cd.getVoteDetails(voteid);
    lastClaimedCheck = false;
    int8 claimVerdict = cd.getFinalVerdict(claimId);
    if (claimVerdict == 0) {
      lastClaimedCheck = true;
    }

    if (claimVerdict == verdict && (claimed == false || flag == 1)) {

      if (check == 1) {
        (perc, , tokensToBeDist) = cd.getClaimRewardDetail(claimId);
      } else {
        (, perc, tokensToBeDist) = cd.getClaimRewardDetail(claimId);
      }

      if (perc > 0) {
        if (check == 1) {
          if (verdict == 1) {
            (, totalTokens,) = cd.getClaimsTokenCA(claimId);
          } else {
            (,, totalTokens) = cd.getClaimsTokenCA(claimId);
          }
        } else {
          if (verdict == 1) {
            (, totalTokens,) = cd.getClaimsTokenMV(claimId);
          } else {
            (,, totalTokens) = cd.getClaimsTokenMV(claimId);
          }
        }
        tokenCalculated = (perc.mul(tokens).mul(tokensToBeDist)).div(totalTokens.mul(100));


      }
    }
  }

  /// @dev Transfers all tokens held by contract to a new contract in case of upgrade.
  function upgrade(address _newAdd) public onlyInternal {
    uint amount = tk.balanceOf(address(this));
    if (amount > 0) {
      require(tk.transfer(_newAdd, amount));
    }

  }

  /// @dev Total reward in token due for claim by a user.
  /// @return total total number of tokens
  function getRewardToBeDistributedByUser(address _add) public view returns (uint total) {
    uint lengthVote = cd.getVoteAddressCALength(_add);
    uint lastIndexCA;
    uint lastIndexMV;
    uint tokenForVoteId;
    uint voteId;
    (lastIndexCA, lastIndexMV) = cd.getRewardDistributedIndex(_add);

    for (uint i = lastIndexCA; i < lengthVote; i++) {
      voteId = cd.getVoteAddressCA(_add, i);
      (tokenForVoteId,,,) = getRewardToBeGiven(1, voteId, 0);
      total = total.add(tokenForVoteId);
    }

    lengthVote = cd.getVoteAddressMemberLength(_add);

    for (uint j = lastIndexMV; j < lengthVote; j++) {
      voteId = cd.getVoteAddressMember(_add, j);
      (tokenForVoteId,,,) = getRewardToBeGiven(0, voteId, 0);
      total = total.add(tokenForVoteId);
    }
    return (total);
  }

  /// @dev Gets reward amount and claiming status for a given claim id.
  /// @return reward amount of tokens to user.
  /// @return claimed true if already claimed false if yet to be claimed.
  function getRewardAndClaimedStatus(uint check, uint claimId) public view returns (uint reward, bool claimed) {
    uint voteId;
    uint claimid;
    uint lengthVote;

    if (check == 1) {
      lengthVote = cd.getVoteAddressCALength(msg.sender);
      for (uint i = 0; i < lengthVote; i++) {
        voteId = cd.getVoteAddressCA(msg.sender, i);
        (, claimid, , claimed) = cd.getVoteDetails(voteId);
        if (claimid == claimId) {break;}
      }
    } else {
      lengthVote = cd.getVoteAddressMemberLength(msg.sender);
      for (uint j = 0; j < lengthVote; j++) {
        voteId = cd.getVoteAddressMember(msg.sender, j);
        (, claimid, , claimed) = cd.getVoteDetails(voteId);
        if (claimid == claimId) {break;}
      }
    }
    (reward,,,) = getRewardToBeGiven(check, voteId, 1);

  }

  /**
   * @dev Function used to claim all pending rewards : Claims Assessment + Risk Assessment + Governance
   * Claim assesment, Risk assesment, Governance rewards
   */
  function claimAllPendingReward(uint records) public isMemberAndcheckPause {
    _claimRewardToBeDistributed(records);
    pooledStaking.withdrawReward(msg.sender);
    uint governanceRewards = gv.claimReward(msg.sender, records);
    if (governanceRewards > 0) {
      require(tk.transfer(msg.sender, governanceRewards));
    }
  }

  /**
   * @dev Function used to get pending rewards of a particular user address.
   * @param _add user address.
   * @return total reward amount of the user
   */
  function getAllPendingRewardOfUser(address _add) public view returns (uint) {
    uint caReward = getRewardToBeDistributedByUser(_add);
    uint pooledStakingReward = pooledStaking.stakerReward(_add);
    uint governanceReward = gv.getPendingReward(_add);
    return caReward.add(pooledStakingReward).add(governanceReward);
  }

  /// @dev Rewards/Punishes users who  participated in Claims assessment.
  //    Unlocking and burning of the tokens will also depend upon the status of claim.
  /// @param claimid Claim Id.
  function _rewardAgainstClaim(uint claimid, uint coverid, uint status) internal {

    uint premiumNXM = qd.getCoverPremiumNXM(coverid);
    uint distributableTokens = premiumNXM.mul(cd.claimRewardPerc()).div(100); // 20% of premium

    uint percCA;
    uint percMV;

    (percCA, percMV) = cd.getRewardStatus(status);
    cd.setClaimRewardDetail(claimid, percCA, percMV, distributableTokens);

    if (percCA > 0 || percMV > 0) {
      tc.mint(address(this), distributableTokens);
    }

    // denied
    if (status == 6 || status == 9 || status == 11) {

      cd.changeFinalVerdict(claimid, - 1);
      td.setDepositCN(coverid, false); // Unset flag
      tf.burnDepositCN(coverid); // burn Deposited CN

    // accepted
    } else if (status == 7 || status == 8 || status == 10) {

      cd.changeFinalVerdict(claimid, 1);
      td.setDepositCN(coverid, false); // Unset flag
      tf.unlockCN(coverid);

      bool payoutSucceeded = attemptClaimPayout(coverid);

      // 12 = payout pending, 14 = payout succeeded
      uint nextStatus = payoutSucceeded ? 14 : 12;
      c1.setClaimStatus(claimid, nextStatus);
    }
  }

  /// @dev Computes the result of Claim Assessors Voting for a given claim id.
  function _changeClaimStatusCA(uint claimid, uint coverid, uint status) internal {
    // Check if voting should be closed or not
    if (c1.checkVoteClosing(claimid) == 1) {
      uint caTokens = c1.getCATokens(claimid, 0); // converted in cover currency.
      uint accept;
      uint deny;
      uint acceptAndDeny;
      bool rewardOrPunish;
      uint sumAssured;
      (, accept) = cd.getClaimVote(claimid, 1);
      (, deny) = cd.getClaimVote(claimid, - 1);
      acceptAndDeny = accept.add(deny);
      accept = accept.mul(100);
      deny = deny.mul(100);

      if (caTokens == 0) {
        status = 3;
      } else {
        sumAssured = qd.getCoverSumAssured(coverid).mul(DECIMAL1E18);
        // Min threshold reached tokens used for voting > 5* sum assured
        if (caTokens > sumAssured.mul(5)) {

          if (accept.div(acceptAndDeny) > 70) {
            status = 7;
            qd.changeCoverStatusNo(coverid, uint8(QuotationData.CoverStatus.ClaimAccepted));
            rewardOrPunish = true;
          } else if (deny.div(acceptAndDeny) > 70) {
            status = 6;
            qd.changeCoverStatusNo(coverid, uint8(QuotationData.CoverStatus.ClaimDenied));
            rewardOrPunish = true;
          } else if (accept.div(acceptAndDeny) > deny.div(acceptAndDeny)) {
            status = 4;
          } else {
            status = 5;
          }

        } else {

          if (accept.div(acceptAndDeny) > deny.div(acceptAndDeny)) {
            status = 2;
          } else {
            status = 3;
          }
        }
      }

      c1.setClaimStatus(claimid, status);

      if (rewardOrPunish) {
        _rewardAgainstClaim(claimid, coverid, status);
      }
    }
  }

  /// @dev Computes the result of Member Voting for a given claim id.
  function _changeClaimStatusMV(uint claimid, uint coverid, uint status) internal {

    // Check if voting should be closed or not
    if (c1.checkVoteClosing(claimid) == 1) {
      uint8 coverStatus;
      uint statusOrig = status;
      uint mvTokens = c1.getCATokens(claimid, 1); // converted in cover currency.

      // If tokens used for acceptance >50%, claim is accepted
      uint sumAssured = qd.getCoverSumAssured(coverid).mul(DECIMAL1E18);
      uint thresholdUnreached = 0;
      // Minimum threshold for member voting is reached only when
      // value of tokens used for voting > 5* sum assured of claim id
      if (mvTokens < sumAssured.mul(5)) {
        thresholdUnreached = 1;
      }

      uint accept;
      (, accept) = cd.getClaimMVote(claimid, 1);
      uint deny;
      (, deny) = cd.getClaimMVote(claimid, - 1);

      if (accept.add(deny) > 0) {
        if (accept.mul(100).div(accept.add(deny)) >= 50 && statusOrig > 1 &&
        statusOrig <= 5 && thresholdUnreached == 0) {
          status = 8;
          coverStatus = uint8(QuotationData.CoverStatus.ClaimAccepted);
        } else if (deny.mul(100).div(accept.add(deny)) >= 50 && statusOrig > 1 &&
        statusOrig <= 5 && thresholdUnreached == 0) {
          status = 9;
          coverStatus = uint8(QuotationData.CoverStatus.ClaimDenied);
        }
      }

      if (thresholdUnreached == 1 && (statusOrig == 2 || statusOrig == 4)) {
        status = 10;
        coverStatus = uint8(QuotationData.CoverStatus.ClaimAccepted);
      } else if (thresholdUnreached == 1 && (statusOrig == 5 || statusOrig == 3 || statusOrig == 1)) {
        status = 11;
        coverStatus = uint8(QuotationData.CoverStatus.ClaimDenied);
      }

      c1.setClaimStatus(claimid, status);
      qd.changeCoverStatusNo(coverid, uint8(coverStatus));
      // Reward/Punish Claim Assessors and Members who participated in Claims assessment
      _rewardAgainstClaim(claimid, coverid, status);
    }
  }

  /// @dev Allows a user to claim all pending  Claims assessment rewards.
  function _claimRewardToBeDistributed(uint _records) internal {
    uint lengthVote = cd.getVoteAddressCALength(msg.sender);
    uint voteid;
    uint lastIndex;
    (lastIndex,) = cd.getRewardDistributedIndex(msg.sender);
    uint total = 0;
    uint tokenForVoteId = 0;
    bool lastClaimedCheck;
    uint _days = td.lockCADays();
    bool claimed;
    uint counter = 0;
    uint claimId;
    uint perc;
    uint i;
    uint lastClaimed = lengthVote;

    for (i = lastIndex; i < lengthVote && counter < _records; i++) {
      voteid = cd.getVoteAddressCA(msg.sender, i);
      (tokenForVoteId, lastClaimedCheck, , perc) = getRewardToBeGiven(1, voteid, 0);
      if (lastClaimed == lengthVote && lastClaimedCheck == true) {
        lastClaimed = i;
      }
      (, claimId, , claimed) = cd.getVoteDetails(voteid);

      if (perc > 0 && !claimed) {
        counter++;
        cd.setRewardClaimed(voteid, true);
      } else if (perc == 0 && cd.getFinalVerdict(claimId) != 0 && !claimed) {
        (perc,,) = cd.getClaimRewardDetail(claimId);
        if (perc == 0) {
          counter++;
        }
        cd.setRewardClaimed(voteid, true);
      }
      if (tokenForVoteId > 0) {
        total = tokenForVoteId.add(total);
      }
    }
    if (lastClaimed == lengthVote) {
      cd.setRewardDistributedIndexCA(msg.sender, i);
    }
    else {
      cd.setRewardDistributedIndexCA(msg.sender, lastClaimed);
    }
    lengthVote = cd.getVoteAddressMemberLength(msg.sender);
    lastClaimed = lengthVote;
    _days = _days.mul(counter);
    if (tc.tokensLockedAtTime(msg.sender, "CLA", now) > 0) {
      tc.reduceLock(msg.sender, "CLA", _days);
    }
    (, lastIndex) = cd.getRewardDistributedIndex(msg.sender);
    lastClaimed = lengthVote;
    counter = 0;
    for (i = lastIndex; i < lengthVote && counter < _records; i++) {
      voteid = cd.getVoteAddressMember(msg.sender, i);
      (tokenForVoteId, lastClaimedCheck,,) = getRewardToBeGiven(0, voteid, 0);
      if (lastClaimed == lengthVote && lastClaimedCheck == true) {
        lastClaimed = i;
      }
      (, claimId, , claimed) = cd.getVoteDetails(voteid);
      if (claimed == false && cd.getFinalVerdict(claimId) != 0) {
        cd.setRewardClaimed(voteid, true);
        counter++;
      }
      if (tokenForVoteId > 0) {
        total = tokenForVoteId.add(total);
      }
    }
    if (total > 0) {
      require(tk.transfer(msg.sender, total));
    }
    if (lastClaimed == lengthVote) {
      cd.setRewardDistributedIndexMV(msg.sender, i);
    }
    else {
      cd.setRewardDistributedIndexMV(msg.sender, lastClaimed);
    }
  }

  /**
   * @dev Function used to claim the commission earned by the staker.
   */
  function _claimStakeCommission(uint _records, address _user) external onlyInternal {
    uint total = 0;
    uint len = td.getStakerStakedContractLength(_user);
    uint lastCompletedStakeCommission = td.lastCompletedStakeCommission(_user);
    uint commissionEarned;
    uint commissionRedeemed;
    uint maxCommission;
    uint lastCommisionRedeemed = len;
    uint counter;
    uint i;

    for (i = lastCompletedStakeCommission; i < len && counter < _records; i++) {
      commissionRedeemed = td.getStakerRedeemedStakeCommission(_user, i);
      commissionEarned = td.getStakerEarnedStakeCommission(_user, i);
      maxCommission = td.getStakerInitialStakedAmountOnContract(
        _user, i).mul(td.stakerMaxCommissionPer()).div(100);
      if (lastCommisionRedeemed == len && maxCommission != commissionEarned)
        lastCommisionRedeemed = i;
      td.pushRedeemedStakeCommissions(_user, i, commissionEarned.sub(commissionRedeemed));
      total = total.add(commissionEarned.sub(commissionRedeemed));
      counter++;
    }
    if (lastCommisionRedeemed == len) {
      td.setLastCompletedStakeCommissionIndex(_user, i);
    } else {
      td.setLastCompletedStakeCommissionIndex(_user, lastCommisionRedeemed);
    }

    if (total > 0)
      require(tk.transfer(_user, total)); // solhint-disable-line
  }

}

File 18 of 38 : MemberRoles.sol
/* Copyright (C) 2017 GovBlocks.io
  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "../claims/ClaimsReward.sol";
import "../cover/QuotationData.sol";
import "../token/TokenController.sol";
import "../token/TokenData.sol";
import "../token/TokenFunctions.sol";
import "./Governance.sol";
import "./external/Governed.sol";

contract MemberRoles is Governed, Iupgradable {

  TokenController public dAppToken;
  TokenData internal td;
  QuotationData internal qd;
  ClaimsReward internal cr;
  Governance internal gv;
  TokenFunctions internal tf;
  NXMToken public tk;

  struct MemberRoleDetails {
    uint memberCounter;
    mapping(address => bool) memberActive;
    address[] memberAddress;
    address authorized;
  }

  enum Role {UnAssigned, AdvisoryBoard, Member, Owner}

  event MemberRole(uint256 indexed roleId, bytes32 roleName, string roleDescription);

  event switchedMembership(address indexed previousMember, address indexed newMember, uint timeStamp);

  event ClaimPayoutAddressSet(address indexed member, address indexed payoutAddress);

  MemberRoleDetails[] internal memberRoleData;
  bool internal constructorCheck;
  uint public maxABCount;
  bool public launched;
  uint public launchedOn;

  mapping (address => address payable) internal claimPayoutAddress;

  modifier checkRoleAuthority(uint _memberRoleId) {
    if (memberRoleData[_memberRoleId].authorized != address(0))
      require(msg.sender == memberRoleData[_memberRoleId].authorized);
    else
      require(isAuthorizedToGovern(msg.sender), "Not Authorized");
    _;
  }

  /**
   * @dev to swap advisory board member
   * @param _newABAddress is address of new AB member
   * @param _removeAB is advisory board member to be removed
   */
  function swapABMember(
    address _newABAddress,
    address _removeAB
  )
  external
  checkRoleAuthority(uint(Role.AdvisoryBoard)) {

    _updateRole(_newABAddress, uint(Role.AdvisoryBoard), true);
    _updateRole(_removeAB, uint(Role.AdvisoryBoard), false);

  }

  /**
   * @dev to swap the owner address
   * @param _newOwnerAddress is the new owner address
   */
  function swapOwner(
    address _newOwnerAddress
  )
  external {
    require(msg.sender == address(ms));
    _updateRole(ms.owner(), uint(Role.Owner), false);
    _updateRole(_newOwnerAddress, uint(Role.Owner), true);
  }

  /**
   * @dev is used to add initital advisory board members
   * @param abArray is the list of initial advisory board members
   */
  function addInitialABMembers(address[] calldata abArray) external onlyOwner {

    //Ensure that NXMaster has initialized.
    require(ms.masterInitialized());

    require(maxABCount >=
      SafeMath.add(numberOfMembers(uint(Role.AdvisoryBoard)), abArray.length)
    );
    //AB count can't exceed maxABCount
    for (uint i = 0; i < abArray.length; i++) {
      require(checkRole(abArray[i], uint(MemberRoles.Role.Member)));
      _updateRole(abArray[i], uint(Role.AdvisoryBoard), true);
    }
  }

  /**
   * @dev to change max number of AB members allowed
   * @param _val is the new value to be set
   */
  function changeMaxABCount(uint _val) external onlyInternal {
    maxABCount = _val;
  }

  /**
   * @dev Iupgradable Interface to update dependent contract address
   */
  function changeDependentContractAddress() public {
    td = TokenData(ms.getLatestAddress("TD"));
    cr = ClaimsReward(ms.getLatestAddress("CR"));
    qd = QuotationData(ms.getLatestAddress("QD"));
    gv = Governance(ms.getLatestAddress("GV"));
    tf = TokenFunctions(ms.getLatestAddress("TF"));
    tk = NXMToken(ms.tokenAddress());
    dAppToken = TokenController(ms.getLatestAddress("TC"));
  }

  /**
   * @dev to change the master address
   * @param _masterAddress is the new master address
   */
  function changeMasterAddress(address _masterAddress) public {
    if (masterAddress != address(0))
      require(masterAddress == msg.sender);
    masterAddress = _masterAddress;
    ms = INXMMaster(_masterAddress);
    nxMasterAddress = _masterAddress;

  }

  /**
   * @dev to initiate the member roles
   * @param _firstAB is the address of the first AB member
   * @param memberAuthority is the authority (role) of the member
   */
  function memberRolesInitiate(address _firstAB, address memberAuthority) public {
    require(!constructorCheck);
    _addInitialMemberRoles(_firstAB, memberAuthority);
    constructorCheck = true;
  }

  /// @dev Adds new member role
  /// @param _roleName New role name
  /// @param _roleDescription New description hash
  /// @param _authorized Authorized member against every role id
  function addRole(//solhint-disable-line
    bytes32 _roleName,
    string memory _roleDescription,
    address _authorized
  )
  public
  onlyAuthorizedToGovern {
    _addRole(_roleName, _roleDescription, _authorized);
  }

  /// @dev Assign or Delete a member from specific role.
  /// @param _memberAddress Address of Member
  /// @param _roleId RoleId to update
  /// @param _active active is set to be True if we want to assign this role to member, False otherwise!
  function updateRole(//solhint-disable-line
    address _memberAddress,
    uint _roleId,
    bool _active
  )
  public
  checkRoleAuthority(_roleId) {
    _updateRole(_memberAddress, _roleId, _active);
  }

  /**
   * @dev to add members before launch
   * @param userArray is list of addresses of members
   * @param tokens is list of tokens minted for each array element
   */
  function addMembersBeforeLaunch(address[] memory userArray, uint[] memory tokens) public onlyOwner {
    require(!launched);

    for (uint i = 0; i < userArray.length; i++) {
      require(!ms.isMember(userArray[i]));
      dAppToken.addToWhitelist(userArray[i]);
      _updateRole(userArray[i], uint(Role.Member), true);
      dAppToken.mint(userArray[i], tokens[i]);
    }
    launched = true;
    launchedOn = now;

  }

  /**
    * @dev Called by user to pay joining membership fee
    */
  function payJoiningFee(address _userAddress) public payable {
    require(_userAddress != address(0));
    require(!ms.isPause(), "Emergency Pause Applied");
    if (msg.sender == address(ms.getLatestAddress("QT"))) {
      require(td.walletAddress() != address(0), "No walletAddress present");
      dAppToken.addToWhitelist(_userAddress);
      _updateRole(_userAddress, uint(Role.Member), true);
      td.walletAddress().transfer(msg.value);
    } else {
      require(!qd.refundEligible(_userAddress));
      require(!ms.isMember(_userAddress));
      require(msg.value == td.joiningFee());
      qd.setRefundEligible(_userAddress, true);
    }
  }

  /**
   * @dev to perform kyc verdict
   * @param _userAddress whose kyc is being performed
   * @param verdict of kyc process
   */
  function kycVerdict(address payable _userAddress, bool verdict) public {

    require(msg.sender == qd.kycAuthAddress());
    require(!ms.isPause());
    require(_userAddress != address(0));
    require(!ms.isMember(_userAddress));
    require(qd.refundEligible(_userAddress));
    if (verdict) {
      qd.setRefundEligible(_userAddress, false);
      uint fee = td.joiningFee();
      dAppToken.addToWhitelist(_userAddress);
      _updateRole(_userAddress, uint(Role.Member), true);
      td.walletAddress().transfer(fee); // solhint-disable-line

    } else {
      qd.setRefundEligible(_userAddress, false);
      _userAddress.transfer(td.joiningFee()); // solhint-disable-line
    }
  }

  /**
   * @dev Called by existed member if wish to Withdraw membership.
   */
  function withdrawMembership() public {

    require(!ms.isPause() && ms.isMember(msg.sender));
    require(dAppToken.totalLockedBalance(msg.sender, now) == 0); // solhint-disable-line
    require(!tf.isLockedForMemberVote(msg.sender)); // No locked tokens for Member/Governance voting
    require(cr.getAllPendingRewardOfUser(msg.sender) == 0); // No pending reward to be claimed(claim assesment).
    require(dAppToken.tokensUnlockable(msg.sender, "CLA") == 0, "Member should have no CLA unlockable tokens");

    gv.removeDelegation(msg.sender);
    dAppToken.burnFrom(msg.sender, tk.balanceOf(msg.sender));
    _updateRole(msg.sender, uint(Role.Member), false);
    dAppToken.removeFromWhitelist(msg.sender); // need clarification on whitelist

    if (claimPayoutAddress[msg.sender] != address(0)) {
      claimPayoutAddress[msg.sender] = address(0);
      emit ClaimPayoutAddressSet(msg.sender, address(0));
    }
  }

  /**
   * @dev Called by existed member if wish to switch membership to other address.
   * @param _add address of user to forward membership.
   */
  function switchMembership(address _add) external {

    require(!ms.isPause() && ms.isMember(msg.sender) && !ms.isMember(_add));
    require(dAppToken.totalLockedBalance(msg.sender, now) == 0); // solhint-disable-line
    require(!tf.isLockedForMemberVote(msg.sender)); // No locked tokens for Member/Governance voting
    require(cr.getAllPendingRewardOfUser(msg.sender) == 0); // No pending reward to be claimed(claim assesment).
    require(dAppToken.tokensUnlockable(msg.sender, "CLA") == 0, "Member should have no CLA unlockable tokens");

    gv.removeDelegation(msg.sender);
    dAppToken.addToWhitelist(_add);
    _updateRole(_add, uint(Role.Member), true);
    tk.transferFrom(msg.sender, _add, tk.balanceOf(msg.sender));
    _updateRole(msg.sender, uint(Role.Member), false);
    dAppToken.removeFromWhitelist(msg.sender);

    address payable previousPayoutAddress = claimPayoutAddress[msg.sender];

    if (previousPayoutAddress != address(0)) {

      address payable storedAddress = previousPayoutAddress == _add ? address(0) : previousPayoutAddress;

      claimPayoutAddress[msg.sender] = address(0);
      claimPayoutAddress[_add] = storedAddress;

      // emit event for old address reset
      emit ClaimPayoutAddressSet(msg.sender, address(0));

      if (storedAddress != address(0)) {
        // emit event for setting the payout address on the new member address if it's non zero
        emit ClaimPayoutAddressSet(_add, storedAddress);
      }
    }

    emit switchedMembership(msg.sender, _add, now);
  }

  function getClaimPayoutAddress(address payable _member) external view returns (address payable) {
    address payable payoutAddress = claimPayoutAddress[_member];
    return payoutAddress != address(0) ? payoutAddress : _member;
  }

  function setClaimPayoutAddress(address payable _address) external {

    require(!ms.isPause(), "system is paused");
    require(ms.isMember(msg.sender), "sender is not a member");
    require(_address != msg.sender, "should be different than the member address");

    claimPayoutAddress[msg.sender] = _address;
    emit ClaimPayoutAddressSet(msg.sender, _address);
  }

  /// @dev Return number of member roles
  function totalRoles() public view returns (uint256) {//solhint-disable-line
    return memberRoleData.length;
  }

  /// @dev Change Member Address who holds the authority to Add/Delete any member from specific role.
  /// @param _roleId roleId to update its Authorized Address
  /// @param _newAuthorized New authorized address against role id
  function changeAuthorized(uint _roleId, address _newAuthorized) public checkRoleAuthority(_roleId) {//solhint-disable-line
    memberRoleData[_roleId].authorized = _newAuthorized;
  }

  /// @dev Gets the member addresses assigned by a specific role
  /// @param _memberRoleId Member role id
  /// @return roleId Role id
  /// @return allMemberAddress Member addresses of specified role id
  function members(uint _memberRoleId) public view returns (uint, address[] memory memberArray) {//solhint-disable-line
    uint length = memberRoleData[_memberRoleId].memberAddress.length;
    uint i;
    uint j = 0;
    memberArray = new address[](memberRoleData[_memberRoleId].memberCounter);
    for (i = 0; i < length; i++) {
      address member = memberRoleData[_memberRoleId].memberAddress[i];
      if (memberRoleData[_memberRoleId].memberActive[member] && !_checkMemberInArray(member, memberArray)) {//solhint-disable-line
        memberArray[j] = member;
        j++;
      }
    }

    return (_memberRoleId, memberArray);
  }

  /// @dev Gets all members' length
  /// @param _memberRoleId Member role id
  /// @return memberRoleData[_memberRoleId].memberCounter Member length
  function numberOfMembers(uint _memberRoleId) public view returns (uint) {//solhint-disable-line
    return memberRoleData[_memberRoleId].memberCounter;
  }

  /// @dev Return member address who holds the right to add/remove any member from specific role.
  function authorized(uint _memberRoleId) public view returns (address) {//solhint-disable-line
    return memberRoleData[_memberRoleId].authorized;
  }

  /// @dev Get All role ids array that has been assigned to a member so far.
  function roles(address _memberAddress) public view returns (uint[] memory) {//solhint-disable-line
    uint length = memberRoleData.length;
    uint[] memory assignedRoles = new uint[](length);
    uint counter = 0;
    for (uint i = 1; i < length; i++) {
      if (memberRoleData[i].memberActive[_memberAddress]) {
        assignedRoles[counter] = i;
        counter++;
      }
    }
    return assignedRoles;
  }

  /// @dev Returns true if the given role id is assigned to a member.
  /// @param _memberAddress Address of member
  /// @param _roleId Checks member's authenticity with the roleId.
  /// i.e. Returns true if this roleId is assigned to member
  function checkRole(address _memberAddress, uint _roleId) public view returns (bool) {//solhint-disable-line
    if (_roleId == uint(Role.UnAssigned))
      return true;
    else
      if (memberRoleData[_roleId].memberActive[_memberAddress]) //solhint-disable-line
        return true;
      else
        return false;
  }

  /// @dev Return total number of members assigned against each role id.
  /// @return totalMembers Total members in particular role id
  function getMemberLengthForAllRoles() public view returns (uint[] memory totalMembers) {//solhint-disable-line
    totalMembers = new uint[](memberRoleData.length);
    for (uint i = 0; i < memberRoleData.length; i++) {
      totalMembers[i] = numberOfMembers(i);
    }
  }

  /**
   * @dev to update the member roles
   * @param _memberAddress in concern
   * @param _roleId the id of role
   * @param _active if active is true, add the member, else remove it
   */
  function _updateRole(address _memberAddress,
    uint _roleId,
    bool _active) internal {
    // require(_roleId != uint(Role.TokenHolder), "Membership to Token holder is detected automatically");
    if (_active) {
      require(!memberRoleData[_roleId].memberActive[_memberAddress]);
      memberRoleData[_roleId].memberCounter = SafeMath.add(memberRoleData[_roleId].memberCounter, 1);
      memberRoleData[_roleId].memberActive[_memberAddress] = true;
      memberRoleData[_roleId].memberAddress.push(_memberAddress);
    } else {
      require(memberRoleData[_roleId].memberActive[_memberAddress]);
      memberRoleData[_roleId].memberCounter = SafeMath.sub(memberRoleData[_roleId].memberCounter, 1);
      delete memberRoleData[_roleId].memberActive[_memberAddress];
    }
  }

  /// @dev Adds new member role
  /// @param _roleName New role name
  /// @param _roleDescription New description hash
  /// @param _authorized Authorized member against every role id
  function _addRole(
    bytes32 _roleName,
    string memory _roleDescription,
    address _authorized
  ) internal {
    emit MemberRole(memberRoleData.length, _roleName, _roleDescription);
    memberRoleData.push(MemberRoleDetails(0, new address[](0), _authorized));
  }

  /**
   * @dev to check if member is in the given member array
   * @param _memberAddress in concern
   * @param memberArray in concern
   * @return boolean to represent the presence
   */
  function _checkMemberInArray(
    address _memberAddress,
    address[] memory memberArray
  )
  internal
  pure
  returns (bool memberExists)
  {
    uint i;
    for (i = 0; i < memberArray.length; i++) {
      if (memberArray[i] == _memberAddress) {
        memberExists = true;
        break;
      }
    }
  }

  /**
   * @dev to add initial member roles
   * @param _firstAB is the member address to be added
   * @param memberAuthority is the member authority(role) to be added for
   */
  function _addInitialMemberRoles(address _firstAB, address memberAuthority) internal {
    maxABCount = 5;
    _addRole("Unassigned", "Unassigned", address(0));
    _addRole(
      "Advisory Board",
      "Selected few members that are deeply entrusted by the dApp. An ideal advisory board should be a mix of skills of domain, governance, research, technology, consulting etc to improve the performance of the dApp.", //solhint-disable-line
      address(0)
    );
    _addRole(
      "Member",
      "Represents all users of Mutual.", //solhint-disable-line
      memberAuthority
    );
    _addRole(
      "Owner",
      "Represents Owner of Mutual.", //solhint-disable-line
      address(0)
    );
    // _updateRole(_firstAB, uint(Role.AdvisoryBoard), true);
    _updateRole(_firstAB, uint(Role.Owner), true);
    // _updateRole(_firstAB, uint(Role.Member), true);
    launchedOn = 0;
  }

  function memberAtIndex(uint _memberRoleId, uint index) external view returns (address, bool) {
    address memberAddress = memberRoleData[_memberRoleId].memberAddress[index];
    return (memberAddress, memberRoleData[_memberRoleId].memberActive[memberAddress]);
  }

  function membersLength(uint _memberRoleId) external view returns (uint) {
    return memberRoleData[_memberRoleId].memberAddress.length;
  }
}

File 19 of 38 : TokenData.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../abstract/Iupgradable.sol";

contract TokenData is Iupgradable {
  using SafeMath for uint;

  address payable public walletAddress;
  uint public lockTokenTimeAfterCoverExp;
  uint public bookTime;
  uint public lockCADays;
  uint public lockMVDays;
  uint public scValidDays;
  uint public joiningFee;
  uint public stakerCommissionPer;
  uint public stakerMaxCommissionPer;
  uint public tokenExponent;
  uint public priceStep;

  struct StakeCommission {
    uint commissionEarned;
    uint commissionRedeemed;
  }

  struct Stake {
    address stakedContractAddress;
    uint stakedContractIndex;
    uint dateAdd;
    uint stakeAmount;
    uint unlockedAmount;
    uint burnedAmount;
    uint unLockableBeforeLastBurn;
  }

  struct Staker {
    address stakerAddress;
    uint stakerIndex;
  }

  struct CoverNote {
    uint amount;
    bool isDeposited;
  }

  /**
   * @dev mapping of uw address to array of sc address to fetch
   * all staked contract address of underwriter, pushing
   * data into this array of Stake returns stakerIndex
   */
  mapping(address => Stake[]) public stakerStakedContracts;

  /**
   * @dev mapping of sc address to array of UW address to fetch
   * all underwritters of the staked smart contract
   * pushing data into this mapped array returns scIndex
   */
  mapping(address => Staker[]) public stakedContractStakers;

  /**
   * @dev mapping of staked contract Address to the array of StakeCommission
   * here index of this array is stakedContractIndex
   */
  mapping(address => mapping(uint => StakeCommission)) public stakedContractStakeCommission;

  mapping(address => uint) public lastCompletedStakeCommission;

  /**
   * @dev mapping of the staked contract address to the current
   * staker index who will receive commission.
   */
  mapping(address => uint) public stakedContractCurrentCommissionIndex;

  /**
   * @dev mapping of the staked contract address to the
   * current staker index to burn token from.
   */
  mapping(address => uint) public stakedContractCurrentBurnIndex;

  /**
   * @dev mapping to return true if Cover Note deposited against coverId
   */
  mapping(uint => CoverNote) public depositedCN;

  mapping(address => uint) internal isBookedTokens;

  event Commission(
    address indexed stakedContractAddress,
    address indexed stakerAddress,
    uint indexed scIndex,
    uint commissionAmount
  );

  constructor(address payable _walletAdd) public {
    walletAddress = _walletAdd;
    bookTime = 12 hours;
    joiningFee = 2000000000000000; // 0.002 Ether
    lockTokenTimeAfterCoverExp = 35 days;
    scValidDays = 250;
    lockCADays = 7 days;
    lockMVDays = 2 days;
    stakerCommissionPer = 20;
    stakerMaxCommissionPer = 50;
    tokenExponent = 4;
    priceStep = 1000;
  }

  /**
   * @dev Change the wallet address which receive Joining Fee
   */
  function changeWalletAddress(address payable _address) external onlyInternal {
    walletAddress = _address;
  }

  /**
   * @dev Gets Uint Parameters of a code
   * @param code whose details we want
   * @return string value of the code
   * @return associated amount (time or perc or value) to the code
   */
  function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {
    codeVal = code;
    if (code == "TOKEXP") {

      val = tokenExponent;

    } else if (code == "TOKSTEP") {

      val = priceStep;

    } else if (code == "RALOCKT") {

      val = scValidDays;

    } else if (code == "RACOMM") {

      val = stakerCommissionPer;

    } else if (code == "RAMAXC") {

      val = stakerMaxCommissionPer;

    } else if (code == "CABOOKT") {

      val = bookTime / (1 hours);

    } else if (code == "CALOCKT") {

      val = lockCADays / (1 days);

    } else if (code == "MVLOCKT") {

      val = lockMVDays / (1 days);

    } else if (code == "QUOLOCKT") {

      val = lockTokenTimeAfterCoverExp / (1 days);

    } else if (code == "JOINFEE") {

      val = joiningFee;

    }
  }

  /**
  * @dev Just for interface
  */
  function changeDependentContractAddress() public {//solhint-disable-line
  }

  /**
   * @dev to get the contract staked by a staker
   * @param _stakerAddress is the address of the staker
   * @param _stakerIndex is the index of staker
   * @return the address of staked contract
   */
  function getStakerStakedContractByIndex(
    address _stakerAddress,
    uint _stakerIndex
  )
  public
  view
  returns (address stakedContractAddress)
  {
    stakedContractAddress = stakerStakedContracts[
    _stakerAddress][_stakerIndex].stakedContractAddress;
  }

  /**
   * @dev to get the staker's staked burned
   * @param _stakerAddress is the address of the staker
   * @param _stakerIndex is the index of staker
   * @return amount burned
   */
  function getStakerStakedBurnedByIndex(
    address _stakerAddress,
    uint _stakerIndex
  )
  public
  view
  returns (uint burnedAmount)
  {
    burnedAmount = stakerStakedContracts[
    _stakerAddress][_stakerIndex].burnedAmount;
  }

  /**
   * @dev to get the staker's staked unlockable before the last burn
   * @param _stakerAddress is the address of the staker
   * @param _stakerIndex is the index of staker
   * @return unlockable staked tokens
   */
  function getStakerStakedUnlockableBeforeLastBurnByIndex(
    address _stakerAddress,
    uint _stakerIndex
  )
  public
  view
  returns (uint unlockable)
  {
    unlockable = stakerStakedContracts[
    _stakerAddress][_stakerIndex].unLockableBeforeLastBurn;
  }

  /**
   * @dev to get the staker's staked contract index
   * @param _stakerAddress is the address of the staker
   * @param _stakerIndex is the index of staker
   * @return is the index of the smart contract address
   */
  function getStakerStakedContractIndex(
    address _stakerAddress,
    uint _stakerIndex
  )
  public
  view
  returns (uint scIndex)
  {
    scIndex = stakerStakedContracts[
    _stakerAddress][_stakerIndex].stakedContractIndex;
  }

  /**
   * @dev to get the staker index of the staked contract
   * @param _stakedContractAddress is the address of the staked contract
   * @param _stakedContractIndex is the index of staked contract
   * @return is the index of the staker
   */
  function getStakedContractStakerIndex(
    address _stakedContractAddress,
    uint _stakedContractIndex
  )
  public
  view
  returns (uint sIndex)
  {
    sIndex = stakedContractStakers[
    _stakedContractAddress][_stakedContractIndex].stakerIndex;
  }

  /**
   * @dev to get the staker's initial staked amount on the contract
   * @param _stakerAddress is the address of the staker
   * @param _stakerIndex is the index of staker
   * @return staked amount
   */
  function getStakerInitialStakedAmountOnContract(
    address _stakerAddress,
    uint _stakerIndex
  )
  public
  view
  returns (uint amount)
  {
    amount = stakerStakedContracts[
    _stakerAddress][_stakerIndex].stakeAmount;
  }

  /**
   * @dev to get the staker's staked contract length
   * @param _stakerAddress is the address of the staker
   * @return length of staked contract
   */
  function getStakerStakedContractLength(
    address _stakerAddress
  )
  public
  view
  returns (uint length)
  {
    length = stakerStakedContracts[_stakerAddress].length;
  }

  /**
   * @dev to get the staker's unlocked tokens which were staked
   * @param _stakerAddress is the address of the staker
   * @param _stakerIndex is the index of staker
   * @return amount
   */
  function getStakerUnlockedStakedTokens(
    address _stakerAddress,
    uint _stakerIndex
  )
  public
  view
  returns (uint amount)
  {
    amount = stakerStakedContracts[
    _stakerAddress][_stakerIndex].unlockedAmount;
  }

  /**
   * @dev pushes the unlocked staked tokens by a staker.
   * @param _stakerAddress address of staker.
   * @param _stakerIndex index of the staker to distribute commission.
   * @param _amount amount to be given as commission.
   */
  function pushUnlockedStakedTokens(
    address _stakerAddress,
    uint _stakerIndex,
    uint _amount
  )
  public
  onlyInternal
  {
    stakerStakedContracts[_stakerAddress][
    _stakerIndex].unlockedAmount = stakerStakedContracts[_stakerAddress][
    _stakerIndex].unlockedAmount.add(_amount);
  }

  /**
   * @dev pushes the Burned tokens for a staker.
   * @param _stakerAddress address of staker.
   * @param _stakerIndex index of the staker.
   * @param _amount amount to be burned.
   */
  function pushBurnedTokens(
    address _stakerAddress,
    uint _stakerIndex,
    uint _amount
  )
  public
  onlyInternal
  {
    stakerStakedContracts[_stakerAddress][
    _stakerIndex].burnedAmount = stakerStakedContracts[_stakerAddress][
    _stakerIndex].burnedAmount.add(_amount);
  }

  /**
   * @dev pushes the unLockable tokens for a staker before last burn.
   * @param _stakerAddress address of staker.
   * @param _stakerIndex index of the staker.
   * @param _amount amount to be added to unlockable.
   */
  function pushUnlockableBeforeLastBurnTokens(
    address _stakerAddress,
    uint _stakerIndex,
    uint _amount
  )
  public
  onlyInternal
  {
    stakerStakedContracts[_stakerAddress][
    _stakerIndex].unLockableBeforeLastBurn = stakerStakedContracts[_stakerAddress][
    _stakerIndex].unLockableBeforeLastBurn.add(_amount);
  }

  /**
   * @dev sets the unLockable tokens for a staker before last burn.
   * @param _stakerAddress address of staker.
   * @param _stakerIndex index of the staker.
   * @param _amount amount to be added to unlockable.
   */
  function setUnlockableBeforeLastBurnTokens(
    address _stakerAddress,
    uint _stakerIndex,
    uint _amount
  )
  public
  onlyInternal
  {
    stakerStakedContracts[_stakerAddress][
    _stakerIndex].unLockableBeforeLastBurn = _amount;
  }

  /**
   * @dev pushes the earned commission earned by a staker.
   * @param _stakerAddress address of staker.
   * @param _stakedContractAddress address of smart contract.
   * @param _stakedContractIndex index of the staker to distribute commission.
   * @param _commissionAmount amount to be given as commission.
   */
  function pushEarnedStakeCommissions(
    address _stakerAddress,
    address _stakedContractAddress,
    uint _stakedContractIndex,
    uint _commissionAmount
  )
  public
  onlyInternal
  {
    stakedContractStakeCommission[_stakedContractAddress][_stakedContractIndex].
    commissionEarned = stakedContractStakeCommission[_stakedContractAddress][
    _stakedContractIndex].commissionEarned.add(_commissionAmount);

    emit Commission(
      _stakerAddress,
      _stakedContractAddress,
      _stakedContractIndex,
      _commissionAmount
    );
  }

  /**
   * @dev pushes the redeemed commission redeemed by a staker.
   * @param _stakerAddress address of staker.
   * @param _stakerIndex index of the staker to distribute commission.
   * @param _amount amount to be given as commission.
   */
  function pushRedeemedStakeCommissions(
    address _stakerAddress,
    uint _stakerIndex,
    uint _amount
  )
  public
  onlyInternal
  {
    uint stakedContractIndex = stakerStakedContracts[
    _stakerAddress][_stakerIndex].stakedContractIndex;
    address stakedContractAddress = stakerStakedContracts[
    _stakerAddress][_stakerIndex].stakedContractAddress;
    stakedContractStakeCommission[stakedContractAddress][stakedContractIndex].
    commissionRedeemed = stakedContractStakeCommission[
    stakedContractAddress][stakedContractIndex].commissionRedeemed.add(_amount);
  }

  /**
   * @dev Gets stake commission given to an underwriter
   * for particular stakedcontract on given index.
   * @param _stakerAddress address of staker.
   * @param _stakerIndex index of the staker commission.
   */
  function getStakerEarnedStakeCommission(
    address _stakerAddress,
    uint _stakerIndex
  )
  public
  view
  returns (uint)
  {
    return _getStakerEarnedStakeCommission(_stakerAddress, _stakerIndex);
  }

  /**
   * @dev Gets stake commission redeemed by an underwriter
   * for particular staked contract on given index.
   * @param _stakerAddress address of staker.
   * @param _stakerIndex index of the staker commission.
   * @return commissionEarned total amount given to staker.
   */
  function getStakerRedeemedStakeCommission(
    address _stakerAddress,
    uint _stakerIndex
  )
  public
  view
  returns (uint)
  {
    return _getStakerRedeemedStakeCommission(_stakerAddress, _stakerIndex);
  }

  /**
   * @dev Gets total stake commission given to an underwriter
   * @param _stakerAddress address of staker.
   * @return totalCommissionEarned total commission earned by staker.
   */
  function getStakerTotalEarnedStakeCommission(
    address _stakerAddress
  )
  public
  view
  returns (uint totalCommissionEarned)
  {
    totalCommissionEarned = 0;
    for (uint i = 0; i < stakerStakedContracts[_stakerAddress].length; i++) {
      totalCommissionEarned = totalCommissionEarned.
      add(_getStakerEarnedStakeCommission(_stakerAddress, i));
    }
  }

  /**
   * @dev Gets total stake commission given to an underwriter
   * @param _stakerAddress address of staker.
   * @return totalCommissionEarned total commission earned by staker.
   */
  function getStakerTotalReedmedStakeCommission(
    address _stakerAddress
  )
  public
  view
  returns (uint totalCommissionRedeemed)
  {
    totalCommissionRedeemed = 0;
    for (uint i = 0; i < stakerStakedContracts[_stakerAddress].length; i++) {
      totalCommissionRedeemed = totalCommissionRedeemed.add(
        _getStakerRedeemedStakeCommission(_stakerAddress, i));
    }
  }

  /**
   * @dev set flag to deposit/ undeposit cover note
   * against a cover Id
   * @param coverId coverId of Cover
   * @param flag true/false for deposit/undeposit
   */
  function setDepositCN(uint coverId, bool flag) public onlyInternal {

    if (flag == true) {
      require(!depositedCN[coverId].isDeposited, "Cover note already deposited");
    }

    depositedCN[coverId].isDeposited = flag;
  }

  /**
   * @dev set locked cover note amount
   * against a cover Id
   * @param coverId coverId of Cover
   * @param amount amount of nxm to be locked
   */
  function setDepositCNAmount(uint coverId, uint amount) public onlyInternal {

    depositedCN[coverId].amount = amount;
  }

  /**
   * @dev to get the staker address on a staked contract
   * @param _stakedContractAddress is the address of the staked contract in concern
   * @param _stakedContractIndex is the index of staked contract's index
   * @return address of staker
   */
  function getStakedContractStakerByIndex(
    address _stakedContractAddress,
    uint _stakedContractIndex
  )
  public
  view
  returns (address stakerAddress)
  {
    stakerAddress = stakedContractStakers[
    _stakedContractAddress][_stakedContractIndex].stakerAddress;
  }

  /**
   * @dev to get the length of stakers on a staked contract
   * @param _stakedContractAddress is the address of the staked contract in concern
   * @return length in concern
   */
  function getStakedContractStakersLength(
    address _stakedContractAddress
  )
  public
  view
  returns (uint length)
  {
    length = stakedContractStakers[_stakedContractAddress].length;
  }

  /**
   * @dev Adds a new stake record.
   * @param _stakerAddress staker address.
   * @param _stakedContractAddress smart contract address.
   * @param _amount amountof NXM to be staked.
   */
  function addStake(
    address _stakerAddress,
    address _stakedContractAddress,
    uint _amount
  )
  public
  onlyInternal
  returns (uint scIndex)
  {
    scIndex = (stakedContractStakers[_stakedContractAddress].push(
      Staker(_stakerAddress, stakerStakedContracts[_stakerAddress].length))).sub(1);
    stakerStakedContracts[_stakerAddress].push(
      Stake(_stakedContractAddress, scIndex, now, _amount, 0, 0, 0));
  }

  /**
   * @dev books the user's tokens for maintaining Assessor Velocity,
   * i.e. once a token is used to cast a vote as a Claims assessor,
   * @param _of user's address.
   */
  function bookCATokens(address _of) public onlyInternal {
    require(!isCATokensBooked(_of), "Tokens already booked");
    isBookedTokens[_of] = now.add(bookTime);
  }

  /**
   * @dev to know if claim assessor's tokens are booked or not
   * @param _of is the claim assessor's address in concern
   * @return boolean representing the status of tokens booked
   */
  function isCATokensBooked(address _of) public view returns (bool res) {
    if (now < isBookedTokens[_of])
      res = true;
  }

  /**
   * @dev Sets the index which will receive commission.
   * @param _stakedContractAddress smart contract address.
   * @param _index current index.
   */
  function setStakedContractCurrentCommissionIndex(
    address _stakedContractAddress,
    uint _index
  )
  public
  onlyInternal
  {
    stakedContractCurrentCommissionIndex[_stakedContractAddress] = _index;
  }

  /**
   * @dev Sets the last complete commission index
   * @param _stakerAddress smart contract address.
   * @param _index current index.
   */
  function setLastCompletedStakeCommissionIndex(
    address _stakerAddress,
    uint _index
  )
  public
  onlyInternal
  {
    lastCompletedStakeCommission[_stakerAddress] = _index;
  }

  /**
   * @dev Sets the index till which commission is distrubuted.
   * @param _stakedContractAddress smart contract address.
   * @param _index current index.
   */
  function setStakedContractCurrentBurnIndex(
    address _stakedContractAddress,
    uint _index
  )
  public
  onlyInternal
  {
    stakedContractCurrentBurnIndex[_stakedContractAddress] = _index;
  }

  /**
   * @dev Updates Uint Parameters of a code
   * @param code whose details we want to update
   * @param val value to set
   */
  function updateUintParameters(bytes8 code, uint val) public {
    require(ms.checkIsAuthToGoverned(msg.sender));
    if (code == "TOKEXP") {

      _setTokenExponent(val);

    } else if (code == "TOKSTEP") {

      _setPriceStep(val);

    } else if (code == "RALOCKT") {

      _changeSCValidDays(val);

    } else if (code == "RACOMM") {

      _setStakerCommissionPer(val);

    } else if (code == "RAMAXC") {

      _setStakerMaxCommissionPer(val);

    } else if (code == "CABOOKT") {

      _changeBookTime(val * 1 hours);

    } else if (code == "CALOCKT") {

      _changelockCADays(val * 1 days);

    } else if (code == "MVLOCKT") {

      _changelockMVDays(val * 1 days);

    } else if (code == "QUOLOCKT") {

      _setLockTokenTimeAfterCoverExp(val * 1 days);

    } else if (code == "JOINFEE") {

      _setJoiningFee(val);

    } else {
      revert("Invalid param code");
    }
  }

  /**
   * @dev Internal function to get stake commission given to an
   * underwriter for particular stakedcontract on given index.
   * @param _stakerAddress address of staker.
   * @param _stakerIndex index of the staker commission.
   */
  function _getStakerEarnedStakeCommission(
    address _stakerAddress,
    uint _stakerIndex
  )
  internal
  view
  returns (uint amount)
  {
    uint _stakedContractIndex;
    address _stakedContractAddress;
    _stakedContractAddress = stakerStakedContracts[
    _stakerAddress][_stakerIndex].stakedContractAddress;
    _stakedContractIndex = stakerStakedContracts[
    _stakerAddress][_stakerIndex].stakedContractIndex;
    amount = stakedContractStakeCommission[
    _stakedContractAddress][_stakedContractIndex].commissionEarned;
  }

  /**
   * @dev Internal function to get stake commission redeemed by an
   * underwriter for particular stakedcontract on given index.
   * @param _stakerAddress address of staker.
   * @param _stakerIndex index of the staker commission.
   */
  function _getStakerRedeemedStakeCommission(
    address _stakerAddress,
    uint _stakerIndex
  )
  internal
  view
  returns (uint amount)
  {
    uint _stakedContractIndex;
    address _stakedContractAddress;
    _stakedContractAddress = stakerStakedContracts[
    _stakerAddress][_stakerIndex].stakedContractAddress;
    _stakedContractIndex = stakerStakedContracts[
    _stakerAddress][_stakerIndex].stakedContractIndex;
    amount = stakedContractStakeCommission[
    _stakedContractAddress][_stakedContractIndex].commissionRedeemed;
  }

  /**
   * @dev to set the percentage of staker commission
   * @param _val is new percentage value
   */
  function _setStakerCommissionPer(uint _val) internal {
    stakerCommissionPer = _val;
  }

  /**
   * @dev to set the max percentage of staker commission
   * @param _val is new percentage value
   */
  function _setStakerMaxCommissionPer(uint _val) internal {
    stakerMaxCommissionPer = _val;
  }

  /**
   * @dev to set the token exponent value
   * @param _val is new value
   */
  function _setTokenExponent(uint _val) internal {
    tokenExponent = _val;
  }

  /**
   * @dev to set the price step
   * @param _val is new value
   */
  function _setPriceStep(uint _val) internal {
    priceStep = _val;
  }

  /**
   * @dev Changes number of days for which NXM needs to staked in case of underwriting
   */
  function _changeSCValidDays(uint _days) internal {
    scValidDays = _days;
  }

  /**
   * @dev Changes the time period up to which tokens will be locked.
   *      Used to generate the validity period of tokens booked by
   *      a user for participating in claim's assessment/claim's voting.
   */
  function _changeBookTime(uint _time) internal {
    bookTime = _time;
  }

  /**
   * @dev Changes lock CA days - number of days for which tokens
   * are locked while submitting a vote.
   */
  function _changelockCADays(uint _val) internal {
    lockCADays = _val;
  }

  /**
   * @dev Changes lock MV days - number of days for which tokens are locked
   * while submitting a vote.
   */
  function _changelockMVDays(uint _val) internal {
    lockMVDays = _val;
  }

  /**
   * @dev Changes extra lock period for a cover, post its expiry.
   */
  function _setLockTokenTimeAfterCoverExp(uint time) internal {
    lockTokenTimeAfterCoverExp = time;
  }

  /**
   * @dev Set the joining fee for membership
   */
  function _setJoiningFee(uint _amount) internal {
    joiningFee = _amount;
  }
}

File 20 of 38 : TokenFunctions.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../interfaces/IPooledStaking.sol";
import "../cover/QuotationData.sol";
import "./NXMToken.sol";
import "./TokenController.sol";
import "./TokenData.sol";

contract TokenFunctions is Iupgradable {
  using SafeMath for uint;

  NXMToken public tk;
  TokenController public tc;
  TokenData public td;
  QuotationData public qd;
  IPooledStaking public pooledStaking;

  event BurnCATokens(uint claimId, address addr, uint amount);

  /**
   * @dev Rewards stakers on purchase of cover on smart contract.
   * @param _contractAddress smart contract address.
   * @param _coverPriceNXM cover price in NXM.
   */
  function pushStakerRewards(address _contractAddress, uint _coverPriceNXM) external onlyInternal {
    uint rewardValue = _coverPriceNXM.mul(td.stakerCommissionPer()).div(100);
    pooledStaking.accumulateReward(_contractAddress, rewardValue);
  }

  /**
   * @dev Returns amount of NXM Tokens locked as Cover Note for given coverId.
   * @param _of address of the coverHolder.
   * @param _coverId coverId of the cover.
   */
  function getUserLockedCNTokens(address _of, uint _coverId) external view returns (uint) {
    return _getUserLockedCNTokens(_of, _coverId);
  }

  /**
   * @dev to get the all the cover locked tokens of a user
   * @param _of is the user address in concern
   * @return amount locked
   */
  function getUserAllLockedCNTokens(address _of) external view returns (uint amount) {
    for (uint i = 0; i < qd.getUserCoverLength(_of); i++) {
      amount = amount.add(_getUserLockedCNTokens(_of, qd.getAllCoversOfUser(_of)[i]));
    }
  }

  /**
   * @dev Returns amount of NXM Tokens locked as Cover Note against given coverId.
   * @param _coverId coverId of the cover.
   */
  function getLockedCNAgainstCover(uint _coverId) external view returns (uint) {
    return _getLockedCNAgainstCover(_coverId);
  }

  /**
   * @dev Change Dependent Contract Address
   */
  function changeDependentContractAddress() public {
    tk = NXMToken(ms.tokenAddress());
    td = TokenData(ms.getLatestAddress("TD"));
    tc = TokenController(ms.getLatestAddress("TC"));
    qd = QuotationData(ms.getLatestAddress("QD"));
    pooledStaking = IPooledStaking(ms.getLatestAddress("PS"));
  }

  /**
   * @dev Set the flag to check if cover note is deposited against the cover id
   * @param coverId Cover Id.
   */
  function depositCN(uint coverId) public onlyInternal returns (bool success) {
    require(_getLockedCNAgainstCover(coverId) > 0, "No cover note available");
    td.setDepositCN(coverId, true);
    success = true;
  }

  /**
   * @param _of address of Member
   * @param _coverId Cover Id
   * @param _lockTime Pending Time + Cover Period 7*1 days
   */
  function extendCNEPOff(address _of, uint _coverId, uint _lockTime) public onlyInternal {
    uint timeStamp = now.add(_lockTime);
    uint coverValidUntil = qd.getValidityOfCover(_coverId);
    if (timeStamp >= coverValidUntil) {
      bytes32 reason = keccak256(abi.encodePacked("CN", _of, _coverId));
      tc.extendLockOf(_of, reason, timeStamp);
    }
  }

  /**
   * @dev to burn the deposited cover tokens
   * @param coverId is id of cover whose tokens have to be burned
   * @return the status of the successful burning
   */
  function burnDepositCN(uint coverId) public onlyInternal returns (bool success) {
    address _of = qd.getCoverMemberAddress(coverId);
    uint amount;
    (amount,) = td.depositedCN(coverId);
    amount = (amount.mul(50)).div(100);
    bytes32 reason = keccak256(abi.encodePacked("CN", _of, coverId));
    tc.burnLockedTokens(_of, reason, amount);
    success = true;
  }

  /**
   * @dev Unlocks covernote locked against a given cover
   * @param coverId id of cover
   */
  function unlockCN(uint coverId) public onlyInternal {
    (, bool isDeposited) = td.depositedCN(coverId);
    require(!isDeposited, "Cover note is deposited and can not be released");
    uint lockedCN = _getLockedCNAgainstCover(coverId);
    if (lockedCN != 0) {
      address coverHolder = qd.getCoverMemberAddress(coverId);
      bytes32 reason = keccak256(abi.encodePacked("CN", coverHolder, coverId));
      tc.releaseLockedTokens(coverHolder, reason, lockedCN);
    }
  }

  /**
   * @dev Burns tokens used for fraudulent voting against a claim
   * @param claimid Claim Id.
   * @param _value number of tokens to be burned
   * @param _of Claim Assessor's address.
   */
  function burnCAToken(uint claimid, uint _value, address _of) public {

    require(ms.checkIsAuthToGoverned(msg.sender));
    tc.burnLockedTokens(_of, "CLA", _value);
    emit BurnCATokens(claimid, _of, _value);
  }

  /**
   * @dev to lock cover note tokens
   * @param coverNoteAmount is number of tokens to be locked
   * @param coverPeriod is cover period in concern
   * @param coverId is the cover id of cover in concern
   * @param _of address whose tokens are to be locked
   */
  function lockCN(
    uint coverNoteAmount,
    uint coverPeriod,
    uint coverId,
    address _of
  )
  public
  onlyInternal
  {
    uint validity = (coverPeriod * 1 days).add(td.lockTokenTimeAfterCoverExp());
    bytes32 reason = keccak256(abi.encodePacked("CN", _of, coverId));
    td.setDepositCNAmount(coverId, coverNoteAmount);
    tc.lockOf(_of, reason, coverNoteAmount, validity);
  }

  /**
   * @dev to check if a  member is locked for member vote
   * @param _of is the member address in concern
   * @return the boolean status
   */
  function isLockedForMemberVote(address _of) public view returns (bool) {
    return now < tk.isLockedForMV(_of);
  }

  /**
   * @dev Returns amount of NXM Tokens locked as Cover Note for given coverId.
   * @param _coverId coverId of the cover.
   */
  function _getLockedCNAgainstCover(uint _coverId) internal view returns (uint) {
    address coverHolder = qd.getCoverMemberAddress(_coverId);
    bytes32 reason = keccak256(abi.encodePacked("CN", coverHolder, _coverId));
    return tc.tokensLockedAtTime(coverHolder, reason, now);
  }

  /**
   * @dev Returns amount of NXM Tokens locked as Cover Note for given coverId.
   * @param _of address of the coverHolder.
   * @param _coverId coverId of the cover.
   */
  function _getUserLockedCNTokens(address _of, uint _coverId) internal view returns (uint) {
    bytes32 reason = keccak256(abi.encodePacked("CN", _of, _coverId));
    return tc.tokensLockedAtTime(_of, reason, now);
  }
}

File 21 of 38 : QuotationData.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../abstract/Iupgradable.sol";

contract QuotationData is Iupgradable {
  using SafeMath for uint;

  enum HCIDStatus {NA, kycPending, kycPass, kycFailedOrRefunded, kycPassNoCover}

  enum CoverStatus {Active, ClaimAccepted, ClaimDenied, CoverExpired, ClaimSubmitted, Requested}

  struct Cover {
    address payable memberAddress;
    bytes4 currencyCode;
    uint sumAssured;
    uint16 coverPeriod;
    uint validUntil;
    address scAddress;
    uint premiumNXM;
  }

  struct HoldCover {
    uint holdCoverId;
    address payable userAddress;
    address scAddress;
    bytes4 coverCurr;
    uint[] coverDetails;
    uint16 coverPeriod;
  }

  address public authQuoteEngine;

  mapping(bytes4 => uint) internal currencyCSA;
  mapping(address => uint[]) internal userCover;
  mapping(address => uint[]) public userHoldedCover;
  mapping(address => bool) public refundEligible;
  mapping(address => mapping(bytes4 => uint)) internal currencyCSAOfSCAdd;
  mapping(uint => uint8) public coverStatus;
  mapping(uint => uint) public holdedCoverIDStatus;
  mapping(uint => bool) public timestampRepeated;


  Cover[] internal allCovers;
  HoldCover[] internal allCoverHolded;

  uint public stlp;
  uint public stl;
  uint public pm;
  uint public minDays;
  uint public tokensRetained;
  address public kycAuthAddress;

  event CoverDetailsEvent(
    uint indexed cid,
    address scAdd,
    uint sumAssured,
    uint expiry,
    uint premium,
    uint premiumNXM,
    bytes4 curr
  );

  event CoverStatusEvent(uint indexed cid, uint8 statusNum);

  constructor(address _authQuoteAdd, address _kycAuthAdd) public {
    authQuoteEngine = _authQuoteAdd;
    kycAuthAddress = _kycAuthAdd;
    stlp = 90;
    stl = 100;
    pm = 30;
    minDays = 30;
    tokensRetained = 10;
    allCovers.push(Cover(address(0), "0x00", 0, 0, 0, address(0), 0));
    uint[] memory arr = new uint[](1);
    allCoverHolded.push(HoldCover(0, address(0), address(0), 0x00, arr, 0));

  }

  /// @dev Adds the amount in Total Sum Assured of a given currency of a given smart contract address.
  /// @param _add Smart Contract Address.
  /// @param _amount Amount to be added.
  function addInTotalSumAssuredSC(address _add, bytes4 _curr, uint _amount) external onlyInternal {
    currencyCSAOfSCAdd[_add][_curr] = currencyCSAOfSCAdd[_add][_curr].add(_amount);
  }

  /// @dev Subtracts the amount from Total Sum Assured of a given currency and smart contract address.
  /// @param _add Smart Contract Address.
  /// @param _amount Amount to be subtracted.
  function subFromTotalSumAssuredSC(address _add, bytes4 _curr, uint _amount) external onlyInternal {
    currencyCSAOfSCAdd[_add][_curr] = currencyCSAOfSCAdd[_add][_curr].sub(_amount);
  }

  /// @dev Subtracts the amount from Total Sum Assured of a given currency.
  /// @param _curr Currency Name.
  /// @param _amount Amount to be subtracted.
  function subFromTotalSumAssured(bytes4 _curr, uint _amount) external onlyInternal {
    currencyCSA[_curr] = currencyCSA[_curr].sub(_amount);
  }

  /// @dev Adds the amount in Total Sum Assured of a given currency.
  /// @param _curr Currency Name.
  /// @param _amount Amount to be added.
  function addInTotalSumAssured(bytes4 _curr, uint _amount) external onlyInternal {
    currencyCSA[_curr] = currencyCSA[_curr].add(_amount);
  }

  /// @dev sets bit for timestamp to avoid replay attacks.
  function setTimestampRepeated(uint _timestamp) external onlyInternal {
    timestampRepeated[_timestamp] = true;
  }

  /// @dev Creates a blank new cover.
  function addCover(
    uint16 _coverPeriod,
    uint _sumAssured,
    address payable _userAddress,
    bytes4 _currencyCode,
    address _scAddress,
    uint premium,
    uint premiumNXM
  )
  external
  onlyInternal
  {
    uint expiryDate = now.add(uint(_coverPeriod).mul(1 days));
    allCovers.push(Cover(_userAddress, _currencyCode,
      _sumAssured, _coverPeriod, expiryDate, _scAddress, premiumNXM));
    uint cid = allCovers.length.sub(1);
    userCover[_userAddress].push(cid);
    emit CoverDetailsEvent(cid, _scAddress, _sumAssured, expiryDate, premium, premiumNXM, _currencyCode);
  }

  /// @dev create holded cover which will process after verdict of KYC.
  function addHoldCover(
    address payable from,
    address scAddress,
    bytes4 coverCurr,
    uint[] calldata coverDetails,
    uint16 coverPeriod
  )
  external
  onlyInternal
  {
    uint holdedCoverLen = allCoverHolded.length;
    holdedCoverIDStatus[holdedCoverLen] = uint(HCIDStatus.kycPending);
    allCoverHolded.push(HoldCover(holdedCoverLen, from, scAddress,
      coverCurr, coverDetails, coverPeriod));
    userHoldedCover[from].push(allCoverHolded.length.sub(1));

  }

  ///@dev sets refund eligible bit.
  ///@param _add user address.
  ///@param status indicates if user have pending kyc.
  function setRefundEligible(address _add, bool status) external onlyInternal {
    refundEligible[_add] = status;
  }

  /// @dev to set current status of particular holded coverID (1 for not completed KYC,
  /// 2 for KYC passed, 3 for failed KYC or full refunded,
  /// 4 for KYC completed but cover not processed)
  function setHoldedCoverIDStatus(uint holdedCoverID, uint status) external onlyInternal {
    holdedCoverIDStatus[holdedCoverID] = status;
  }

  /**
   * @dev to set address of kyc authentication
   * @param _add is the new address
   */
  function setKycAuthAddress(address _add) external onlyInternal {
    kycAuthAddress = _add;
  }

  /// @dev Changes authorised address for generating quote off chain.
  function changeAuthQuoteEngine(address _add) external onlyInternal {
    authQuoteEngine = _add;
  }

  /**
   * @dev Gets Uint Parameters of a code
   * @param code whose details we want
   * @return string value of the code
   * @return associated amount (time or perc or value) to the code
   */
  function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {
    codeVal = code;

    if (code == "STLP") {
      val = stlp;

    } else if (code == "STL") {

      val = stl;

    } else if (code == "PM") {

      val = pm;

    } else if (code == "QUOMIND") {

      val = minDays;

    } else if (code == "QUOTOK") {

      val = tokensRetained;

    }

  }

  /// @dev Gets Product details.
  /// @return  _minDays minimum cover period.
  /// @return  _PM Profit margin.
  /// @return  _STL short term Load.
  /// @return  _STLP short term load period.
  function getProductDetails()
  external
  view
  returns (
    uint _minDays,
    uint _pm,
    uint _stl,
    uint _stlp
  )
  {

    _minDays = minDays;
    _pm = pm;
    _stl = stl;
    _stlp = stlp;
  }

  /// @dev Gets total number covers created till date.
  function getCoverLength() external view returns (uint len) {
    return (allCovers.length);
  }

  /// @dev Gets Authorised Engine address.
  function getAuthQuoteEngine() external view returns (address _add) {
    _add = authQuoteEngine;
  }

  /// @dev Gets the Total Sum Assured amount of a given currency.
  function getTotalSumAssured(bytes4 _curr) external view returns (uint amount) {
    amount = currencyCSA[_curr];
  }

  /// @dev Gets all the Cover ids generated by a given address.
  /// @param _add User's address.
  /// @return allCover array of covers.
  function getAllCoversOfUser(address _add) external view returns (uint[] memory allCover) {
    return (userCover[_add]);
  }

  /// @dev Gets total number of covers generated by a given address
  function getUserCoverLength(address _add) external view returns (uint len) {
    len = userCover[_add].length;
  }

  /// @dev Gets the status of a given cover.
  function getCoverStatusNo(uint _cid) external view returns (uint8) {
    return coverStatus[_cid];
  }

  /// @dev Gets the Cover Period (in days) of a given cover.
  function getCoverPeriod(uint _cid) external view returns (uint32 cp) {
    cp = allCovers[_cid].coverPeriod;
  }

  /// @dev Gets the Sum Assured Amount of a given cover.
  function getCoverSumAssured(uint _cid) external view returns (uint sa) {
    sa = allCovers[_cid].sumAssured;
  }

  /// @dev Gets the Currency Name in which a given cover is assured.
  function getCurrencyOfCover(uint _cid) external view returns (bytes4 curr) {
    curr = allCovers[_cid].currencyCode;
  }

  /// @dev Gets the validity date (timestamp) of a given cover.
  function getValidityOfCover(uint _cid) external view returns (uint date) {
    date = allCovers[_cid].validUntil;
  }

  /// @dev Gets Smart contract address of cover.
  function getscAddressOfCover(uint _cid) external view returns (uint, address) {
    return (_cid, allCovers[_cid].scAddress);
  }

  /// @dev Gets the owner address of a given cover.
  function getCoverMemberAddress(uint _cid) external view returns (address payable _add) {
    _add = allCovers[_cid].memberAddress;
  }

  /// @dev Gets the premium amount of a given cover in NXM.
  function getCoverPremiumNXM(uint _cid) external view returns (uint _premiumNXM) {
    _premiumNXM = allCovers[_cid].premiumNXM;
  }

  /// @dev Provides the details of a cover Id
  /// @param _cid cover Id
  /// @return memberAddress cover user address.
  /// @return scAddress smart contract Address
  /// @return currencyCode currency of cover
  /// @return sumAssured sum assured of cover
  /// @return premiumNXM premium in NXM
  function getCoverDetailsByCoverID1(
    uint _cid
  )
  external
  view
  returns (
    uint cid,
    address _memberAddress,
    address _scAddress,
    bytes4 _currencyCode,
    uint _sumAssured,
    uint premiumNXM
  )
  {
    return (
    _cid,
    allCovers[_cid].memberAddress,
    allCovers[_cid].scAddress,
    allCovers[_cid].currencyCode,
    allCovers[_cid].sumAssured,
    allCovers[_cid].premiumNXM
    );
  }

  /// @dev Provides details of a cover Id
  /// @param _cid cover Id
  /// @return status status of cover.
  /// @return sumAssured Sum assurance of cover.
  /// @return coverPeriod Cover Period of cover (in days).
  /// @return validUntil is validity of cover.
  function getCoverDetailsByCoverID2(
    uint _cid
  )
  external
  view
  returns (
    uint cid,
    uint8 status,
    uint sumAssured,
    uint16 coverPeriod,
    uint validUntil
  )
  {

    return (
    _cid,
    coverStatus[_cid],
    allCovers[_cid].sumAssured,
    allCovers[_cid].coverPeriod,
    allCovers[_cid].validUntil
    );
  }

  /// @dev Provides details of a holded cover Id
  /// @param _hcid holded cover Id
  /// @return scAddress SmartCover address of cover.
  /// @return coverCurr currency of cover.
  /// @return coverPeriod Cover Period of cover (in days).
  function getHoldedCoverDetailsByID1(
    uint _hcid
  )
  external
  view
  returns (
    uint hcid,
    address scAddress,
    bytes4 coverCurr,
    uint16 coverPeriod
  )
  {
    return (
    _hcid,
    allCoverHolded[_hcid].scAddress,
    allCoverHolded[_hcid].coverCurr,
    allCoverHolded[_hcid].coverPeriod
    );
  }

  /// @dev Gets total number holded covers created till date.
  function getUserHoldedCoverLength(address _add) external view returns (uint) {
    return userHoldedCover[_add].length;
  }

  /// @dev Gets holded cover index by index of user holded covers.
  function getUserHoldedCoverByIndex(address _add, uint index) external view returns (uint) {
    return userHoldedCover[_add][index];
  }

  /// @dev Provides the details of a holded cover Id
  /// @param _hcid holded cover Id
  /// @return memberAddress holded cover user address.
  /// @return coverDetails array contains SA, Cover Currency Price,Price in NXM, Expiration time of Qoute.
  function getHoldedCoverDetailsByID2(
    uint _hcid
  )
  external
  view
  returns (
    uint hcid,
    address payable memberAddress,
    uint[] memory coverDetails
  )
  {
    return (
    _hcid,
    allCoverHolded[_hcid].userAddress,
    allCoverHolded[_hcid].coverDetails
    );
  }

  /// @dev Gets the Total Sum Assured amount of a given currency and smart contract address.
  function getTotalSumAssuredSC(address _add, bytes4 _curr) external view returns (uint amount) {
    amount = currencyCSAOfSCAdd[_add][_curr];
  }

  //solhint-disable-next-line
  function changeDependentContractAddress() public {}

  /// @dev Changes the status of a given cover.
  /// @param _cid cover Id.
  /// @param _stat New status.
  function changeCoverStatusNo(uint _cid, uint8 _stat) public onlyInternal {
    coverStatus[_cid] = _stat;
    emit CoverStatusEvent(_cid, _stat);
  }

  /**
   * @dev Updates Uint Parameters of a code
   * @param code whose details we want to update
   * @param val value to set
   */
  function updateUintParameters(bytes8 code, uint val) public {

    require(ms.checkIsAuthToGoverned(msg.sender));
    if (code == "STLP") {
      _changeSTLP(val);

    } else if (code == "STL") {

      _changeSTL(val);

    } else if (code == "PM") {

      _changePM(val);

    } else if (code == "QUOMIND") {

      _changeMinDays(val);

    } else if (code == "QUOTOK") {

      _setTokensRetained(val);

    } else {

      revert("Invalid param code");
    }

  }

  /// @dev Changes the existing Profit Margin value
  function _changePM(uint _pm) internal {
    pm = _pm;
  }

  /// @dev Changes the existing Short Term Load Period (STLP) value.
  function _changeSTLP(uint _stlp) internal {
    stlp = _stlp;
  }

  /// @dev Changes the existing Short Term Load (STL) value.
  function _changeSTL(uint _stl) internal {
    stl = _stl;
  }

  /// @dev Changes the existing Minimum cover period (in days)
  function _changeMinDays(uint _days) internal {
    minDays = _days;
  }

  /**
   * @dev to set the the amount of tokens retained
   * @param val is the amount retained
   */
  function _setTokensRetained(uint val) internal {
    tokensRetained = val;
  }
}

File 22 of 38 : ProposalCategory.sol
/* Copyright (C) 2017 GovBlocks.io
  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;

import "../../abstract/Iupgradable.sol";
import "./MemberRoles.sol";
import "./external/Governed.sol";
import "./external/IProposalCategory.sol";

contract ProposalCategory is Governed, IProposalCategory, Iupgradable {

  bool public constructorCheck;
  MemberRoles internal mr;

  struct CategoryStruct {
    uint memberRoleToVote;
    uint majorityVotePerc;
    uint quorumPerc;
    uint[] allowedToCreateProposal;
    uint closingTime;
    uint minStake;
  }

  struct CategoryAction {
    uint defaultIncentive;
    address contractAddress;
    bytes2 contractName;
  }

  CategoryStruct[] internal allCategory;
  mapping(uint => CategoryAction) internal categoryActionData;
  mapping(uint => uint) public categoryABReq;
  mapping(uint => uint) public isSpecialResolution;
  mapping(uint => bytes) public categoryActionHashes;

  bool public categoryActionHashUpdated;

  /**
  * @dev Adds new category (Discontinued, moved functionality to newCategory)
  * @param _name Category name
  * @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
  * @param _majorityVotePerc Majority Vote threshold for Each voting layer
  * @param _quorumPerc minimum threshold percentage required in voting to calculate result
  * @param _allowedToCreateProposal Member roles allowed to create the proposal
  * @param _closingTime Vote closing time for Each voting layer
  * @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
  * @param _contractAddress address of contract to call after proposal is accepted
  * @param _contractName name of contract to be called after proposal is accepted
  * @param _incentives rewards to distributed after proposal is accepted
  */
  function addCategory(
    string calldata _name,
    uint _memberRoleToVote,
    uint _majorityVotePerc,
    uint _quorumPerc,
    uint[] calldata _allowedToCreateProposal,
    uint _closingTime,
    string calldata _actionHash,
    address _contractAddress,
    bytes2 _contractName,
    uint[] calldata _incentives
  ) external {}

  /**
  * @dev Initiates Default settings for Proposal Category contract (Adding default categories)
  */
  function proposalCategoryInitiate() external {}

  /**
  * @dev Initiates Default action function hashes for existing categories
  * To be called after the contract has been upgraded by governance
  */
  function updateCategoryActionHashes() external onlyOwner {

    require(!categoryActionHashUpdated, "Category action hashes already updated");
    categoryActionHashUpdated = true;
    categoryActionHashes[1] = abi.encodeWithSignature("addRole(bytes32,string,address)");
    categoryActionHashes[2] = abi.encodeWithSignature("updateRole(address,uint256,bool)");
    categoryActionHashes[3] = abi.encodeWithSignature("newCategory(string,uint256,uint256,uint256,uint256[],uint256,string,address,bytes2,uint256[],string)"); // solhint-disable-line
    categoryActionHashes[4] = abi.encodeWithSignature("editCategory(uint256,string,uint256,uint256,uint256,uint256[],uint256,string,address,bytes2,uint256[],string)"); // solhint-disable-line
    categoryActionHashes[5] = abi.encodeWithSignature("upgradeContractImplementation(bytes2,address)");
    categoryActionHashes[6] = abi.encodeWithSignature("startEmergencyPause()");
    categoryActionHashes[7] = abi.encodeWithSignature("addEmergencyPause(bool,bytes4)");
    categoryActionHashes[8] = abi.encodeWithSignature("burnCAToken(uint256,uint256,address)");
    categoryActionHashes[9] = abi.encodeWithSignature("setUserClaimVotePausedOn(address)");
    categoryActionHashes[12] = abi.encodeWithSignature("transferEther(uint256,address)");
    categoryActionHashes[13] = abi.encodeWithSignature("addInvestmentAssetCurrency(bytes4,address,bool,uint64,uint64,uint8)"); // solhint-disable-line
    categoryActionHashes[14] = abi.encodeWithSignature("changeInvestmentAssetHoldingPerc(bytes4,uint64,uint64)");
    categoryActionHashes[15] = abi.encodeWithSignature("changeInvestmentAssetStatus(bytes4,bool)");
    categoryActionHashes[16] = abi.encodeWithSignature("swapABMember(address,address)");
    categoryActionHashes[17] = abi.encodeWithSignature("addCurrencyAssetCurrency(bytes4,address,uint256)");
    categoryActionHashes[20] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
    categoryActionHashes[21] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
    categoryActionHashes[22] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
    categoryActionHashes[23] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
    categoryActionHashes[24] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
    categoryActionHashes[25] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
    categoryActionHashes[26] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
    categoryActionHashes[27] = abi.encodeWithSignature("updateAddressParameters(bytes8,address)");
    categoryActionHashes[28] = abi.encodeWithSignature("updateOwnerParameters(bytes8,address)");
    categoryActionHashes[29] = abi.encodeWithSignature("upgradeMultipleContracts(bytes2[],address[])");
    categoryActionHashes[30] = abi.encodeWithSignature("changeCurrencyAssetAddress(bytes4,address)");
    categoryActionHashes[31] = abi.encodeWithSignature("changeCurrencyAssetBaseMin(bytes4,uint256)");
    categoryActionHashes[32] = abi.encodeWithSignature("changeInvestmentAssetAddressAndDecimal(bytes4,address,uint8)"); // solhint-disable-line
    categoryActionHashes[33] = abi.encodeWithSignature("externalLiquidityTrade()");
  }

  /**
  * @dev Gets Total number of categories added till now
  */
  function totalCategories() external view returns (uint) {
    return allCategory.length;
  }

  /**
  * @dev Gets category details
  */
  function category(uint _categoryId) external view returns (uint, uint, uint, uint, uint[] memory, uint, uint) {
    return (
    _categoryId,
    allCategory[_categoryId].memberRoleToVote,
    allCategory[_categoryId].majorityVotePerc,
    allCategory[_categoryId].quorumPerc,
    allCategory[_categoryId].allowedToCreateProposal,
    allCategory[_categoryId].closingTime,
    allCategory[_categoryId].minStake
    );
  }

  /**
  * @dev Gets category ab required and isSpecialResolution
  * @return the category id
  * @return if AB voting is required
  * @return is category a special resolution
  */
  function categoryExtendedData(uint _categoryId) external view returns (uint, uint, uint) {
    return (
    _categoryId,
    categoryABReq[_categoryId],
    isSpecialResolution[_categoryId]
    );
  }

  /**
   * @dev Gets the category acion details
   * @param _categoryId is the category id in concern
   * @return the category id
   * @return the contract address
   * @return the contract name
   * @return the default incentive
   */
  function categoryAction(uint _categoryId) external view returns (uint, address, bytes2, uint) {

    return (
    _categoryId,
    categoryActionData[_categoryId].contractAddress,
    categoryActionData[_categoryId].contractName,
    categoryActionData[_categoryId].defaultIncentive
    );
  }

  /**
   * @dev Gets the category acion details of a category id
   * @param _categoryId is the category id in concern
   * @return the category id
   * @return the contract address
   * @return the contract name
   * @return the default incentive
   * @return action function hash
   */
  function categoryActionDetails(uint _categoryId) external view returns (uint, address, bytes2, uint, bytes memory) {
    return (
    _categoryId,
    categoryActionData[_categoryId].contractAddress,
    categoryActionData[_categoryId].contractName,
    categoryActionData[_categoryId].defaultIncentive,
    categoryActionHashes[_categoryId]
    );
  }

  /**
  * @dev Updates dependant contract addresses
  */
  function changeDependentContractAddress() public {
    mr = MemberRoles(ms.getLatestAddress("MR"));
  }

  /**
  * @dev Adds new category
  * @param _name Category name
  * @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
  * @param _majorityVotePerc Majority Vote threshold for Each voting layer
  * @param _quorumPerc minimum threshold percentage required in voting to calculate result
  * @param _allowedToCreateProposal Member roles allowed to create the proposal
  * @param _closingTime Vote closing time for Each voting layer
  * @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
  * @param _contractAddress address of contract to call after proposal is accepted
  * @param _contractName name of contract to be called after proposal is accepted
  * @param _incentives rewards to distributed after proposal is accepted
  * @param _functionHash function signature to be executed
  */
  function newCategory(
    string memory _name,
    uint _memberRoleToVote,
    uint _majorityVotePerc,
    uint _quorumPerc,
    uint[] memory _allowedToCreateProposal,
    uint _closingTime,
    string memory _actionHash,
    address _contractAddress,
    bytes2 _contractName,
    uint[] memory _incentives,
    string memory _functionHash
  )
  public
  onlyAuthorizedToGovern
  {

    require(_quorumPerc <= 100 && _majorityVotePerc <= 100, "Invalid percentage");

    require((_contractName == "EX" && _contractAddress == address(0)) || bytes(_functionHash).length > 0);

    require(_incentives[3] <= 1, "Invalid special resolution flag");

    //If category is special resolution role authorized should be member
    if (_incentives[3] == 1) {
      require(_memberRoleToVote == uint(MemberRoles.Role.Member));
      _majorityVotePerc = 0;
      _quorumPerc = 0;
    }

    _addCategory(
      _name,
      _memberRoleToVote,
      _majorityVotePerc,
      _quorumPerc,
      _allowedToCreateProposal,
      _closingTime,
      _actionHash,
      _contractAddress,
      _contractName,
      _incentives
    );


    if (bytes(_functionHash).length > 0 && abi.encodeWithSignature(_functionHash).length == 4) {
      categoryActionHashes[allCategory.length - 1] = abi.encodeWithSignature(_functionHash);
    }
  }

  /**
   * @dev Changes the master address and update it's instance
   * @param _masterAddress is the new master address
   */
  function changeMasterAddress(address _masterAddress) public {
    if (masterAddress != address(0))
      require(masterAddress == msg.sender);
    masterAddress = _masterAddress;
    ms = INXMMaster(_masterAddress);
    nxMasterAddress = _masterAddress;

  }

  /**
  * @dev Updates category details (Discontinued, moved functionality to editCategory)
  * @param _categoryId Category id that needs to be updated
  * @param _name Category name
  * @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
  * @param _allowedToCreateProposal Member roles allowed to create the proposal
  * @param _majorityVotePerc Majority Vote threshold for Each voting layer
  * @param _quorumPerc minimum threshold percentage required in voting to calculate result
  * @param _closingTime Vote closing time for Each voting layer
  * @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
  * @param _contractAddress address of contract to call after proposal is accepted
  * @param _contractName name of contract to be called after proposal is accepted
  * @param _incentives rewards to distributed after proposal is accepted
  */
  function updateCategory(
    uint _categoryId,
    string memory _name,
    uint _memberRoleToVote,
    uint _majorityVotePerc,
    uint _quorumPerc,
    uint[] memory _allowedToCreateProposal,
    uint _closingTime,
    string memory _actionHash,
    address _contractAddress,
    bytes2 _contractName,
    uint[] memory _incentives
  ) public {}

  /**
  * @dev Updates category details
  * @param _categoryId Category id that needs to be updated
  * @param _name Category name
  * @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
  * @param _allowedToCreateProposal Member roles allowed to create the proposal
  * @param _majorityVotePerc Majority Vote threshold for Each voting layer
  * @param _quorumPerc minimum threshold percentage required in voting to calculate result
  * @param _closingTime Vote closing time for Each voting layer
  * @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
  * @param _contractAddress address of contract to call after proposal is accepted
  * @param _contractName name of contract to be called after proposal is accepted
  * @param _incentives rewards to distributed after proposal is accepted
  * @param _functionHash function signature to be executed
  */
  function editCategory(
    uint _categoryId,
    string memory _name,
    uint _memberRoleToVote,
    uint _majorityVotePerc,
    uint _quorumPerc,
    uint[] memory _allowedToCreateProposal,
    uint _closingTime,
    string memory _actionHash,
    address _contractAddress,
    bytes2 _contractName,
    uint[] memory _incentives,
    string memory _functionHash
  )
  public
  onlyAuthorizedToGovern
  {
    require(_verifyMemberRoles(_memberRoleToVote, _allowedToCreateProposal) == 1, "Invalid Role");

    require(_quorumPerc <= 100 && _majorityVotePerc <= 100, "Invalid percentage");

    require((_contractName == "EX" && _contractAddress == address(0)) || bytes(_functionHash).length > 0);

    require(_incentives[3] <= 1, "Invalid special resolution flag");

    //If category is special resolution role authorized should be member
    if (_incentives[3] == 1) {
      require(_memberRoleToVote == uint(MemberRoles.Role.Member));
      _majorityVotePerc = 0;
      _quorumPerc = 0;
    }

    delete categoryActionHashes[_categoryId];
    if (bytes(_functionHash).length > 0 && abi.encodeWithSignature(_functionHash).length == 4) {
      categoryActionHashes[_categoryId] = abi.encodeWithSignature(_functionHash);
    }
    allCategory[_categoryId].memberRoleToVote = _memberRoleToVote;
    allCategory[_categoryId].majorityVotePerc = _majorityVotePerc;
    allCategory[_categoryId].closingTime = _closingTime;
    allCategory[_categoryId].allowedToCreateProposal = _allowedToCreateProposal;
    allCategory[_categoryId].minStake = _incentives[0];
    allCategory[_categoryId].quorumPerc = _quorumPerc;
    categoryActionData[_categoryId].defaultIncentive = _incentives[1];
    categoryActionData[_categoryId].contractName = _contractName;
    categoryActionData[_categoryId].contractAddress = _contractAddress;
    categoryABReq[_categoryId] = _incentives[2];
    isSpecialResolution[_categoryId] = _incentives[3];
    emit Category(_categoryId, _name, _actionHash);
  }

  /**
  * @dev Internal call to add new category
  * @param _name Category name
  * @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
  * @param _majorityVotePerc Majority Vote threshold for Each voting layer
  * @param _quorumPerc minimum threshold percentage required in voting to calculate result
  * @param _allowedToCreateProposal Member roles allowed to create the proposal
  * @param _closingTime Vote closing time for Each voting layer
  * @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
  * @param _contractAddress address of contract to call after proposal is accepted
  * @param _contractName name of contract to be called after proposal is accepted
  * @param _incentives rewards to distributed after proposal is accepted
  */
  function _addCategory(
    string memory _name,
    uint _memberRoleToVote,
    uint _majorityVotePerc,
    uint _quorumPerc,
    uint[] memory _allowedToCreateProposal,
    uint _closingTime,
    string memory _actionHash,
    address _contractAddress,
    bytes2 _contractName,
    uint[] memory _incentives
  )
  internal
  {
    require(_verifyMemberRoles(_memberRoleToVote, _allowedToCreateProposal) == 1, "Invalid Role");
    allCategory.push(
      CategoryStruct(
        _memberRoleToVote,
        _majorityVotePerc,
        _quorumPerc,
        _allowedToCreateProposal,
        _closingTime,
        _incentives[0]
      )
    );
    uint categoryId = allCategory.length - 1;
    categoryActionData[categoryId] = CategoryAction(_incentives[1], _contractAddress, _contractName);
    categoryABReq[categoryId] = _incentives[2];
    isSpecialResolution[categoryId] = _incentives[3];
    emit Category(categoryId, _name, _actionHash);
  }

  /**
  * @dev Internal call to check if given roles are valid or not
  */
  function _verifyMemberRoles(uint _memberRoleToVote, uint[] memory _allowedToCreateProposal)
  internal view returns (uint) {
    uint totalRoles = mr.totalRoles();
    if (_memberRoleToVote >= totalRoles) {
      return 0;
    }
    for (uint i = 0; i < _allowedToCreateProposal.length; i++) {
      if (_allowedToCreateProposal[i] >= totalRoles) {
        return 0;
      }
    }
    return 1;
  }

}

File 23 of 38 : OZIERC20.sol
pragma solidity ^0.5.0;


/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
interface OZIERC20 {
  function transfer(address to, uint256 value) external returns (bool);

  function approve(address spender, uint256 value)
  external returns (bool);

  function transferFrom(address from, address to, uint256 value)
  external returns (bool);

  function totalSupply() external view returns (uint256);

  function balanceOf(address who) external view returns (uint256);

  function allowance(address owner, address spender)
  external view returns (uint256);

  event Transfer(
    address indexed from,
    address indexed to,
    uint256 value
  );

  event Approval(
    address indexed owner,
    address indexed spender,
    uint256 value
  );
}

File 24 of 38 : OZSafeMath.sol
pragma solidity ^0.5.0;


/**
 * @title SafeMath
 * @dev Math operations with safety checks that revert on error
 */
library OZSafeMath {

  /**
  * @dev Multiplies two numbers, reverts on overflow.
  */
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
    if (a == 0) {
      return 0;
    }

    uint256 c = a * b;
    require(c / a == b);

    return c;
  }

  /**
  * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
  */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b > 0); // Solidity only automatically asserts when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold

    return c;
  }

  /**
  * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b <= a);
    uint256 c = a - b;

    return c;
  }

  /**
  * @dev Adds two numbers, reverts on overflow.
  */
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c >= a);

    return c;
  }

  /**
  * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
  * reverts when dividing by zero.
  */
  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b != 0);
    return a % b;
  }
}

File 25 of 38 : Iupgradable.sol
pragma solidity ^0.5.0;

import "./INXMMaster.sol";

contract Iupgradable {

  INXMMaster public ms;
  address public nxMasterAddress;

  modifier onlyInternal {
    require(ms.isInternal(msg.sender));
    _;
  }

  modifier isMemberAndcheckPause {
    require(ms.isPause() == false && ms.isMember(msg.sender) == true);
    _;
  }

  modifier onlyOwner {
    require(ms.isOwner(msg.sender));
    _;
  }

  modifier checkPause {
    require(ms.isPause() == false);
    _;
  }

  modifier isMember {
    require(ms.isMember(msg.sender), "Not member");
    _;
  }

  /**
   * @dev Iupgradable Interface to update dependent contract address
   */
  function changeDependentContractAddress() public;

  /**
   * @dev change master address
   * @param _masterAddress is the new address
   */
  function changeMasterAddress(address _masterAddress) public {
    if (address(ms) != address(0)) {
      require(address(ms) == msg.sender, "Not master");
    }

    ms = INXMMaster(_masterAddress);
    nxMasterAddress = _masterAddress;
  }

}

File 26 of 38 : IPooledStaking.sol
pragma solidity ^0.5.0;


interface IPooledStaking {

  function accumulateReward(address contractAddress, uint amount) external;

  function pushBurn(address contractAddress, uint amount) external;

  function hasPendingActions() external view returns (bool);

  function contractStake(address contractAddress) external view returns (uint);

  function stakerReward(address staker) external view returns (uint);

  function stakerDeposit(address staker) external view returns (uint);

  function stakerContractStake(address staker, address contractAddress) external view returns (uint);

  function withdraw(uint amount) external;

  function stakerMaxWithdrawable(address stakerAddress) external view returns (uint);

  function withdrawReward(address stakerAddress) external;
}

File 27 of 38 : IERC1132.sol
pragma solidity ^0.5.0;

/**
 * @title ERC1132 interface
 * @dev see https://github.com/ethereum/EIPs/issues/1132
 */

contract IERC1132 {
  /**
   * @dev Reasons why a user's tokens have been locked
   */
  mapping(address => bytes32[]) public lockReason;

  /**
   * @dev locked token structure
   */
  struct LockToken {
    uint256 amount;
    uint256 validity;
    bool claimed;
  }

  /**
   * @dev Holds number & validity of tokens locked for a given reason for
   *      a specified address
   */
  mapping(address => mapping(bytes32 => LockToken)) public locked;

  /**
   * @dev Records data of all the tokens Locked
   */
  event Locked(
    address indexed _of,
    bytes32 indexed _reason,
    uint256 _amount,
    uint256 _validity
  );

  /**
   * @dev Records data of all the tokens unlocked
   */
  event Unlocked(
    address indexed _of,
    bytes32 indexed _reason,
    uint256 _amount
  );

  /**
   * @dev Locks a specified amount of tokens against an address,
   *      for a specified reason and time
   * @param _reason The reason to lock tokens
   * @param _amount Number of tokens to be locked
   * @param _time Lock time in seconds
   */
  function lock(bytes32 _reason, uint256 _amount, uint256 _time)
  public returns (bool);

  /**
   * @dev Returns tokens locked for a specified address for a
   *      specified reason
   *
   * @param _of The address whose tokens are locked
   * @param _reason The reason to query the lock tokens for
   */
  function tokensLocked(address _of, bytes32 _reason)
  public view returns (uint256 amount);

  /**
   * @dev Returns tokens locked for a specified address for a
   *      specified reason at a specific time
   *
   * @param _of The address whose tokens are locked
   * @param _reason The reason to query the lock tokens for
   * @param _time The timestamp to query the lock tokens for
   */
  function tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time)
  public view returns (uint256 amount);

  /**
   * @dev Returns total tokens held by an address (locked + transferable)
   * @param _of The address to query the total balance of
   */
  function totalBalanceOf(address _of)
  public view returns (uint256 amount);

  /**
   * @dev Extends lock for a specified reason and time
   * @param _reason The reason to lock tokens
   * @param _time Lock extension time in seconds
   */
  function extendLock(bytes32 _reason, uint256 _time)
  public returns (bool);

  /**
   * @dev Increase number of tokens locked for a specified reason
   * @param _reason The reason to lock tokens
   * @param _amount Number of tokens to be increased
   */
  function increaseLockAmount(bytes32 _reason, uint256 _amount)
  public returns (bool);

  /**
   * @dev Returns unlockable tokens for a specified address for a specified reason
   * @param _of The address to query the the unlockable token count of
   * @param _reason The reason to query the unlockable tokens for
   */
  function tokensUnlockable(address _of, bytes32 _reason)
  public view returns (uint256 amount);

  /**
   * @dev Unlocks the unlockable tokens of a specified address
   * @param _of Address of user, claiming back unlockable tokens
   */
  function unlock(address _of)
  public returns (uint256 unlockableTokens);

  /**
   * @dev Gets the unlockable tokens of a specified address
   * @param _of The address to query the the unlockable token count of
   */
  function getUnlockableTokens(address _of)
  public view returns (uint256 unlockableTokens);

}

File 28 of 38 : Governance.sol
// /* Copyright (C) 2017 GovBlocks.io

//   This program is free software: you can redistribute it and/or modify
//     it under the terms of the GNU General Public License as published by
//     the Free Software Foundation, either version 3 of the License, or
//     (at your option) any later version.

//   This program is distributed in the hope that it will be useful,
//     but WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//     GNU General Public License for more details.

//   You should have received a copy of the GNU General Public License
//     along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "../token/TokenController.sol";
import "./MemberRoles.sol";
import "./ProposalCategory.sol";
import "./external/IGovernance.sol";

contract Governance is IGovernance, Iupgradable {

  using SafeMath for uint;

  enum ProposalStatus {
    Draft,
    AwaitingSolution,
    VotingStarted,
    Accepted,
    Rejected,
    Majority_Not_Reached_But_Accepted,
    Denied
  }

  struct ProposalData {
    uint propStatus;
    uint finalVerdict;
    uint category;
    uint commonIncentive;
    uint dateUpd;
    address owner;
  }

  struct ProposalVote {
    address voter;
    uint proposalId;
    uint dateAdd;
  }

  struct VoteTally {
    mapping(uint => uint) memberVoteValue;
    mapping(uint => uint) abVoteValue;
    uint voters;
  }

  struct DelegateVote {
    address follower;
    address leader;
    uint lastUpd;
  }

  ProposalVote[] internal allVotes;
  DelegateVote[] public allDelegation;

  mapping(uint => ProposalData) internal allProposalData;
  mapping(uint => bytes[]) internal allProposalSolutions;
  mapping(address => uint[]) internal allVotesByMember;
  mapping(uint => mapping(address => bool)) public rewardClaimed;
  mapping(address => mapping(uint => uint)) public memberProposalVote;
  mapping(address => uint) public followerDelegation;
  mapping(address => uint) internal followerCount;
  mapping(address => uint[]) internal leaderDelegation;
  mapping(uint => VoteTally) public proposalVoteTally;
  mapping(address => bool) public isOpenForDelegation;
  mapping(address => uint) public lastRewardClaimed;

  bool internal constructorCheck;
  uint public tokenHoldingTime;
  uint internal roleIdAllowedToCatgorize;
  uint internal maxVoteWeigthPer;
  uint internal specialResolutionMajPerc;
  uint internal maxFollowers;
  uint internal totalProposals;
  uint internal maxDraftTime;

  MemberRoles internal memberRole;
  ProposalCategory internal proposalCategory;
  TokenController internal tokenInstance;

  mapping(uint => uint) public proposalActionStatus;
  mapping(uint => uint) internal proposalExecutionTime;
  mapping(uint => mapping(address => bool)) public proposalRejectedByAB;
  mapping(uint => uint) internal actionRejectedCount;

  bool internal actionParamsInitialised;
  uint internal actionWaitingTime;
  uint constant internal AB_MAJ_TO_REJECT_ACTION = 3;

  enum ActionStatus {
    Pending,
    Accepted,
    Rejected,
    Executed,
    NoAction
  }

  /**
  * @dev Called whenever an action execution is failed.
  */
  event ActionFailed (
    uint256 proposalId
  );

  /**
  * @dev Called whenever an AB member rejects the action execution.
  */
  event ActionRejected (
    uint256 indexed proposalId,
    address rejectedBy
  );

  /**
  * @dev Checks if msg.sender is proposal owner
  */
  modifier onlyProposalOwner(uint _proposalId) {
    require(msg.sender == allProposalData[_proposalId].owner, "Not allowed");
    _;
  }

  /**
  * @dev Checks if proposal is opened for voting
  */
  modifier voteNotStarted(uint _proposalId) {
    require(allProposalData[_proposalId].propStatus < uint(ProposalStatus.VotingStarted));
    _;
  }

  /**
  * @dev Checks if msg.sender is allowed to create proposal under given category
  */
  modifier isAllowed(uint _categoryId) {
    require(allowedToCreateProposal(_categoryId), "Not allowed");
    _;
  }

  /**
  * @dev Checks if msg.sender is allowed categorize proposal under given category
  */
  modifier isAllowedToCategorize() {
    require(memberRole.checkRole(msg.sender, roleIdAllowedToCatgorize), "Not allowed");
    _;
  }

  /**
  * @dev Checks if msg.sender had any pending rewards to be claimed
  */
  modifier checkPendingRewards {
    require(getPendingReward(msg.sender) == 0, "Claim reward");
    _;
  }

  /**
  * @dev Event emitted whenever a proposal is categorized
  */
  event ProposalCategorized(
    uint indexed proposalId,
    address indexed categorizedBy,
    uint categoryId
  );

  /**
   * @dev Removes delegation of an address.
   * @param _add address to undelegate.
   */
  function removeDelegation(address _add) external onlyInternal {
    _unDelegate(_add);
  }

  /**
  * @dev Creates a new proposal
  * @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal
  * @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective
  */
  function createProposal(
    string calldata _proposalTitle,
    string calldata _proposalSD,
    string calldata _proposalDescHash,
    uint _categoryId
  )
  external isAllowed(_categoryId)
  {
    require(ms.isMember(msg.sender), "Not Member");

    _createProposal(_proposalTitle, _proposalSD, _proposalDescHash, _categoryId);
  }

  /**
  * @dev Edits the details of an existing proposal
  * @param _proposalId Proposal id that details needs to be updated
  * @param _proposalDescHash Proposal description hash having long and short description of proposal.
  */
  function updateProposal(
    uint _proposalId,
    string calldata _proposalTitle,
    string calldata _proposalSD,
    string calldata _proposalDescHash
  )
  external onlyProposalOwner(_proposalId)
  {
    require(
      allProposalSolutions[_proposalId].length < 2,
      "Not allowed"
    );
    allProposalData[_proposalId].propStatus = uint(ProposalStatus.Draft);
    allProposalData[_proposalId].category = 0;
    allProposalData[_proposalId].commonIncentive = 0;
    emit Proposal(
      allProposalData[_proposalId].owner,
      _proposalId,
      now,
      _proposalTitle,
      _proposalSD,
      _proposalDescHash
    );
  }

  /**
  * @dev Categorizes proposal to proceed further. Categories shows the proposal objective.
  */
  function categorizeProposal(
    uint _proposalId,
    uint _categoryId,
    uint _incentive
  )
  external
  voteNotStarted(_proposalId) isAllowedToCategorize
  {
    _categorizeProposal(_proposalId, _categoryId, _incentive);
  }

  /**
  * @dev Submit proposal with solution
  * @param _proposalId Proposal id
  * @param _solutionHash Solution hash contains  parameters, values and description needed according to proposal
  */
  function submitProposalWithSolution(
    uint _proposalId,
    string calldata _solutionHash,
    bytes calldata _action
  )
  external
  onlyProposalOwner(_proposalId)
  {

    require(allProposalData[_proposalId].propStatus == uint(ProposalStatus.AwaitingSolution));

    _proposalSubmission(_proposalId, _solutionHash, _action);
  }

  /**
  * @dev Creates a new proposal with solution
  * @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal
  * @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective
  * @param _solutionHash Solution hash contains  parameters, values and description needed according to proposal
  */
  function createProposalwithSolution(
    string calldata _proposalTitle,
    string calldata _proposalSD,
    string calldata _proposalDescHash,
    uint _categoryId,
    string calldata _solutionHash,
    bytes calldata _action
  )
  external isAllowed(_categoryId)
  {


    uint proposalId = totalProposals;

    _createProposal(_proposalTitle, _proposalSD, _proposalDescHash, _categoryId);

    require(_categoryId > 0);

    _proposalSubmission(
      proposalId,
      _solutionHash,
      _action
    );
  }

  /**
   * @dev Submit a vote on the proposal.
   * @param _proposalId to vote upon.
   * @param _solutionChosen is the chosen vote.
   */
  function submitVote(uint _proposalId, uint _solutionChosen) external {

    require(allProposalData[_proposalId].propStatus ==
      uint(Governance.ProposalStatus.VotingStarted), "Not allowed");

    require(_solutionChosen < allProposalSolutions[_proposalId].length);


    _submitVote(_proposalId, _solutionChosen);
  }

  /**
   * @dev Closes the proposal.
   * @param _proposalId of proposal to be closed.
   */
  function closeProposal(uint _proposalId) external {
    uint category = allProposalData[_proposalId].category;


    uint _memberRole;
    if (allProposalData[_proposalId].dateUpd.add(maxDraftTime) <= now &&
      allProposalData[_proposalId].propStatus < uint(ProposalStatus.VotingStarted)) {
      _updateProposalStatus(_proposalId, uint(ProposalStatus.Denied));
    } else {
      require(canCloseProposal(_proposalId) == 1);
      (, _memberRole,,,,,) = proposalCategory.category(allProposalData[_proposalId].category);
      if (_memberRole == uint(MemberRoles.Role.AdvisoryBoard)) {
        _closeAdvisoryBoardVote(_proposalId, category);
      } else {
        _closeMemberVote(_proposalId, category);
      }
    }

  }

  /**
   * @dev Claims reward for member.
   * @param _memberAddress to claim reward of.
   * @param _maxRecords maximum number of records to claim reward for.
   _proposals list of proposals of which reward will be claimed.
   * @return amount of pending reward.
   */
  function claimReward(address _memberAddress, uint _maxRecords)
  external returns (uint pendingDAppReward)
  {

    uint voteId;
    address leader;
    uint lastUpd;

    require(msg.sender == ms.getLatestAddress("CR"));

    uint delegationId = followerDelegation[_memberAddress];
    DelegateVote memory delegationData = allDelegation[delegationId];
    if (delegationId > 0 && delegationData.leader != address(0)) {
      leader = delegationData.leader;
      lastUpd = delegationData.lastUpd;
    } else
      leader = _memberAddress;

    uint proposalId;
    uint totalVotes = allVotesByMember[leader].length;
    uint lastClaimed = totalVotes;
    uint j;
    uint i;
    for (i = lastRewardClaimed[_memberAddress]; i < totalVotes && j < _maxRecords; i++) {
      voteId = allVotesByMember[leader][i];
      proposalId = allVotes[voteId].proposalId;
      if (proposalVoteTally[proposalId].voters > 0 && (allVotes[voteId].dateAdd > (
      lastUpd.add(tokenHoldingTime)) || leader == _memberAddress)) {
        if (allProposalData[proposalId].propStatus > uint(ProposalStatus.VotingStarted)) {
          if (!rewardClaimed[voteId][_memberAddress]) {
            pendingDAppReward = pendingDAppReward.add(
              allProposalData[proposalId].commonIncentive.div(
                proposalVoteTally[proposalId].voters
              )
            );
            rewardClaimed[voteId][_memberAddress] = true;
            j++;
          }
        } else {
          if (lastClaimed == totalVotes) {
            lastClaimed = i;
          }
        }
      }
    }

    if (lastClaimed == totalVotes) {
      lastRewardClaimed[_memberAddress] = i;
    } else {
      lastRewardClaimed[_memberAddress] = lastClaimed;
    }

    if (j > 0) {
      emit RewardClaimed(
        _memberAddress,
        pendingDAppReward
      );
    }
  }

  /**
   * @dev Sets delegation acceptance status of individual user
   * @param _status delegation acceptance status
   */
  function setDelegationStatus(bool _status) external isMemberAndcheckPause checkPendingRewards {
    isOpenForDelegation[msg.sender] = _status;
  }

  /**
   * @dev Delegates vote to an address.
   * @param _add is the address to delegate vote to.
   */
  function delegateVote(address _add) external isMemberAndcheckPause checkPendingRewards {

    require(ms.masterInitialized());

    require(allDelegation[followerDelegation[_add]].leader == address(0));

    if (followerDelegation[msg.sender] > 0) {
      require((allDelegation[followerDelegation[msg.sender]].lastUpd).add(tokenHoldingTime) < now);
    }

    require(!alreadyDelegated(msg.sender));
    require(!memberRole.checkRole(msg.sender, uint(MemberRoles.Role.Owner)));
    require(!memberRole.checkRole(msg.sender, uint(MemberRoles.Role.AdvisoryBoard)));


    require(followerCount[_add] < maxFollowers);

    if (allVotesByMember[msg.sender].length > 0) {
      require((allVotes[allVotesByMember[msg.sender][allVotesByMember[msg.sender].length - 1]].dateAdd).add(tokenHoldingTime)
        < now);
    }

    require(ms.isMember(_add));

    require(isOpenForDelegation[_add]);

    allDelegation.push(DelegateVote(msg.sender, _add, now));
    followerDelegation[msg.sender] = allDelegation.length - 1;
    leaderDelegation[_add].push(allDelegation.length - 1);
    followerCount[_add]++;
    lastRewardClaimed[msg.sender] = allVotesByMember[_add].length;
  }

  /**
   * @dev Undelegates the sender
   */
  function unDelegate() external isMemberAndcheckPause checkPendingRewards {
    _unDelegate(msg.sender);
  }

  /**
   * @dev Triggers action of accepted proposal after waiting time is finished
   */
  function triggerAction(uint _proposalId) external {
    require(proposalActionStatus[_proposalId] == uint(ActionStatus.Accepted) && proposalExecutionTime[_proposalId] <= now, "Cannot trigger");
    _triggerAction(_proposalId, allProposalData[_proposalId].category);
  }

  /**
   * @dev Provides option to Advisory board member to reject proposal action execution within actionWaitingTime, if found suspicious
   */
  function rejectAction(uint _proposalId) external {
    require(memberRole.checkRole(msg.sender, uint(MemberRoles.Role.AdvisoryBoard)) && proposalExecutionTime[_proposalId] > now);

    require(proposalActionStatus[_proposalId] == uint(ActionStatus.Accepted));

    require(!proposalRejectedByAB[_proposalId][msg.sender]);

    require(
      keccak256(proposalCategory.categoryActionHashes(allProposalData[_proposalId].category))
      != keccak256(abi.encodeWithSignature("swapABMember(address,address)"))
    );

    proposalRejectedByAB[_proposalId][msg.sender] = true;
    actionRejectedCount[_proposalId]++;
    emit ActionRejected(_proposalId, msg.sender);
    if (actionRejectedCount[_proposalId] == AB_MAJ_TO_REJECT_ACTION) {
      proposalActionStatus[_proposalId] = uint(ActionStatus.Rejected);
    }
  }

  /**
   * @dev Sets intial actionWaitingTime value
   * To be called after governance implementation has been updated
   */
  function setInitialActionParameters() external onlyOwner {
    require(!actionParamsInitialised);
    actionParamsInitialised = true;
    actionWaitingTime = 24 * 1 hours;
  }

  /**
   * @dev Gets Uint Parameters of a code
   * @param code whose details we want
   * @return string value of the code
   * @return associated amount (time or perc or value) to the code
   */
  function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {

    codeVal = code;

    if (code == "GOVHOLD") {

      val = tokenHoldingTime / (1 days);

    } else if (code == "MAXFOL") {

      val = maxFollowers;

    } else if (code == "MAXDRFT") {

      val = maxDraftTime / (1 days);

    } else if (code == "EPTIME") {

      val = ms.pauseTime() / (1 days);

    } else if (code == "ACWT") {

      val = actionWaitingTime / (1 hours);

    }
  }

  /**
   * @dev Gets all details of a propsal
   * @param _proposalId whose details we want
   * @return proposalId
   * @return category
   * @return status
   * @return finalVerdict
   * @return totalReward
   */
  function proposal(uint _proposalId)
  external
  view
  returns (
    uint proposalId,
    uint category,
    uint status,
    uint finalVerdict,
    uint totalRewar
  )
  {
    return (
    _proposalId,
    allProposalData[_proposalId].category,
    allProposalData[_proposalId].propStatus,
    allProposalData[_proposalId].finalVerdict,
    allProposalData[_proposalId].commonIncentive
    );
  }

  /**
   * @dev Gets some details of a propsal
   * @param _proposalId whose details we want
   * @return proposalId
   * @return number of all proposal solutions
   * @return amount of votes
   */
  function proposalDetails(uint _proposalId) external view returns (uint, uint, uint) {
    return (
    _proposalId,
    allProposalSolutions[_proposalId].length,
    proposalVoteTally[_proposalId].voters
    );
  }

  /**
   * @dev Gets solution action on a proposal
   * @param _proposalId whose details we want
   * @param _solution whose details we want
   * @return action of a solution on a proposal
   */
  function getSolutionAction(uint _proposalId, uint _solution) external view returns (uint, bytes memory) {
    return (
    _solution,
    allProposalSolutions[_proposalId][_solution]
    );
  }

  /**
   * @dev Gets length of propsal
   * @return length of propsal
   */
  function getProposalLength() external view returns (uint) {
    return totalProposals;
  }

  /**
   * @dev Get followers of an address
   * @return get followers of an address
   */
  function getFollowers(address _add) external view returns (uint[] memory) {
    return leaderDelegation[_add];
  }

  /**
   * @dev Gets pending rewards of a member
   * @param _memberAddress in concern
   * @return amount of pending reward
   */
  function getPendingReward(address _memberAddress)
  public view returns (uint pendingDAppReward)
  {
    uint delegationId = followerDelegation[_memberAddress];
    address leader;
    uint lastUpd;
    DelegateVote memory delegationData = allDelegation[delegationId];

    if (delegationId > 0 && delegationData.leader != address(0)) {
      leader = delegationData.leader;
      lastUpd = delegationData.lastUpd;
    } else
      leader = _memberAddress;

    uint proposalId;
    for (uint i = lastRewardClaimed[_memberAddress]; i < allVotesByMember[leader].length; i++) {
      if (allVotes[allVotesByMember[leader][i]].dateAdd > (
      lastUpd.add(tokenHoldingTime)) || leader == _memberAddress) {
        if (!rewardClaimed[allVotesByMember[leader][i]][_memberAddress]) {
          proposalId = allVotes[allVotesByMember[leader][i]].proposalId;
          if (proposalVoteTally[proposalId].voters > 0 && allProposalData[proposalId].propStatus
          > uint(ProposalStatus.VotingStarted)) {
            pendingDAppReward = pendingDAppReward.add(
              allProposalData[proposalId].commonIncentive.div(
                proposalVoteTally[proposalId].voters
              )
            );
          }
        }
      }
    }
  }

  /**
   * @dev Updates Uint Parameters of a code
   * @param code whose details we want to update
   * @param val value to set
   */
  function updateUintParameters(bytes8 code, uint val) public {

    require(ms.checkIsAuthToGoverned(msg.sender));
    if (code == "GOVHOLD") {

      tokenHoldingTime = val * 1 days;

    } else if (code == "MAXFOL") {

      maxFollowers = val;

    } else if (code == "MAXDRFT") {

      maxDraftTime = val * 1 days;

    } else if (code == "EPTIME") {

      ms.updatePauseTime(val * 1 days);

    } else if (code == "ACWT") {

      actionWaitingTime = val * 1 hours;

    } else {

      revert("Invalid code");

    }
  }

  /**
  * @dev Updates all dependency addresses to latest ones from Master
  */
  function changeDependentContractAddress() public {
    tokenInstance = TokenController(ms.dAppLocker());
    memberRole = MemberRoles(ms.getLatestAddress("MR"));
    proposalCategory = ProposalCategory(ms.getLatestAddress("PC"));
  }

  /**
  * @dev Checks if msg.sender is allowed to create a proposal under given category
  */
  function allowedToCreateProposal(uint category) public view returns (bool check) {
    if (category == 0)
      return true;
    uint[] memory mrAllowed;
    (,,,, mrAllowed,,) = proposalCategory.category(category);
    for (uint i = 0; i < mrAllowed.length; i++) {
      if (mrAllowed[i] == 0 || memberRole.checkRole(msg.sender, mrAllowed[i]))
        return true;
    }
  }

  /**
   * @dev Checks if an address is already delegated
   * @param _add in concern
   * @return bool value if the address is delegated or not
   */
  function alreadyDelegated(address _add) public view returns (bool delegated) {
    for (uint i = 0; i < leaderDelegation[_add].length; i++) {
      if (allDelegation[leaderDelegation[_add][i]].leader == _add) {
        return true;
      }
    }
  }

  /**
  * @dev Checks If the proposal voting time is up and it's ready to close
  *      i.e. Closevalue is 1 if proposal is ready to be closed, 2 if already closed, 0 otherwise!
  * @param _proposalId Proposal id to which closing value is being checked
  */
  function canCloseProposal(uint _proposalId)
  public
  view
  returns (uint)
  {
    uint dateUpdate;
    uint pStatus;
    uint _closingTime;
    uint _roleId;
    uint majority;
    pStatus = allProposalData[_proposalId].propStatus;
    dateUpdate = allProposalData[_proposalId].dateUpd;
    (, _roleId, majority, , , _closingTime,) = proposalCategory.category(allProposalData[_proposalId].category);
    if (
      pStatus == uint(ProposalStatus.VotingStarted)
    ) {
      uint numberOfMembers = memberRole.numberOfMembers(_roleId);
      if (_roleId == uint(MemberRoles.Role.AdvisoryBoard)) {
        if (proposalVoteTally[_proposalId].abVoteValue[1].mul(100).div(numberOfMembers) >= majority
        || proposalVoteTally[_proposalId].abVoteValue[1].add(proposalVoteTally[_proposalId].abVoteValue[0]) == numberOfMembers
          || dateUpdate.add(_closingTime) <= now) {

          return 1;
        }
      } else {
        if (numberOfMembers == proposalVoteTally[_proposalId].voters
          || dateUpdate.add(_closingTime) <= now)
          return 1;
      }
    } else if (pStatus > uint(ProposalStatus.VotingStarted)) {
      return 2;
    } else {
      return 0;
    }
  }

  /**
   * @dev Gets Id of member role allowed to categorize the proposal
   * @return roleId allowed to categorize the proposal
   */
  function allowedToCatgorize() public view returns (uint roleId) {
    return roleIdAllowedToCatgorize;
  }

  /**
   * @dev Gets vote tally data
   * @param _proposalId in concern
   * @param _solution of a proposal id
   * @return member vote value
   * @return advisory board vote value
   * @return amount of votes
   */
  function voteTallyData(uint _proposalId, uint _solution) public view returns (uint, uint, uint) {
    return (proposalVoteTally[_proposalId].memberVoteValue[_solution],
    proposalVoteTally[_proposalId].abVoteValue[_solution], proposalVoteTally[_proposalId].voters);
  }

  /**
   * @dev Internal call to create proposal
   * @param _proposalTitle of proposal
   * @param _proposalSD is short description of proposal
   * @param _proposalDescHash IPFS hash value of propsal
   * @param _categoryId of proposal
   */
  function _createProposal(
    string memory _proposalTitle,
    string memory _proposalSD,
    string memory _proposalDescHash,
    uint _categoryId
  )
  internal
  {
    require(proposalCategory.categoryABReq(_categoryId) == 0 || _categoryId == 0);
    uint _proposalId = totalProposals;
    allProposalData[_proposalId].owner = msg.sender;
    allProposalData[_proposalId].dateUpd = now;
    allProposalSolutions[_proposalId].push("");
    totalProposals++;

    emit Proposal(
      msg.sender,
      _proposalId,
      now,
      _proposalTitle,
      _proposalSD,
      _proposalDescHash
    );

    if (_categoryId > 0)
      _categorizeProposal(_proposalId, _categoryId, 0);
  }

  /**
   * @dev Internal call to categorize a proposal
   * @param _proposalId of proposal
   * @param _categoryId of proposal
   * @param _incentive is commonIncentive
   */
  function _categorizeProposal(
    uint _proposalId,
    uint _categoryId,
    uint _incentive
  )
  internal
  {
    require(
      _categoryId > 0 && _categoryId < proposalCategory.totalCategories(),
      "Invalid category"
    );
    allProposalData[_proposalId].category = _categoryId;
    allProposalData[_proposalId].commonIncentive = _incentive;
    allProposalData[_proposalId].propStatus = uint(ProposalStatus.AwaitingSolution);

    emit ProposalCategorized(_proposalId, msg.sender, _categoryId);
  }

  /**
   * @dev Internal call to add solution to a proposal
   * @param _proposalId in concern
   * @param _action on that solution
   * @param _solutionHash string value
   */
  function _addSolution(uint _proposalId, bytes memory _action, string memory _solutionHash)
  internal
  {
    allProposalSolutions[_proposalId].push(_action);
    emit Solution(_proposalId, msg.sender, allProposalSolutions[_proposalId].length - 1, _solutionHash, now);
  }

  /**
  * @dev Internal call to add solution and open proposal for voting
  */
  function _proposalSubmission(
    uint _proposalId,
    string memory _solutionHash,
    bytes memory _action
  )
  internal
  {

    uint _categoryId = allProposalData[_proposalId].category;
    if (proposalCategory.categoryActionHashes(_categoryId).length == 0) {
      require(keccak256(_action) == keccak256(""));
      proposalActionStatus[_proposalId] = uint(ActionStatus.NoAction);
    }

    _addSolution(
      _proposalId,
      _action,
      _solutionHash
    );

    _updateProposalStatus(_proposalId, uint(ProposalStatus.VotingStarted));
    (, , , , , uint closingTime,) = proposalCategory.category(_categoryId);
    emit CloseProposalOnTime(_proposalId, closingTime.add(now));

  }

  /**
   * @dev Internal call to submit vote
   * @param _proposalId of proposal in concern
   * @param _solution for that proposal
   */
  function _submitVote(uint _proposalId, uint _solution) internal {

    uint delegationId = followerDelegation[msg.sender];
    uint mrSequence;
    uint majority;
    uint closingTime;
    (, mrSequence, majority, , , closingTime,) = proposalCategory.category(allProposalData[_proposalId].category);

    require(allProposalData[_proposalId].dateUpd.add(closingTime) > now, "Closed");

    require(memberProposalVote[msg.sender][_proposalId] == 0, "Not allowed");
    require((delegationId == 0) || (delegationId > 0 && allDelegation[delegationId].leader == address(0) &&
    _checkLastUpd(allDelegation[delegationId].lastUpd)));

    require(memberRole.checkRole(msg.sender, mrSequence), "Not Authorized");
    uint totalVotes = allVotes.length;

    allVotesByMember[msg.sender].push(totalVotes);
    memberProposalVote[msg.sender][_proposalId] = totalVotes;

    allVotes.push(ProposalVote(msg.sender, _proposalId, now));

    emit Vote(msg.sender, _proposalId, totalVotes, now, _solution);
    if (mrSequence == uint(MemberRoles.Role.Owner)) {
      if (_solution == 1)
        _callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), allProposalData[_proposalId].category, 1, MemberRoles.Role.Owner);
      else
        _updateProposalStatus(_proposalId, uint(ProposalStatus.Rejected));

    } else {
      uint numberOfMembers = memberRole.numberOfMembers(mrSequence);
      _setVoteTally(_proposalId, _solution, mrSequence);

      if (mrSequence == uint(MemberRoles.Role.AdvisoryBoard)) {
        if (proposalVoteTally[_proposalId].abVoteValue[1].mul(100).div(numberOfMembers)
        >= majority
          || (proposalVoteTally[_proposalId].abVoteValue[1].add(proposalVoteTally[_proposalId].abVoteValue[0])) == numberOfMembers) {
          emit VoteCast(_proposalId);
        }
      } else {
        if (numberOfMembers == proposalVoteTally[_proposalId].voters)
          emit VoteCast(_proposalId);
      }
    }

  }

  /**
   * @dev Internal call to set vote tally of a proposal
   * @param _proposalId of proposal in concern
   * @param _solution of proposal in concern
   * @param mrSequence number of members for a role
   */
  function _setVoteTally(uint _proposalId, uint _solution, uint mrSequence) internal
  {
    uint categoryABReq;
    uint isSpecialResolution;
    (, categoryABReq, isSpecialResolution) = proposalCategory.categoryExtendedData(allProposalData[_proposalId].category);
    if (memberRole.checkRole(msg.sender, uint(MemberRoles.Role.AdvisoryBoard)) && (categoryABReq > 0) ||
      mrSequence == uint(MemberRoles.Role.AdvisoryBoard)) {
      proposalVoteTally[_proposalId].abVoteValue[_solution]++;
    }
    tokenInstance.lockForMemberVote(msg.sender, tokenHoldingTime);
    if (mrSequence != uint(MemberRoles.Role.AdvisoryBoard)) {
      uint voteWeight;
      uint voters = 1;
      uint tokenBalance = tokenInstance.totalBalanceOf(msg.sender);
      uint totalSupply = tokenInstance.totalSupply();
      if (isSpecialResolution == 1) {
        voteWeight = tokenBalance.add(10 ** 18);
      } else {
        voteWeight = (_minOf(tokenBalance, maxVoteWeigthPer.mul(totalSupply).div(100))).add(10 ** 18);
      }
      DelegateVote memory delegationData;
      for (uint i = 0; i < leaderDelegation[msg.sender].length; i++) {
        delegationData = allDelegation[leaderDelegation[msg.sender][i]];
        if (delegationData.leader == msg.sender &&
          _checkLastUpd(delegationData.lastUpd)) {
          if (memberRole.checkRole(delegationData.follower, mrSequence)) {
            tokenBalance = tokenInstance.totalBalanceOf(delegationData.follower);
            tokenInstance.lockForMemberVote(delegationData.follower, tokenHoldingTime);
            voters++;
            if (isSpecialResolution == 1) {
              voteWeight = voteWeight.add(tokenBalance.add(10 ** 18));
            } else {
              voteWeight = voteWeight.add((_minOf(tokenBalance, maxVoteWeigthPer.mul(totalSupply).div(100))).add(10 ** 18));
            }
          }
        }
      }
      proposalVoteTally[_proposalId].memberVoteValue[_solution] = proposalVoteTally[_proposalId].memberVoteValue[_solution].add(voteWeight);
      proposalVoteTally[_proposalId].voters = proposalVoteTally[_proposalId].voters + voters;
    }
  }

  /**
   * @dev Gets minimum of two numbers
   * @param a one of the two numbers
   * @param b one of the two numbers
   * @return minimum number out of the two
   */
  function _minOf(uint a, uint b) internal pure returns (uint res) {
    res = a;
    if (res > b)
      res = b;
  }

  /**
   * @dev Check the time since last update has exceeded token holding time or not
   * @param _lastUpd is last update time
   * @return the bool which tells if the time since last update has exceeded token holding time or not
   */
  function _checkLastUpd(uint _lastUpd) internal view returns (bool) {
    return (now - _lastUpd) > tokenHoldingTime;
  }

  /**
  * @dev Checks if the vote count against any solution passes the threshold value or not.
  */
  function _checkForThreshold(uint _proposalId, uint _category) internal view returns (bool check) {
    uint categoryQuorumPerc;
    uint roleAuthorized;
    (, roleAuthorized, , categoryQuorumPerc, , ,) = proposalCategory.category(_category);
    check = ((proposalVoteTally[_proposalId].memberVoteValue[0]
    .add(proposalVoteTally[_proposalId].memberVoteValue[1]))
    .mul(100))
    .div(
      tokenInstance.totalSupply().add(
        memberRole.numberOfMembers(roleAuthorized).mul(10 ** 18)
      )
    ) >= categoryQuorumPerc;
  }

  /**
   * @dev Called when vote majority is reached
   * @param _proposalId of proposal in concern
   * @param _status of proposal in concern
   * @param category of proposal in concern
   * @param max vote value of proposal in concern
   */
  function _callIfMajReached(uint _proposalId, uint _status, uint category, uint max, MemberRoles.Role role) internal {

    allProposalData[_proposalId].finalVerdict = max;
    _updateProposalStatus(_proposalId, _status);
    emit ProposalAccepted(_proposalId);
    if (proposalActionStatus[_proposalId] != uint(ActionStatus.NoAction)) {
      if (role == MemberRoles.Role.AdvisoryBoard) {
        _triggerAction(_proposalId, category);
      } else {
        proposalActionStatus[_proposalId] = uint(ActionStatus.Accepted);
        proposalExecutionTime[_proposalId] = actionWaitingTime.add(now);
      }
    }
  }

  /**
   * @dev Internal function to trigger action of accepted proposal
   */
  function _triggerAction(uint _proposalId, uint _categoryId) internal {
    proposalActionStatus[_proposalId] = uint(ActionStatus.Executed);
    bytes2 contractName;
    address actionAddress;
    bytes memory _functionHash;
    (, actionAddress, contractName, , _functionHash) = proposalCategory.categoryActionDetails(_categoryId);
    if (contractName == "MS") {
      actionAddress = address(ms);
    } else if (contractName != "EX") {
      actionAddress = ms.getLatestAddress(contractName);
    }
    // solhint-disable-next-line avoid-low-level-calls
    (bool actionStatus,) = actionAddress.call(abi.encodePacked(_functionHash, allProposalSolutions[_proposalId][1]));
    if (actionStatus) {
      emit ActionSuccess(_proposalId);
    } else {
      proposalActionStatus[_proposalId] = uint(ActionStatus.Accepted);
      emit ActionFailed(_proposalId);
    }
  }

  /**
   * @dev Internal call to update proposal status
   * @param _proposalId of proposal in concern
   * @param _status of proposal to set
   */
  function _updateProposalStatus(uint _proposalId, uint _status) internal {
    if (_status == uint(ProposalStatus.Rejected) || _status == uint(ProposalStatus.Denied)) {
      proposalActionStatus[_proposalId] = uint(ActionStatus.NoAction);
    }
    allProposalData[_proposalId].dateUpd = now;
    allProposalData[_proposalId].propStatus = _status;
  }

  /**
   * @dev Internal call to undelegate a follower
   * @param _follower is address of follower to undelegate
   */
  function _unDelegate(address _follower) internal {
    uint followerId = followerDelegation[_follower];
    if (followerId > 0) {

      followerCount[allDelegation[followerId].leader] = followerCount[allDelegation[followerId].leader].sub(1);
      allDelegation[followerId].leader = address(0);
      allDelegation[followerId].lastUpd = now;

      lastRewardClaimed[_follower] = allVotesByMember[_follower].length;
    }
  }

  /**
   * @dev Internal call to close member voting
   * @param _proposalId of proposal in concern
   * @param category of proposal in concern
   */
  function _closeMemberVote(uint _proposalId, uint category) internal {
    uint isSpecialResolution;
    uint abMaj;
    (, abMaj, isSpecialResolution) = proposalCategory.categoryExtendedData(category);
    if (isSpecialResolution == 1) {
      uint acceptedVotePerc = proposalVoteTally[_proposalId].memberVoteValue[1].mul(100)
      .div(
        tokenInstance.totalSupply().add(
          memberRole.numberOfMembers(uint(MemberRoles.Role.Member)).mul(10 ** 18)
        ));
      if (acceptedVotePerc >= specialResolutionMajPerc) {
        _callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, MemberRoles.Role.Member);
      } else {
        _updateProposalStatus(_proposalId, uint(ProposalStatus.Denied));
      }
    } else {
      if (_checkForThreshold(_proposalId, category)) {
        uint majorityVote;
        (,, majorityVote,,,,) = proposalCategory.category(category);
        if (
          ((proposalVoteTally[_proposalId].memberVoteValue[1].mul(100))
          .div(proposalVoteTally[_proposalId].memberVoteValue[0]
          .add(proposalVoteTally[_proposalId].memberVoteValue[1])
          ))
          >= majorityVote
        ) {
          _callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, MemberRoles.Role.Member);
        } else {
          _updateProposalStatus(_proposalId, uint(ProposalStatus.Rejected));
        }
      } else {
        if (abMaj > 0 && proposalVoteTally[_proposalId].abVoteValue[1].mul(100)
        .div(memberRole.numberOfMembers(uint(MemberRoles.Role.AdvisoryBoard))) >= abMaj) {
          _callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, MemberRoles.Role.Member);
        } else {
          _updateProposalStatus(_proposalId, uint(ProposalStatus.Denied));
        }
      }
    }

    if (proposalVoteTally[_proposalId].voters > 0) {
      tokenInstance.mint(ms.getLatestAddress("CR"), allProposalData[_proposalId].commonIncentive);
    }
  }

  /**
   * @dev Internal call to close advisory board voting
   * @param _proposalId of proposal in concern
   * @param category of proposal in concern
   */
  function _closeAdvisoryBoardVote(uint _proposalId, uint category) internal {
    uint _majorityVote;
    MemberRoles.Role _roleId = MemberRoles.Role.AdvisoryBoard;
    (,, _majorityVote,,,,) = proposalCategory.category(category);
    if (proposalVoteTally[_proposalId].abVoteValue[1].mul(100)
    .div(memberRole.numberOfMembers(uint(_roleId))) >= _majorityVote) {
      _callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, _roleId);
    } else {
      _updateProposalStatus(_proposalId, uint(ProposalStatus.Denied));
    }

  }

}

File 29 of 38 : Governed.sol
/* Copyright (C) 2017 GovBlocks.io
  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;


interface IMaster {
  function getLatestAddress(bytes2 _module) external view returns (address);
}

contract Governed {

  address public masterAddress; // Name of the dApp, needs to be set by contracts inheriting this contract

  /// @dev modifier that allows only the authorized addresses to execute the function
  modifier onlyAuthorizedToGovern() {
    IMaster ms = IMaster(masterAddress);
    require(ms.getLatestAddress("GV") == msg.sender, "Not authorized");
    _;
  }

  /// @dev checks if an address is authorized to govern
  function isAuthorizedToGovern(address _toCheck) public view returns (bool) {
    IMaster ms = IMaster(masterAddress);
    return (ms.getLatestAddress("GV") == _toCheck);
  }

}

File 30 of 38 : Claims.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "../capital/Pool.sol";
import "../claims/ClaimsReward.sol";
import "../token/NXMToken.sol";
import "../token/TokenController.sol";
import "../token/TokenFunctions.sol";
import "./ClaimsData.sol";

contract Claims is Iupgradable {
  using SafeMath for uint;

  TokenFunctions internal tf;
  NXMToken internal tk;
  TokenController internal tc;
  ClaimsReward internal cr;
  Pool internal p1;
  ClaimsData internal cd;
  TokenData internal td;
  QuotationData internal qd;

  uint private constant DECIMAL1E18 = uint(10) ** 18;

  /**
   * @dev Sets the status of claim using claim id.
   * @param claimId claim id.
   * @param stat status to be set.
   */
  function setClaimStatus(uint claimId, uint stat) external onlyInternal {
    _setClaimStatus(claimId, stat);
  }

  /**
   * @dev Gets claim details of claim id = pending claim start + given index
   */
  function getClaimFromNewStart(
    uint index
  )
  external
  view
  returns (
    uint coverId,
    uint claimId,
    int8 voteCA,
    int8 voteMV,
    uint statusnumber
  )
  {
    (coverId, claimId, voteCA, voteMV, statusnumber) = cd.getClaimFromNewStart(index, msg.sender);
  }

  /**
   * @dev Gets details of a claim submitted by the calling user, at a given index
   */
  function getUserClaimByIndex(
    uint index
  )
  external
  view
  returns (
    uint status,
    uint coverId,
    uint claimId
  )
  {
    uint statusno;
    (statusno, coverId, claimId) = cd.getUserClaimByIndex(index, msg.sender);
    status = statusno;
  }

  /**
   * @dev Gets details of a given claim id.
   * @param _claimId Claim Id.
   * @return status Current status of claim id
   * @return finalVerdict Decision made on the claim, 1 -> acceptance, -1 -> denial
   * @return claimOwner Address through which claim is submitted
   * @return coverId Coverid associated with the claim id
   */
  function getClaimbyIndex(uint _claimId) external view returns (
    uint claimId,
    uint status,
    int8 finalVerdict,
    address claimOwner,
    uint coverId
  )
  {
    uint stat;
    claimId = _claimId;
    (, coverId, finalVerdict, stat,,) = cd.getClaim(_claimId);
    claimOwner = qd.getCoverMemberAddress(coverId);
    status = stat;
  }

  /**
   * @dev Calculates total amount that has been used to assess a claim.
   * Computaion:Adds acceptCA(tokens used for voting in favor of a claim)
   * denyCA(tokens used for voting against a claim) *  current token price.
   * @param claimId Claim Id.
   * @param member Member type 0 -> Claim Assessors, else members.
   * @return tokens Total Amount used in Claims assessment.
   */
  function getCATokens(uint claimId, uint member) external view returns (uint tokens) {
    uint coverId;
    (, coverId) = cd.getClaimCoverId(claimId);

    bytes4 currency = qd.getCurrencyOfCover(coverId);
    address asset = cr.getCurrencyAssetAddress(currency);
    uint tokenx1e18 = p1.getTokenPrice(asset);

    uint accept;
    uint deny;
    if (member == 0) {
      (, accept, deny) = cd.getClaimsTokenCA(claimId);
    } else {
      (, accept, deny) = cd.getClaimsTokenMV(claimId);
    }
    tokens = ((accept.add(deny)).mul(tokenx1e18)).div(DECIMAL1E18); // amount (not in tokens)
  }

  /**
   * Iupgradable Interface to update dependent contract address
   */
  function changeDependentContractAddress() public onlyInternal {
    tk = NXMToken(ms.tokenAddress());
    td = TokenData(ms.getLatestAddress("TD"));
    tf = TokenFunctions(ms.getLatestAddress("TF"));
    tc = TokenController(ms.getLatestAddress("TC"));
    p1 = Pool(ms.getLatestAddress("P1"));
    cr = ClaimsReward(ms.getLatestAddress("CR"));
    cd = ClaimsData(ms.getLatestAddress("CD"));
    qd = QuotationData(ms.getLatestAddress("QD"));
  }

  /**
   * @dev Updates the pending claim start variable,
   * the lowest claim id with a pending decision/payout.
   */
  function changePendingClaimStart() public onlyInternal {

    uint origstat;
    uint state12Count;
    uint pendingClaimStart = cd.pendingClaimStart();
    uint actualClaimLength = cd.actualClaimLength();
    for (uint i = pendingClaimStart; i < actualClaimLength; i++) {
      (, , , origstat, , state12Count) = cd.getClaim(i);

      if (origstat > 5 && ((origstat != 12) || (origstat == 12 && state12Count >= 60))) {
        cd.setpendingClaimStart(i);
      } else {
        break;
      }
    }
  }

  /**
   * @dev Submits a claim for a given cover note.
   * Adds claim to queue incase of emergency pause else directly submits the claim.
   * @param coverId Cover Id.
   */
  function submitClaim(uint coverId) public {
    address qadd = qd.getCoverMemberAddress(coverId);
    require(qadd == msg.sender);
    uint8 cStatus;
    (, cStatus,,,) = qd.getCoverDetailsByCoverID2(coverId);
    require(cStatus != uint8(QuotationData.CoverStatus.ClaimSubmitted), "Claim already submitted");
    require(cStatus != uint8(QuotationData.CoverStatus.CoverExpired), "Cover already expired");
    if (ms.isPause() == false) {
      _addClaim(coverId, now, qadd);
    } else {
      cd.setClaimAtEmergencyPause(coverId, now, false);
      qd.changeCoverStatusNo(coverId, uint8(QuotationData.CoverStatus.Requested));
    }
  }

  /**
   * @dev Submits the Claims queued once the emergency pause is switched off.
   */
  function submitClaimAfterEPOff() public onlyInternal {
    uint lengthOfClaimSubmittedAtEP = cd.getLengthOfClaimSubmittedAtEP();
    uint firstClaimIndexToSubmitAfterEP = cd.getFirstClaimIndexToSubmitAfterEP();
    uint coverId;
    uint dateUpd;
    bool submit;
    address qadd;
    for (uint i = firstClaimIndexToSubmitAfterEP; i < lengthOfClaimSubmittedAtEP; i++) {
      (coverId, dateUpd, submit) = cd.getClaimOfEmergencyPauseByIndex(i);
      require(submit == false);
      qadd = qd.getCoverMemberAddress(coverId);
      _addClaim(coverId, dateUpd, qadd);
      cd.setClaimSubmittedAtEPTrue(i, true);
    }
    cd.setFirstClaimIndexToSubmitAfterEP(lengthOfClaimSubmittedAtEP);
  }

  /**
   * @dev Castes vote for members who have tokens locked under Claims Assessment
   * @param claimId  claim id.
   * @param verdict 1 for Accept,-1 for Deny.
   */
  function submitCAVote(uint claimId, int8 verdict) public isMemberAndcheckPause {
    require(checkVoteClosing(claimId) != 1);
    require(cd.userClaimVotePausedOn(msg.sender).add(cd.pauseDaysCA()) < now);
    uint tokens = tc.tokensLockedAtTime(msg.sender, "CLA", now.add(cd.claimDepositTime()));
    require(tokens > 0);
    uint stat;
    (, stat) = cd.getClaimStatusNumber(claimId);
    require(stat == 0);
    require(cd.getUserClaimVoteCA(msg.sender, claimId) == 0);
    td.bookCATokens(msg.sender);
    cd.addVote(msg.sender, tokens, claimId, verdict);
    cd.callVoteEvent(msg.sender, claimId, "CAV", tokens, now, verdict);
    uint voteLength = cd.getAllVoteLength();
    cd.addClaimVoteCA(claimId, voteLength);
    cd.setUserClaimVoteCA(msg.sender, claimId, voteLength);
    cd.setClaimTokensCA(claimId, verdict, tokens);
    tc.extendLockOf(msg.sender, "CLA", td.lockCADays());
    int close = checkVoteClosing(claimId);
    if (close == 1) {
      cr.changeClaimStatus(claimId);
    }
  }

  /**
   * @dev Submits a member vote for assessing a claim.
   * Tokens other than those locked under Claims
   * Assessment can be used to cast a vote for a given claim id.
   * @param claimId Selected claim id.
   * @param verdict 1 for Accept,-1 for Deny.
   */
  function submitMemberVote(uint claimId, int8 verdict) public isMemberAndcheckPause {
    require(checkVoteClosing(claimId) != 1);
    uint stat;
    uint tokens = tc.totalBalanceOf(msg.sender);
    (, stat) = cd.getClaimStatusNumber(claimId);
    require(stat >= 1 && stat <= 5);
    require(cd.getUserClaimVoteMember(msg.sender, claimId) == 0);
    cd.addVote(msg.sender, tokens, claimId, verdict);
    cd.callVoteEvent(msg.sender, claimId, "MV", tokens, now, verdict);
    tc.lockForMemberVote(msg.sender, td.lockMVDays());
    uint voteLength = cd.getAllVoteLength();
    cd.addClaimVotemember(claimId, voteLength);
    cd.setUserClaimVoteMember(msg.sender, claimId, voteLength);
    cd.setClaimTokensMV(claimId, verdict, tokens);
    int close = checkVoteClosing(claimId);
    if (close == 1) {
      cr.changeClaimStatus(claimId);
    }
  }

  /**
  * @dev Pause Voting of All Pending Claims when Emergency Pause Start.
  */
  function pauseAllPendingClaimsVoting() public onlyInternal {
    uint firstIndex = cd.pendingClaimStart();
    uint actualClaimLength = cd.actualClaimLength();
    for (uint i = firstIndex; i < actualClaimLength; i++) {
      if (checkVoteClosing(i) == 0) {
        uint dateUpd = cd.getClaimDateUpd(i);
        cd.setPendingClaimDetails(i, (dateUpd.add(cd.maxVotingTime())).sub(now), false);
      }
    }
  }

  /**
   * @dev Resume the voting phase of all Claims paused due to an emergency pause.
   */
  function startAllPendingClaimsVoting() public onlyInternal {
    uint firstIndx = cd.getFirstClaimIndexToStartVotingAfterEP();
    uint i;
    uint lengthOfClaimVotingPause = cd.getLengthOfClaimVotingPause();
    for (i = firstIndx; i < lengthOfClaimVotingPause; i++) {
      uint pendingTime;
      uint claimID;
      (claimID, pendingTime,) = cd.getPendingClaimDetailsByIndex(i);
      uint pTime = (now.sub(cd.maxVotingTime())).add(pendingTime);
      cd.setClaimdateUpd(claimID, pTime);
      cd.setPendingClaimVoteStatus(i, true);
      uint coverid;
      (, coverid) = cd.getClaimCoverId(claimID);
      address qadd = qd.getCoverMemberAddress(coverid);
      tf.extendCNEPOff(qadd, coverid, pendingTime.add(cd.claimDepositTime()));
    }
    cd.setFirstClaimIndexToStartVotingAfterEP(i);
  }

  /**
   * @dev Checks if voting of a claim should be closed or not.
   * @param claimId Claim Id.
   * @return close 1 -> voting should be closed, 0 -> if voting should not be closed,
   * -1 -> voting has already been closed.
   */
  function checkVoteClosing(uint claimId) public view returns (int8 close) {
    close = 0;
    uint status;
    (, status) = cd.getClaimStatusNumber(claimId);
    uint dateUpd = cd.getClaimDateUpd(claimId);
    if (status == 12 && dateUpd.add(cd.payoutRetryTime()) < now) {
      if (cd.getClaimState12Count(claimId) < 60)
        close = 1;
    }

    if (status > 5 && status != 12) {
      close = - 1;
    } else if (status != 12 && dateUpd.add(cd.maxVotingTime()) <= now) {
      close = 1;
    } else if (status != 12 && dateUpd.add(cd.minVotingTime()) >= now) {
      close = 0;
    } else if (status == 0 || (status >= 1 && status <= 5)) {
      close = _checkVoteClosingFinal(claimId, status);
    }

  }

  /**
   * @dev Checks if voting of a claim should be closed or not.
   * Internally called by checkVoteClosing method
   * for Claims whose status number is 0 or status number lie between 2 and 6.
   * @param claimId Claim Id.
   * @param status Current status of claim.
   * @return close 1 if voting should be closed,0 in case voting should not be closed,
   * -1 if voting has already been closed.
   */
  function _checkVoteClosingFinal(uint claimId, uint status) internal view returns (int8 close) {
    close = 0;
    uint coverId;
    (, coverId) = cd.getClaimCoverId(claimId);

    bytes4 currency = qd.getCurrencyOfCover(coverId);
    address asset = cr.getCurrencyAssetAddress(currency);
    uint tokenx1e18 = p1.getTokenPrice(asset);

    uint accept;
    uint deny;
    (, accept, deny) = cd.getClaimsTokenCA(claimId);
    uint caTokens = ((accept.add(deny)).mul(tokenx1e18)).div(DECIMAL1E18);
    (, accept, deny) = cd.getClaimsTokenMV(claimId);
    uint mvTokens = ((accept.add(deny)).mul(tokenx1e18)).div(DECIMAL1E18);
    uint sumassured = qd.getCoverSumAssured(coverId).mul(DECIMAL1E18);
    if (status == 0 && caTokens >= sumassured.mul(10)) {
      close = 1;
    } else if (status >= 1 && status <= 5 && mvTokens >= sumassured.mul(10)) {
      close = 1;
    }
  }

  /**
   * @dev Changes the status of an existing claim id, based on current
   * status and current conditions of the system
   * @param claimId Claim Id.
   * @param stat status number.
   */
  function _setClaimStatus(uint claimId, uint stat) internal {

    uint origstat;
    uint state12Count;
    uint dateUpd;
    uint coverId;
    (, coverId, , origstat, dateUpd, state12Count) = cd.getClaim(claimId);
    (, origstat) = cd.getClaimStatusNumber(claimId);

    if (stat == 12 && origstat == 12) {
      cd.updateState12Count(claimId, 1);
    }
    cd.setClaimStatus(claimId, stat);

    if (state12Count >= 60 && stat == 12) {
      cd.setClaimStatus(claimId, 13);
      qd.changeCoverStatusNo(coverId, uint8(QuotationData.CoverStatus.ClaimDenied));
    }

    cd.setClaimdateUpd(claimId, now);
  }

  /**
   * @dev Submits a claim for a given cover note.
   * Set deposits flag against cover.
   */
  function _addClaim(uint coverId, uint time, address add) internal {
    tf.depositCN(coverId);
    uint len = cd.actualClaimLength();
    cd.addClaim(len, coverId, add, now);
    cd.callClaimEvent(coverId, add, len, time);
    qd.changeCoverStatusNo(coverId, uint8(QuotationData.CoverStatus.ClaimSubmitted));
  }
}

File 31 of 38 : ClaimsData.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../abstract/Iupgradable.sol";

contract ClaimsData is Iupgradable {
  using SafeMath for uint;

  struct Claim {
    uint coverId;
    uint dateUpd;
  }

  struct Vote {
    address voter;
    uint tokens;
    uint claimId;
    int8 verdict;
    bool rewardClaimed;
  }

  struct ClaimsPause {
    uint coverid;
    uint dateUpd;
    bool submit;
  }

  struct ClaimPauseVoting {
    uint claimid;
    uint pendingTime;
    bool voting;
  }

  struct RewardDistributed {
    uint lastCAvoteIndex;
    uint lastMVvoteIndex;

  }

  struct ClaimRewardDetails {
    uint percCA;
    uint percMV;
    uint tokenToBeDist;

  }

  struct ClaimTotalTokens {
    uint accept;
    uint deny;
  }

  struct ClaimRewardStatus {
    uint percCA;
    uint percMV;
  }

  ClaimRewardStatus[] internal rewardStatus;

  Claim[] internal allClaims;
  Vote[] internal allvotes;
  ClaimsPause[] internal claimPause;
  ClaimPauseVoting[] internal claimPauseVotingEP;

  mapping(address => RewardDistributed) internal voterVoteRewardReceived;
  mapping(uint => ClaimRewardDetails) internal claimRewardDetail;
  mapping(uint => ClaimTotalTokens) internal claimTokensCA;
  mapping(uint => ClaimTotalTokens) internal claimTokensMV;
  mapping(uint => int8) internal claimVote;
  mapping(uint => uint) internal claimsStatus;
  mapping(uint => uint) internal claimState12Count;
  mapping(uint => uint[]) internal claimVoteCA;
  mapping(uint => uint[]) internal claimVoteMember;
  mapping(address => uint[]) internal voteAddressCA;
  mapping(address => uint[]) internal voteAddressMember;
  mapping(address => uint[]) internal allClaimsByAddress;
  mapping(address => mapping(uint => uint)) internal userClaimVoteCA;
  mapping(address => mapping(uint => uint)) internal userClaimVoteMember;
  mapping(address => uint) public userClaimVotePausedOn;

  uint internal claimPauseLastsubmit;
  uint internal claimStartVotingFirstIndex;
  uint public pendingClaimStart;
  uint public claimDepositTime;
  uint public maxVotingTime;
  uint public minVotingTime;
  uint public payoutRetryTime;
  uint public claimRewardPerc;
  uint public minVoteThreshold;
  uint public maxVoteThreshold;
  uint public majorityConsensus;
  uint public pauseDaysCA;

  event ClaimRaise(
    uint indexed coverId,
    address indexed userAddress,
    uint claimId,
    uint dateSubmit
  );

  event VoteCast(
    address indexed userAddress,
    uint indexed claimId,
    bytes4 indexed typeOf,
    uint tokens,
    uint submitDate,
    int8 verdict
  );

  constructor() public {
    pendingClaimStart = 1;
    maxVotingTime = 48 * 1 hours;
    minVotingTime = 12 * 1 hours;
    payoutRetryTime = 24 * 1 hours;
    allvotes.push(Vote(address(0), 0, 0, 0, false));
    allClaims.push(Claim(0, 0));
    claimDepositTime = 7 days;
    claimRewardPerc = 20;
    minVoteThreshold = 5;
    maxVoteThreshold = 10;
    majorityConsensus = 70;
    pauseDaysCA = 3 days;
    _addRewardIncentive();
  }

  /**
   * @dev Updates the pending claim start variable,
   * the lowest claim id with a pending decision/payout.
   */
  function setpendingClaimStart(uint _start) external onlyInternal {
    require(pendingClaimStart <= _start);
    pendingClaimStart = _start;
  }

  /**
   * @dev Updates the max vote index for which claim assessor has received reward
   * @param _voter address of the voter.
   * @param caIndex last index till which reward was distributed for CA
   */
  function setRewardDistributedIndexCA(address _voter, uint caIndex) external onlyInternal {
    voterVoteRewardReceived[_voter].lastCAvoteIndex = caIndex;

  }

  /**
   * @dev Used to pause claim assessor activity for 3 days
   * @param user Member address whose claim voting ability needs to be paused
   */
  function setUserClaimVotePausedOn(address user) external {
    require(ms.checkIsAuthToGoverned(msg.sender));
    userClaimVotePausedOn[user] = now;
  }

  /**
   * @dev Updates the max vote index for which member has received reward
   * @param _voter address of the voter.
   * @param mvIndex last index till which reward was distributed for member
   */
  function setRewardDistributedIndexMV(address _voter, uint mvIndex) external onlyInternal {

    voterVoteRewardReceived[_voter].lastMVvoteIndex = mvIndex;
  }

  /**
   * @param claimid claim id.
   * @param percCA reward Percentage reward for claim assessor
   * @param percMV reward Percentage reward for members
   * @param tokens total tokens to be rewarded
   */
  function setClaimRewardDetail(
    uint claimid,
    uint percCA,
    uint percMV,
    uint tokens
  )
  external
  onlyInternal
  {
    claimRewardDetail[claimid].percCA = percCA;
    claimRewardDetail[claimid].percMV = percMV;
    claimRewardDetail[claimid].tokenToBeDist = tokens;
  }

  /**
   * @dev Sets the reward claim status against a vote id.
   * @param _voteid vote Id.
   * @param claimed true if reward for vote is claimed, else false.
   */
  function setRewardClaimed(uint _voteid, bool claimed) external onlyInternal {
    allvotes[_voteid].rewardClaimed = claimed;
  }

  /**
   * @dev Sets the final vote's result(either accepted or declined)of a claim.
   * @param _claimId Claim Id.
   * @param _verdict 1 if claim is accepted,-1 if declined.
   */
  function changeFinalVerdict(uint _claimId, int8 _verdict) external onlyInternal {
    claimVote[_claimId] = _verdict;
  }

  /**
   * @dev Creates a new claim.
   */
  function addClaim(
    uint _claimId,
    uint _coverId,
    address _from,
    uint _nowtime
  )
  external
  onlyInternal
  {
    allClaims.push(Claim(_coverId, _nowtime));
    allClaimsByAddress[_from].push(_claimId);
  }

  /**
   * @dev Add Vote's details of a given claim.
   */
  function addVote(
    address _voter,
    uint _tokens,
    uint claimId,
    int8 _verdict
  )
  external
  onlyInternal
  {
    allvotes.push(Vote(_voter, _tokens, claimId, _verdict, false));
  }

  /**
   * @dev Stores the id of the claim assessor vote given to a claim.
   * Maintains record of all votes given by all the CA to a claim.
   * @param _claimId Claim Id to which vote has given by the CA.
   * @param _voteid Vote Id.
   */
  function addClaimVoteCA(uint _claimId, uint _voteid) external onlyInternal {
    claimVoteCA[_claimId].push(_voteid);
  }

  /**
   * @dev Sets the id of the vote.
   * @param _from Claim assessor's address who has given the vote.
   * @param _claimId Claim Id for which vote has been given by the CA.
   * @param _voteid Vote Id which will be stored against the given _from and claimid.
   */
  function setUserClaimVoteCA(
    address _from,
    uint _claimId,
    uint _voteid
  )
  external
  onlyInternal
  {
    userClaimVoteCA[_from][_claimId] = _voteid;
    voteAddressCA[_from].push(_voteid);
  }

  /**
   * @dev Stores the tokens locked by the Claim Assessors during voting of a given claim.
   * @param _claimId Claim Id.
   * @param _vote 1 for accept and increases the tokens of claim as accept,
   * -1 for deny and increases the tokens of claim as deny.
   * @param _tokens Number of tokens.
   */
  function setClaimTokensCA(uint _claimId, int8 _vote, uint _tokens) external onlyInternal {
    if (_vote == 1)
      claimTokensCA[_claimId].accept = claimTokensCA[_claimId].accept.add(_tokens);
    if (_vote == - 1)
      claimTokensCA[_claimId].deny = claimTokensCA[_claimId].deny.add(_tokens);
  }

  /**
   * @dev Stores the tokens locked by the Members during voting of a given claim.
   * @param _claimId Claim Id.
   * @param _vote 1 for accept and increases the tokens of claim as accept,
   * -1 for deny and increases the tokens of claim as deny.
   * @param _tokens Number of tokens.
   */
  function setClaimTokensMV(uint _claimId, int8 _vote, uint _tokens) external onlyInternal {
    if (_vote == 1)
      claimTokensMV[_claimId].accept = claimTokensMV[_claimId].accept.add(_tokens);
    if (_vote == - 1)
      claimTokensMV[_claimId].deny = claimTokensMV[_claimId].deny.add(_tokens);
  }

  /**
   * @dev Stores the id of the member vote given to a claim.
   * Maintains record of all votes given by all the Members to a claim.
   * @param _claimId Claim Id to which vote has been given by the Member.
   * @param _voteid Vote Id.
   */
  function addClaimVotemember(uint _claimId, uint _voteid) external onlyInternal {
    claimVoteMember[_claimId].push(_voteid);
  }

  /**
   * @dev Sets the id of the vote.
   * @param _from Member's address who has given the vote.
   * @param _claimId Claim Id for which vote has been given by the Member.
   * @param _voteid Vote Id which will be stored against the given _from and claimid.
   */
  function setUserClaimVoteMember(
    address _from,
    uint _claimId,
    uint _voteid
  )
  external
  onlyInternal
  {
    userClaimVoteMember[_from][_claimId] = _voteid;
    voteAddressMember[_from].push(_voteid);

  }

  /**
   * @dev Increases the count of failure until payout of a claim is successful.
   */
  function updateState12Count(uint _claimId, uint _cnt) external onlyInternal {
    claimState12Count[_claimId] = claimState12Count[_claimId].add(_cnt);
  }

  /**
   * @dev Sets status of a claim.
   * @param _claimId Claim Id.
   * @param _stat Status number.
   */
  function setClaimStatus(uint _claimId, uint _stat) external onlyInternal {
    claimsStatus[_claimId] = _stat;
  }

  /**
   * @dev Sets the timestamp of a given claim at which the Claim's details has been updated.
   * @param _claimId Claim Id of claim which has been changed.
   * @param _dateUpd timestamp at which claim is updated.
   */
  function setClaimdateUpd(uint _claimId, uint _dateUpd) external onlyInternal {
    allClaims[_claimId].dateUpd = _dateUpd;
  }

  /**
   @dev Queues Claims during Emergency Pause.
   */
  function setClaimAtEmergencyPause(
    uint _coverId,
    uint _dateUpd,
    bool _submit
  )
  external
  onlyInternal
  {
    claimPause.push(ClaimsPause(_coverId, _dateUpd, _submit));
  }

  /**
   * @dev Set submission flag for Claims queued during emergency pause.
   * Set to true after EP is turned off and the claim is submitted .
   */
  function setClaimSubmittedAtEPTrue(uint _index, bool _submit) external onlyInternal {
    claimPause[_index].submit = _submit;
  }

  /**
   * @dev Sets the index from which claim needs to be
   * submitted when emergency pause is swithched off.
   */
  function setFirstClaimIndexToSubmitAfterEP(
    uint _firstClaimIndexToSubmit
  )
  external
  onlyInternal
  {
    claimPauseLastsubmit = _firstClaimIndexToSubmit;
  }

  /**
   * @dev Sets the pending vote duration for a claim in case of emergency pause.
   */
  function setPendingClaimDetails(
    uint _claimId,
    uint _pendingTime,
    bool _voting
  )
  external
  onlyInternal
  {
    claimPauseVotingEP.push(ClaimPauseVoting(_claimId, _pendingTime, _voting));
  }

  /**
   * @dev Sets voting flag true after claim is reopened for voting after emergency pause.
   */
  function setPendingClaimVoteStatus(uint _claimId, bool _vote) external onlyInternal {
    claimPauseVotingEP[_claimId].voting = _vote;
  }

  /**
   * @dev Sets the index from which claim needs to be
   * reopened when emergency pause is swithched off.
   */
  function setFirstClaimIndexToStartVotingAfterEP(
    uint _claimStartVotingFirstIndex
  )
  external
  onlyInternal
  {
    claimStartVotingFirstIndex = _claimStartVotingFirstIndex;
  }

  /**
   * @dev Calls Vote Event.
   */
  function callVoteEvent(
    address _userAddress,
    uint _claimId,
    bytes4 _typeOf,
    uint _tokens,
    uint _submitDate,
    int8 _verdict
  )
  external
  onlyInternal
  {
    emit VoteCast(
      _userAddress,
      _claimId,
      _typeOf,
      _tokens,
      _submitDate,
      _verdict
    );
  }

  /**
   * @dev Calls Claim Event.
   */
  function callClaimEvent(
    uint _coverId,
    address _userAddress,
    uint _claimId,
    uint _datesubmit
  )
  external
  onlyInternal
  {
    emit ClaimRaise(_coverId, _userAddress, _claimId, _datesubmit);
  }

  /**
   * @dev Gets Uint Parameters by parameter code
   * @param code whose details we want
   * @return string value of the parameter
   * @return associated amount (time or perc or value) to the code
   */
  function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {
    codeVal = code;
    if (code == "CAMAXVT") {
      val = maxVotingTime / (1 hours);

    } else if (code == "CAMINVT") {

      val = minVotingTime / (1 hours);

    } else if (code == "CAPRETRY") {

      val = payoutRetryTime / (1 hours);

    } else if (code == "CADEPT") {

      val = claimDepositTime / (1 days);

    } else if (code == "CAREWPER") {

      val = claimRewardPerc;

    } else if (code == "CAMINTH") {

      val = minVoteThreshold;

    } else if (code == "CAMAXTH") {

      val = maxVoteThreshold;

    } else if (code == "CACONPER") {

      val = majorityConsensus;

    } else if (code == "CAPAUSET") {
      val = pauseDaysCA / (1 days);
    }

  }

  /**
   * @dev Get claim queued during emergency pause by index.
   */
  function getClaimOfEmergencyPauseByIndex(
    uint _index
  )
  external
  view
  returns (
    uint coverId,
    uint dateUpd,
    bool submit
  )
  {
    coverId = claimPause[_index].coverid;
    dateUpd = claimPause[_index].dateUpd;
    submit = claimPause[_index].submit;
  }

  /**
   * @dev Gets the Claim's details of given claimid.
   */
  function getAllClaimsByIndex(
    uint _claimId
  )
  external
  view
  returns (
    uint coverId,
    int8 vote,
    uint status,
    uint dateUpd,
    uint state12Count
  )
  {
    return (
    allClaims[_claimId].coverId,
    claimVote[_claimId],
    claimsStatus[_claimId],
    allClaims[_claimId].dateUpd,
    claimState12Count[_claimId]
    );
  }

  /**
   * @dev Gets the vote id of a given claim of a given Claim Assessor.
   */
  function getUserClaimVoteCA(
    address _add,
    uint _claimId
  )
  external
  view
  returns (uint idVote)
  {
    return userClaimVoteCA[_add][_claimId];
  }

  /**
   * @dev Gets the vote id of a given claim of a given member.
   */
  function getUserClaimVoteMember(
    address _add,
    uint _claimId
  )
  external
  view
  returns (uint idVote)
  {
    return userClaimVoteMember[_add][_claimId];
  }

  /**
   * @dev Gets the count of all votes.
   */
  function getAllVoteLength() external view returns (uint voteCount) {
    return allvotes.length.sub(1); // Start Index always from 1.
  }

  /**
   * @dev Gets the status number of a given claim.
   * @param _claimId Claim id.
   * @return statno Status Number.
   */
  function getClaimStatusNumber(uint _claimId) external view returns (uint claimId, uint statno) {
    return (_claimId, claimsStatus[_claimId]);
  }

  /**
   * @dev Gets the reward percentage to be distributed for a given status id
   * @param statusNumber the number of type of status
   * @return percCA reward Percentage for claim assessor
   * @return percMV reward Percentage for members
   */
  function getRewardStatus(uint statusNumber) external view returns (uint percCA, uint percMV) {
    return (rewardStatus[statusNumber].percCA, rewardStatus[statusNumber].percMV);
  }

  /**
   * @dev Gets the number of tries that have been made for a successful payout of a Claim.
   */
  function getClaimState12Count(uint _claimId) external view returns (uint num) {
    num = claimState12Count[_claimId];
  }

  /**
   * @dev Gets the last update date of a claim.
   */
  function getClaimDateUpd(uint _claimId) external view returns (uint dateupd) {
    dateupd = allClaims[_claimId].dateUpd;
  }

  /**
   * @dev Gets all Claims created by a user till date.
   * @param _member user's address.
   * @return claimarr List of Claims id.
   */
  function getAllClaimsByAddress(address _member) external view returns (uint[] memory claimarr) {
    return allClaimsByAddress[_member];
  }

  /**
   * @dev Gets the number of tokens that has been locked
   * while giving vote to a claim by  Claim Assessors.
   * @param _claimId Claim Id.
   * @return accept Total number of tokens when CA accepts the claim.
   * @return deny Total number of tokens when CA declines the claim.
   */
  function getClaimsTokenCA(
    uint _claimId
  )
  external
  view
  returns (
    uint claimId,
    uint accept,
    uint deny
  )
  {
    return (
    _claimId,
    claimTokensCA[_claimId].accept,
    claimTokensCA[_claimId].deny
    );
  }

  /**
   * @dev Gets the number of tokens that have been
   * locked while assessing a claim as a member.
   * @param _claimId Claim Id.
   * @return accept Total number of tokens in acceptance of the claim.
   * @return deny Total number of tokens against the claim.
   */
  function getClaimsTokenMV(
    uint _claimId
  )
  external
  view
  returns (
    uint claimId,
    uint accept,
    uint deny
  )
  {
    return (
    _claimId,
    claimTokensMV[_claimId].accept,
    claimTokensMV[_claimId].deny
    );
  }

  /**
   * @dev Gets the total number of votes cast as Claims assessor for/against a given claim
   */
  function getCaClaimVotesToken(uint _claimId) external view returns (uint claimId, uint cnt) {
    claimId = _claimId;
    cnt = 0;
    for (uint i = 0; i < claimVoteCA[_claimId].length; i++) {
      cnt = cnt.add(allvotes[claimVoteCA[_claimId][i]].tokens);
    }
  }

  /**
   * @dev Gets the total number of tokens cast as a member for/against a given claim
   */
  function getMemberClaimVotesToken(
    uint _claimId
  )
  external
  view
  returns (uint claimId, uint cnt)
  {
    claimId = _claimId;
    cnt = 0;
    for (uint i = 0; i < claimVoteMember[_claimId].length; i++) {
      cnt = cnt.add(allvotes[claimVoteMember[_claimId][i]].tokens);
    }
  }

  /**
   * @dev Provides information of a vote when given its vote id.
   * @param _voteid Vote Id.
   */
  function getVoteDetails(uint _voteid)
  external view
  returns (
    uint tokens,
    uint claimId,
    int8 verdict,
    bool rewardClaimed
  )
  {
    return (
    allvotes[_voteid].tokens,
    allvotes[_voteid].claimId,
    allvotes[_voteid].verdict,
    allvotes[_voteid].rewardClaimed
    );
  }

  /**
   * @dev Gets the voter's address of a given vote id.
   */
  function getVoterVote(uint _voteid) external view returns (address voter) {
    return allvotes[_voteid].voter;
  }

  /**
   * @dev Provides information of a Claim when given its claim id.
   * @param _claimId Claim Id.
   */
  function getClaim(
    uint _claimId
  )
  external
  view
  returns (
    uint claimId,
    uint coverId,
    int8 vote,
    uint status,
    uint dateUpd,
    uint state12Count
  )
  {
    return (
    _claimId,
    allClaims[_claimId].coverId,
    claimVote[_claimId],
    claimsStatus[_claimId],
    allClaims[_claimId].dateUpd,
    claimState12Count[_claimId]
    );
  }

  /**
   * @dev Gets the total number of votes of a given claim.
   * @param _claimId Claim Id.
   * @param _ca if 1: votes given by Claim Assessors to a claim,
   * else returns the number of votes of given by Members to a claim.
   * @return len total number of votes for/against a given claim.
   */
  function getClaimVoteLength(
    uint _claimId,
    uint8 _ca
  )
  external
  view
  returns (uint claimId, uint len)
  {
    claimId = _claimId;
    if (_ca == 1)
      len = claimVoteCA[_claimId].length;
    else
      len = claimVoteMember[_claimId].length;
  }

  /**
   * @dev Gets the verdict of a vote using claim id and index.
   * @param _ca 1 for vote given as a CA, else for vote given as a member.
   * @return ver 1 if vote was given in favour,-1 if given in against.
   */
  function getVoteVerdict(
    uint _claimId,
    uint _index,
    uint8 _ca
  )
  external
  view
  returns (int8 ver)
  {
    if (_ca == 1)
      ver = allvotes[claimVoteCA[_claimId][_index]].verdict;
    else
      ver = allvotes[claimVoteMember[_claimId][_index]].verdict;
  }

  /**
   * @dev Gets the Number of tokens of a vote using claim id and index.
   * @param _ca 1 for vote given as a CA, else for vote given as a member.
   * @return tok Number of tokens.
   */
  function getVoteToken(
    uint _claimId,
    uint _index,
    uint8 _ca
  )
  external
  view
  returns (uint tok)
  {
    if (_ca == 1)
      tok = allvotes[claimVoteCA[_claimId][_index]].tokens;
    else
      tok = allvotes[claimVoteMember[_claimId][_index]].tokens;
  }

  /**
   * @dev Gets the Voter's address of a vote using claim id and index.
   * @param _ca 1 for vote given as a CA, else for vote given as a member.
   * @return voter Voter's address.
   */
  function getVoteVoter(
    uint _claimId,
    uint _index,
    uint8 _ca
  )
  external
  view
  returns (address voter)
  {
    if (_ca == 1)
      voter = allvotes[claimVoteCA[_claimId][_index]].voter;
    else
      voter = allvotes[claimVoteMember[_claimId][_index]].voter;
  }

  /**
   * @dev Gets total number of Claims created by a user till date.
   * @param _add User's address.
   */
  function getUserClaimCount(address _add) external view returns (uint len) {
    len = allClaimsByAddress[_add].length;
  }

  /**
   * @dev Calculates number of Claims that are in pending state.
   */
  function getClaimLength() external view returns (uint len) {
    len = allClaims.length.sub(pendingClaimStart);
  }

  /**
   * @dev Gets the Number of all the Claims created till date.
   */
  function actualClaimLength() external view returns (uint len) {
    len = allClaims.length;
  }

  /**
   * @dev Gets details of a claim.
   * @param _index claim id = pending claim start + given index
   * @param _add User's address.
   * @return coverid cover against which claim has been submitted.
   * @return claimId Claim  Id.
   * @return voteCA verdict of vote given as a Claim Assessor.
   * @return voteMV verdict of vote given as a Member.
   * @return statusnumber Status of claim.
   */
  function getClaimFromNewStart(
    uint _index,
    address _add
  )
  external
  view
  returns (
    uint coverid,
    uint claimId,
    int8 voteCA,
    int8 voteMV,
    uint statusnumber
  )
  {
    uint i = pendingClaimStart.add(_index);
    coverid = allClaims[i].coverId;
    claimId = i;
    if (userClaimVoteCA[_add][i] > 0)
      voteCA = allvotes[userClaimVoteCA[_add][i]].verdict;
    else
      voteCA = 0;

    if (userClaimVoteMember[_add][i] > 0)
      voteMV = allvotes[userClaimVoteMember[_add][i]].verdict;
    else
      voteMV = 0;

    statusnumber = claimsStatus[i];
  }

  /**
   * @dev Gets details of a claim of a user at a given index.
   */
  function getUserClaimByIndex(
    uint _index,
    address _add
  )
  external
  view
  returns (
    uint status,
    uint coverid,
    uint claimId
  )
  {
    claimId = allClaimsByAddress[_add][_index];
    status = claimsStatus[claimId];
    coverid = allClaims[claimId].coverId;
  }

  /**
   * @dev Gets Id of all the votes given to a claim.
   * @param _claimId Claim Id.
   * @return ca id of all the votes given by Claim assessors to a claim.
   * @return mv id of all the votes given by members to a claim.
   */
  function getAllVotesForClaim(
    uint _claimId
  )
  external
  view
  returns (
    uint claimId,
    uint[] memory ca,
    uint[] memory mv
  )
  {
    return (_claimId, claimVoteCA[_claimId], claimVoteMember[_claimId]);
  }

  /**
   * @dev Gets Number of tokens deposit in a vote using
   * Claim assessor's address and claim id.
   * @return tokens Number of deposited tokens.
   */
  function getTokensClaim(
    address _of,
    uint _claimId
  )
  external
  view
  returns (
    uint claimId,
    uint tokens
  )
  {
    return (_claimId, allvotes[userClaimVoteCA[_of][_claimId]].tokens);
  }

  /**
   * @param _voter address of the voter.
   * @return lastCAvoteIndex last index till which reward was distributed for CA
   * @return lastMVvoteIndex last index till which reward was distributed for member
   */
  function getRewardDistributedIndex(
    address _voter
  )
  external
  view
  returns (
    uint lastCAvoteIndex,
    uint lastMVvoteIndex
  )
  {
    return (
    voterVoteRewardReceived[_voter].lastCAvoteIndex,
    voterVoteRewardReceived[_voter].lastMVvoteIndex
    );
  }

  /**
   * @param claimid claim id.
   * @return perc_CA reward Percentage for claim assessor
   * @return perc_MV reward Percentage for members
   * @return tokens total tokens to be rewarded
   */
  function getClaimRewardDetail(
    uint claimid
  )
  external
  view
  returns (
    uint percCA,
    uint percMV,
    uint tokens
  )
  {
    return (
    claimRewardDetail[claimid].percCA,
    claimRewardDetail[claimid].percMV,
    claimRewardDetail[claimid].tokenToBeDist
    );
  }

  /**
   * @dev Gets cover id of a claim.
   */
  function getClaimCoverId(uint _claimId) external view returns (uint claimId, uint coverid) {
    return (_claimId, allClaims[_claimId].coverId);
  }

  /**
   * @dev Gets total number of tokens staked during voting by Claim Assessors.
   * @param _claimId Claim Id.
   * @param _verdict 1 to get total number of accept tokens, -1 to get total number of deny tokens.
   * @return token token Number of tokens(either accept or deny on the basis of verdict given as parameter).
   */
  function getClaimVote(uint _claimId, int8 _verdict) external view returns (uint claimId, uint token) {
    claimId = _claimId;
    token = 0;
    for (uint i = 0; i < claimVoteCA[_claimId].length; i++) {
      if (allvotes[claimVoteCA[_claimId][i]].verdict == _verdict)
        token = token.add(allvotes[claimVoteCA[_claimId][i]].tokens);
    }
  }

  /**
   * @dev Gets total number of tokens staked during voting by Members.
   * @param _claimId Claim Id.
   * @param _verdict 1 to get total number of accept tokens,
   *  -1 to get total number of deny tokens.
   * @return token token Number of tokens(either accept or
   * deny on the basis of verdict given as parameter).
   */
  function getClaimMVote(uint _claimId, int8 _verdict) external view returns (uint claimId, uint token) {
    claimId = _claimId;
    token = 0;
    for (uint i = 0; i < claimVoteMember[_claimId].length; i++) {
      if (allvotes[claimVoteMember[_claimId][i]].verdict == _verdict)
        token = token.add(allvotes[claimVoteMember[_claimId][i]].tokens);
    }
  }

  /**
   * @param _voter address  of voteid
   * @param index index to get voteid in CA
   */
  function getVoteAddressCA(address _voter, uint index) external view returns (uint) {
    return voteAddressCA[_voter][index];
  }

  /**
   * @param _voter address  of voter
   * @param index index to get voteid in member vote
   */
  function getVoteAddressMember(address _voter, uint index) external view returns (uint) {
    return voteAddressMember[_voter][index];
  }

  /**
   * @param _voter address  of voter
   */
  function getVoteAddressCALength(address _voter) external view returns (uint) {
    return voteAddressCA[_voter].length;
  }

  /**
   * @param _voter address  of voter
   */
  function getVoteAddressMemberLength(address _voter) external view returns (uint) {
    return voteAddressMember[_voter].length;
  }

  /**
   * @dev Gets the Final result of voting of a claim.
   * @param _claimId Claim id.
   * @return verdict 1 if claim is accepted, -1 if declined.
   */
  function getFinalVerdict(uint _claimId) external view returns (int8 verdict) {
    return claimVote[_claimId];
  }

  /**
   * @dev Get number of Claims queued for submission during emergency pause.
   */
  function getLengthOfClaimSubmittedAtEP() external view returns (uint len) {
    len = claimPause.length;
  }

  /**
   * @dev Gets the index from which claim needs to be
   * submitted when emergency pause is swithched off.
   */
  function getFirstClaimIndexToSubmitAfterEP() external view returns (uint indexToSubmit) {
    indexToSubmit = claimPauseLastsubmit;
  }

  /**
   * @dev Gets number of Claims to be reopened for voting post emergency pause period.
   */
  function getLengthOfClaimVotingPause() external view returns (uint len) {
    len = claimPauseVotingEP.length;
  }

  /**
   * @dev Gets claim details to be reopened for voting after emergency pause.
   */
  function getPendingClaimDetailsByIndex(
    uint _index
  )
  external
  view
  returns (
    uint claimId,
    uint pendingTime,
    bool voting
  )
  {
    claimId = claimPauseVotingEP[_index].claimid;
    pendingTime = claimPauseVotingEP[_index].pendingTime;
    voting = claimPauseVotingEP[_index].voting;
  }

  /**
   * @dev Gets the index from which claim needs to be reopened when emergency pause is swithched off.
   */
  function getFirstClaimIndexToStartVotingAfterEP() external view returns (uint firstindex) {
    firstindex = claimStartVotingFirstIndex;
  }

  /**
   * @dev Updates Uint Parameters of a code
   * @param code whose details we want to update
   * @param val value to set
   */
  function updateUintParameters(bytes8 code, uint val) public {
    require(ms.checkIsAuthToGoverned(msg.sender));
    if (code == "CAMAXVT") {
      _setMaxVotingTime(val * 1 hours);

    } else if (code == "CAMINVT") {

      _setMinVotingTime(val * 1 hours);

    } else if (code == "CAPRETRY") {

      _setPayoutRetryTime(val * 1 hours);

    } else if (code == "CADEPT") {

      _setClaimDepositTime(val * 1 days);

    } else if (code == "CAREWPER") {

      _setClaimRewardPerc(val);

    } else if (code == "CAMINTH") {

      _setMinVoteThreshold(val);

    } else if (code == "CAMAXTH") {

      _setMaxVoteThreshold(val);

    } else if (code == "CACONPER") {

      _setMajorityConsensus(val);

    } else if (code == "CAPAUSET") {
      _setPauseDaysCA(val * 1 days);
    } else {

      revert("Invalid param code");
    }

  }

  /**
   * @dev Iupgradable Interface to update dependent contract address
   */
  function changeDependentContractAddress() public onlyInternal {}

  /**
   * @dev Adds status under which a claim can lie.
   * @param percCA reward percentage for claim assessor
   * @param percMV reward percentage for members
   */
  function _pushStatus(uint percCA, uint percMV) internal {
    rewardStatus.push(ClaimRewardStatus(percCA, percMV));
  }

  /**
   * @dev adds reward incentive for all possible claim status for Claim assessors and members
   */
  function _addRewardIncentive() internal {
    _pushStatus(0, 0); // 0  Pending-Claim Assessor Vote
    _pushStatus(0, 0); // 1 Pending-Claim Assessor Vote Denied, Pending Member Vote
    _pushStatus(0, 0); // 2 Pending-CA Vote Threshold not Reached Accept, Pending Member Vote
    _pushStatus(0, 0); // 3 Pending-CA Vote Threshold not Reached Deny, Pending Member Vote
    _pushStatus(0, 0); // 4 Pending-CA Consensus not reached Accept, Pending Member Vote
    _pushStatus(0, 0); // 5 Pending-CA Consensus not reached Deny, Pending Member Vote
    _pushStatus(100, 0); // 6 Final-Claim Assessor Vote Denied
    _pushStatus(100, 0); // 7 Final-Claim Assessor Vote Accepted
    _pushStatus(0, 100); // 8 Final-Claim Assessor Vote Denied, MV Accepted
    _pushStatus(0, 100); // 9 Final-Claim Assessor Vote Denied, MV Denied
    _pushStatus(0, 0); // 10 Final-Claim Assessor Vote Accept, MV Nodecision
    _pushStatus(0, 0); // 11 Final-Claim Assessor Vote Denied, MV Nodecision
    _pushStatus(0, 0); // 12 Claim Accepted Payout Pending
    _pushStatus(0, 0); // 13 Claim Accepted No Payout
    _pushStatus(0, 0); // 14 Claim Accepted Payout Done
  }

  /**
   * @dev Sets Maximum time(in seconds) for which claim assessment voting is open
   */
  function _setMaxVotingTime(uint _time) internal {
    maxVotingTime = _time;
  }

  /**
   *  @dev Sets Minimum time(in seconds) for which claim assessment voting is open
   */
  function _setMinVotingTime(uint _time) internal {
    minVotingTime = _time;
  }

  /**
   *  @dev Sets Minimum vote threshold required
   */
  function _setMinVoteThreshold(uint val) internal {
    minVoteThreshold = val;
  }

  /**
   *  @dev Sets Maximum vote threshold required
   */
  function _setMaxVoteThreshold(uint val) internal {
    maxVoteThreshold = val;
  }

  /**
   *  @dev Sets the value considered as Majority Consenus in voting
   */
  function _setMajorityConsensus(uint val) internal {
    majorityConsensus = val;
  }

  /**
   * @dev Sets the payout retry time
   */
  function _setPayoutRetryTime(uint _time) internal {
    payoutRetryTime = _time;
  }

  /**
   *  @dev Sets percentage of reward given for claim assessment
   */
  function _setClaimRewardPerc(uint _val) internal {

    claimRewardPerc = _val;
  }

  /**
   * @dev Sets the time for which claim is deposited.
   */
  function _setClaimDepositTime(uint _time) internal {

    claimDepositTime = _time;
  }

  /**
   *  @dev Sets number of days claim assessment will be paused
   */
  function _setPauseDaysCA(uint val) internal {
    pauseDaysCA = val;
  }
}

File 32 of 38 : IUniswapV2Router02.sol
pragma solidity ^0.5.0;

import './IUniswapV2Router01.sol';

contract IUniswapV2Router02 is IUniswapV2Router01 {

  function removeLiquidityETHSupportingFeeOnTransferTokens(
    address token,
    uint liquidity,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline
  ) external returns (uint amountETH);

  function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
    address token,
    uint liquidity,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline,
    bool approveMax, uint8 v, bytes32 r, bytes32 s
  ) external returns (uint amountETH);

  function swapExactTokensForTokensSupportingFeeOnTransferTokens(
    uint amountIn,
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
  ) external;

  function swapExactETHForTokensSupportingFeeOnTransferTokens(
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
  ) external payable;

  function swapExactTokensForETHSupportingFeeOnTransferTokens(
    uint amountIn,
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
  ) external;
}

File 33 of 38 : TwapOracle.sol
/* Copyright (C) 2020 NexusMutual.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

import "@uniswap/lib/contracts/libraries/FixedPoint.sol";
import "@uniswap/v2-periphery/contracts/libraries/UniswapV2OracleLibrary.sol";

contract TwapOracle {
  using FixedPoint for *;

  struct Bucket {
    uint timestamp;
    uint price0Cumulative;
    uint price1Cumulative;
  }

  event Updated(address indexed pair, uint timestamp, uint price0Cumulative, uint price1Cumulative);

  uint constant public periodSize = 1800;
  uint constant public periodsPerWindow = 8;
  uint constant public windowSize = periodSize * periodsPerWindow;

  address public factory;

  // token pair => Bucket[8]
  mapping(address => Bucket[8]) public buckets;

  constructor (address _factory) public {
    factory = _factory;
  }

  /* utils */

  // https://uniswap.org/docs/v2/smart-contract-integration/getting-pair-addresses/
  function _pairFor(address _factory, address tokenA, address tokenB) internal pure returns (address pair) {

    // sort tokens
    (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);

    require(token0 != token1, "TWAP: identical addresses");
    require(token0 != address(0), "TWAP: zero address");

    pair = address(uint(keccak256(abi.encodePacked(
        hex'ff',
        _factory,
        keccak256(abi.encodePacked(token0, token1)),
        hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f'
      ))));
  }

  function timestampToIndex(uint timestamp) internal pure returns (uint index) {
    uint epochPeriod = timestamp / periodSize;
    return epochPeriod % periodsPerWindow;
  }

  function pairFor(address tokenA, address tokenB) external view returns (address pair) {
    return _pairFor(factory, tokenA, tokenB);
  }

  function currentBucketIndex() external view returns (uint index) {
    return timestampToIndex(block.timestamp);
  }

  /* update */

  function update(address[] calldata pairs) external {

    for (uint i = 0; i < pairs.length; i++) {

      // note: not reusing canUpdate() because we need the bucket variable
      address pair = pairs[i];
      uint index = timestampToIndex(block.timestamp);
      Bucket storage bucket = buckets[pair][index];

      if (block.timestamp - bucket.timestamp < periodSize) {
        continue;
      }

      (uint price0Cumulative, uint price1Cumulative,) = UniswapV2OracleLibrary.currentCumulativePrices(pair);
      bucket.timestamp = block.timestamp;
      bucket.price0Cumulative = price0Cumulative;
      bucket.price1Cumulative = price1Cumulative;

      emit Updated(pair, block.timestamp, price0Cumulative, price1Cumulative);
    }
  }

  function canUpdate(address pair) external view returns (bool) {

    uint index = timestampToIndex(block.timestamp);
    Bucket storage bucket = buckets[pair][index];
    uint timeElapsed = block.timestamp - bucket.timestamp;

    return timeElapsed > periodSize;
  }

  /* consult */

  function _getCumulativePrices(
    address tokenIn,
    address tokenOut
  ) internal view returns (uint priceCumulativeStart, uint priceCumulativeEnd, uint timeElapsed) {

    uint currentIndex = timestampToIndex(block.timestamp);
    uint firstBucketIndex = (currentIndex + 1) % periodsPerWindow;

    address pair = _pairFor(factory, tokenIn, tokenOut);
    Bucket storage firstBucket = buckets[pair][firstBucketIndex];

    timeElapsed = block.timestamp - firstBucket.timestamp;
    require(timeElapsed <= windowSize, "TWAP: missing historical reading");
    require(timeElapsed >= windowSize - periodSize * 2, "TWAP: unexpected time elapsed");

    (uint price0Cumulative, uint price1Cumulative,) = UniswapV2OracleLibrary.currentCumulativePrices(pair);

    if (tokenIn < tokenOut) {
      return (firstBucket.price0Cumulative, price0Cumulative, timeElapsed);
    }

    return (firstBucket.price1Cumulative, price1Cumulative, timeElapsed);
  }

  function _computeAmountOut(
    uint priceCumulativeStart,
    uint priceCumulativeEnd,
    uint timeElapsed,
    uint amountIn
  ) internal pure returns (uint amountOut) {

    // overflow is desired.
    FixedPoint.uq112x112 memory priceAverage = FixedPoint.uq112x112(
      uint224((priceCumulativeEnd - priceCumulativeStart) / timeElapsed)
    );

    return priceAverage.mul(amountIn).decode144();
  }

  /**
   *  @dev Returns the amount out corresponding to the amount in for a given token using the
   *  @dev   moving average over the time range [now - [windowSize, windowSize - periodSize * 2], now]
   *  @dev   update must have been called for the bucket corresponding to timestamp `now - windowSize`
   */
  function consult(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut) {

    uint pastPriceCumulative;
    uint currentPriceCumulative;
    uint timeElapsed;

    (pastPriceCumulative, currentPriceCumulative, timeElapsed) = _getCumulativePrices(tokenIn, tokenOut);

    return _computeAmountOut(
      pastPriceCumulative,
      currentPriceCumulative,
      timeElapsed,
      amountIn
    );
  }

}

File 34 of 38 : IUniswapV2Router01.sol
pragma solidity ^0.5.0;

interface IUniswapV2Router01 {

  function factory() external pure returns (address);

  function WETH() external pure returns (address);

  function addLiquidity(
    address tokenA,
    address tokenB,
    uint amountADesired,
    uint amountBDesired,
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline
  ) external returns (uint amountA, uint amountB, uint liquidity);

  function addLiquidityETH(
    address token,
    uint amountTokenDesired,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline
  ) external payable returns (uint amountToken, uint amountETH, uint liquidity);

  function removeLiquidity(
    address tokenA,
    address tokenB,
    uint liquidity,
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline
  ) external returns (uint amountA, uint amountB);

  function removeLiquidityETH(
    address token,
    uint liquidity,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline
  ) external returns (uint amountToken, uint amountETH);

  function removeLiquidityWithPermit(
    address tokenA,
    address tokenB,
    uint liquidity,
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline,
    bool approveMax, uint8 v, bytes32 r, bytes32 s
  ) external returns (uint amountA, uint amountB);

  function removeLiquidityETHWithPermit(
    address token,
    uint liquidity,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline,
    bool approveMax, uint8 v, bytes32 r, bytes32 s
  ) external returns (uint amountToken, uint amountETH);

  function swapExactTokensForTokens(
    uint amountIn,
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
  ) external returns (uint[] memory amounts);

  function swapTokensForExactTokens(
    uint amountOut,
    uint amountInMax,
    address[] calldata path,
    address to,
    uint deadline
  ) external returns (uint[] memory amounts);

  function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
  external
  payable
  returns (uint[] memory amounts);

  function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
  external
  returns (uint[] memory amounts);

  function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
  external
  returns (uint[] memory amounts);

  function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
  external
  payable
  returns (uint[] memory amounts);

  function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);

  function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);

  function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);

  function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);

  function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

File 35 of 38 : FixedPoint.sol
pragma solidity >=0.4.0;

// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
library FixedPoint {
    // range: [0, 2**112 - 1]
    // resolution: 1 / 2**112
    struct uq112x112 {
        uint224 _x;
    }

    // range: [0, 2**144 - 1]
    // resolution: 1 / 2**112
    struct uq144x112 {
        uint _x;
    }

    uint8 private constant RESOLUTION = 112;

    // encode a uint112 as a UQ112x112
    function encode(uint112 x) internal pure returns (uq112x112 memory) {
        return uq112x112(uint224(x) << RESOLUTION);
    }

    // encodes a uint144 as a UQ144x112
    function encode144(uint144 x) internal pure returns (uq144x112 memory) {
        return uq144x112(uint256(x) << RESOLUTION);
    }

    // divide a UQ112x112 by a uint112, returning a UQ112x112
    function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) {
        require(x != 0, 'FixedPoint: DIV_BY_ZERO');
        return uq112x112(self._x / uint224(x));
    }

    // multiply a UQ112x112 by a uint, returning a UQ144x112
    // reverts on overflow
    function mul(uq112x112 memory self, uint y) internal pure returns (uq144x112 memory) {
        uint z;
        require(y == 0 || (z = uint(self._x) * y) / y == uint(self._x), "FixedPoint: MULTIPLICATION_OVERFLOW");
        return uq144x112(z);
    }

    // returns a UQ112x112 which represents the ratio of the numerator to the denominator
    // equivalent to encode(numerator).div(denominator)
    function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) {
        require(denominator > 0, "FixedPoint: DIV_BY_ZERO");
        return uq112x112((uint224(numerator) << RESOLUTION) / denominator);
    }

    // decode a UQ112x112 into a uint112 by truncating after the radix point
    function decode(uq112x112 memory self) internal pure returns (uint112) {
        return uint112(self._x >> RESOLUTION);
    }

    // decode a UQ144x112 into a uint144 by truncating after the radix point
    function decode144(uq144x112 memory self) internal pure returns (uint144) {
        return uint144(self._x >> RESOLUTION);
    }
}

File 36 of 38 : UniswapV2OracleLibrary.sol
pragma solidity >=0.5.0;

import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import '@uniswap/lib/contracts/libraries/FixedPoint.sol';

// library with helper methods for oracles that are concerned with computing average prices
library UniswapV2OracleLibrary {
    using FixedPoint for *;

    // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1]
    function currentBlockTimestamp() internal view returns (uint32) {
        return uint32(block.timestamp % 2 ** 32);
    }

    // produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
    function currentCumulativePrices(
        address pair
    ) internal view returns (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) {
        blockTimestamp = currentBlockTimestamp();
        price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
        price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();

        // if time has elapsed since the last update on the pair, mock the accumulated price values
        (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves();
        if (blockTimestampLast != blockTimestamp) {
            // subtraction overflow is desired
            uint32 timeElapsed = blockTimestamp - blockTimestampLast;
            // addition overflow is desired
            // counterfactual
            price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed;
            // counterfactual
            price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed;
        }
    }
}

File 37 of 38 : IGovernance.sol
/* Copyright (C) 2017 GovBlocks.io

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

contract IGovernance {

  event Proposal(
    address indexed proposalOwner,
    uint256 indexed proposalId,
    uint256 dateAdd,
    string proposalTitle,
    string proposalSD,
    string proposalDescHash
  );

  event Solution(
    uint256 indexed proposalId,
    address indexed solutionOwner,
    uint256 indexed solutionId,
    string solutionDescHash,
    uint256 dateAdd
  );

  event Vote(
    address indexed from,
    uint256 indexed proposalId,
    uint256 indexed voteId,
    uint256 dateAdd,
    uint256 solutionChosen
  );

  event RewardClaimed(
    address indexed member,
    uint gbtReward
  );

  /// @dev VoteCast event is called whenever a vote is cast that can potentially close the proposal.
  event VoteCast (uint256 proposalId);

  /// @dev ProposalAccepted event is called when a proposal is accepted so that a server can listen that can
  ///      call any offchain actions
  event ProposalAccepted (uint256 proposalId);

  /// @dev CloseProposalOnTime event is called whenever a proposal is created or updated to close it on time.
  event CloseProposalOnTime (
    uint256 indexed proposalId,
    uint256 time
  );

  /// @dev ActionSuccess event is called whenever an onchain action is executed.
  event ActionSuccess (
    uint256 proposalId
  );

  /// @dev Creates a new proposal
  /// @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal
  /// @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective
  function createProposal(
    string calldata _proposalTitle,
    string calldata _proposalSD,
    string calldata _proposalDescHash,
    uint _categoryId
  )
  external;

  /// @dev Edits the details of an existing proposal and creates new version
  /// @param _proposalId Proposal id that details needs to be updated
  /// @param _proposalDescHash Proposal description hash having long and short description of proposal.
  function updateProposal(
    uint _proposalId,
    string calldata _proposalTitle,
    string calldata _proposalSD,
    string calldata _proposalDescHash
  )
  external;

  /// @dev Categorizes proposal to proceed further. Categories shows the proposal objective.
  function categorizeProposal(
    uint _proposalId,
    uint _categoryId,
    uint _incentives
  )
  external;

  /// @dev Submit proposal with solution
  /// @param _proposalId Proposal id
  /// @param _solutionHash Solution hash contains  parameters, values and description needed according to proposal
  function submitProposalWithSolution(
    uint _proposalId,
    string calldata _solutionHash,
    bytes calldata _action
  )
  external;

  /// @dev Creates a new proposal with solution and votes for the solution
  /// @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal
  /// @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective
  /// @param _solutionHash Solution hash contains  parameters, values and description needed according to proposal
  function createProposalwithSolution(
    string calldata _proposalTitle,
    string calldata _proposalSD,
    string calldata _proposalDescHash,
    uint _categoryId,
    string calldata _solutionHash,
    bytes calldata _action
  )
  external;

  /// @dev Casts vote
  /// @param _proposalId Proposal id
  /// @param _solutionChosen solution chosen while voting. _solutionChosen[0] is the chosen solution
  function submitVote(uint _proposalId, uint _solutionChosen) external;

  function closeProposal(uint _proposalId) external;

  function claimReward(address _memberAddress, uint _maxRecords) external returns (uint pendingDAppReward);

  function proposal(uint _proposalId)
  external
  view
  returns (
    uint proposalId,
    uint category,
    uint status,
    uint finalVerdict,
    uint totalReward
  );

  function canCloseProposal(uint _proposalId) public view returns (uint closeValue);

  function allowedToCatgorize() public view returns (uint roleId);

}

File 38 of 38 : IProposalCategory.sol
/* Copyright (C) 2017 GovBlocks.io
  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
  This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/ */

pragma solidity ^0.5.0;

contract IProposalCategory {

  event Category(
    uint indexed categoryId,
    string categoryName,
    string actionHash
  );

  /// @dev Adds new category
  /// @param _name Category name
  /// @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
  /// @param _allowedToCreateProposal Member roles allowed to create the proposal
  /// @param _majorityVotePerc Majority Vote threshold for Each voting layer
  /// @param _quorumPerc minimum threshold percentage required in voting to calculate result
  /// @param _closingTime Vote closing time for Each voting layer
  /// @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
  /// @param _contractAddress address of contract to call after proposal is accepted
  /// @param _contractName name of contract to be called after proposal is accepted
  /// @param _incentives rewards to distributed after proposal is accepted
  function addCategory(
    string calldata _name,
    uint _memberRoleToVote,
    uint _majorityVotePerc,
    uint _quorumPerc,
    uint[] calldata _allowedToCreateProposal,
    uint _closingTime,
    string calldata _actionHash,
    address _contractAddress,
    bytes2 _contractName,
    uint[] calldata _incentives
  )
  external;

  /// @dev gets category details
  function category(uint _categoryId)
  external
  view
  returns (
    uint categoryId,
    uint memberRoleToVote,
    uint majorityVotePerc,
    uint quorumPerc,
    uint[] memory allowedToCreateProposal,
    uint closingTime,
    uint minStake
  );

  ///@dev gets category action details
  function categoryAction(uint _categoryId)
  external
  view
  returns (
    uint categoryId,
    address contractAddress,
    bytes2 contractName,
    uint defaultIncentive
  );

  /// @dev Gets Total number of categories added till now
  function totalCategories() external view returns (uint numberOfCategories);

  /// @dev Updates category details
  /// @param _categoryId Category id that needs to be updated
  /// @param _name Category name
  /// @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
  /// @param _allowedToCreateProposal Member roles allowed to create the proposal
  /// @param _majorityVotePerc Majority Vote threshold for Each voting layer
  /// @param _quorumPerc minimum threshold percentage required in voting to calculate result
  /// @param _closingTime Vote closing time for Each voting layer
  /// @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
  /// @param _contractAddress address of contract to call after proposal is accepted
  /// @param _contractName name of contract to be called after proposal is accepted
  /// @param _incentives rewards to distributed after proposal is accepted
  function updateCategory(
    uint _categoryId,
    string memory _name,
    uint _memberRoleToVote,
    uint _majorityVotePerc,
    uint _quorumPerc,
    uint[] memory _allowedToCreateProposal,
    uint _closingTime,
    string memory _actionHash,
    address _contractAddress,
    bytes2 _contractName,
    uint[] memory _incentives
  )
  public;

}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/modules/capital/SwapAgent.sol": {
      "SwapAgent": "0xcafea6a946406b0b48a77348a4f70dfeed0f4664"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address[]","name":"_assets","type":"address[]"},{"internalType":"uint112[]","name":"_minAmounts","type":"uint112[]"},{"internalType":"uint112[]","name":"_maxAmounts","type":"uint112[]"},{"internalType":"uint256[]","name":"_maxSlippageRatios","type":"uint256[]"},{"internalType":"address","name":"_master","type":"address"},{"internalType":"address","name":"_priceOracle","type":"address"},{"internalType":"address","name":"_twapOracle","type":"address"},{"internalType":"address","name":"_swapController","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":false,"internalType":"uint256","name":"ethIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nxmOut","type":"uint256"}],"name":"NXMBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":false,"internalType":"uint256","name":"nxmIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethOut","type":"uint256"}],"name":"NXMSold","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Payout","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fromAsset","type":"address"},{"indexed":true,"internalType":"address","name":"toAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"Swapped","type":"event"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"ETH","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MAX_BUY_SELL_MCR_ETH_FRACTION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MAX_MCR_RATIO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MCR_RATIO_DECIMALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"uint112","name":"_min","type":"uint112"},{"internalType":"uint112","name":"_max","type":"uint112"},{"internalType":"uint256","name":"_maxSlippageRatio","type":"uint256"}],"name":"addAsset","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"assetData","outputs":[{"internalType":"uint112","name":"minAmount","type":"uint112"},{"internalType":"uint112","name":"maxAmount","type":"uint112"},{"internalType":"uint32","name":"lastSwapTime","type":"uint32"},{"internalType":"uint256","name":"maxSlippageRatio","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"assets","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"minTokensOut","type":"uint256"}],"name":"buyNXM","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"nxmAmount","type":"uint256"},{"internalType":"uint256","name":"currentTotalAssetValue","type":"uint256"},{"internalType":"uint256","name":"mcrEth","type":"uint256"}],"name":"calculateEthForNXM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"totalAssetValue","type":"uint256"},{"internalType":"uint256","name":"mcrEth","type":"uint256"}],"name":"calculateMCRRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"currentTotalAssetValue","type":"uint256"},{"internalType":"uint256","name":"mcrEth","type":"uint256"}],"name":"calculateNXMForEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"totalAssetValue","type":"uint256"},{"internalType":"uint256","name":"mcrEth","type":"uint256"}],"name":"calculateTokenSpotPrice","outputs":[{"internalType":"uint256","name":"tokenPrice","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[],"name":"changeDependentContractAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"masterAddress","type":"address"}],"name":"changeMasterAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"getAssetDetails","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint112","name":"min","type":"uint112"},{"internalType":"uint112","name":"max","type":"uint112"},{"internalType":"uint32","name":"lastAssetSwapTime","type":"uint32"},{"internalType":"uint256","name":"maxSlippageRatio","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getAssets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"nxmAmount","type":"uint256"}],"name":"getEthForNXM","outputs":[{"internalType":"uint256","name":"ethAmount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getMCRRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"ethAmount","type":"uint256"}],"name":"getNXMForEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPoolValueInEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"tokenAmountIn","type":"uint256"},{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"}],"name":"getSwapQuote","outputs":[{"internalType":"uint256","name":"tokenAmountOut","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getTokenPrice","outputs":[{"internalType":"uint256","name":"tokenPrice","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getWei","outputs":[{"internalType":"uint256","name":"weiToPay","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"smartCAdd","type":"address"},{"internalType":"bytes4","name":"coverCurr","type":"bytes4"},{"internalType":"uint256[]","name":"coverDetails","type":"uint256[]"},{"internalType":"uint16","name":"coverPeriod","type":"uint16"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"makeCoverBegin","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"smartCAdd","type":"address"},{"internalType":"bytes4","name":"coverCurr","type":"bytes4"},{"internalType":"uint256[]","name":"coverDetails","type":"uint256[]"},{"internalType":"uint16","name":"coverPeriod","type":"uint16"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"makeCoverUsingCA","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"master","outputs":[{"internalType":"contract INXMMaster","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"mcr","outputs":[{"internalType":"contract MCR","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"minPoolEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nxmToken","outputs":[{"internalType":"contract NXMToken","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"priceFeedOracle","outputs":[{"internalType":"contract PriceFeedOracle","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"quotation","outputs":[{"internalType":"contract Quotation","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"removeAsset","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"minEthOut","type":"uint256"}],"name":"sellNXM","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"sellNXMTokens","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address payable","name":"payoutAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sendClaimPayout","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"sendEther","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"uint112","name":"_min","type":"uint112"},{"internalType":"uint112","name":"_max","type":"uint112"},{"internalType":"uint256","name":"_maxSlippageRatio","type":"uint256"}],"name":"setAssetDetails","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"fromTokenAddress","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"swapAssetForETH","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"swapController","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"toTokenAddress","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"swapETHForAsset","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokenController","outputs":[{"internalType":"contract TokenController","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address payable","name":"destination","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferAsset","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferAssetFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"twapOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes8","name":"code","type":"bytes8"},{"internalType":"address","name":"value","type":"address"}],"name":"updateAddressParameters","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes8","name":"code","type":"bytes8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"updateUintParameters","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"newPoolAddress","type":"address"}],"name":"upgradeCapitalPool","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b50604051620050d9380380620050d983398181016040526101008110156200003857600080fd5b81019080805160405193929190846401000000008211156200005957600080fd5b9083019060208201858111156200006f57600080fd5b82518660208202830111640100000000821117156200008d57600080fd5b82525081516020918201928201910280838360005b83811015620000bc578181015183820152602001620000a2565b5050505090500160405260200180516040519392919084640100000000821115620000e657600080fd5b908301906020820185811115620000fc57600080fd5b82518660208202830111640100000000821117156200011a57600080fd5b82525081516020918201928201910280838360005b83811015620001495781810151838201526020016200012f565b50505050905001604052602001805160405193929190846401000000008211156200017357600080fd5b9083019060208201858111156200018957600080fd5b8251866020820283011164010000000082111715620001a757600080fd5b82525081516020918201928201910280838360005b83811015620001d6578181015183820152602001620001bc565b50505050905001604052602001805160405193929190846401000000008211156200020057600080fd5b9083019060208201858111156200021657600080fd5b82518660208202830111640100000000821117156200023457600080fd5b82525081516020918201928201910280838360005b838110156200026357818101518382015260200162000249565b5050505091909101604090815260208301519083015160608401516080909401516000805460ff60a01b1916600160a01b17905589518b51939750919550925014620002e5576040805162461bcd60e51b81526020600482015260156024820152600080516020620050b9833981519152604482015290519081900360640190fd5b85518851146200032b576040805162461bcd60e51b81526020600482015260156024820152600080516020620050b9833981519152604482015290519081900360640190fd5b845188511462000371576040805162461bcd60e51b81526020600482015260156024820152600080516020620050b9833981519152604482015290519081900360640190fd5b60005b8851811015620006265760008982815181106200038d57fe5b6020026020010151905060006001600160a01b0316816001600160a01b0316141562000400576040805162461bcd60e51b815260206004820152601b60248201527f506f6f6c3a206173736574206973207a65726f20616464726573730000000000604482015290519081900360640190fd5b8882815181106200040d57fe5b60200260200101516001600160701b03168883815181106200042b57fe5b60200260200101516001600160701b0316101562000482576040805162461bcd60e51b815260206004820152600f60248201526e2837b7b61d1036b0bc101e1036b4b760891b604482015290519081900360640190fd5b670de0b6b3a76400008783815181106200049857fe5b60200260200101511115620004e6576040805162461bcd60e51b815260206004820152600f60248201526e2837b7b61d1036b0bc101e1036b4b760891b604482015290519081900360640190fd5b6001805480820182556000919091527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b0319166001600160a01b03831617905588518990839081106200053f57fe5b6020908102919091018101516001600160a01b03831660009081526002909252604090912080546001600160701b0319166001600160701b0390921691909117905587518890839081106200059057fe5b602002602001015160026000836001600160a01b03166001600160a01b03168152602001908152602001600020600001600e6101000a8154816001600160701b0302191690836001600160701b03160217905550868281518110620005f157fe5b6020908102919091018101516001600160a01b0390921660009081526002909152604090206001908101919091550162000374565b50600080546001600160a01b039586166001600160a01b031991821617909155600a80549486169482169490941790935560078054928516928416929092179091556008805491909316911617905550505050614a3080620006896000396000f3fe6080604052600436106102885760003560e01c806374bd0ace1161015a578063b8caedc4116100c1578063ddf403aa1161007a578063ddf403aa14610bc7578063e942c56414610bdc578063e967eb1a14610c06578063eddd9d8214610c1b578063ee97f7f314610c30578063ffa6de3a14610c4557610288565b8063b8caedc414610aca578063b9ab992714610adf578063cf35bdd014610af4578063d02641a014610b1e578063d311afb414610b51578063d46655f414610b9457610288565b80639c9b6451116101135780639c9b6451146109245780639dd86e0f14610994578063a52f930f146109ce578063aafa936d14610a20578063ad4cc43e14610a72578063b222a4d314610ab557610288565b806374bd0ace1461077557806375466b9b146107b35780638076083d146107f65780638322fff2146108dd5780639043292a146108f2578063963bbc601461090757610288565b806343400ed7116101fe57806365a21992116101b757806365a219921461058857806366fd551d146105be57806367e4ac2c146105f157806368da80af1461065657806369f36c321461073057806370310cb71461074557610288565b806343400ed714610455578063439e2e451461048b578063441da1ba146104ce5780634779be08146104ff57806349386d87146105145780634a5e42b11461055557610288565b80631f50c989116102505780631f50c9891461033d5780632892b29c1461035257806328dd37731461037c5780632f04d798146103ac5780633f494325146103c157806341fee44a146103eb57610288565b806304e8266f1461028a5780630bb76fff146102cc5780630e29df22146102e15780630ea9c984146102e957806311267784146102fe575b005b34801561029657600080fd5b506102ba600480360360408110156102ad57600080fd5b5080359060200135610c84565b60408051918252519081900360200190f35b3480156102d857600080fd5b506102ba610cf5565b610288610cfb565b3480156102f557600080fd5b50610288610cfd565b34801561030a57600080fd5b506102886004803603606081101561032157600080fd5b506001600160a01b038135169060208101359060400135610f44565b34801561034957600080fd5b506102ba6111df565b34801561035e57600080fd5b506102ba6004803603602081101561037557600080fd5b50356111e5565b34801561038857600080fd5b506102886004803603604081101561039f57600080fd5b5080359060200135611283565b3480156103b857600080fd5b506102ba611887565b3480156103cd57600080fd5b506102ba600480360360208110156103e457600080fd5b5035611a38565b3480156103f757600080fd5b5061041e6004803603602081101561040e57600080fd5b50356001600160a01b0316611ace565b604080516001600160701b03958616815293909416602084015263ffffffff90911682840152606082015290519081900360800190f35b34801561046157600080fd5b506102ba6004803603606081101561047857600080fd5b5080359060208101359060400135611b0d565b34801561049757600080fd5b50610288600480360360608110156104ae57600080fd5b506001600160a01b03813581169160208101359091169060400135611c26565b3480156104da57600080fd5b506104e3611eac565b604080516001600160a01b039092168252519081900360200190f35b34801561050b57600080fd5b506102ba611ebb565b34801561052057600080fd5b506102ba6004803603606081101561053757600080fd5b508035906001600160a01b0360208201358116916040013516611ec0565b34801561056157600080fd5b506102886004803603602081101561057857600080fd5b50356001600160a01b0316611f61565b34801561059457600080fd5b506102ba600480360360608110156105ab57600080fd5b508035906020810135906040013561215c565b3480156105ca57600080fd5b50610288600480360360208110156105e157600080fd5b50356001600160a01b03166122a0565b3480156105fd57600080fd5b506106066124ce565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561064257818101518382015260200161062a565b505050509050019250505060405180910390f35b610288600480360360e081101561066c57600080fd5b6001600160a01b03823516916001600160e01b0319602082013516918101906060810160408201356401000000008111156106a657600080fd5b8201836020820111156106b857600080fd5b803590602001918460208302840111640100000000831117156106da57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505061ffff83351693505060ff602083013516916040810135915060600135612530565b34801561073c57600080fd5b506102ba612864565b34801561075157600080fd5b506102ba6004803603604081101561076857600080fd5b5080359060200135612900565b34801561078157600080fd5b5061079f6004803603602081101561079857600080fd5b5035612918565b604080519115158252519081900360200190f35b3480156107bf57600080fd5b5061079f600480360360608110156107d657600080fd5b506001600160a01b03813581169160208101359091169060400135612aab565b34801561080257600080fd5b50610288600480360360e081101561081957600080fd5b6001600160a01b03823516916001600160e01b03196020820135169181019060608101604082013564010000000081111561085357600080fd5b82018360208201111561086557600080fd5b8035906020019184602083028401116401000000008311171561088757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505061ffff83351693505060ff602083013516916040810135915060600135612cb1565b3480156108e957600080fd5b506104e3612e95565b3480156108fe57600080fd5b506104e3612ead565b6102886004803603602081101561091d57600080fd5b5035612ebc565b34801561093057600080fd5b506109576004803603602081101561094757600080fd5b50356001600160a01b0316613267565b604080519586526001600160701b039485166020870152929093168483015263ffffffff1660608401526080830191909152519081900360a00190f35b3480156109a057600080fd5b50610288600480360360408110156109b757600080fd5b506001600160c01b03198135169060200135613377565b3480156109da57600080fd5b50610288600480360360808110156109f157600080fd5b506001600160a01b03813516906001600160701b036020820135811691604081013590911690606001356134a0565b348015610a2c57600080fd5b5061028860048036036080811015610a4357600080fd5b506001600160a01b03813516906001600160701b036020820135811691604081013590911690606001356137c7565b348015610a7e57600080fd5b5061028860048036036040811015610a9557600080fd5b5080356001600160c01b03191690602001356001600160a01b03166139d4565b348015610ac157600080fd5b506102ba613b35565b348015610ad657600080fd5b506104e3613b3b565b348015610aeb57600080fd5b506104e3613b4a565b348015610b0057600080fd5b506104e360048036036020811015610b1757600080fd5b5035613b59565b348015610b2a57600080fd5b506102ba60048036036020811015610b4157600080fd5b50356001600160a01b0316613b80565b348015610b5d57600080fd5b5061028860048036036060811015610b7457600080fd5b506001600160a01b03813581169160208101359091169060400135613ca2565b348015610ba057600080fd5b5061028860048036036020811015610bb757600080fd5b50356001600160a01b0316613e2e565b348015610bd357600080fd5b506104e3613ead565b348015610be857600080fd5b506102ba60048036036020811015610bff57600080fd5b5035613ebc565b348015610c1257600080fd5b506104e3613ec7565b348015610c2757600080fd5b506104e3613ed6565b348015610c3c57600080fd5b506104e3613ee5565b348015610c5157600080fd5b5061028860048036036060811015610c6857600080fd5b506001600160a01b038135169060208101359060400135613ef4565b600080610c918484612900565b90506000610ca660048063ffffffff61419d16565b600a0a9050610cea6624859b044a8000610cde83610cd262588040818a60048a0a63ffffffff61419d16565b9063ffffffff6141f616565b9063ffffffff61423816565b925050505b92915050565b60095481565b565b6000809054906101000a90046001600160a01b03166001600160a01b0316639d76ea586040518163ffffffff1660e01b815260040160206040518083038186803b158015610d4a57600080fd5b505afa158015610d5e573d6000803e3d6000fd5b505050506040513d6020811015610d7457600080fd5b5051600480546001600160a01b0319166001600160a01b03928316178155600054604080516227050b60e31b815261544360f01b9381019390935251921691630138285891602480820192602092909190829003018186803b158015610dd957600080fd5b505afa158015610ded573d6000803e3d6000fd5b505050506040513d6020811015610e0357600080fd5b5051600580546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261145560f21b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610e6857600080fd5b505afa158015610e7c573d6000803e3d6000fd5b505050506040513d6020811015610e9257600080fd5b5051600380546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b8152614d4360f01b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610ef757600080fd5b505afa158015610f0b573d6000803e3d6000fd5b505050506040513d6020811015610f2157600080fd5b5051600680546001600160a01b0319166001600160a01b03909216919091179055565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015610f9157600080fd5b505afa158015610fa5573d6000803e3d6000fd5b505050506040513d6020811015610fbb57600080fd5b505115611002576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b6008546001600160a01b0316331461105c576040805162461bcd60e51b81526020600482015260186024820152772837b7b61d103737ba1039bbb0b821b7b73a3937b63632b960411b604482015290519081900360640190fd5b600054600160a01b900460ff166110a8576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b191681556007546001600160a01b038581168084526002602090815260408086208151631ab6b2b760e01b8152949095166004850152602484019490945260448301919091526064820186905260848201859052915173cafea6a946406b0b48a77348a4f70dfeed0f466492631ab6b2b79260a48082019391829003018186803b15801561113e57600080fd5b505af4158015611152573d6000803e3d6000fd5b505050506040513d602081101561116857600080fd5b50516040805185815260208101839052815192935073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee926001600160a01b038816927fa078c4190abe07940190effc1846be0ccf03ad6007bc9e93f9697d0b460befbb928290030190a350506000805460ff60a01b1916600160a01b1790555050565b6101f481565b6000806111f0611887565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561124257600080fd5b505afa158015611256573d6000803e3d6000fd5b505050506040513d602081101561126c57600080fd5b5051905061127b848383611b0d565b949350505050565b6000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b1580156112ce57600080fd5b505afa1580156112e2573d6000803e3d6000fd5b505050506040513d60208110156112f857600080fd5b5051611344576040805162461bcd60e51b815260206004820152601660248201527521b0b63632b91034b9903737ba10309036b2b6b132b960511b604482015290519081900360640190fd5b600054600160a01b900460ff16611390576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b19811690915560408051600162f6c75960e01b0319815290516001600160a01b039092169163ff0938a791600480820192602092909190829003018186803b1580156113e457600080fd5b505afa1580156113f8573d6000803e3d6000fd5b505050506040513d602081101561140e57600080fd5b505115611455576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b60048054604080516370a0823160e01b815233938101939093525184926001600160a01b03909216916370a08231916024808301926020929190829003018186803b1580156114a357600080fd5b505afa1580156114b7573d6000803e3d6000fd5b505050506040513d60208110156114cd57600080fd5b50511015611522576040805162461bcd60e51b815260206004820152601860248201527f506f6f6c3a204e6f7420656e6f7567682062616c616e63650000000000000000604482015290519081900360640190fd5b60048054604080516398fd371f60e01b815233938101939093525142926001600160a01b03909216916398fd371f916024808301926020929190829003018186803b15801561157057600080fd5b505afa158015611584573d6000803e3d6000fd5b505050506040513d602081101561159a57600080fd5b505111156115d95760405162461bcd60e51b81526004018080602001828103825260268152602001806149d66026913960400191505060405180910390fd5b60006115e3611887565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561163557600080fd5b505afa158015611649573d6000803e3d6000fd5b505050506040513d602081101561165f57600080fd5b505190506000611670858484611b0d565b905081611683848363ffffffff61429216565b10156116c05760405162461bcd60e51b815260040180806020018281038252602181526020018061487a6021913960400191505060405180910390fd5b83811015611715576040805162461bcd60e51b815260206004820152601860248201527f506f6f6c3a206574684f7574203c206d696e4574684f75740000000000000000604482015290519081900360640190fd5b6005546040805163079cc67960e41b81523360048201526024810188905290516001600160a01b03909216916379cc6790916044808201926020929091908290030181600087803b15801561176957600080fd5b505af115801561177d573d6000803e3d6000fd5b505050506040513d602081101561179357600080fd5b5050604051600090339083908381818185875af1925050503d80600081146117d7576040519150601f19603f3d011682016040523d82523d6000602084013e6117dc565b606091505b5050905080611832576040805162461bcd60e51b815260206004820152601a60248201527f506f6f6c3a2053656c6c207472616e73666572206661696c6564000000000000604482015290519081900360640190fd5b6040805187815260208101849052815133927f45ea3d587ef2c627bc7085da467e1df0748c42d3ee4074b2f6b942f6876636c5928290030190a250506000805460ff60a01b1916600160a01b17905550505050565b600047815b600154811015611a32576000600182815481106118a557fe5b6000918252602080832090910154600a546040805163696ae5e360e01b81526001600160a01b03938416600482018190529151919650869594929093169263696ae5e3926024808301939192829003018186803b15801561190557600080fd5b505afa158015611919573d6000803e3d6000fd5b505050506040513d602081101561192f57600080fd5b5051905080611977576040805162461bcd60e51b815260206004820152600f60248201526e506f6f6c3a207a65726f207261746560881b604482015290519081900360640190fd5b604080516370a0823160e01b815230600482015290516000916001600160a01b038516916370a0823191602480820192602092909190829003018186803b1580156119c157600080fd5b505afa1580156119d5573d6000803e3d6000fd5b505050506040513d60208110156119eb57600080fd5b505190506000611a0d670de0b6b3a7640000610cd2848663ffffffff61419d16565b9050611a1f878263ffffffff61423816565b9650506001909401935061188c92505050565b50905090565b600080611a43611887565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9557600080fd5b505afa158015611aa9573d6000803e3d6000fd5b505050506040513d6020811015611abf57600080fd5b5051905061127b84838361215c565b600260205260009081526040902080546001909101546001600160701b0380831692600160701b810490911691600160e01b90910463ffffffff169084565b600080611b1a8484610c84565b90506000611b3a670de0b6b3a7640000610cd2888563ffffffff61419d16565b90506000611b4e868363ffffffff61429216565b90506000611b5c8287610c84565b90506000611b8b6103e8610cd26103cf611b7f6002838b8963ffffffff61423816565b9063ffffffff61419d16565b90506000828210611b9c5782611b9e565b815b90506000611bbe670de0b6b3a7640000610cd2848e63ffffffff61419d16565b9050611bd8612710610cd28b6101f463ffffffff61419d16565b811115611c165760405162461bcd60e51b815260040180806020018281038252603881526020018061489b6038913960400191505060405180910390fd5b96505050505050505b9392505050565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b158015611c7157600080fd5b505afa158015611c85573d6000803e3d6000fd5b505050506040513d6020811015611c9b57600080fd5b5051611cd85760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b600054600160a01b900460ff16611d24576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b191681556001600160a01b038416815260026020526040902054600160701b90046001600160701b031615611d9e576040805162461bcd60e51b8152602060048201526012602482015271506f6f6c3a206d6178206e6f74207a65726f60701b604482015290519081900360640190fd5b6001600160a01b038216611deb576040805162461bcd60e51b815260206004820152600f60248201526e506f6f6c3a2064657374207a65726f60881b604482015290519081900360640190fd5b604080516370a0823160e01b8152306004820152905184916000916001600160a01b038416916370a08231916024808301926020929190829003018186803b158015611e3657600080fd5b505afa158015611e4a573d6000803e3d6000fd5b505050506040513d6020811015611e6057600080fd5b505190506000818411611e735783611e75565b815b9050611e916001600160a01b038416868363ffffffff6142d416565b50506000805460ff60a01b1916600160a01b17905550505050565b6003546001600160a01b031681565b600481565b6040805163ca31d03f60e01b8152600481018590526001600160a01b03808516602483015283166044820152905160009173cafea6a946406b0b48a77348a4f70dfeed0f46649163ca31d03f91606480820192602092909190829003018186803b158015611f2d57600080fd5b505af4158015611f41573d6000803e3d6000fd5b505050506040513d6020811015611f5757600080fd5b5051949350505050565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b158015611fac57600080fd5b505afa158015611fc0573d6000803e3d6000fd5b505050506040513d6020811015611fd657600080fd5b50516120135760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b60005b600154811015612113576001818154811061202d57fe5b6000918252602090912001546001600160a01b038381169116146120505761210b565b6001600160a01b03821660009081526002602052604081208181556001908101919091558054600019810190811061208457fe5b600091825260209091200154600180546001600160a01b0390921691839081106120aa57fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060018054806120e357fe5b600082815260209020810160001990810180546001600160a01b031916905501905550612159565b600101612016565b506040805162461bcd60e51b8152602060048201526015602482015274141bdbdb0e88185cdcd95d081b9bdd08199bdd5b99605a1b604482015290519081900360640190fd5b50565b6000612176612710610cd2846101f463ffffffff61419d16565b8411156121b45760405162461bcd60e51b815260040180806020018281038252603e81526020018061483c603e913960400191505060405180910390fd5b8215806121d4575064e8d4a510006121d2838563ffffffff6141f616565b115b15612205576624859b044a80006121fd81610cd287670de0b6b3a764000063ffffffff61419d16565b915050611c1f565b6000612211848461432b565b90506000612225858763ffffffff61423816565b90506000612233828661432b565b90506000612247848363ffffffff61429216565b9050600061226782610cd28b670de0b6b3a764000063ffffffff61419d16565b90506000612282826624859b044a800063ffffffff61423816565b9050611c1681610cd28c670de0b6b3a764000063ffffffff61419d16565b6000546001600160a01b0316156122fd576000546001600160a01b031633146122fd576040805162461bcd60e51b815260206004820152600a6024820152692737ba1036b0b9ba32b960b11b604482015290519081900360640190fd5b600054600160a01b900460ff16612349576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b191681556040514791906001600160a01b0384169083908381818185875af1925050503d80600081146123a1576040519150601f19603f3d011682016040523d82523d6000602084013e6123a6565b606091505b50509050806123f4576040805162461bcd60e51b8152602060048201526015602482015274141bdbdb0e881d1c985b9cd9995c8819985a5b1959605a1b604482015290519081900360640190fd5b60005b6001548110156124b55760006001828154811061241057fe5b6000918252602080832090910154604080516370a0823160e01b815230600482015290516001600160a01b03909216945084926370a0823192602480840193829003018186803b15801561246357600080fd5b505afa158015612477573d6000803e3d6000fd5b505050506040513d602081101561248d57600080fd5b505190506124ab6001600160a01b038316878363ffffffff6142d416565b50506001016123f7565b50506000805460ff60a01b1916600160a01b1790555050565b6060600180548060200260200160405190810160405280929190818152602001828054801561252657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612508575b5050505050905090565b6000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b15801561257b57600080fd5b505afa15801561258f573d6000803e3d6000fd5b505050506040513d60208110156125a557600080fd5b50516125f1576040805162461bcd60e51b815260206004820152601660248201527521b0b63632b91034b9903737ba10309036b2b6b132b960511b604482015290519081900360640190fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561263e57600080fd5b505afa158015612652573d6000803e3d6000fd5b505050506040513d602081101561266857600080fd5b5051156126af576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b6208aa8960eb1b6001600160e01b0319871614612713576040805162461bcd60e51b815260206004820152601b60248201527f506f6f6c3a20556e657870656374656420617373657420747970650000000000604482015290519081900360640190fd5b8460018151811061272057fe5b602002602001015134146127655760405162461bcd60e51b81526004018080602001828103825260278152602001806149af6027913960400191505060405180910390fd5b60035460405163250bc23960e11b815233600482018181526001600160a01b038b811660248501526001600160e01b03198b16604485015261ffff8916608485015260ff881660a485015260c4840187905260e48401869052610100606485019081528a516101048601528a519190951694634a178472948d938d938d938d938d938d938d93919261012401906020808a01910280838360005b838110156128175781810151838201526020016127ff565b505050509050019950505050505050505050600060405180830381600087803b15801561284357600080fd5b505af1158015612857573d6000803e3d6000fd5b5050505050505050505050565b60008061286f611887565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156128c157600080fd5b505afa1580156128d5573d6000803e3d6000fd5b505050506040513d60208110156128eb57600080fd5b505190506128f98282612900565b9250505090565b6000611c1f82610cd28561271063ffffffff61419d16565b600080546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b15801561296457600080fd5b505afa158015612978573d6000803e3d6000fd5b505050506040513d602081101561298e57600080fd5b50516129da576040805162461bcd60e51b815260206004820152601660248201527521b0b63632b91034b9903737ba10309036b2b6b132b960511b604482015290519081900360640190fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015612a2757600080fd5b505afa158015612a3b573d6000803e3d6000fd5b505050506040513d6020811015612a5157600080fd5b505115612a98576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b612aa3826000611283565b506001919050565b60008054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b158015612af757600080fd5b505afa158015612b0b573d6000803e3d6000fd5b505050506040513d6020811015612b2157600080fd5b5051612b5e5760405162461bcd60e51b81526004018080602001828103825260228152602001806149636022913960400191505060405180910390fd5b600054600160a01b900460ff16612baa576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b191681556001600160a01b03851673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415612c35576040516001600160a01b038516908490600081818185875af1925050503d8060008114612c25576040519150601f19603f3d011682016040523d82523d6000602084013e612c2a565b606091505b505080915050612c43565b612c4085858561435b565b90505b8015612c9557846001600160a01b0316846001600160a01b03167febe7adb4feddf2afa5de463169fdc706254a55c9cf2b930ac84bb49e28cfe6bb856040518082815260200191505060405180910390a35b90506000805460ff60a01b1916600160a01b1790559392505050565b6000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b158015612cfc57600080fd5b505afa158015612d10573d6000803e3d6000fd5b505050506040513d6020811015612d2657600080fd5b5051612d72576040805162461bcd60e51b815260206004820152601660248201527521b0b63632b91034b9903737ba10309036b2b6b132b960511b604482015290519081900360640190fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015612dbf57600080fd5b505afa158015612dd3573d6000803e3d6000fd5b505050506040513d6020811015612de957600080fd5b505115612e30576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b6208aa8960eb1b6001600160e01b031987161415612765576040805162461bcd60e51b815260206004820152601b60248201527f506f6f6c3a20556e657870656374656420617373657420747970650000000000604482015290519081900360640190fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b6007546001600160a01b031681565b6000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b158015612f0757600080fd5b505afa158015612f1b573d6000803e3d6000fd5b505050506040513d6020811015612f3157600080fd5b5051612f7d576040805162461bcd60e51b815260206004820152601660248201527521b0b63632b91034b9903737ba10309036b2b6b132b960511b604482015290519081900360640190fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015612fca57600080fd5b505afa158015612fde573d6000803e3d6000fd5b505050506040513d6020811015612ff457600080fd5b50511561303b576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b3480613080576040805162461bcd60e51b815260206004820152600f60248201526e0506f6f6c3a20657468496e203e203608c1b604482015290519081900360640190fd5b600061309a8261308e611887565b9063ffffffff61429216565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156130ec57600080fd5b505afa158015613100573d6000803e3d6000fd5b505050506040513d602081101561311657600080fd5b5051905060006131268383612900565b9050619c408111156131695760405162461bcd60e51b81526004018080602001828103825260248152602001806148d36024913960400191505060405180910390fd5b600061317685858561215c565b9050858110156131b75760405162461bcd60e51b81526004018080602001828103825260298152602001806149186029913960400191505060405180910390fd5b600554604080516340c10f1960e01b81523360048201526024810184905290516001600160a01b03909216916340c10f199160448082019260009290919082900301818387803b15801561320a57600080fd5b505af115801561321e573d6000803e3d6000fd5b5050604080518881526020810185905281513394507fb4bf7fd11bc97d76c39ed13880388ee221d6971dd18f90e65179e0056d24671193509081900390910190a2505050505050565b600080600080600080869050806001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156132c957600080fd5b505afa1580156132dd573d6000803e3d6000fd5b505050506040513d60208110156132f357600080fd5b505195506132ff6147f4565b505050506001600160a01b0393909316600090815260026020908152604091829020825160808101845281546001600160701b03808216808452600160701b8304909116948301859052600160e01b90910463ffffffff16948201859052600190920154606090910181905294969095509093919250565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b1580156133c257600080fd5b505afa1580156133d6573d6000803e3d6000fd5b505050506040513d60208110156133ec57600080fd5b50516134295760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b6609a929cbe8aa8960cb1b6001600160c01b03198316141561344f57600981905561349c565b6040805162461bcd60e51b815260206004820152601760248201527f506f6f6c3a20756e6b6e6f776e20706172616d65746572000000000000000000604482015290519081900360640190fd5b5050565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b1580156134eb57600080fd5b505afa1580156134ff573d6000803e3d6000fd5b505050506040513d602081101561351557600080fd5b50516135525760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b6001600160a01b0384166135ad576040805162461bcd60e51b815260206004820152601b60248201527f506f6f6c3a206173736574206973207a65726f20616464726573730000000000604482015290519081900360640190fd5b826001600160701b0316826001600160701b03161015613606576040805162461bcd60e51b815260206004820152600f60248201526e2837b7b61d1036b0bc101e1036b4b760891b604482015290519081900360640190fd5b670de0b6b3a7640000811115613663576040805162461bcd60e51b815260206004820152601c60248201527f506f6f6c3a206d617820736c69707061676520726174696f203e203100000000604482015290519081900360640190fd5b60005b6001548110156136e6576001818154811061367d57fe5b6000918252602090912001546001600160a01b03868116911614156136de576040805162461bcd60e51b8152602060048201526012602482015271506f6f6c3a2061737365742065786973747360701b604482015290519081900360640190fd5b600101613666565b506001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b039096166001600160a01b031990961686179055604080516080810182526001600160701b0395861681529385166020858101918252600086840181815260608801968752988152600290915291909120935184549151965163ffffffff16600160e01b026001600160e01b03978716600160701b02600160701b600160e01b0319929097166001600160701b0319909316929092171694909417949094169290921781559051910155565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b15801561381257600080fd5b505afa158015613826573d6000803e3d6000fd5b505050506040513d602081101561383c57600080fd5b50516138795760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b816001600160701b0316836001600160701b031611156138d2576040805162461bcd60e51b815260206004820152600f60248201526e0a0deded87440dad2dc407c40dac2f608b1b604482015290519081900360640190fd5b670de0b6b3a764000081111561392f576040805162461bcd60e51b815260206004820152601c60248201527f506f6f6c3a206d617820736c69707061676520726174696f203e203100000000604482015290519081900360640190fd5b60005b600154811015612113576001818154811061394957fe5b6000918252602090912001546001600160a01b0386811691161461396c576139c6565b506001600160a01b038416600090815260026020526040902080546001600160701b03848116600160701b02600160701b600160e01b03199187166001600160701b031990931692909217161781556001018190556139ce565b600101613932565b50505050565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b158015613a1f57600080fd5b505afa158015613a33573d6000803e3d6000fd5b505050506040513d6020811015613a4957600080fd5b5051613a865760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b630545741560e41b6001600160c01b031983161415613abf57600780546001600160a01b0319166001600160a01b03831617905561349c565b630535741560e41b6001600160c01b031983161415613af857600880546001600160a01b0319166001600160a01b03831617905561349c565b67141490d7d191515160c21b6001600160c01b03198316141561344f57600a80546001600160a01b0319166001600160a01b03831617905561349c565b619c4081565b6006546001600160a01b031681565b600a546001600160a01b031681565b60018181548110613b6657fe5b6000918252602090912001546001600160a01b0316905081565b600080613b8b611887565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b158015613bdd57600080fd5b505afa158015613bf1573d6000803e3d6000fd5b505050506040513d6020811015613c0757600080fd5b505190506000613c178383610c84565b600a5460408051632ae2073360e11b81526001600160a01b0389811660048301526024820185905291519394509116916355c40e6691604480820192602092909190829003018186803b158015613c6d57600080fd5b505afa158015613c81573d6000803e3d6000fd5b505050506040513d6020811015613c9757600080fd5b505195945050505050565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b158015613ced57600080fd5b505afa158015613d01573d6000803e3d6000fd5b505050506040513d6020811015613d1757600080fd5b5051613d545760405162461bcd60e51b81526004018080602001828103825260228152602001806149636022913960400191505060405180910390fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015613da157600080fd5b505afa158015613db5573d6000803e3d6000fd5b505050506040513d6020811015613dcb57600080fd5b505115613e12576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b826139ce6001600160a01b03821684308563ffffffff6144ad16565b6000546001600160a01b031615613e8b576000546001600160a01b03163314613e8b576040805162461bcd60e51b815260206004820152600a6024820152692737ba1036b0b9ba32b960b11b604482015290519081900360640190fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6008546001600160a01b031681565b6000610cef826111e5565b6004546001600160a01b031681565b6005546001600160a01b031681565b6000546001600160a01b031681565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015613f4157600080fd5b505afa158015613f55573d6000803e3d6000fd5b505050506040513d6020811015613f6b57600080fd5b505115613fb2576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b6008546001600160a01b0316331461400c576040805162461bcd60e51b81526020600482015260186024820152772837b7b61d103737ba1039bbb0b821b7b73a3937b63632b960411b604482015290519081900360640190fd5b600054600160a01b900460ff16614058576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b191681556001600160a01b03808516808352600260209081526040808520600754600954835163d56b5b1560e01b8152919096166004820152602481018290526044810194909452606484018890526084840187905260a48401949094525192939273cafea6a946406b0b48a77348a4f70dfeed0f46649263d56b5b159260c4808301939192829003018186803b1580156140fb57600080fd5b505af415801561410f573d6000803e3d6000fd5b505050506040513d602081101561412557600080fd5b5051604080518681526020810183905281519293506001600160a01b0388169273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee927fa078c4190abe07940190effc1846be0ccf03ad6007bc9e93f9697d0b460befbb928290030190a350506000805460ff60a01b1916600160a01b179055505050565b6000826141ac57506000610cef565b828202828482816141b957fe5b0414611c1f5760405162461bcd60e51b81526004018080602001828103825260218152602001806148f76021913960400191505060405180910390fd5b6000611c1f83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614507565b600082820183811015611c1f576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000611c1f83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506145a9565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052614326908490614603565b505050565b6000611c1f83610cd284611b7f838383838383838360038362588040670de0b6b3a764000063ffffffff61419d16565b600061436f846001600160a01b03166147bb565b61437b57506000611c1f565b604080516001600160a01b038581166024830152604480830186905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17815292518251889460009360609390871692869282918083835b602083106143f95780518252601f1990920191602091820191016143da565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461445b576040519150601f19603f3d011682016040523d82523d6000602084013e614460565b606091505b509150915081614477576000945050505050611c1f565b805161448a576001945050505050611c1f565b80806020019051602081101561449f57600080fd5b505198975050505050505050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526139ce908590614603565b600081836145935760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614558578181015183820152602001614540565b50505050905090810190601f1680156145855780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161459f57fe5b0495945050505050565b600081848411156145fb5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315614558578181015183820152602001614540565b505050900390565b614615826001600160a01b03166147bb565b614666576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106146a45780518252601f199092019160209182019101614685565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114614706576040519150601f19603f3d011682016040523d82523d6000602084013e61470b565b606091505b509150915081614762576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b8051156139ce5780806020019051602081101561477e57600080fd5b50516139ce5760405162461bcd60e51b815260040180806020018281038252602a815260200180614985602a913960400191505060405180910390fd5b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061127b575050151592915050565b6040805160808101825260008082526020820181905291810182905260608101919091529056fe5265656e7472616e637947756172643a207265656e7472616e742063616c6c00506f6f6c3a2050757263686173657320776f72746820686967686572207468616e203525206f66204d435265746820617265206e6f7420616c6c6f776564506f6f6c3a204d4352252063616e6e6f742066616c6c2062656c6f772031303025506f6f6c3a2053616c657320776f727468206d6f7265207468616e203525206f66204d435265746820617265206e6f7420616c6c6f776564506f6f6c3a2043616e6e6f74207075726368617365206966204d435225203e2034303025536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77506f6f6c3a20746f6b656e734f7574206973206c657373207468616e206d696e546f6b656e734f757443616c6c6572206973206e6f7420617574686f72697a656420746f20676f7665726e43616c6c6572206973206e6f7420616e20696e7465726e616c20636f6e74726163745361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564506f6f6c3a2045544820616d6f756e7420646f6573206e6f74206d61746368207072656d69756d506f6f6c3a204e584d20746f6b656e7320617265206c6f636b656420666f7220766f74696e67a265627a7a72315820ac66113c020348db134b54bfcf79295308d58fff60cdd9c6bff72265dfed7dba64736f6c63430005110032506f6f6c3a206c656e677468206d69736d61746368000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000001bfd82675dbcc7762c84019ca518e701c0cd07e000000000000000000000000cafeae8bf47145bd573bfc436d1728cff224ca13000000000000000000000000cafea1c9f94e077df44d95c4a1ad5a5747a18b5c000000000000000000000000551d5500f613a4bec77ba8b834b5eed52ad5764f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000d3c21bcecceda1000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000001a784379d99db4200000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000058d15e17628000

Deployed Bytecode

0x6080604052600436106102885760003560e01c806374bd0ace1161015a578063b8caedc4116100c1578063ddf403aa1161007a578063ddf403aa14610bc7578063e942c56414610bdc578063e967eb1a14610c06578063eddd9d8214610c1b578063ee97f7f314610c30578063ffa6de3a14610c4557610288565b8063b8caedc414610aca578063b9ab992714610adf578063cf35bdd014610af4578063d02641a014610b1e578063d311afb414610b51578063d46655f414610b9457610288565b80639c9b6451116101135780639c9b6451146109245780639dd86e0f14610994578063a52f930f146109ce578063aafa936d14610a20578063ad4cc43e14610a72578063b222a4d314610ab557610288565b806374bd0ace1461077557806375466b9b146107b35780638076083d146107f65780638322fff2146108dd5780639043292a146108f2578063963bbc601461090757610288565b806343400ed7116101fe57806365a21992116101b757806365a219921461058857806366fd551d146105be57806367e4ac2c146105f157806368da80af1461065657806369f36c321461073057806370310cb71461074557610288565b806343400ed714610455578063439e2e451461048b578063441da1ba146104ce5780634779be08146104ff57806349386d87146105145780634a5e42b11461055557610288565b80631f50c989116102505780631f50c9891461033d5780632892b29c1461035257806328dd37731461037c5780632f04d798146103ac5780633f494325146103c157806341fee44a146103eb57610288565b806304e8266f1461028a5780630bb76fff146102cc5780630e29df22146102e15780630ea9c984146102e957806311267784146102fe575b005b34801561029657600080fd5b506102ba600480360360408110156102ad57600080fd5b5080359060200135610c84565b60408051918252519081900360200190f35b3480156102d857600080fd5b506102ba610cf5565b610288610cfb565b3480156102f557600080fd5b50610288610cfd565b34801561030a57600080fd5b506102886004803603606081101561032157600080fd5b506001600160a01b038135169060208101359060400135610f44565b34801561034957600080fd5b506102ba6111df565b34801561035e57600080fd5b506102ba6004803603602081101561037557600080fd5b50356111e5565b34801561038857600080fd5b506102886004803603604081101561039f57600080fd5b5080359060200135611283565b3480156103b857600080fd5b506102ba611887565b3480156103cd57600080fd5b506102ba600480360360208110156103e457600080fd5b5035611a38565b3480156103f757600080fd5b5061041e6004803603602081101561040e57600080fd5b50356001600160a01b0316611ace565b604080516001600160701b03958616815293909416602084015263ffffffff90911682840152606082015290519081900360800190f35b34801561046157600080fd5b506102ba6004803603606081101561047857600080fd5b5080359060208101359060400135611b0d565b34801561049757600080fd5b50610288600480360360608110156104ae57600080fd5b506001600160a01b03813581169160208101359091169060400135611c26565b3480156104da57600080fd5b506104e3611eac565b604080516001600160a01b039092168252519081900360200190f35b34801561050b57600080fd5b506102ba611ebb565b34801561052057600080fd5b506102ba6004803603606081101561053757600080fd5b508035906001600160a01b0360208201358116916040013516611ec0565b34801561056157600080fd5b506102886004803603602081101561057857600080fd5b50356001600160a01b0316611f61565b34801561059457600080fd5b506102ba600480360360608110156105ab57600080fd5b508035906020810135906040013561215c565b3480156105ca57600080fd5b50610288600480360360208110156105e157600080fd5b50356001600160a01b03166122a0565b3480156105fd57600080fd5b506106066124ce565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561064257818101518382015260200161062a565b505050509050019250505060405180910390f35b610288600480360360e081101561066c57600080fd5b6001600160a01b03823516916001600160e01b0319602082013516918101906060810160408201356401000000008111156106a657600080fd5b8201836020820111156106b857600080fd5b803590602001918460208302840111640100000000831117156106da57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505061ffff83351693505060ff602083013516916040810135915060600135612530565b34801561073c57600080fd5b506102ba612864565b34801561075157600080fd5b506102ba6004803603604081101561076857600080fd5b5080359060200135612900565b34801561078157600080fd5b5061079f6004803603602081101561079857600080fd5b5035612918565b604080519115158252519081900360200190f35b3480156107bf57600080fd5b5061079f600480360360608110156107d657600080fd5b506001600160a01b03813581169160208101359091169060400135612aab565b34801561080257600080fd5b50610288600480360360e081101561081957600080fd5b6001600160a01b03823516916001600160e01b03196020820135169181019060608101604082013564010000000081111561085357600080fd5b82018360208201111561086557600080fd5b8035906020019184602083028401116401000000008311171561088757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505061ffff83351693505060ff602083013516916040810135915060600135612cb1565b3480156108e957600080fd5b506104e3612e95565b3480156108fe57600080fd5b506104e3612ead565b6102886004803603602081101561091d57600080fd5b5035612ebc565b34801561093057600080fd5b506109576004803603602081101561094757600080fd5b50356001600160a01b0316613267565b604080519586526001600160701b039485166020870152929093168483015263ffffffff1660608401526080830191909152519081900360a00190f35b3480156109a057600080fd5b50610288600480360360408110156109b757600080fd5b506001600160c01b03198135169060200135613377565b3480156109da57600080fd5b50610288600480360360808110156109f157600080fd5b506001600160a01b03813516906001600160701b036020820135811691604081013590911690606001356134a0565b348015610a2c57600080fd5b5061028860048036036080811015610a4357600080fd5b506001600160a01b03813516906001600160701b036020820135811691604081013590911690606001356137c7565b348015610a7e57600080fd5b5061028860048036036040811015610a9557600080fd5b5080356001600160c01b03191690602001356001600160a01b03166139d4565b348015610ac157600080fd5b506102ba613b35565b348015610ad657600080fd5b506104e3613b3b565b348015610aeb57600080fd5b506104e3613b4a565b348015610b0057600080fd5b506104e360048036036020811015610b1757600080fd5b5035613b59565b348015610b2a57600080fd5b506102ba60048036036020811015610b4157600080fd5b50356001600160a01b0316613b80565b348015610b5d57600080fd5b5061028860048036036060811015610b7457600080fd5b506001600160a01b03813581169160208101359091169060400135613ca2565b348015610ba057600080fd5b5061028860048036036020811015610bb757600080fd5b50356001600160a01b0316613e2e565b348015610bd357600080fd5b506104e3613ead565b348015610be857600080fd5b506102ba60048036036020811015610bff57600080fd5b5035613ebc565b348015610c1257600080fd5b506104e3613ec7565b348015610c2757600080fd5b506104e3613ed6565b348015610c3c57600080fd5b506104e3613ee5565b348015610c5157600080fd5b5061028860048036036060811015610c6857600080fd5b506001600160a01b038135169060208101359060400135613ef4565b600080610c918484612900565b90506000610ca660048063ffffffff61419d16565b600a0a9050610cea6624859b044a8000610cde83610cd262588040818a60048a0a63ffffffff61419d16565b9063ffffffff6141f616565b9063ffffffff61423816565b925050505b92915050565b60095481565b565b6000809054906101000a90046001600160a01b03166001600160a01b0316639d76ea586040518163ffffffff1660e01b815260040160206040518083038186803b158015610d4a57600080fd5b505afa158015610d5e573d6000803e3d6000fd5b505050506040513d6020811015610d7457600080fd5b5051600480546001600160a01b0319166001600160a01b03928316178155600054604080516227050b60e31b815261544360f01b9381019390935251921691630138285891602480820192602092909190829003018186803b158015610dd957600080fd5b505afa158015610ded573d6000803e3d6000fd5b505050506040513d6020811015610e0357600080fd5b5051600580546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261145560f21b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610e6857600080fd5b505afa158015610e7c573d6000803e3d6000fd5b505050506040513d6020811015610e9257600080fd5b5051600380546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b8152614d4360f01b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610ef757600080fd5b505afa158015610f0b573d6000803e3d6000fd5b505050506040513d6020811015610f2157600080fd5b5051600680546001600160a01b0319166001600160a01b03909216919091179055565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015610f9157600080fd5b505afa158015610fa5573d6000803e3d6000fd5b505050506040513d6020811015610fbb57600080fd5b505115611002576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b6008546001600160a01b0316331461105c576040805162461bcd60e51b81526020600482015260186024820152772837b7b61d103737ba1039bbb0b821b7b73a3937b63632b960411b604482015290519081900360640190fd5b600054600160a01b900460ff166110a8576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b191681556007546001600160a01b038581168084526002602090815260408086208151631ab6b2b760e01b8152949095166004850152602484019490945260448301919091526064820186905260848201859052915173cafea6a946406b0b48a77348a4f70dfeed0f466492631ab6b2b79260a48082019391829003018186803b15801561113e57600080fd5b505af4158015611152573d6000803e3d6000fd5b505050506040513d602081101561116857600080fd5b50516040805185815260208101839052815192935073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee926001600160a01b038816927fa078c4190abe07940190effc1846be0ccf03ad6007bc9e93f9697d0b460befbb928290030190a350506000805460ff60a01b1916600160a01b1790555050565b6101f481565b6000806111f0611887565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561124257600080fd5b505afa158015611256573d6000803e3d6000fd5b505050506040513d602081101561126c57600080fd5b5051905061127b848383611b0d565b949350505050565b6000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b1580156112ce57600080fd5b505afa1580156112e2573d6000803e3d6000fd5b505050506040513d60208110156112f857600080fd5b5051611344576040805162461bcd60e51b815260206004820152601660248201527521b0b63632b91034b9903737ba10309036b2b6b132b960511b604482015290519081900360640190fd5b600054600160a01b900460ff16611390576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b19811690915560408051600162f6c75960e01b0319815290516001600160a01b039092169163ff0938a791600480820192602092909190829003018186803b1580156113e457600080fd5b505afa1580156113f8573d6000803e3d6000fd5b505050506040513d602081101561140e57600080fd5b505115611455576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b60048054604080516370a0823160e01b815233938101939093525184926001600160a01b03909216916370a08231916024808301926020929190829003018186803b1580156114a357600080fd5b505afa1580156114b7573d6000803e3d6000fd5b505050506040513d60208110156114cd57600080fd5b50511015611522576040805162461bcd60e51b815260206004820152601860248201527f506f6f6c3a204e6f7420656e6f7567682062616c616e63650000000000000000604482015290519081900360640190fd5b60048054604080516398fd371f60e01b815233938101939093525142926001600160a01b03909216916398fd371f916024808301926020929190829003018186803b15801561157057600080fd5b505afa158015611584573d6000803e3d6000fd5b505050506040513d602081101561159a57600080fd5b505111156115d95760405162461bcd60e51b81526004018080602001828103825260268152602001806149d66026913960400191505060405180910390fd5b60006115e3611887565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561163557600080fd5b505afa158015611649573d6000803e3d6000fd5b505050506040513d602081101561165f57600080fd5b505190506000611670858484611b0d565b905081611683848363ffffffff61429216565b10156116c05760405162461bcd60e51b815260040180806020018281038252602181526020018061487a6021913960400191505060405180910390fd5b83811015611715576040805162461bcd60e51b815260206004820152601860248201527f506f6f6c3a206574684f7574203c206d696e4574684f75740000000000000000604482015290519081900360640190fd5b6005546040805163079cc67960e41b81523360048201526024810188905290516001600160a01b03909216916379cc6790916044808201926020929091908290030181600087803b15801561176957600080fd5b505af115801561177d573d6000803e3d6000fd5b505050506040513d602081101561179357600080fd5b5050604051600090339083908381818185875af1925050503d80600081146117d7576040519150601f19603f3d011682016040523d82523d6000602084013e6117dc565b606091505b5050905080611832576040805162461bcd60e51b815260206004820152601a60248201527f506f6f6c3a2053656c6c207472616e73666572206661696c6564000000000000604482015290519081900360640190fd5b6040805187815260208101849052815133927f45ea3d587ef2c627bc7085da467e1df0748c42d3ee4074b2f6b942f6876636c5928290030190a250506000805460ff60a01b1916600160a01b17905550505050565b600047815b600154811015611a32576000600182815481106118a557fe5b6000918252602080832090910154600a546040805163696ae5e360e01b81526001600160a01b03938416600482018190529151919650869594929093169263696ae5e3926024808301939192829003018186803b15801561190557600080fd5b505afa158015611919573d6000803e3d6000fd5b505050506040513d602081101561192f57600080fd5b5051905080611977576040805162461bcd60e51b815260206004820152600f60248201526e506f6f6c3a207a65726f207261746560881b604482015290519081900360640190fd5b604080516370a0823160e01b815230600482015290516000916001600160a01b038516916370a0823191602480820192602092909190829003018186803b1580156119c157600080fd5b505afa1580156119d5573d6000803e3d6000fd5b505050506040513d60208110156119eb57600080fd5b505190506000611a0d670de0b6b3a7640000610cd2848663ffffffff61419d16565b9050611a1f878263ffffffff61423816565b9650506001909401935061188c92505050565b50905090565b600080611a43611887565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9557600080fd5b505afa158015611aa9573d6000803e3d6000fd5b505050506040513d6020811015611abf57600080fd5b5051905061127b84838361215c565b600260205260009081526040902080546001909101546001600160701b0380831692600160701b810490911691600160e01b90910463ffffffff169084565b600080611b1a8484610c84565b90506000611b3a670de0b6b3a7640000610cd2888563ffffffff61419d16565b90506000611b4e868363ffffffff61429216565b90506000611b5c8287610c84565b90506000611b8b6103e8610cd26103cf611b7f6002838b8963ffffffff61423816565b9063ffffffff61419d16565b90506000828210611b9c5782611b9e565b815b90506000611bbe670de0b6b3a7640000610cd2848e63ffffffff61419d16565b9050611bd8612710610cd28b6101f463ffffffff61419d16565b811115611c165760405162461bcd60e51b815260040180806020018281038252603881526020018061489b6038913960400191505060405180910390fd5b96505050505050505b9392505050565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b158015611c7157600080fd5b505afa158015611c85573d6000803e3d6000fd5b505050506040513d6020811015611c9b57600080fd5b5051611cd85760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b600054600160a01b900460ff16611d24576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b191681556001600160a01b038416815260026020526040902054600160701b90046001600160701b031615611d9e576040805162461bcd60e51b8152602060048201526012602482015271506f6f6c3a206d6178206e6f74207a65726f60701b604482015290519081900360640190fd5b6001600160a01b038216611deb576040805162461bcd60e51b815260206004820152600f60248201526e506f6f6c3a2064657374207a65726f60881b604482015290519081900360640190fd5b604080516370a0823160e01b8152306004820152905184916000916001600160a01b038416916370a08231916024808301926020929190829003018186803b158015611e3657600080fd5b505afa158015611e4a573d6000803e3d6000fd5b505050506040513d6020811015611e6057600080fd5b505190506000818411611e735783611e75565b815b9050611e916001600160a01b038416868363ffffffff6142d416565b50506000805460ff60a01b1916600160a01b17905550505050565b6003546001600160a01b031681565b600481565b6040805163ca31d03f60e01b8152600481018590526001600160a01b03808516602483015283166044820152905160009173cafea6a946406b0b48a77348a4f70dfeed0f46649163ca31d03f91606480820192602092909190829003018186803b158015611f2d57600080fd5b505af4158015611f41573d6000803e3d6000fd5b505050506040513d6020811015611f5757600080fd5b5051949350505050565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b158015611fac57600080fd5b505afa158015611fc0573d6000803e3d6000fd5b505050506040513d6020811015611fd657600080fd5b50516120135760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b60005b600154811015612113576001818154811061202d57fe5b6000918252602090912001546001600160a01b038381169116146120505761210b565b6001600160a01b03821660009081526002602052604081208181556001908101919091558054600019810190811061208457fe5b600091825260209091200154600180546001600160a01b0390921691839081106120aa57fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060018054806120e357fe5b600082815260209020810160001990810180546001600160a01b031916905501905550612159565b600101612016565b506040805162461bcd60e51b8152602060048201526015602482015274141bdbdb0e88185cdcd95d081b9bdd08199bdd5b99605a1b604482015290519081900360640190fd5b50565b6000612176612710610cd2846101f463ffffffff61419d16565b8411156121b45760405162461bcd60e51b815260040180806020018281038252603e81526020018061483c603e913960400191505060405180910390fd5b8215806121d4575064e8d4a510006121d2838563ffffffff6141f616565b115b15612205576624859b044a80006121fd81610cd287670de0b6b3a764000063ffffffff61419d16565b915050611c1f565b6000612211848461432b565b90506000612225858763ffffffff61423816565b90506000612233828661432b565b90506000612247848363ffffffff61429216565b9050600061226782610cd28b670de0b6b3a764000063ffffffff61419d16565b90506000612282826624859b044a800063ffffffff61423816565b9050611c1681610cd28c670de0b6b3a764000063ffffffff61419d16565b6000546001600160a01b0316156122fd576000546001600160a01b031633146122fd576040805162461bcd60e51b815260206004820152600a6024820152692737ba1036b0b9ba32b960b11b604482015290519081900360640190fd5b600054600160a01b900460ff16612349576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b191681556040514791906001600160a01b0384169083908381818185875af1925050503d80600081146123a1576040519150601f19603f3d011682016040523d82523d6000602084013e6123a6565b606091505b50509050806123f4576040805162461bcd60e51b8152602060048201526015602482015274141bdbdb0e881d1c985b9cd9995c8819985a5b1959605a1b604482015290519081900360640190fd5b60005b6001548110156124b55760006001828154811061241057fe5b6000918252602080832090910154604080516370a0823160e01b815230600482015290516001600160a01b03909216945084926370a0823192602480840193829003018186803b15801561246357600080fd5b505afa158015612477573d6000803e3d6000fd5b505050506040513d602081101561248d57600080fd5b505190506124ab6001600160a01b038316878363ffffffff6142d416565b50506001016123f7565b50506000805460ff60a01b1916600160a01b1790555050565b6060600180548060200260200160405190810160405280929190818152602001828054801561252657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612508575b5050505050905090565b6000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b15801561257b57600080fd5b505afa15801561258f573d6000803e3d6000fd5b505050506040513d60208110156125a557600080fd5b50516125f1576040805162461bcd60e51b815260206004820152601660248201527521b0b63632b91034b9903737ba10309036b2b6b132b960511b604482015290519081900360640190fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561263e57600080fd5b505afa158015612652573d6000803e3d6000fd5b505050506040513d602081101561266857600080fd5b5051156126af576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b6208aa8960eb1b6001600160e01b0319871614612713576040805162461bcd60e51b815260206004820152601b60248201527f506f6f6c3a20556e657870656374656420617373657420747970650000000000604482015290519081900360640190fd5b8460018151811061272057fe5b602002602001015134146127655760405162461bcd60e51b81526004018080602001828103825260278152602001806149af6027913960400191505060405180910390fd5b60035460405163250bc23960e11b815233600482018181526001600160a01b038b811660248501526001600160e01b03198b16604485015261ffff8916608485015260ff881660a485015260c4840187905260e48401869052610100606485019081528a516101048601528a519190951694634a178472948d938d938d938d938d938d938d93919261012401906020808a01910280838360005b838110156128175781810151838201526020016127ff565b505050509050019950505050505050505050600060405180830381600087803b15801561284357600080fd5b505af1158015612857573d6000803e3d6000fd5b5050505050505050505050565b60008061286f611887565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156128c157600080fd5b505afa1580156128d5573d6000803e3d6000fd5b505050506040513d60208110156128eb57600080fd5b505190506128f98282612900565b9250505090565b6000611c1f82610cd28561271063ffffffff61419d16565b600080546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b15801561296457600080fd5b505afa158015612978573d6000803e3d6000fd5b505050506040513d602081101561298e57600080fd5b50516129da576040805162461bcd60e51b815260206004820152601660248201527521b0b63632b91034b9903737ba10309036b2b6b132b960511b604482015290519081900360640190fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015612a2757600080fd5b505afa158015612a3b573d6000803e3d6000fd5b505050506040513d6020811015612a5157600080fd5b505115612a98576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b612aa3826000611283565b506001919050565b60008054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b158015612af757600080fd5b505afa158015612b0b573d6000803e3d6000fd5b505050506040513d6020811015612b2157600080fd5b5051612b5e5760405162461bcd60e51b81526004018080602001828103825260228152602001806149636022913960400191505060405180910390fd5b600054600160a01b900460ff16612baa576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b191681556001600160a01b03851673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415612c35576040516001600160a01b038516908490600081818185875af1925050503d8060008114612c25576040519150601f19603f3d011682016040523d82523d6000602084013e612c2a565b606091505b505080915050612c43565b612c4085858561435b565b90505b8015612c9557846001600160a01b0316846001600160a01b03167febe7adb4feddf2afa5de463169fdc706254a55c9cf2b930ac84bb49e28cfe6bb856040518082815260200191505060405180910390a35b90506000805460ff60a01b1916600160a01b1790559392505050565b6000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b158015612cfc57600080fd5b505afa158015612d10573d6000803e3d6000fd5b505050506040513d6020811015612d2657600080fd5b5051612d72576040805162461bcd60e51b815260206004820152601660248201527521b0b63632b91034b9903737ba10309036b2b6b132b960511b604482015290519081900360640190fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015612dbf57600080fd5b505afa158015612dd3573d6000803e3d6000fd5b505050506040513d6020811015612de957600080fd5b505115612e30576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b6208aa8960eb1b6001600160e01b031987161415612765576040805162461bcd60e51b815260206004820152601b60248201527f506f6f6c3a20556e657870656374656420617373657420747970650000000000604482015290519081900360640190fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b6007546001600160a01b031681565b6000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b158015612f0757600080fd5b505afa158015612f1b573d6000803e3d6000fd5b505050506040513d6020811015612f3157600080fd5b5051612f7d576040805162461bcd60e51b815260206004820152601660248201527521b0b63632b91034b9903737ba10309036b2b6b132b960511b604482015290519081900360640190fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015612fca57600080fd5b505afa158015612fde573d6000803e3d6000fd5b505050506040513d6020811015612ff457600080fd5b50511561303b576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b3480613080576040805162461bcd60e51b815260206004820152600f60248201526e0506f6f6c3a20657468496e203e203608c1b604482015290519081900360640190fd5b600061309a8261308e611887565b9063ffffffff61429216565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156130ec57600080fd5b505afa158015613100573d6000803e3d6000fd5b505050506040513d602081101561311657600080fd5b5051905060006131268383612900565b9050619c408111156131695760405162461bcd60e51b81526004018080602001828103825260248152602001806148d36024913960400191505060405180910390fd5b600061317685858561215c565b9050858110156131b75760405162461bcd60e51b81526004018080602001828103825260298152602001806149186029913960400191505060405180910390fd5b600554604080516340c10f1960e01b81523360048201526024810184905290516001600160a01b03909216916340c10f199160448082019260009290919082900301818387803b15801561320a57600080fd5b505af115801561321e573d6000803e3d6000fd5b5050604080518881526020810185905281513394507fb4bf7fd11bc97d76c39ed13880388ee221d6971dd18f90e65179e0056d24671193509081900390910190a2505050505050565b600080600080600080869050806001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156132c957600080fd5b505afa1580156132dd573d6000803e3d6000fd5b505050506040513d60208110156132f357600080fd5b505195506132ff6147f4565b505050506001600160a01b0393909316600090815260026020908152604091829020825160808101845281546001600160701b03808216808452600160701b8304909116948301859052600160e01b90910463ffffffff16948201859052600190920154606090910181905294969095509093919250565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b1580156133c257600080fd5b505afa1580156133d6573d6000803e3d6000fd5b505050506040513d60208110156133ec57600080fd5b50516134295760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b6609a929cbe8aa8960cb1b6001600160c01b03198316141561344f57600981905561349c565b6040805162461bcd60e51b815260206004820152601760248201527f506f6f6c3a20756e6b6e6f776e20706172616d65746572000000000000000000604482015290519081900360640190fd5b5050565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b1580156134eb57600080fd5b505afa1580156134ff573d6000803e3d6000fd5b505050506040513d602081101561351557600080fd5b50516135525760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b6001600160a01b0384166135ad576040805162461bcd60e51b815260206004820152601b60248201527f506f6f6c3a206173736574206973207a65726f20616464726573730000000000604482015290519081900360640190fd5b826001600160701b0316826001600160701b03161015613606576040805162461bcd60e51b815260206004820152600f60248201526e2837b7b61d1036b0bc101e1036b4b760891b604482015290519081900360640190fd5b670de0b6b3a7640000811115613663576040805162461bcd60e51b815260206004820152601c60248201527f506f6f6c3a206d617820736c69707061676520726174696f203e203100000000604482015290519081900360640190fd5b60005b6001548110156136e6576001818154811061367d57fe5b6000918252602090912001546001600160a01b03868116911614156136de576040805162461bcd60e51b8152602060048201526012602482015271506f6f6c3a2061737365742065786973747360701b604482015290519081900360640190fd5b600101613666565b506001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b039096166001600160a01b031990961686179055604080516080810182526001600160701b0395861681529385166020858101918252600086840181815260608801968752988152600290915291909120935184549151965163ffffffff16600160e01b026001600160e01b03978716600160701b02600160701b600160e01b0319929097166001600160701b0319909316929092171694909417949094169290921781559051910155565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b15801561381257600080fd5b505afa158015613826573d6000803e3d6000fd5b505050506040513d602081101561383c57600080fd5b50516138795760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b816001600160701b0316836001600160701b031611156138d2576040805162461bcd60e51b815260206004820152600f60248201526e0a0deded87440dad2dc407c40dac2f608b1b604482015290519081900360640190fd5b670de0b6b3a764000081111561392f576040805162461bcd60e51b815260206004820152601c60248201527f506f6f6c3a206d617820736c69707061676520726174696f203e203100000000604482015290519081900360640190fd5b60005b600154811015612113576001818154811061394957fe5b6000918252602090912001546001600160a01b0386811691161461396c576139c6565b506001600160a01b038416600090815260026020526040902080546001600160701b03848116600160701b02600160701b600160e01b03199187166001600160701b031990931692909217161781556001018190556139ce565b600101613932565b50505050565b60005460408051632c1a733d60e11b815233600482015290516001600160a01b0390921691635834e67a91602480820192602092909190829003018186803b158015613a1f57600080fd5b505afa158015613a33573d6000803e3d6000fd5b505050506040513d6020811015613a4957600080fd5b5051613a865760405162461bcd60e51b81526004018080602001828103825260228152602001806149416022913960400191505060405180910390fd5b630545741560e41b6001600160c01b031983161415613abf57600780546001600160a01b0319166001600160a01b03831617905561349c565b630535741560e41b6001600160c01b031983161415613af857600880546001600160a01b0319166001600160a01b03831617905561349c565b67141490d7d191515160c21b6001600160c01b03198316141561344f57600a80546001600160a01b0319166001600160a01b03831617905561349c565b619c4081565b6006546001600160a01b031681565b600a546001600160a01b031681565b60018181548110613b6657fe5b6000918252602090912001546001600160a01b0316905081565b600080613b8b611887565b90506000600660009054906101000a90046001600160a01b03166001600160a01b031663b775a49d6040518163ffffffff1660e01b815260040160206040518083038186803b158015613bdd57600080fd5b505afa158015613bf1573d6000803e3d6000fd5b505050506040513d6020811015613c0757600080fd5b505190506000613c178383610c84565b600a5460408051632ae2073360e11b81526001600160a01b0389811660048301526024820185905291519394509116916355c40e6691604480820192602092909190829003018186803b158015613c6d57600080fd5b505afa158015613c81573d6000803e3d6000fd5b505050506040513d6020811015613c9757600080fd5b505195945050505050565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b158015613ced57600080fd5b505afa158015613d01573d6000803e3d6000fd5b505050506040513d6020811015613d1757600080fd5b5051613d545760405162461bcd60e51b81526004018080602001828103825260228152602001806149636022913960400191505060405180910390fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015613da157600080fd5b505afa158015613db5573d6000803e3d6000fd5b505050506040513d6020811015613dcb57600080fd5b505115613e12576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b826139ce6001600160a01b03821684308563ffffffff6144ad16565b6000546001600160a01b031615613e8b576000546001600160a01b03163314613e8b576040805162461bcd60e51b815260206004820152600a6024820152692737ba1036b0b9ba32b960b11b604482015290519081900360640190fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6008546001600160a01b031681565b6000610cef826111e5565b6004546001600160a01b031681565b6005546001600160a01b031681565b6000546001600160a01b031681565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015613f4157600080fd5b505afa158015613f55573d6000803e3d6000fd5b505050506040513d6020811015613f6b57600080fd5b505115613fb2576040805162461bcd60e51b815260206004820152601060248201526f14de5cdd195b481a5cc81c185d5cd95960821b604482015290519081900360640190fd5b6008546001600160a01b0316331461400c576040805162461bcd60e51b81526020600482015260186024820152772837b7b61d103737ba1039bbb0b821b7b73a3937b63632b960411b604482015290519081900360640190fd5b600054600160a01b900460ff16614058576040805162461bcd60e51b815260206004820152601f602482015260008051602061481c833981519152604482015290519081900360640190fd5b6000805460ff60a01b191681556001600160a01b03808516808352600260209081526040808520600754600954835163d56b5b1560e01b8152919096166004820152602481018290526044810194909452606484018890526084840187905260a48401949094525192939273cafea6a946406b0b48a77348a4f70dfeed0f46649263d56b5b159260c4808301939192829003018186803b1580156140fb57600080fd5b505af415801561410f573d6000803e3d6000fd5b505050506040513d602081101561412557600080fd5b5051604080518681526020810183905281519293506001600160a01b0388169273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee927fa078c4190abe07940190effc1846be0ccf03ad6007bc9e93f9697d0b460befbb928290030190a350506000805460ff60a01b1916600160a01b179055505050565b6000826141ac57506000610cef565b828202828482816141b957fe5b0414611c1f5760405162461bcd60e51b81526004018080602001828103825260218152602001806148f76021913960400191505060405180910390fd5b6000611c1f83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614507565b600082820183811015611c1f576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000611c1f83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506145a9565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052614326908490614603565b505050565b6000611c1f83610cd284611b7f838383838383838360038362588040670de0b6b3a764000063ffffffff61419d16565b600061436f846001600160a01b03166147bb565b61437b57506000611c1f565b604080516001600160a01b038581166024830152604480830186905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17815292518251889460009360609390871692869282918083835b602083106143f95780518252601f1990920191602091820191016143da565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461445b576040519150601f19603f3d011682016040523d82523d6000602084013e614460565b606091505b509150915081614477576000945050505050611c1f565b805161448a576001945050505050611c1f565b80806020019051602081101561449f57600080fd5b505198975050505050505050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526139ce908590614603565b600081836145935760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614558578181015183820152602001614540565b50505050905090810190601f1680156145855780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161459f57fe5b0495945050505050565b600081848411156145fb5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315614558578181015183820152602001614540565b505050900390565b614615826001600160a01b03166147bb565b614666576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106146a45780518252601f199092019160209182019101614685565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114614706576040519150601f19603f3d011682016040523d82523d6000602084013e61470b565b606091505b509150915081614762576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b8051156139ce5780806020019051602081101561477e57600080fd5b50516139ce5760405162461bcd60e51b815260040180806020018281038252602a815260200180614985602a913960400191505060405180910390fd5b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061127b575050151592915050565b6040805160808101825260008082526020820181905291810182905260608101919091529056fe5265656e7472616e637947756172643a207265656e7472616e742063616c6c00506f6f6c3a2050757263686173657320776f72746820686967686572207468616e203525206f66204d435265746820617265206e6f7420616c6c6f776564506f6f6c3a204d4352252063616e6e6f742066616c6c2062656c6f772031303025506f6f6c3a2053616c657320776f727468206d6f7265207468616e203525206f66204d435265746820617265206e6f7420616c6c6f776564506f6f6c3a2043616e6e6f74207075726368617365206966204d435225203e2034303025536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77506f6f6c3a20746f6b656e734f7574206973206c657373207468616e206d696e546f6b656e734f757443616c6c6572206973206e6f7420617574686f72697a656420746f20676f7665726e43616c6c6572206973206e6f7420616e20696e7465726e616c20636f6e74726163745361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564506f6f6c3a2045544820616d6f756e7420646f6573206e6f74206d61746368207072656d69756d506f6f6c3a204e584d20746f6b656e7320617265206c6f636b656420666f7220766f74696e67a265627a7a72315820ac66113c020348db134b54bfcf79295308d58fff60cdd9c6bff72265dfed7dba64736f6c63430005110032

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000001bfd82675dbcc7762c84019ca518e701c0cd07e000000000000000000000000cafeae8bf47145bd573bfc436d1728cff224ca13000000000000000000000000cafea1c9f94e077df44d95c4a1ad5a5747a18b5c000000000000000000000000551d5500f613a4bec77ba8b834b5eed52ad5764f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000d3c21bcecceda1000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000001a784379d99db4200000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000058d15e17628000

-----Decoded View---------------
Arg [0] : _assets (address[]): 0x6B175474E89094C44Da98b954EedeAC495271d0F
Arg [1] : _minAmounts (uint112[]): 1000000000000000000000000
Arg [2] : _maxAmounts (uint112[]): 2000000000000000000000000
Arg [3] : _maxSlippageRatios (uint256[]): 25000000000000000
Arg [4] : _master (address): 0x01BFd82675DBCc7762C84019cA518e701C0cD07e
Arg [5] : _priceOracle (address): 0xcafeaE8bF47145BD573BFC436D1728Cff224cA13
Arg [6] : _twapOracle (address): 0xcafea1C9f94e077DF44D95c4A1ad5a5747a18b5C
Arg [7] : _swapController (address): 0x551D5500F613a4beC77BA8B834b5eEd52ad5764f

-----Encoded View---------------
16 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000140
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [3] : 00000000000000000000000000000000000000000000000000000000000001c0
Arg [4] : 00000000000000000000000001bfd82675dbcc7762c84019ca518e701c0cd07e
Arg [5] : 000000000000000000000000cafeae8bf47145bd573bfc436d1728cff224ca13
Arg [6] : 000000000000000000000000cafea1c9f94e077df44d95c4a1ad5a5747a18b5c
Arg [7] : 000000000000000000000000551d5500f613a4bec77ba8b834b5eed52ad5764f
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [9] : 0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [11] : 00000000000000000000000000000000000000000000d3c21bcecceda1000000
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [13] : 00000000000000000000000000000000000000000001a784379d99db42000000
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [15] : 0000000000000000000000000000000000000000000000000058d15e17628000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.