Contract 0xb0a51d5e242b6a86420ec34660b14d8b9cd4d5fc

 
Txn Hash
Block
From
To
Value [Txn Fee]
0x5c3bb0ac63fe5522fdc09c78415e3cf53c879ce727801c773178688774b4cec397468612021-08-04 17:30:501 day 1 hr ago0x4fdb601aebf2c6ad947d97a00b7eeaf71cc5bf93 IN  Soteria Finance: SOTE Token0 BNB0.00022026
0x4dbea8b844b530a5b989872372f24ba3e9fb677764273cc9074b0764af55cba397261262021-08-03 23:34:171 day 19 hrs ago0xe63b24365ba463eb4c9fbf6cb793de590b6a8f09 IN  Soteria Finance: SOTE Token0 BNB0.00022176
0x0b127ef6488d8f3b5823de54aae09aa073d33ced4419bfc593b0b0b9bf02936797084042021-08-03 8:13:562 days 10 hrs ago0x4fdb601aebf2c6ad947d97a00b7eeaf71cc5bf93 IN  Soteria Finance: SOTE Token0 BNB0.00022026
0x2bdf93232be66db6c38ff21b769116643de5d7f43687254019ebafd3b4a943f096943712021-08-02 19:39:022 days 22 hrs ago0xe4fa808b1b55b22292dee5d02a9cee5618219889 IN  Soteria Finance: SOTE Token0 BNB0.00022176
0x08014c1c0db8ebad53f188b3bad7305429a6e220e2c89dc58d6f8b2e26a819f096929572021-08-02 18:26:313 days 10 mins ago0xf42f002e0082582efffcb3e2afc38617d4a54da9 IN  Soteria Finance: SOTE Token0 BNB0.00022176
0x1895eff451269c94d0dad1b380c4b8e4fc4a022562ca731a5e9cf2507f8352e996596742021-08-01 13:37:444 days 4 hrs ago0xa5309ab126e2b1f52d200eb7bf3b694748be7e6f IN  Soteria Finance: SOTE Token0 BNB0.00014676
0xfcdfb573f700cf1de54ba8043bec8f30137a548f7f7c5b6495b242356ca169b996515402021-08-01 6:35:144 days 12 hrs ago0x7b3c5c1ff47fa7817b9212760e6c321880ce9fd8 IN  Soteria Finance: SOTE Token0 BNB0.00022026
0x02cb6867318ff3a640a1f91babbbb49aeaa5b9a17308a6d297f80c265892dc6a96276792021-07-31 10:05:315 days 8 hrs ago0x13948b4389cfe659e0e0d1377fdfe8867ac300a3 IN  Soteria Finance: SOTE Token0 BNB0.00022176
0xbb891e827ab2bfcd610c37ca4227030671da47917026bb2cb5422dfe35ce220894957892021-07-26 14:15:3410 days 4 hrs ago0x395d3833f618cf6e0e793d6516f94fb6df91d223 IN  Soteria Finance: SOTE Token0 BNB0.00014676
0xf765cce646142aa0ca0f54b3660c0af471168b9a2a9d6478a95c26545814e82394950002021-07-26 13:34:3810 days 5 hrs ago0x030593886191bd2f4a638f61e3bb1ebfef086776 IN  Soteria Finance: SOTE Token0 BNB0.00007242
0x2ba78addfc222bae744048a22682da243f6a41038d36db4c6e64576a7ff6216c94949852021-07-26 13:33:5310 days 5 hrs ago0x030593886191bd2f4a638f61e3bb1ebfef086776 IN  Soteria Finance: SOTE Token0 BNB0.00007242
0x4896fe29a12065baa6b4003bdbb52afa7f5713a8afdb27e12d277f0f1a1c464394546682021-07-25 3:24:4211 days 15 hrs ago0x3bc23cf1f9fbbb8c332be106804f46ff8074f9a8 IN  Soteria Finance: SOTE Token0 BNB0.00007242
0x41edc0e3121422580656686ad2dfdb2d43ec3fd272ebd1e5208edcd13184597f94546212021-07-25 3:22:2111 days 15 hrs ago0x3bc23cf1f9fbbb8c332be106804f46ff8074f9a8 IN  Soteria Finance: SOTE Token0 BNB0.00007242
0xdc002812f4627f6557d38878e5a04714b5c05c5250c05e438b7736533b8401bb94519152021-07-25 1:06:5611 days 17 hrs ago0x7c530944671704b91da6308b0aa59e7fbd86b625 IN  Soteria Finance: SOTE Token0 BNB0.000266112
0x4f3c5f11692a18e46d5f36a60db4fa138be31c19d6cd62e869e13dcdb6705fe394470002021-07-24 21:00:5311 days 21 hrs ago0x8e7852b4e034374fdcc9d01e3438478ebd4fa9a8 IN  Soteria Finance: SOTE Token0 BNB0.00022176
0xf443431abd23b90b3a40a84dba7b66ed6fa320a1dfc6329743394eaed596f20694392852021-07-24 14:29:1812 days 4 hrs ago0x22755883c970af108e1a8bb4cb925dc991fed71b IN  Soteria Finance: SOTE Token0 BNB0.000101388
0x1063df81eaf1646649ed893e1526ddfe6c277c47e248b10da0a6a280780deabc94305212021-07-24 7:05:5512 days 11 hrs ago0xb363dff88b3a6b717426eebdc18f716fdc837084 IN  Soteria Finance: SOTE Token0 BNB0.00022176
0xd3d90f5abbe5d70580ad2f1f3d10556d392e08f545f04353677af25dcbb2099e94263312021-07-24 3:35:2712 days 15 hrs ago0x24be7d398e09b054a00ae6c1914081da50ba2a4e IN  Soteria Finance: SOTE Token0 BNB0.00007242
0x2c90bdc02e67aa1b37b92cd781c82a419c260f7a1ab095610924aa9412d676e694134582021-07-23 16:49:4013 days 1 hr ago0x6aa3690391b5febb98e31932159dc72921ed3bfe IN  Soteria Finance: SOTE Token0 BNB0.00022176
0x178082eb165ccb08a4814308147df03f2ddd8a144da1cd3b749ad6021b1c060894133702021-07-23 16:45:1513 days 1 hr ago0x42838970285c5fef7e2f408a059eb6410bc60518 IN  Soteria Finance: SOTE Token0 BNB0.0002261952
0x4eadcc97ffafc2bda140e3eebd627974a85e952e43bdd4785b6bf5e4f0155da593394772021-07-21 3:04:1015 days 15 hrs ago0x9e159565a5651ea3fdc8b7c272d3fb24f9fdfac1 IN  Soteria Finance: SOTE Token0 BNB0.00007242
0x555f89927e8b7074265ce065c71131b2a45756b17dab2575bdbc037b5bf38dbc93394722021-07-21 3:03:5515 days 15 hrs ago0x9e159565a5651ea3fdc8b7c272d3fb24f9fdfac1 IN  Soteria Finance: SOTE Token0 BNB0.00007242
0xeca2724b74e2d8c7d53ad04eb26ea3f01ab1c3c2a0ec3ff5b4a3016eae715dc793393852021-07-21 2:59:3415 days 15 hrs ago0x9e159565a5651ea3fdc8b7c272d3fb24f9fdfac1 IN  Soteria Finance: SOTE Token0 BNB0.00022176
0xeb40ff8d18f78957b9930f31ef0da5ac6512551fc62c9e0635c954d7a6108c1293335542021-07-20 22:08:0115 days 20 hrs ago0x10b385eb6c517943da02e58a76c97b5a16e1c31d IN  Soteria Finance: SOTE Token0 BNB0.00022176
0x21a752226b24bc9ec03332da5a04bf2616266f9fb26a10ff2b7915cea0d7035793335032021-07-20 22:05:2815 days 20 hrs ago0x10b385eb6c517943da02e58a76c97b5a16e1c31d IN  Soteria Finance: SOTE Token0 BNB0.00022176
[ Download CSV Export 

OVERVIEW

Soteria Finance aims to offer blockchain based mutual / cover for defi project. Token users will be able to purchase Bakery cover, stake for rewards, or stake for claims assessment.

Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xd55cc63d494b625b69c5d6191a3e310a42944f61375ef16e0b36c01950f97b9354709752021-03-07 11:41:42151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xdfba7f31af14c3124f901c0e65a7da1b9bd39e6a09e05eb45391b1ecabc45a1254709752021-03-07 11:41:42151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xabf48ea15218463ef01fa199db00e9ea1580e806c3e8ca3214631d6966bf8e8854709752021-03-07 11:41:42151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x27e30f129b2232a6a703d901baa4201e97fcc1cd8d58ac4de738f92da191c2f654709752021-03-07 11:41:42151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xf836adba84a2ddabd608bbccecfb1af740744f961edd9ed59d141ccd8db5d31454709752021-03-07 11:41:42151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x6183607ea70b08d89c832777872ad978f3d9673645a3a2f213b0f2fc023d9c8354709742021-03-07 11:41:39151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x8d563431783606495f5599acd630cafb0701fa715f76ddbcf8d25cbaf11d196854709742021-03-07 11:41:39151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xada9976dd4717c6c69c7ecb30690ac072460f1e0baa9aeba287736821b9a1b0054709742021-03-07 11:41:39151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x539a6c8610c4ebc70d895801e8a77b34e7cadd0072a0480887d39bd61c477a8154709742021-03-07 11:41:39151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xd6f5acd184f1b67e52c10ab396b799aa5299c65c421b9ed2849be3f8cefd994754709742021-03-07 11:41:39151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xff2ee5ae4f8e6c30115ef8677386158d58a22b7696dac7c077a7a0cfd8d9a9ac54709732021-03-07 11:41:36151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xeb67826627ef15dbb463313bf67b467f980b4a89908b674f2734a4f461cc8c0354709732021-03-07 11:41:36151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x8138835e559077cce167b0c23b71a7fe60655b33234ee4cd526d9e75b3e6165554709732021-03-07 11:41:36151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x99a5badc66fff869d783114686688e414265f4e130395b74996c9ef2c320925454709732021-03-07 11:41:36151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x1417f846c09d01d2038a595cecdf42266b4f8db46af3364ce5eb06a9b053cb1254709732021-03-07 11:41:36151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xb707aaddafccae1b60846a8684f080fe522c8f4cc32733766f40d2143ab5944b54709722021-03-07 11:41:33151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x589932be7c45f0121817180a17c9471c609e23f29e79763989262ddabfc6f1ea54709722021-03-07 11:41:33151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x06009434e89d12979a973f231ed0d54755f4bee50024f7d304650cabb20ee09354709722021-03-07 11:41:33151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x1c6c0b9d9b244f2dc8a7c53ed793f33251804e60b7b278cb8da1e525b586010054709722021-03-07 11:41:33151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x1c5de909adcc03c6cd549d82d398c1e6c37e082490c3197f812642e3526f1a1954709712021-03-07 11:41:30151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xee8fe0fd297dc48d1fd9afcf1870119e62248a69e8b094101164612ceef706c654709712021-03-07 11:41:30151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xaf4ff657ee7c89a9b12d49c66254ef11e1a788af6ecb84adbe1a9673594cf05b54709712021-03-07 11:41:30151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xe5a37690c53d06c0b27edf3ab8ea77241472f4a1ed55efe21f34391c4ec6d18f54709712021-03-07 11:41:30151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0xe9c34b6369adf183a9a7cf83b35faf0ed90ad073dc746555d04b7f1ec837c1bc54709712021-03-07 11:41:30151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
0x9451ae23034e3bf1f0ed296482bcadc06a52623118bf5915adf8aa376073fc2e54709712021-03-07 11:41:30151 days 6 hrs ago Soteria Finance: wSOTE Token Soteria Finance: SOTE Token0 BNB
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SOTEToken

Compiler Version
v0.5.17+commit.d19bba13

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, GNU GPLv3 license

Contract Source Code (Solidity)

/**
 *Submitted for verification at BscScan.com on 2020-12-21
*/

pragma solidity 0.5.17;


/* Copyright (C) 2017 Soteria.fund

  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/ */
contract ISOTEMaster {

    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);
}

contract Iupgradable {

    ISOTEMaster public ms;
    address public soteMasterAddress;

    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 = ISOTEMaster(_masterAddress);
        soteMasterAddress = _masterAddress;
    }

}

/**
 * @title SafeMath
 * @dev Math operations with safety checks that revert on error
 */
library SafeMath {

    /**
    * @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;
    }
}

/* Copyright (C) 2017 Soteria.fund

  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/ */
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;
    }
}

/* 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/ */
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;

}

/* 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/ */
contract IMaster {
    function getLatestAddress(bytes2 _module) public 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);
    } 

}

/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
interface IERC20 {
    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
    );
}

/* Copyright (C) 2017 Soteria.fund

  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/ */
contract SOTEToken is IERC20 {
    using SafeMath 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 = "SOTE";
    string public symbol = "SOTE";
    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);
    }
}

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;
}

/* Copyright (C) 2020 Soteria.fund

  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/ */
contract TokenFunctions is Iupgradable {
    using SafeMath for uint;

    MCR internal m1;
    MemberRoles internal mr;
    SOTEToken public tk;
    TokenController internal tc;
    TokenData internal td;
    QuotationData internal qd;
    ClaimsReward internal cr;
    Governance internal gv;
    PoolData internal pd;
    IPooledStaking 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 _coverPriceSOTE cover price in SOTE.
     */
    function pushStakerRewards(address _contractAddress, uint _coverPriceSOTE) external onlyInternal {
        uint rewardValue = _coverPriceSOTE.mul(td.stakerCommissionPer()).div(100);
        pooledStaking.accumulateReward(_contractAddress, rewardValue);
    }

    /**
    * @dev Deprecated in favor of burnStakedTokens
    */
    function burnStakerLockedToken(uint, bytes4, uint) external {
        // noop
    }

    /**
    * @dev Burns tokens staked on smart contract covered by coverId. Called when a payout is succesfully executed.
    * @param coverId cover id
    * @param coverCurrency cover currency
    * @param sumAssured amount of $curr to burn
    */
    function burnStakedTokens(uint coverId, bytes4 coverCurrency, uint sumAssured) external onlyInternal {
        (, address scAddress) = qd.getscAddressOfCover(coverId);
        uint tokenPrice = m1.calculateTokenPrice(coverCurrency);
        uint burnSOTEAmount = sumAssured.mul(1e18).div(tokenPrice);
        pooledStaking.pushBurn(scAddress, burnSOTEAmount);
    }

    /**
     * @dev Gets the total staked SOTE tokens against
     * Smart contract by all stakers
     * @param _stakedContractAddress smart contract address.
     * @return amount total staked SOTE tokens.
     */
    function deprecated_getTotalStakedTokensOnSmartContract(
        address _stakedContractAddress
    )
        external
        view
        returns(uint)
    {
        uint stakedAmount = 0;
        address stakerAddress;
        uint staketLen = td.getStakedContractStakersLength(_stakedContractAddress);

        for (uint i = 0; i < staketLen; i++) {
            stakerAddress = td.getStakedContractStakerByIndex(_stakedContractAddress, i);
            uint stakerIndex = td.getStakedContractStakerIndex(
                _stakedContractAddress, i);
            uint currentlyStaked;
            (, currentlyStaked) = _deprecated_unlockableBeforeBurningAndCanBurn(stakerAddress,
                _stakedContractAddress, stakerIndex);
            stakedAmount = stakedAmount.add(currentlyStaked);
        }

        return stakedAmount;
    }

    /**
     * @dev Returns amount of SOTE 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 SOTE 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 Returns total amount of staked SOTE Tokens on all smart contracts.
     * @param _stakerAddress address of the Staker.
     */
    function deprecated_getStakerAllLockedTokens(address _stakerAddress) external view returns (uint amount) {
        uint stakedAmount = 0;
        address scAddress;
        uint scIndex;
        for (uint i = 0; i < td.getStakerStakedContractLength(_stakerAddress); i++) {
            scAddress = td.getStakerStakedContractByIndex(_stakerAddress, i);
            scIndex = td.getStakerStakedContractIndex(_stakerAddress, i);
            uint currentlyStaked;
            (, currentlyStaked) = _deprecated_unlockableBeforeBurningAndCanBurn(_stakerAddress, scAddress, i);
            stakedAmount = stakedAmount.add(currentlyStaked);
        }
        amount = stakedAmount;
    }

    /**
     * @dev Returns total unlockable amount of staked SOTE Tokens on all smart contract .
     * @param _stakerAddress address of the Staker.
     */
    function deprecated_getStakerAllUnlockableStakedTokens(
        address _stakerAddress
    )
    external
    view
    returns (uint amount)
    {
        uint unlockableAmount = 0;
        address scAddress;
        uint scIndex;
        for (uint i = 0; i < td.getStakerStakedContractLength(_stakerAddress); i++) {
            scAddress = td.getStakerStakedContractByIndex(_stakerAddress, i);
            scIndex = td.getStakerStakedContractIndex(_stakerAddress, i);
            unlockableAmount = unlockableAmount.add(
                _deprecated_getStakerUnlockableTokensOnSmartContract(_stakerAddress, scAddress,
                scIndex));
        }
        amount = unlockableAmount;
    }

    /**
     * @dev Change Dependent Contract Address
     */
    function changeDependentContractAddress() public {
        tk = SOTEToken(ms.tokenAddress());
        td = TokenData(ms.getLatestAddress("TD"));
        tc = TokenController(ms.getLatestAddress("TC"));
        cr = ClaimsReward(ms.getLatestAddress("CR"));
        qd = QuotationData(ms.getLatestAddress("QD"));
        m1 = MCR(ms.getLatestAddress("MC"));
        gv = Governance(ms.getLatestAddress("GV"));
        mr = MemberRoles(ms.getLatestAddress("MR"));
        pd = PoolData(ms.getLatestAddress("PD"));
        pooledStaking = IPooledStaking(ms.getLatestAddress("PS"));
    }

    /**
     * @dev Gets the Token price in a given currency
     * @param curr Currency name.
     * @return price Token Price.
     */
    function getTokenPrice(bytes4 curr) public view returns(uint price) {
        price = m1.calculateTokenPrice(curr);
    }

    /**
     * @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 Internal function to gets amount of locked SOTE tokens,
     * staked against smartcontract by index
     * @param _stakerAddress address of user
     * @param _stakedContractAddress staked contract address
     * @param _stakedContractIndex index of staking
     */
    function deprecated_getStakerLockedTokensOnSmartContract (
        address _stakerAddress,
        address _stakedContractAddress,
        uint _stakedContractIndex
    )
        public
        view
        returns
        (uint amount)
    {
        amount = _deprecated_getStakerLockedTokensOnSmartContract(_stakerAddress,
            _stakedContractAddress, _stakedContractIndex);
    }

    /**
     * @dev Function to gets unlockable amount of locked SOTE
     * tokens, staked against smartcontract by index
     * @param stakerAddress address of staker
     * @param stakedContractAddress staked contract address
     * @param stakerIndex index of staking
     */
    function deprecated_getStakerUnlockableTokensOnSmartContract (
        address stakerAddress,
        address stakedContractAddress,
        uint stakerIndex
    )
        public
        view
        returns (uint)
    {
        return _deprecated_getStakerUnlockableTokensOnSmartContract(stakerAddress, stakedContractAddress,
        td.getStakerStakedContractIndex(stakerAddress, stakerIndex));
    }

    /**
     * @dev releases unlockable staked tokens to staker
     */
    function deprecated_unlockStakerUnlockableTokens(address _stakerAddress) public checkPause {
        uint unlockableAmount;
        address scAddress;
        bytes32 reason;
        uint scIndex;
        for (uint i = 0; i < td.getStakerStakedContractLength(_stakerAddress); i++) {
            scAddress = td.getStakerStakedContractByIndex(_stakerAddress, i);
            scIndex = td.getStakerStakedContractIndex(_stakerAddress, i);
            unlockableAmount = _deprecated_getStakerUnlockableTokensOnSmartContract(
            _stakerAddress, scAddress,
            scIndex);
            td.setUnlockableBeforeLastBurnTokens(_stakerAddress, i, 0);
            td.pushUnlockedStakedTokens(_stakerAddress, i, unlockableAmount);
            reason = keccak256(abi.encodePacked("UW", _stakerAddress, scAddress, scIndex));
            tc.releaseLockedTokens(_stakerAddress, reason, unlockableAmount);
        }
    }

    /**
     * @dev to get tokens of staker locked before burning that are allowed to burn
     * @param stakerAdd is the address of the staker
     * @param stakedAdd is the address of staked contract in concern
     * @param stakerIndex is the staker index in concern
     * @return amount of unlockable tokens
     * @return amount of tokens that can burn
     */
    function _deprecated_unlockableBeforeBurningAndCanBurn(
        address stakerAdd,
        address stakedAdd,
        uint stakerIndex
    )
    public
    view
    returns
    (uint amount, uint canBurn) {

        uint dateAdd;
        uint initialStake;
        uint totalBurnt;
        uint ub;
        (, , dateAdd, initialStake, , totalBurnt, ub) = td.stakerStakedContracts(stakerAdd, stakerIndex);
        canBurn = _deprecated_calculateStakedTokens(initialStake, now.sub(dateAdd).div(1 days), td.scValidDays());
        // Can't use SafeMaths for int.
        int v = int(initialStake - (canBurn) - (totalBurnt) - (
            td.getStakerUnlockedStakedTokens(stakerAdd, stakerIndex)) - (ub));
        uint currentLockedTokens = _deprecated_getStakerLockedTokensOnSmartContract(
            stakerAdd, stakedAdd, td.getStakerStakedContractIndex(stakerAdd, stakerIndex));
        if (v < 0) {
            v = 0;
        }
        amount = uint(v);
        if (canBurn > currentLockedTokens.sub(amount).sub(ub)) {
            canBurn = currentLockedTokens.sub(amount).sub(ub);
        }
    }

    /**
     * @dev to get tokens of staker that are unlockable
     * @param _stakerAddress is the address of the staker
     * @param _stakedContractAddress is the address of staked contract in concern
     * @param _stakedContractIndex is the staked contract index in concern
     * @return amount of unlockable tokens
     */
    function _deprecated_getStakerUnlockableTokensOnSmartContract (
        address _stakerAddress,
        address _stakedContractAddress,
        uint _stakedContractIndex
    )
        public
        view
        returns
        (uint amount)
    {
        uint initialStake;
        uint stakerIndex = td.getStakedContractStakerIndex(
            _stakedContractAddress, _stakedContractIndex);
        uint burnt;
        (, , , initialStake, , burnt,) = td.stakerStakedContracts(_stakerAddress, stakerIndex);
        uint alreadyUnlocked = td.getStakerUnlockedStakedTokens(_stakerAddress, stakerIndex);
        uint currentStakedTokens;
        (, currentStakedTokens) = _deprecated_unlockableBeforeBurningAndCanBurn(_stakerAddress,
            _stakedContractAddress, stakerIndex);
        amount = initialStake.sub(currentStakedTokens).sub(alreadyUnlocked).sub(burnt);
    }

    /**
     * @dev Internal function to get the amount of locked SOTE tokens,
     * staked against smartcontract by index
     * @param _stakerAddress address of user
     * @param _stakedContractAddress staked contract address
     * @param _stakedContractIndex index of staking
     */
    function _deprecated_getStakerLockedTokensOnSmartContract (
        address _stakerAddress,
        address _stakedContractAddress,
        uint _stakedContractIndex
    )
        internal
        view
        returns
        (uint amount)
    {
        bytes32 reason = keccak256(abi.encodePacked("UW", _stakerAddress,
            _stakedContractAddress, _stakedContractIndex));
        amount = tc.tokensLocked(_stakerAddress, reason);
    }

    /**
     * @dev Returns amount of SOTE 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 SOTE 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);
    }

    /**
     * @dev Internal function to gets remaining amount of staked SOTE tokens,
     * against smartcontract by index
     * @param _stakeAmount address of user
     * @param _stakeDays staked contract address
     * @param _validDays index of staking
     */
    function _deprecated_calculateStakedTokens(
        uint _stakeAmount,
        uint _stakeDays,
        uint _validDays
    )
        internal
        pure
        returns (uint amount)
    {
        if (_validDays > _stakeDays) {
            uint rf = ((_validDays.sub(_stakeDays)).mul(100000)).div(_validDays);
            amount = (rf.mul(_stakeAmount)).div(100000);
        } else {
            amount = 0;
        }
    }

    /**
     * @dev Gets the total staked SOTE tokens against Smart contract
     * by all stakers
     * @param _stakedContractAddress smart contract address.
     * @return amount total staked SOTE tokens.
     */
    function _deprecated_burnStakerTokenLockedAgainstSmartContract(
        address _stakerAddress,
        address _stakedContractAddress,
        uint _stakedContractIndex,
        uint _amount
    )
        internal
    {
        uint stakerIndex = td.getStakedContractStakerIndex(
            _stakedContractAddress, _stakedContractIndex);
        td.pushBurnedTokens(_stakerAddress, stakerIndex, _amount);
        bytes32 reason = keccak256(abi.encodePacked("UW", _stakerAddress,
            _stakedContractAddress, _stakedContractIndex));
        tc.burnLockedTokens(_stakerAddress, reason, _amount);
    }
}

/* 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/ */
contract IMemberRoles {

    event MemberRole(uint256 indexed roleId, bytes32 roleName, string roleDescription);
    
    /// @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) public;

    /// @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(address _memberAddress, uint _roleId, bool _active) public;

    /// @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 _authorized New authorized address against role id
    function changeAuthorized(uint _roleId, address _authorized) public;

    /// @dev Return number of member roles
    function totalRoles() public view returns(uint256);

    /// @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 allMemberAddress);

    /// @dev Gets all members' length
    /// @param _memberRoleId Member role id
    /// @return memberRoleData[_memberRoleId].memberAddress.length Member length
    function numberOfMembers(uint _memberRoleId) public view returns(uint);
    
    /// @dev Return member address who holds the right to add/remove any member from specific role.
    function authorized(uint _memberRoleId) public view returns(address);

    /// @dev Get All role ids array that has been assigned to a member so far.
    function roles(address _memberAddress) public view returns(uint[] memory 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);   
}

/**
 * @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);

}

/* Copyright (C) 2020 Soteria.fund

  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/ */
contract TokenController is IERC1132, Iupgradable {
    using SafeMath for uint256;

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

    SOTEToken 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 = SOTEToken(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;
            }
        }   
    }
}


/* Copyright (C) 2017 Soteria.fund

  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/ */
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 = 7;
        shockParameter = 50;
        variationPercX100 = 100; //1%
        iaRatesTime = 24 hours; //24 hours in seconds
        uniswapDeadline = 20 minutes;
        liquidityTradeCallbackTime = 4 hours;
        ethVolumeLimit = 4;
        capacityLimit = 10;
        allCurrencies.push("BNB");
        allCurrencyAssets["BNB"] = CurrencyAssets(address(0), 1000 * DECIMAL1E18, 0);
        allInvestmentCurrencies.push("BNB");
        allInvestmentAssets["BNB"] = InvestmentAssets(address(0), true, 2500, 10000, 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;
    }    
}

/* Copyright (C) 2017 Soteria.fund

  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/ */
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 premiumSOTE;
    }

    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 premiumSOTE,
        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 premiumSOTE
    )   
        external
        onlyInternal
    {
        uint expiryDate = now.add(uint(_coverPeriod).mul(1 days));
        allCovers.push(Cover(_userAddress, _currencyCode,
                _sumAssured, _coverPeriod, expiryDate, _scAddress, premiumSOTE));
        uint cid = allCovers.length.sub(1);
        userCover[_userAddress].push(cid);
        emit CoverDetailsEvent(cid, _scAddress, _sumAssured, expiryDate, premium, premiumSOTE, _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 SOTE.
    function getCoverPremiumSOTE(uint _cid) external view returns(uint _premiumSOTE) {
        _premiumSOTE = allCovers[_cid].premiumSOTE;
    }

    /// @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 premiumSOTE premium in SOTE
    function getCoverDetailsByCoverID1(
        uint _cid
    ) 
        external
        view
        returns (
            uint cid,
            address _memberAddress,
            address _scAddress,
            bytes4 _currencyCode,
            uint _sumAssured,  
            uint premiumSOTE 
        ) 
    {
        return (
            _cid,
            allCovers[_cid].memberAddress,
            allCovers[_cid].scAddress,
            allCovers[_cid].currencyCode,
            allCovers[_cid].sumAssured,
            allCovers[_cid].premiumSOTE
        );
    }

    /// @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 SOTE, 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;
    }
}

/* Copyright (C) 2017 Soteria.fund

  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/ */
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 = 100000000000000000; // 0.1 BNB
        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 sote 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 SOTE 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 SOTE 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;
    }
}

/* Copyright (C) 2017 Soteria.fund

  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/ */
contract Quotation is Iupgradable {
    using SafeMath for uint;

    TokenFunctions internal tf;
    TokenController internal tc;
    TokenData internal td;
    Pool1 internal p1;
    PoolData internal pd;
    QuotationData internal qd;
    MCR internal m1;
    MemberRoles internal mr;
    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"));
        p1 = Pool1(ms.getLatestAddress("P1"));
        pd = PoolData(ms.getLatestAddress("PD"));
        mr = MemberRoles(ms.getLatestAddress("MR"));
    }

    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 SOTE tokens.
     * @param smartCAdd Smart Contract Address
     */ 
    function makeCoverUsingSOTETokens(
        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);
    }

    /**
     * @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
        );
    }

    /** 
     * @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 initiate the membership and the cover 
     * @param smartCAdd is the smart contract address to make cover on
     * @param coverCurr is the currency used to make cover
     * @param coverDetails list of details related to cover like cover amount, expire time, coverCurrPrice and priceSOTE
     * @param coverPeriod is cover period for which cover is being bought
     * @param _v argument from vrs hash 
     * @param _r argument from vrs hash 
     * @param _s argument from vrs hash 
     */
    function initiateMembershipAndCover(
        address smartCAdd,
        bytes4 coverCurr,
        uint[] memory coverDetails,
        uint16 coverPeriod,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
    ) 
        public
        payable
        checkPause
    {
        require(coverDetails[3] > now);
        require(!qd.timestampRepeated(coverDetails[4]));
        qd.setTimestampRepeated(coverDetails[4]);
        require(!ms.isMember(msg.sender));
        require(qd.refundEligible(msg.sender) == false);
        uint joinFee = td.joiningFee();
        uint totalFee = joinFee;
        if (coverCurr == "BNB") {
            totalFee = joinFee.add(coverDetails[1]);
        } else {
            IERC20 erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr));
            require(erc20.transferFrom(msg.sender, address(this), coverDetails[1]));
        }
        require(msg.value == totalFee);
        require(verifySign(coverDetails, coverPeriod, coverCurr, smartCAdd, _v, _r, _s));
        qd.addHoldCover(msg.sender, smartCAdd, coverCurr, coverDetails, coverPeriod);
        qd.setRefundEligible(msg.sender, true);
    }

    /**
     * @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]);
        // if cover period of quote is less than 60 days.
        if (coverPeriod <= 60) {
            p1.closeCoverOraclise(cid, uint64(uint(coverPeriod).mul(1 days)));
        }
        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 vover.
     * @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
    )
        internal
    {
        require(coverDetails[3] > now);
        require(!qd.timestampRepeated(coverDetails[4]));
        qd.setTimestampRepeated(coverDetails[4]);
        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));
                address poolAdd = ms.getLatestAddress("P1");
                if (coverCurr == "BNB") {
                    p1.sendEther.value(coverDetails[1])();
                } else {
                    erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr)); //solhint-disable-line
                    require(erc20.transfer(poolAdd, 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 == "BNB") {
                    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 == "BNB") {
                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");
        }
              
    }
}

contract Factory {
    function getExchange(address token) public view returns (address);
    function getToken(address exchange) public view returns (address);
}

contract Exchange { 
    function getEthToTokenInputPrice(uint256 ethSold) public view returns(uint256);

    function getTokenToEthInputPrice(uint256 tokensSold) public view returns(uint256);

    function ethToTokenSwapInput(uint256 minTokens, uint256 deadline) public payable returns (uint256);

    function ethToTokenTransferInput(uint256 minTokens, uint256 deadline, address recipient)
        public payable returns (uint256);

    function tokenToEthSwapInput(uint256 tokensSold, uint256 minEth, uint256 deadline)
        public payable returns (uint256);

    function tokenToEthTransferInput(uint256 tokensSold, uint256 minEth, uint256 deadline, address recipient) 
        public payable returns (uint256);

    function tokenToTokenSwapInput(
        uint256 tokensSold,
        uint256 minTokensBought,
        uint256 minEthBought,
        uint256 deadline,
        address tokenAddress
    ) 
        public returns (uint256);

    function tokenToTokenTransferInput(
        uint256 tokensSold,
        uint256 minTokensBought,
        uint256 minEthBought,
        uint256 deadline,
        address recipient,
        address tokenAddress
    )
        public returns (uint256);
}

/* Copyright (C) 2017 Soteria.fund

  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/ */
contract Pool2 is Iupgradable {
    using SafeMath for uint;

    MCR internal m1;
    Pool1 internal p1;
    PoolData internal pd;
    Factory internal factory;
    address public uniswapFactoryAddress;
    uint internal constant DECIMAL1E18 = uint(10) ** 18;
    bool internal locked;

    constructor(address _uniswapFactoryAdd) public {
       
        uniswapFactoryAddress = _uniswapFactoryAdd;
        factory = Factory(_uniswapFactoryAdd);
    }

    function() external payable {}

    event Liquidity(bytes16 typeOf, bytes16 functionName);

    event Rebalancing(bytes4 iaCurr, uint tokenAmount);

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

    /**
     * @dev to change the uniswap factory address 
     * @param newFactoryAddress is the new factory address in concern
     * @return the status of the concerned coverId
     */
    function changeUniswapFactoryAddress(address newFactoryAddress) external onlyInternal {
        // require(ms.isOwner(msg.sender) || ms.checkIsAuthToGoverned(msg.sender));
        uniswapFactoryAddress = newFactoryAddress;
        factory = Factory(uniswapFactoryAddress);
    }

    /**
     * @dev On upgrade transfer all investment assets and ether to new Investment Pool
     * @param newPoolAddress New Investment Assest Pool address
     */
    function upgradeInvestmentPool(address payable newPoolAddress) external onlyInternal noReentrancy {
        uint len = pd.getInvestmentCurrencyLen();
        for (uint64 i = 1; i < len; i++) {
            bytes4 iaName = pd.getInvestmentCurrencyByIndex(i);
            _upgradeInvestmentPool(iaName, newPoolAddress);
        }

        if (address(this).balance > 0) {
            Pool2 newP2 = Pool2(newPoolAddress);
            newP2.sendEther.value(address(this).balance)();
        }
    }

    /**
     * @dev Internal Swap of assets between Capital 
     * and Investment Sub pool for excess or insufficient  
     * liquidity conditions of a given currency.
     */ 
    function internalLiquiditySwap(bytes4 curr) external onlyInternal noReentrancy {
        uint caBalance;
        uint baseMin;
        uint varMin;
        (, baseMin, varMin) = pd.getCurrencyAssetVarBase(curr);
        caBalance = _getCurrencyAssetsBalance(curr);

        if (caBalance > uint(baseMin).add(varMin).mul(2)) {
            _internalExcessLiquiditySwap(curr, baseMin, varMin, caBalance);
        } else if (caBalance < uint(baseMin).add(varMin)) {
            _internalInsufficientLiquiditySwap(curr, baseMin, varMin, caBalance);
            
        }
    }

    /**
     * @dev Saves a given investment asset details. To be called daily.
     * @param curr array of Investment asset name.
     * @param rate array of investment asset exchange rate.
     * @param date current date in yyyymmdd.
     */ 
    function saveIADetails(bytes4[] calldata curr, uint64[] calldata rate, uint64 date, bool bit) 
    external checkPause noReentrancy {
        bytes4 maxCurr;
        bytes4 minCurr;
        uint64 maxRate;
        uint64 minRate;
        //ONLY NOTARZIE ADDRESS CAN POST
        require(pd.isnotarise(msg.sender));
        (maxCurr, maxRate, minCurr, minRate) = _calculateIARank(curr, rate);
        pd.saveIARankDetails(maxCurr, maxRate, minCurr, minRate, date);
        pd.updatelastDate(date);
        uint len = curr.length;
        for (uint i = 0; i < len; i++) {
            pd.updateIAAvgRate(curr[i], rate[i]);
        }
    }

    /**
     * @dev External Trade for excess or insufficient  
     * liquidity conditions of a given currency.
     */ 
    function externalLiquidityTrade() external onlyInternal {
        
        bool triggerTrade;
        bytes4 curr;
        bytes4 minIACurr;
        bytes4 maxIACurr;
        uint amount;
        uint minIARate;
        uint maxIARate;
        uint baseMin;
        uint varMin;
        uint caBalance;


        (maxIACurr, maxIARate, minIACurr, minIARate) = pd.getIARankDetailsByDate(pd.getLastDate());
        uint len = pd.getAllCurrenciesLen();
        for (uint64 i = 0; i < len; i++) {
            curr = pd.getCurrenciesByIndex(i);
            (, baseMin, varMin) = pd.getCurrencyAssetVarBase(curr);
            caBalance = _getCurrencyAssetsBalance(curr);

            if (caBalance > uint(baseMin).add(varMin).mul(2)) { //excess
                amount = caBalance.sub(((uint(baseMin).add(varMin)).mul(3)).div(2)); //*10**18;
                triggerTrade = _externalExcessLiquiditySwap(curr, minIACurr, amount);
            } else if (caBalance < uint(baseMin).add(varMin)) { // insufficient
                amount = (((uint(baseMin).add(varMin)).mul(3)).div(2)).sub(caBalance);
                triggerTrade = _externalInsufficientLiquiditySwap(curr, maxIACurr, amount);
            }

            if (triggerTrade) {
                p1.triggerExternalLiquidityTrade();
            }
        }
    }

    /**
     * Iupgradable Interface to update dependent contract address
     */
    function changeDependentContractAddress() public onlyInternal {
        m1 = MCR(ms.getLatestAddress("MC"));
        pd = PoolData(ms.getLatestAddress("PD"));
        p1 = Pool1(ms.getLatestAddress("P1"));
    }

    function sendEther() public payable {
        
    }

    /** 
     * @dev Gets currency asset balance for a given currency name.
     */   
    function _getCurrencyAssetsBalance(bytes4 _curr) public view returns(uint caBalance) {
        if (_curr == "BNB") {
            caBalance = address(p1).balance;
        } else {
            IERC20 erc20 = IERC20(pd.getCurrencyAssetAddress(_curr));
            caBalance = erc20.balanceOf(address(p1));
        }
    }

    /** 
     * @dev Transfers ERC20 investment asset from this Pool to another Pool.
     */ 
    function _transferInvestmentAsset(
        bytes4 _curr,
        address _transferTo,
        uint _amount
    ) 
        internal
    {
        if (_curr == "BNB") {
            if (_amount > address(this).balance)
                _amount = address(this).balance;
            p1.sendEther.value(_amount)();
        } else {
            IERC20 erc20 = IERC20(pd.getInvestmentAssetAddress(_curr));
            if (_amount > erc20.balanceOf(address(this)))
                _amount = erc20.balanceOf(address(this));
            require(erc20.transfer(_transferTo, _amount));
        }
    }

    /**
     * @dev to perform rebalancing 
     * @param iaCurr is the investment asset currency
     * @param iaRate is the investment asset rate
     */
    function _rebalancingLiquidityTrading(
        bytes4 iaCurr,
        uint64 iaRate
    ) 
        internal
        checkPause
    {
        uint amountToSell;
        uint totalRiskBal = pd.getLastVfull();
        uint intermediaryEth;
        uint ethVol = pd.ethVolumeLimit();

        totalRiskBal = (totalRiskBal.mul(100000)).div(DECIMAL1E18);
        Exchange exchange;
        if (totalRiskBal > 0) {
            amountToSell = ((totalRiskBal.mul(2).mul(
                iaRate)).mul(pd.variationPercX100())).div(100 * 100 * 100000);
            amountToSell = (amountToSell.mul(
                10**uint(pd.getInvestmentAssetDecimals(iaCurr)))).div(100); // amount of asset to sell

            if (iaCurr != "BNB" && _checkTradeConditions(iaCurr, iaRate, totalRiskBal)) { 
                exchange = Exchange(factory.getExchange(pd.getInvestmentAssetAddress(iaCurr)));
                intermediaryEth = exchange.getTokenToEthInputPrice(amountToSell);
                if (intermediaryEth > (address(exchange).balance.mul(ethVol)).div(100)) { 
                    intermediaryEth = (address(exchange).balance.mul(ethVol)).div(100);
                    amountToSell = (exchange.getEthToTokenInputPrice(intermediaryEth).mul(995)).div(1000);
                }
                IERC20 erc20;
                erc20 = IERC20(pd.getCurrencyAssetAddress(iaCurr));
                erc20.approve(address(exchange), amountToSell);
                exchange.tokenToEthSwapInput(amountToSell, (exchange.getTokenToEthInputPrice(
                    amountToSell).mul(995)).div(1000), pd.uniswapDeadline().add(now));
            } else if (iaCurr == "BNB" && _checkTradeConditions(iaCurr, iaRate, totalRiskBal)) {

                _transferInvestmentAsset(iaCurr, ms.getLatestAddress("P1"), amountToSell);
            }
            emit Rebalancing(iaCurr, amountToSell); 
        }
    }

    /**
     * @dev Checks whether trading is required for a  
     * given investment asset at a given exchange rate.
     */ 
    function _checkTradeConditions(
        bytes4 curr,
        uint64 iaRate,
        uint totalRiskBal
    )
        internal
        view
        returns(bool check)
    {
        if (iaRate > 0) {
            uint iaBalance =  _getInvestmentAssetBalance(curr).div(DECIMAL1E18);
            if (iaBalance > 0 && totalRiskBal > 0) {
                uint iaMax;
                uint iaMin;
                uint checkNumber;
                uint z;
                (iaMin, iaMax) = pd.getInvestmentAssetHoldingPerc(curr);
                z = pd.variationPercX100();
                checkNumber = (iaBalance.mul(100 * 100000)).div(totalRiskBal.mul(iaRate));
                if ((checkNumber > ((totalRiskBal.mul(iaMax.add(z))).mul(100000)).div(100)) ||
                    (checkNumber < ((totalRiskBal.mul(iaMin.sub(z))).mul(100000)).div(100)))
                    check = true; //eligibleIA
            }
        }
    }    

    /** 
     * @dev Gets the investment asset rank.
     */ 
    function _getIARank(
        bytes4 curr,
        uint64 rateX100,
        uint totalRiskPoolBalance
    ) 
        internal
        view
        returns (int rhsh, int rhsl) //internal function
    {

        uint currentIAmaxHolding;
        uint currentIAminHolding;
        uint iaBalance = _getInvestmentAssetBalance(curr);
        (currentIAminHolding, currentIAmaxHolding) = pd.getInvestmentAssetHoldingPerc(curr);
        
        if (rateX100 > 0) {
            uint rhsf;
            rhsf = (iaBalance.mul(1000000)).div(totalRiskPoolBalance.mul(rateX100));
            rhsh = int(rhsf - currentIAmaxHolding);
            rhsl = int(rhsf - currentIAminHolding);
        }
    }

    /** 
     * @dev Calculates the investment asset rank.
     */  
    function _calculateIARank(
        bytes4[] memory curr,
        uint64[] memory rate
    )
        internal
        view
        returns(
            bytes4 maxCurr,
            uint64 maxRate,
            bytes4 minCurr,
            uint64 minRate
        )  
    {
        int max = 0;
        int min = -1;
        int rhsh;
        int rhsl;
        uint totalRiskPoolBalance;
        (totalRiskPoolBalance, ) = m1.calVtpAndMCRtp();
        uint len = curr.length;
        for (uint i = 0; i < len; i++) {
            rhsl = 0;
            rhsh = 0;
            if (pd.getInvestmentAssetStatus(curr[i])) {
                (rhsh, rhsl) = _getIARank(curr[i], rate[i], totalRiskPoolBalance);
                if (rhsh > max || i == 0) {
                    max = rhsh;
                    maxCurr = curr[i];
                    maxRate = rate[i];
                }
                if (rhsl < min || rhsl == 0 || i == 0) {
                    min = rhsl;
                    minCurr = curr[i];
                    minRate = rate[i];
                }
            }
        }
    }

    /**
     * @dev to get balance of an investment asset 
     * @param _curr is the investment asset in concern
     * @return the balance
     */
    function _getInvestmentAssetBalance(bytes4 _curr) internal view returns (uint balance) {
        if (_curr == "BNB") {
            balance = address(this).balance;
        } else {
            IERC20 erc20 = IERC20(pd.getInvestmentAssetAddress(_curr));
            balance = erc20.balanceOf(address(this));
        }
    }

    /**
     * @dev Creates Excess liquidity trading order for a given currency and a given balance.
     */  
    function _internalExcessLiquiditySwap(bytes4 _curr, uint _baseMin, uint _varMin, uint _caBalance) internal {
        // require(ms.isInternal(msg.sender) || md.isnotarise(msg.sender));
        bytes4 minIACurr;
        // uint amount;
        
        (, , minIACurr, ) = pd.getIARankDetailsByDate(pd.getLastDate());
        if (_curr == minIACurr) {
            // amount = _caBalance.sub(((_baseMin.add(_varMin)).mul(3)).div(2)); //*10**18;
            p1.transferCurrencyAsset(_curr, _caBalance.sub(((_baseMin.add(_varMin)).mul(3)).div(2)));
        } else {
            p1.triggerExternalLiquidityTrade();
        }
    }

    /** 
     * @dev insufficient liquidity swap  
     * for a given currency and a given balance.
     */ 
    function _internalInsufficientLiquiditySwap(bytes4 _curr, uint _baseMin, uint _varMin, uint _caBalance) internal {
        
        bytes4 maxIACurr;
        uint amount;
        
        (maxIACurr, , , ) = pd.getIARankDetailsByDate(pd.getLastDate());
        
        if (_curr == maxIACurr) {
            amount = (((_baseMin.add(_varMin)).mul(3)).div(2)).sub(_caBalance);
            _transferInvestmentAsset(_curr, ms.getLatestAddress("P1"), amount);
        } else {
            IERC20 erc20 = IERC20(pd.getInvestmentAssetAddress(maxIACurr));
            if ((maxIACurr == "BNB" && address(this).balance > 0) || 
            (maxIACurr != "BNB" && erc20.balanceOf(address(this)) > 0))
                p1.triggerExternalLiquidityTrade();
            
        }
    }

    /**
     * @dev Creates External excess liquidity trading  
     * order for a given currency and a given balance.
     * @param curr Currency Asset to Sell
     * @param minIACurr Investment Asset to Buy  
     * @param amount Amount of Currency Asset to Sell
     */  
    function _externalExcessLiquiditySwap(
        bytes4 curr,
        bytes4 minIACurr,
        uint256 amount
    )
        internal
        returns (bool trigger)
    {
        uint intermediaryEth;
        Exchange exchange;
        IERC20 erc20;
        uint ethVol = pd.ethVolumeLimit();
        if (curr == minIACurr) {
            p1.transferCurrencyAsset(curr, amount);
        } else if (curr == "BNB" && minIACurr != "BNB") {
            
            exchange = Exchange(factory.getExchange(pd.getInvestmentAssetAddress(minIACurr)));
            if (amount > (address(exchange).balance.mul(ethVol)).div(100)) { // 4% BNB volume limit 
                amount = (address(exchange).balance.mul(ethVol)).div(100);
                trigger = true;
            }
            p1.transferCurrencyAsset(curr, amount);
            exchange.ethToTokenSwapInput.value(amount)
            (exchange.getEthToTokenInputPrice(amount).mul(995).div(1000), pd.uniswapDeadline().add(now));    
        } else if (curr != "BNB" && minIACurr == "BNB") {
            exchange = Exchange(factory.getExchange(pd.getCurrencyAssetAddress(curr)));
            erc20 = IERC20(pd.getCurrencyAssetAddress(curr));
            intermediaryEth = exchange.getTokenToEthInputPrice(amount);

            if (intermediaryEth > (address(exchange).balance.mul(ethVol)).div(100)) { 
                intermediaryEth = (address(exchange).balance.mul(ethVol)).div(100);
                amount = exchange.getEthToTokenInputPrice(intermediaryEth);
                intermediaryEth = exchange.getTokenToEthInputPrice(amount);
                trigger = true;
            }
            p1.transferCurrencyAsset(curr, amount);
            // erc20.decreaseAllowance(address(exchange), erc20.allowance(address(this), address(exchange)));
            erc20.approve(address(exchange), amount);
            
            exchange.tokenToEthSwapInput(amount, (
                intermediaryEth.mul(995)).div(1000), pd.uniswapDeadline().add(now));   
        } else {
            
            exchange = Exchange(factory.getExchange(pd.getCurrencyAssetAddress(curr)));
            intermediaryEth = exchange.getTokenToEthInputPrice(amount);

            if (intermediaryEth > (address(exchange).balance.mul(ethVol)).div(100)) { 
                intermediaryEth = (address(exchange).balance.mul(ethVol)).div(100);
                amount = exchange.getEthToTokenInputPrice(intermediaryEth);
                trigger = true;
            }
            
            Exchange tmp = Exchange(factory.getExchange(
                pd.getInvestmentAssetAddress(minIACurr))); // minIACurr exchange

            if (intermediaryEth > address(tmp).balance.mul(ethVol).div(100)) { 
                intermediaryEth = address(tmp).balance.mul(ethVol).div(100);
                amount = exchange.getEthToTokenInputPrice(intermediaryEth);
                trigger = true;   
            }
            p1.transferCurrencyAsset(curr, amount);
            erc20 = IERC20(pd.getCurrencyAssetAddress(curr));
            erc20.approve(address(exchange), amount);
            
            exchange.tokenToTokenSwapInput(amount, (tmp.getEthToTokenInputPrice(
                intermediaryEth).mul(995)).div(1000), (intermediaryEth.mul(995)).div(1000), 
                    pd.uniswapDeadline().add(now), pd.getInvestmentAssetAddress(minIACurr));
        }
    }

    /** 
     * @dev insufficient liquidity swap  
     * for a given currency and a given balance.
     * @param curr Currency Asset to buy
     * @param maxIACurr Investment Asset to sell
     * @param amount Amount of Investment Asset to sell
     */ 
    function _externalInsufficientLiquiditySwap(
        bytes4 curr,
        bytes4 maxIACurr,
        uint256 amount
    ) 
        internal
        returns (bool trigger)
    {   

        Exchange exchange;
        IERC20 erc20;
        uint intermediaryEth;
        // uint ethVol = pd.ethVolumeLimit();
        if (curr == maxIACurr) {
            _transferInvestmentAsset(curr, ms.getLatestAddress("P1"), amount);
        } else if (curr == "BNB" && maxIACurr != "BNB") { 
            exchange = Exchange(factory.getExchange(pd.getInvestmentAssetAddress(maxIACurr)));
            intermediaryEth = exchange.getEthToTokenInputPrice(amount);


            if (amount > (address(exchange).balance.mul(pd.ethVolumeLimit())).div(100)) { 
                amount = (address(exchange).balance.mul(pd.ethVolumeLimit())).div(100);
                // amount = exchange.getEthToTokenInputPrice(intermediaryEth);
                intermediaryEth = exchange.getEthToTokenInputPrice(amount);
                trigger = true;
            }
            
            erc20 = IERC20(pd.getCurrencyAssetAddress(maxIACurr));
            if (intermediaryEth > erc20.balanceOf(address(this))) {
                intermediaryEth = erc20.balanceOf(address(this));
            }
            // erc20.decreaseAllowance(address(exchange), erc20.allowance(address(this), address(exchange)));
            erc20.approve(address(exchange), intermediaryEth);
            exchange.tokenToEthTransferInput(intermediaryEth, (
                exchange.getTokenToEthInputPrice(intermediaryEth).mul(995)).div(1000), 
                pd.uniswapDeadline().add(now), address(p1)); 

        } else if (curr != "BNB" && maxIACurr == "BNB") {
            exchange = Exchange(factory.getExchange(pd.getCurrencyAssetAddress(curr)));
            intermediaryEth = exchange.getTokenToEthInputPrice(amount);
            if (intermediaryEth > address(this).balance)
                intermediaryEth = address(this).balance;
            if (intermediaryEth > (address(exchange).balance.mul
            (pd.ethVolumeLimit())).div(100)) { // 4% BNB volume limit 
                intermediaryEth = (address(exchange).balance.mul(pd.ethVolumeLimit())).div(100);
                trigger = true;
            }
            exchange.ethToTokenTransferInput.value(intermediaryEth)((exchange.getEthToTokenInputPrice(
                intermediaryEth).mul(995)).div(1000), pd.uniswapDeadline().add(now), address(p1));   
        } else {
            address currAdd = pd.getCurrencyAssetAddress(curr);
            exchange = Exchange(factory.getExchange(currAdd));
            intermediaryEth = exchange.getTokenToEthInputPrice(amount);
            if (intermediaryEth > (address(exchange).balance.mul(pd.ethVolumeLimit())).div(100)) { 
                intermediaryEth = (address(exchange).balance.mul(pd.ethVolumeLimit())).div(100);
                trigger = true;
            }
            Exchange tmp = Exchange(factory.getExchange(pd.getInvestmentAssetAddress(maxIACurr)));

            if (intermediaryEth > address(tmp).balance.mul(pd.ethVolumeLimit()).div(100)) { 
                intermediaryEth = address(tmp).balance.mul(pd.ethVolumeLimit()).div(100);
                // amount = exchange.getEthToTokenInputPrice(intermediaryEth);
                trigger = true;
            }

            uint maxIAToSell = tmp.getEthToTokenInputPrice(intermediaryEth);

            erc20 = IERC20(pd.getInvestmentAssetAddress(maxIACurr));
            uint maxIABal = erc20.balanceOf(address(this));
            if (maxIAToSell > maxIABal) {
                maxIAToSell = maxIABal;
                intermediaryEth = tmp.getTokenToEthInputPrice(maxIAToSell);
                // amount = exchange.getEthToTokenInputPrice(intermediaryEth);
            }
            amount = exchange.getEthToTokenInputPrice(intermediaryEth);
            erc20.approve(address(tmp), maxIAToSell);
            tmp.tokenToTokenTransferInput(maxIAToSell, (
                amount.mul(995)).div(1000), (
                    intermediaryEth), pd.uniswapDeadline().add(now), address(p1), currAdd);
        }
    }

    /** 
     * @dev Transfers ERC20 investment asset from this Pool to another Pool.
     */ 
    function _upgradeInvestmentPool(
        bytes4 _curr,
        address _newPoolAddress
    ) 
        internal
    {
        IERC20 erc20 = IERC20(pd.getInvestmentAssetAddress(_curr));
        if (erc20.balanceOf(address(this)) > 0)
            require(erc20.transfer(_newPoolAddress, erc20.balanceOf(address(this))));
    }
}

/* Copyright (C) 2017 Soteria.fund

  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/ */
contract Pool1 is Iupgradable {
    using SafeMath for uint;

    Quotation internal q2;
    SOTEToken internal tk;
    TokenController internal tc;
    TokenFunctions internal tf;
    Pool2 internal p2;
    PoolData internal pd;
    MCR internal m1;
    Claims public c1;
    TokenData internal td;
    bool internal locked;

    uint internal constant DECIMAL1E18 = uint(10) ** 18;
    // uint internal constant PRICE_STEP = uint(1000) * DECIMAL1E18;

    event Apiresult(address indexed sender, string msg, bytes32 myid);
    event Payout(address indexed to, uint coverId, uint tokens);

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

    function () external payable {} //solhint-disable-line

    /**
     * @dev Pays out the sum assured in case a claim is accepted
     * @param coverid Cover Id.
     * @param claimid Claim Id.
     * @return succ true if payout is successful, false otherwise. 
     */ 
    function sendClaimPayout(
        uint coverid,
        uint claimid,
        uint sumAssured,
        address payable coverHolder,
        bytes4 coverCurr
    )
        external
        onlyInternal
        noReentrancy
        returns(bool succ)
    {
        
        uint sa = sumAssured.div(DECIMAL1E18);
        bool check;
        IERC20 erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr));

        //Payout
        if (coverCurr == "BNB" && address(this).balance >= sumAssured) {
            // check = _transferCurrencyAsset(coverCurr, coverHolder, sumAssured);
            coverHolder.transfer(sumAssured);
            check = true;
        } else if (coverCurr == "DAI" && erc20.balanceOf(address(this)) >= sumAssured) {
            erc20.transfer(coverHolder, sumAssured);
            check = true;
        }
        
        if (check == true) {
            q2.removeSAFromCSA(coverid, sa);
            pd.changeCurrencyAssetVarMin(coverCurr, 
                pd.getCurrencyAssetVarMin(coverCurr).sub(sumAssured));
            emit Payout(coverHolder, coverid, sumAssured);
            succ = true;
        } else {
            c1.setClaimStatus(claimid, 12);
        }
        _triggerExternalLiquidityTrade();
        // p2.internalLiquiditySwap(coverCurr);

        tf.burnStakerLockedToken(coverid, coverCurr, sumAssured);
    }

    /**
     * @dev to trigger external liquidity trade
     */
    function triggerExternalLiquidityTrade() external onlyInternal {
        _triggerExternalLiquidityTrade();
    }

    ///@dev Oraclize call to close emergency pause.
    function closeEmergencyPause(uint time) external onlyInternal {
        bytes32 myid = _oraclizeQuery();
        _saveApiDetails(myid, "EP", 0);
    }

    /// @dev Calls the Oraclize Query to close a given Claim after a given period of time.
    /// @param id Claim Id to be closed
    /// @param time Time (in seconds) after which Claims assessment voting needs to be closed
    function closeClaimsOraclise(uint id, uint time) external onlyInternal {
        bytes32 myid = _oraclizeQuery();
        _saveApiDetails(myid, "CLA", id);
    }

    /// @dev Calls Oraclize Query to expire a given Cover after a given period of time.
    /// @param id Quote Id to be expired
    /// @param time Time (in seconds) after which the cover should be expired
    function closeCoverOraclise(uint id, uint64 time) external onlyInternal {
        bytes32 myid = _oraclizeQuery();
        _saveApiDetails(myid, "COV", id);
    }

    /// @dev Calls the Oraclize Query to initiate MCR calculation.
    /// @param time Time (in milliseconds) after which the next MCR calculation should be initiated
    function mcrOraclise(uint time) external onlyInternal {
        bytes32 myid = _oraclizeQuery();
        _saveApiDetails(myid, "MCR", 0);
    }

    /// @dev Calls the Oraclize Query in case MCR calculation fails.
    /// @param time Time (in seconds) after which the next MCR calculation should be initiated
    function mcrOracliseFail(uint id, uint time) external onlyInternal {
        bytes32 myid = _oraclizeQuery();
        _saveApiDetails(myid, "MCRF", id);
    }

    /// @dev Oraclize call to update investment asset rates.
    function saveIADetailsOracalise(uint time) external onlyInternal {
        bytes32 myid = _oraclizeQuery();
        _saveApiDetails(myid, "IARB", 0);
    }
    
    /**
     * @dev Transfers all assest (i.e BNB balance, Currency Assest) from old Pool to new Pool
     * @param newPoolAddress Address of the new Pool
     */
    function upgradeCapitalPool(address payable newPoolAddress) external noReentrancy onlyInternal {
        for (uint64 i = 1; i < pd.getAllCurrenciesLen(); i++) {
            bytes4 caName = pd.getCurrenciesByIndex(i);
            _upgradeCapitalPool(caName, newPoolAddress);
        }
        if (address(this).balance > 0) {
            Pool1 newP1 = Pool1(newPoolAddress);
            newP1.sendEther.value(address(this).balance)();
        }
    }

    /**
     * @dev Iupgradable Interface to update dependent contract address
     */
    function changeDependentContractAddress() public {
        m1 = MCR(ms.getLatestAddress("MC"));
        tk = SOTEToken(ms.tokenAddress());
        tf = TokenFunctions(ms.getLatestAddress("TF"));
        tc = TokenController(ms.getLatestAddress("TC"));
        pd = PoolData(ms.getLatestAddress("PD"));
        q2 = Quotation(ms.getLatestAddress("QT"));
        p2 = Pool2(ms.getLatestAddress("P2"));
        c1 = Claims(ms.getLatestAddress("CL"));
        td = TokenData(ms.getLatestAddress("TD"));
    }

    function sendEther() public payable {
        
    }

    /**
     * @dev transfers currency asset to an address
     * @param curr is the currency of currency asset to transfer
     * @param amount is amount of currency asset to transfer
     * @return boolean to represent success or failure
     */
    function transferCurrencyAsset(
        bytes4 curr,
        uint amount
    )
        public
        onlyInternal
        noReentrancy
        returns(bool)
    {
    
        return _transferCurrencyAsset(curr, amount);
    } 

    /// @dev Handles callback of external oracle query.
    function __callback(bytes32 myid, string memory result) public {
        result; //silence compiler warning
        // owner will be removed from production build
        ms.delegateCallBack(myid);
    }

    /// @dev Enables user to purchase cover with funding in BNB.
    /// @param smartCAdd Smart Contract Address
    function makeCoverBegin(
        address smartCAdd,
        bytes4 coverCurr,
        uint[] memory coverDetails,
        uint16 coverPeriod,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
    )
        public
        isMember
        checkPause
        payable
    {
        require(msg.value == coverDetails[1]);
        q2.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
        isMember
        checkPause
    {
        IERC20 erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr));
        require(erc20.transferFrom(msg.sender, address(this), coverDetails[1]), "Transfer failed");
        q2.verifyCoverDetails(msg.sender, smartCAdd, coverCurr, coverDetails, coverPeriod, _v, _r, _s);
    }

    /// @dev Enables user to purchase SOTE at the current token price.
    function buyToken() public payable isMember checkPause returns(bool success) {
        require(msg.value > 0);
        uint tokenPurchased = _getToken(address(this).balance, msg.value);
        tc.mint(msg.sender, tokenPurchased);
        success = true;
    }

    /// @dev Sends a given amount of Ether to a given address.
    /// @param amount amount (in wei) to send.
    /// @param _add Receiver's address.
    /// @return succ True if transfer is a success, otherwise False.
    function transferEther(uint amount, address payable _add) public noReentrancy checkPause returns(bool succ) {
        require(ms.checkIsAuthToGoverned(msg.sender), "Not authorized to Govern");
        succ = _add.send(amount);
    }

    /**
     * @dev Allows selling of SOTE for ether.
     * Seller first needs to give this contract allowance to
     * transfer/burn tokens in the SOTEToken contract
     * @param  _amount Amount of SOTE to sell
     * @return success returns true on successfull sale
     */
    function sellSOTETokens(uint _amount) public isMember noReentrancy checkPause returns(bool success) {
        require(tk.balanceOf(msg.sender) >= _amount, "Not enough balance");
        require(!tf.isLockedForMemberVote(msg.sender), "Member voted");
        require(_amount <= m1.getMaxSellTokens(), "exceeds maximum token sell limit");
        uint sellingPrice = _getWei(_amount);
        tc.burnFrom(msg.sender, _amount);
        msg.sender.transfer(sellingPrice);
        success = true;
    }

    /**
     * @dev gives the investment asset balance
     * @return investment asset balance
     */
    function getInvestmentAssetBalance() public view returns (uint balance) {
        IERC20 erc20;
        uint currTokens;
        for (uint i = 1; i < pd.getInvestmentCurrencyLen(); i++) {
            bytes4 currency = pd.getInvestmentCurrencyByIndex(i);
            erc20 = IERC20(pd.getInvestmentAssetAddress(currency));
            currTokens = erc20.balanceOf(address(p2));
            if (pd.getIAAvgRate(currency) > 0)
                balance = balance.add((currTokens.mul(100)).div(pd.getIAAvgRate(currency)));
        }

        balance = balance.add(address(p2).balance);
    }

    /**
     * @dev Returns the amount of wei a seller will get for selling SOTE
     * @param amount Amount of SOTE to sell
     * @return weiToPay Amount of wei the seller will get
     */
    function getWei(uint amount) public view returns(uint weiToPay) {
        return _getWei(amount);
    }

    /**
     * @dev Returns the amount of token a buyer will get for corresponding wei
     * @param weiPaid Amount of wei 
     * @return tokenToGet Amount of tokens the buyer will get
     */
    function getToken(uint weiPaid) public view returns(uint tokenToGet) {
        return _getToken((address(this).balance).add(weiPaid), weiPaid);
    }

    /**
     * @dev to trigger external liquidity trade
     */
    function _triggerExternalLiquidityTrade() internal {
        if (now > pd.lastLiquidityTradeTrigger().add(pd.liquidityTradeCallbackTime())) {
            pd.setLastLiquidityTradeTrigger();
        }
    }

    /**
     * @dev Returns the amount of wei a seller will get for selling SOTE
     * @param _amount Amount of SOTE to sell
     * @return weiToPay Amount of wei the seller will get
     */
    function _getWei(uint _amount) internal view returns(uint weiToPay) {
        uint tokenPrice;
        uint weiPaid;
        uint tokenSupply = tk.totalSupply();
        uint vtp;
        uint mcrFullperc;
        uint vFull;
        uint mcrtp;
        (mcrFullperc, , vFull, ) = pd.getLastMCR();
        (vtp, ) = m1.calVtpAndMCRtp();

        while (_amount > 0) {
            mcrtp = (mcrFullperc.mul(vtp)).div(vFull);
            tokenPrice = m1.calculateStepTokenPrice("BNB", mcrtp);
            tokenPrice = (tokenPrice.mul(975)).div(1000); //97.5%
            if (_amount <= td.priceStep().mul(DECIMAL1E18)) {
                weiToPay = weiToPay.add((tokenPrice.mul(_amount)).div(DECIMAL1E18));
                break;
            } else {
                _amount = _amount.sub(td.priceStep().mul(DECIMAL1E18));
                tokenSupply = tokenSupply.sub(td.priceStep().mul(DECIMAL1E18));
                weiPaid = (tokenPrice.mul(td.priceStep().mul(DECIMAL1E18))).div(DECIMAL1E18);
                vtp = vtp.sub(weiPaid);
                weiToPay = weiToPay.add(weiPaid);
            }
        }
    }

    /**
     * @dev gives the token
     * @param _poolBalance is the pool balance
     * @param _weiPaid is the amount paid in wei
     * @return the token to get
     */
    function _getToken(uint _poolBalance, uint _weiPaid) internal view returns(uint tokenToGet) {
        uint tokenPrice;
        uint superWeiLeft = (_weiPaid).mul(DECIMAL1E18);
        uint tempTokens;
        uint superWeiSpent;
        uint tokenSupply = tk.totalSupply();
        uint vtp;
        uint mcrFullperc;   
        uint vFull;
        uint mcrtp;
        (mcrFullperc, , vFull, ) = pd.getLastMCR();
        (vtp, ) = m1.calculateVtpAndMCRtp((_poolBalance).sub(_weiPaid));

        require(m1.calculateTokenPrice("BNB") > 0, "Token price can not be zero");
        while (superWeiLeft > 0) {
            mcrtp = (mcrFullperc.mul(vtp)).div(vFull);
            tokenPrice = m1.calculateStepTokenPrice("BNB", mcrtp);            
            tempTokens = superWeiLeft.div(tokenPrice);
            if (tempTokens <= td.priceStep().mul(DECIMAL1E18)) {
                tokenToGet = tokenToGet.add(tempTokens);
                break;
            } else {
                tokenToGet = tokenToGet.add(td.priceStep().mul(DECIMAL1E18));
                tokenSupply = tokenSupply.add(td.priceStep().mul(DECIMAL1E18));
                superWeiSpent = td.priceStep().mul(DECIMAL1E18).mul(tokenPrice);
                superWeiLeft = superWeiLeft.sub(superWeiSpent);
                vtp = vtp.add((td.priceStep().mul(DECIMAL1E18).mul(tokenPrice)).div(DECIMAL1E18));
            }
        }
    }

    /** 
     * @dev Save 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) internal {
        pd.saveApiDetails(myid, _typeof, id);
        pd.addInAllApiCall(myid);
    }

    /**
     * @dev transfers currency asset
     * @param _curr is currency of asset to transfer
     * @param _amount is the amount to be transferred
     * @return boolean representing the success of transfer
     */
    function _transferCurrencyAsset(bytes4 _curr, uint _amount) internal returns(bool succ) {
        if (_curr == "BNB") {
            if (address(this).balance < _amount)
                _amount = address(this).balance;
            p2.sendEther.value(_amount)();
            succ = true;
        } else {
            IERC20 erc20 = IERC20(pd.getCurrencyAssetAddress(_curr)); //solhint-disable-line
            if (erc20.balanceOf(address(this)) < _amount) 
                _amount = erc20.balanceOf(address(this));
            require(erc20.transfer(address(p2), _amount)); 
            succ = true;
            
        }
    } 

    /** 
     * @dev Transfers ERC20 Currency asset from this Pool to another Pool on upgrade.
     */ 
    function _upgradeCapitalPool(
        bytes4 _curr,
        address _newPoolAddress
    ) 
        internal
    {
        IERC20 erc20 = IERC20(pd.getCurrencyAssetAddress(_curr));
        if (erc20.balanceOf(address(this)) > 0)
            require(erc20.transfer(_newPoolAddress, erc20.balanceOf(address(this))));
    }
    
    // step for ApiId
    uint256 public reqc;

    /**
     * @dev oraclize query
     * @return id of oraclize query
     */
    function _oraclizeQuery() 
        internal
        returns (bytes32 id)
    {
        id = keccak256(abi.encodePacked(this, msg.sender, reqc));
        reqc++;
    }
}

/* Copyright (C) 2017 Soteria.fund

  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/ */
contract MCR is Iupgradable {
    using SafeMath for uint;

    Pool1 internal p1;
    PoolData internal pd;
    SOTEToken internal tk;
    QuotationData internal qd;
    MemberRoles internal mr;
    TokenData internal td;
    ProposalCategory internal proposalCategory;

    uint private constant DECIMAL1E18 = uint(10) ** 18;
    uint private constant DECIMAL1E05 = uint(10) ** 5;
    uint private constant DECIMAL1E19 = uint(10) ** 19;
    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
    );

    /** 
     * @dev Adds new MCR data.
     * @param mcrP  Minimum Capital Requirement in percentage.
     * @param vF Pool1 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);
    }

    /**
     * @dev Adds MCR Data for last failed attempt.
     */  
    function addLastMCRData(uint64 date) external checkPause  onlyInternal {
        uint64 lastdate = uint64(pd.getLastMCRDate());
        uint64 failedDate = uint64(date);
        if (failedDate >= lastdate) {
            uint mcrP;
            uint mcrE;
            uint vF;
            (mcrP, mcrE, vF, ) = pd.getLastMCR();
            uint len = pd.getAllCurrenciesLen();
            pd.pushMCRData(mcrP, mcrE, vF, date);
            for (uint j = 0; j < len; j++) {
                bytes4 currName = pd.getCurrenciesByIndex(j);
                pd.updateCAAvgRate(currName, pd.getCAAvgRate(currName));
            }

            emit MCREvent(date, block.number, new bytes4[](0), new uint[](0), mcrE, mcrP, vF);
            // Oraclize call for next MCR calculation
            _callOracliseForMCR();
        }
    }

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

    /** 
     * @dev Gets total sum assured(in BNB).
     * @return amount of sum assured
     */  
    function getAllSumAssurance() public view returns(uint amount) {
        uint len = pd.getAllCurrenciesLen();
        for (uint i = 0; i < len; i++) {
            bytes4 currName = pd.getCurrenciesByIndex(i);
            if (currName == "BNB") {
                amount = amount.add(qd.getTotalSumAssured(currName));
            } else {
                if (pd.getCAAvgRate(currName) > 0)
                    amount = amount.add((qd.getTotalSumAssured(currName).mul(100)).div(pd.getCAAvgRate(currName)));
            }
        }
    }

    /**
     * @dev Calculates V(Tp) and MCR%(Tp), i.e, Pool Fund Value in Ether 
     * and MCR% used in the Token Price Calculation.
     * @return vtp  Pool Fund Value in Ether used for the Token Price Model
     * @return mcrtp MCR% used in the Token Price Model. 
     */ 
    function _calVtpAndMCRtp(uint poolBalance) public view returns(uint vtp, uint mcrtp) {
        vtp = 0;
        IERC20 erc20;
        uint currTokens = 0;
        uint i;
        for (i = 1; i < pd.getAllCurrenciesLen(); i++) {
            bytes4 currency = pd.getCurrenciesByIndex(i);
            erc20 = IERC20(pd.getCurrencyAssetAddress(currency));
            currTokens = erc20.balanceOf(address(p1));
            if (pd.getCAAvgRate(currency) > 0)
                vtp = vtp.add((currTokens.mul(100)).div(pd.getCAAvgRate(currency)));
        }

        vtp = vtp.add(poolBalance).add(p1.getInvestmentAssetBalance());
        uint mcrFullperc;
        uint vFull;
        (mcrFullperc, , vFull, ) = pd.getLastMCR();
        if (vFull > 0) {
            mcrtp = (mcrFullperc.mul(vtp)).div(vFull);
        }
    }

    /**
     * @dev Calculates the Token Price of SOTE in a given currency.
     * @param curr Currency name.
     
     */
    function calculateStepTokenPrice(
        bytes4 curr,
        uint mcrtp
    ) 
        public
        view
        onlyInternal
        returns(uint tokenPrice)
    {
        return _calculateTokenPrice(curr, mcrtp);
    }

    /**
     * @dev Calculates the Token Price of SOTE in a given currency 
     * with provided token supply for dynamic token price calculation
     * @param curr Currency name.
     */ 
    function calculateTokenPrice (bytes4 curr) public view returns(uint tokenPrice) {
        uint mcrtp;
        (, mcrtp) = _calVtpAndMCRtp(address(p1).balance); 
        return _calculateTokenPrice(curr, mcrtp);
    }
    
    function calVtpAndMCRtp() public view returns(uint vtp, uint mcrtp) {
        return _calVtpAndMCRtp(address(p1).balance);
    }

    function calculateVtpAndMCRtp(uint poolBalance) public view returns(uint vtp, uint mcrtp) {
        return _calVtpAndMCRtp(poolBalance);
    }

    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) {
                upperThreshold = vtp.mul(120).mul(100).div((minCap));     //Max Threshold = [MAX(Vtp, Vfull) x 120] / mcrMinCap
            } else {
                upperThreshold = vF.mul(120).mul(100).div((minCap));
            }

            if (vtp > 0) {
                lower = totalSA.mul(DECIMAL1E18).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 max numbers of tokens that can be sold at the moment.
     */ 
    function getMaxSellTokens() public view returns(uint maxTokens) {
        uint baseMin = pd.getCurrencyAssetBaseMin("BNB");
        uint maxTokensAccPoolBal;
        if (address(p1).balance > baseMin.mul(50).div(100)) {
            maxTokensAccPoolBal = address(p1).balance.sub(
            (baseMin.mul(50)).div(100));        
        }
        maxTokensAccPoolBal = (maxTokensAccPoolBal.mul(DECIMAL1E18)).div(
            (calculateTokenPrice("BNB").mul(975)).div(1000));
        uint lastMCRPerc = pd.getLastMCRPerc();
        if (lastMCRPerc > 10000)
            maxTokens = (((uint(lastMCRPerc).sub(10000)).mul(2000)).mul(DECIMAL1E18)).div(10000);
        if (maxTokens > maxTokensAccPoolBal)
            maxTokens = maxTokensAccPoolBal;     
    }

    /**
     * @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 Calls oraclize query to calculate MCR details after 24 hours.
     */ 
    function _callOracliseForMCR() internal {
        p1.mcrOraclise(pd.mcrTime());
    }

    /**
     * @dev Calculates the Token Price of SOTE in a given currency 
     * with provided token supply for dynamic token price calculation
     * @param _curr Currency name.  
     * @return tokenPrice Token price.
     */ 
    function _calculateTokenPrice(
        bytes4 _curr,
        uint mcrtp
    )
        internal
        view
        returns(uint tokenPrice)
    {
        uint getA;
        uint getC;
        uint getCAAvgRate;
        uint tokenExponentValue = td.tokenExponent();
        // uint max = (mcrtp.mul(mcrtp).mul(mcrtp).mul(mcrtp));
        uint max = mcrtp ** tokenExponentValue;
        uint dividingFactor = tokenExponentValue.mul(4); 
        (getA, getC, getCAAvgRate) = pd.getTokenPriceDetails(_curr);
        uint mcrEth = pd.getLastMCREther();
        getC = getC.mul(DECIMAL1E18);
        tokenPrice = (mcrEth.mul(DECIMAL1E18).mul(max).div(getC)).div(10 ** dividingFactor);
        tokenPrice = tokenPrice.add(getA.mul(DECIMAL1E18).div(DECIMAL1E05));
        tokenPrice = tokenPrice.mul(getCAAvgRate * 10); 
        tokenPrice = (tokenPrice).div(10**3);
    } 
    
    /**
     * @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 vtp = 0;
        uint lowerThreshold = 0;
        uint upperThreshold = 0;
        if (len > 1) {
            (vtp, ) = _calVtpAndMCRtp(address(p1).balance);
            (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) {
            vtp = pd.getLastMCRDate(); // due to stack to deep error,we are reusing already declared variable
            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);
            // Oraclize call for next MCR calculation
            if (vtp < newMCRDate) {
                _callOracliseForMCR();
            }
        } else {
            p1.mcrOracliseFail(newMCRDate, pd.mcrFailTime());
        }
    }

}

/* Copyright (C) 2017 Soteria.fund

  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/ */
contract Claims is Iupgradable {
    using SafeMath for uint;

    
    TokenFunctions internal tf;
    SOTEToken internal tk;
    TokenController internal tc;
    ClaimsReward internal cr;
    Pool1 internal p1;
    ClaimsData internal cd;
    TokenData internal td;
    PoolData internal pd;
    Pool2 internal p2;
    QuotationData internal qd;
    MCR internal m1;

    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);
        // status = rewardStatus[statusnumber].claimStatusDesc;
    }

    /**
     * @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 curr = qd.getCurrencyOfCover(coverId);
        uint tokenx1e18 = m1.calculateTokenPrice(curr);
        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 = SOTEToken(ms.tokenAddress());
        td = TokenData(ms.getLatestAddress("TD"));
        tf = TokenFunctions(ms.getLatestAddress("TF"));
        tc = TokenController(ms.getLatestAddress("TC"));
        p1 = Pool1(ms.getLatestAddress("P1"));
        p2 = Pool2(ms.getLatestAddress("P2"));
        pd = PoolData(ms.getLatestAddress("PD"));
        cr = ClaimsReward(ms.getLatestAddress("CR"));
        cd = ClaimsData(ms.getLatestAddress("CD"));
        qd = QuotationData(ms.getLatestAddress("QD"));
        m1 = MCR(ms.getLatestAddress("MC"));
    }

    /**
     * @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()));
            p1.closeClaimsOraclise(claimID, uint64(pTime));
        }
        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 curr = qd.getCurrencyOfCover(coverId);
        uint tokenx1e18 = m1.calculateTokenPrice(curr);
        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));
        }
        uint time = now;
        cd.setClaimdateUpd(claimId, time);

        if (stat >= 2 && stat <= 5) {
            p1.closeClaimsOraclise(claimId, cd.maxVotingTime());
        }

        if (stat == 12 && (dateUpd.add(cd.payoutRetryTime()) <= now) && (state12Count < 60)) {
            p1.closeClaimsOraclise(claimId, cd.payoutRetryTime());
        } else if (stat == 12 && (dateUpd.add(cd.payoutRetryTime()) > now) && (state12Count < 60)) {
            uint64 timeLeft = uint64((dateUpd.add(cd.payoutRetryTime())).sub(now));
            p1.closeClaimsOraclise(claimId, timeLeft);
        }
    }

    /**
     * @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));
        bytes4 curr = qd.getCurrencyOfCover(coverId);
        uint sumAssured = qd.getCoverSumAssured(coverId).mul(DECIMAL1E18);
        pd.changeCurrencyAssetVarMin(curr, pd.getCurrencyAssetVarMin(curr).add(sumAssured));
        p2.internalLiquiditySwap(curr);
        p1.closeClaimsOraclise(len, cd.maxVotingTime());
    }
}

/* Copyright (C) 2020 Soteria.fund

  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.
contract ClaimsReward is Iupgradable {
     using SafeMath for uint;

    SOTEToken internal tk;
    TokenController internal tc;
    TokenFunctions internal tf;
    TokenData internal td;
    QuotationData internal qd;
    Claims internal c1;
    ClaimsData internal cd;
    Pool1 internal p1;
    Pool2 internal p2;
    PoolData internal pd;
    Governance internal gv;
    IPooledStaking internal pooledStaking;

    uint private constant DECIMAL1E18 = uint(10) ** 18;

    function changeDependentContractAddress() public onlyInternal {
        c1 = Claims(ms.getLatestAddress("CL"));
        cd = ClaimsData(ms.getLatestAddress("CD"));
        tk = SOTEToken(ms.tokenAddress());
        tc = TokenController(ms.getLatestAddress("TC"));
        td = TokenData(ms.getLatestAddress("TD"));
        tf = TokenFunctions(ms.getLatestAddress("TF"));
        p1 = Pool1(ms.getLatestAddress("P1"));
        p2 = Pool2(ms.getLatestAddress("P2"));
        pd = PoolData(ms.getLatestAddress("PD"));
        qd = QuotationData(ms.getLatestAddress("QD"));
        gv = Governance(ms.getLatestAddress("GV"));
        pooledStaking = IPooledStaking(ms.getLatestAddress("PS"));
    }

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

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

        uint status;
        (, 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"

            uint sumAssured = qd.getCoverSumAssured(coverid).mul(DECIMAL1E18);
            address payable coverHolder = qd.getCoverMemberAddress(coverid);
            bytes4 coverCurrency = qd.getCurrencyOfCover(coverid);
            bool success = p1.sendClaimPayout(coverid, claimid, sumAssured, coverHolder, coverCurrency);

            if (success) {
                tf.burnStakedTokens(coverid, coverCurrency, sumAssured);
                c1.setClaimStatus(claimid, 14);
            }
        }

        c1.changePendingClaimStart();
    }

    /// @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);

    }
    
    function claimPendingReward(uint records) public isMemberAndcheckPause {
        _claimRewardToBeDistributed(records);
        pooledStaking.withdrawReward(msg.sender);
    }

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

    /**
     * @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 sumAssured, uint status) internal {
        uint premiumSOTE = qd.getCoverPremiumSOTE(coverid);
        bytes4 curr = qd.getCurrencyOfCover(coverid);
        uint distributableTokens = premiumSOTE.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);
        }

        if (status == 6 || status == 9 || status == 11) {
            cd.changeFinalVerdict(claimid, -1);
            td.setDepositCN(coverid, false); // Unset flag
            tf.burnDepositCN(coverid); // burn Deposited CN

            pd.changeCurrencyAssetVarMin(curr, pd.getCurrencyAssetVarMin(curr).sub(sumAssured));
            p2.internalLiquiditySwap(curr);

        } else if (status == 7 || status == 8 || status == 10) {
            cd.changeFinalVerdict(claimid, 1);
            td.setDepositCN(coverid, false); // Unset flag
            tf.unlockCN(coverid);
            bool success = p1.sendClaimPayout(coverid, claimid, sumAssured, qd.getCoverMemberAddress(coverid), curr);
            if (success) {
                tf.burnStakedTokens(coverid, curr, sumAssured);
            }
        }
    }

    /// @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, sumAssured, 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, sumAssured, 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
    }
}

/* 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/ */
contract MemberRoles is IMemberRoles, Governed, Iupgradable {

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

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

    enum Role {UnAssigned, AdvisoryBoard, Member, Owner}

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

    MemberRoleDetails[] internal memberRoleData;
    bool internal constructorCheck;
    uint public maxABCount;
    bool public launched;
    uint public launchedOn;
    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 SOTEMaster 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 = SOTEToken(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 = ISOTEMaster(_masterAddress);
        soteMasterAddress = _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());
            // auto verdict user
            qd.setRefundEligible(_userAddress, false);
            dAppToken.addToWhitelist(_userAddress);
            _updateRole(_userAddress, uint(Role.Member), true);
            td.walletAddress().transfer(msg.value); //solhint-disable-line
        }
    }

    /**
     * @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        
    }


    /**
     * @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);
        emit switchedMembership(msg.sender, _add, now);
    }

    /// @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;
    }
}

/* 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/ */
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 Restricts calls to deprecated functions
    */
    modifier deprecated() {
        revert("Function deprecated");
        _;
    }

    /**
    * @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
        deprecated 
    {
    }

    /**
    * @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("upgradeContract(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 = ISOTEMaster(_masterAddress);
        soteMasterAddress = _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
        deprecated
    {
    }

    /**
    * @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;
    }

}

/* 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/ */
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 Initiates add solution 
    /// @param _solutionHash Solution hash having required data against adding solution
    function addSolution(
        uint _proposalId,
        string calldata _solutionHash, 
        bytes calldata _action
    ) 
        external; 

    /// @dev Opens proposal for voting
    function openProposalForVoting(uint _proposalId) 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 pauseProposal(uint _proposalId) public;
    
    function resumeProposal(uint _proposalId) public;
    
    function allowedToCatgorize() public view returns(uint roleId);

}

/**
 * @title Proxy
 * @dev Gives the possibility to delegate any call to a foreign implementation.
 */
contract Proxy {
    /**
    * @dev Fallback function allowing to perform a delegatecall to the given implementation.
    * This function will return whatever the implementation call returns
    */
    function () external payable {
        address _impl = implementation();
        require(_impl != address(0));

        assembly {
            let ptr := mload(0x40)
            calldatacopy(ptr, 0, calldatasize)
            let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
            let size := returndatasize
            returndatacopy(ptr, 0, size)

            switch result
            case 0 { revert(ptr, size) }
            default { return(ptr, size) }
            }
    }

    /**
    * @dev Tells the address of the implementation where every call will be delegated.
    * @return address of the implementation to which it will be delegated
    */
    function implementation() public view returns (address);
}

contract UpgradeabilityProxy is Proxy {
    /**
    * @dev This event will be emitted every time the implementation gets upgraded
    * @param implementation representing the address of the upgraded implementation
    */
    event Upgraded(address indexed implementation);

    // Storage position of the address of the current implementation
    bytes32 private constant IMPLEMENTATION_POSITION = keccak256("org.govblocks.proxy.implementation");

    /**
    * @dev Constructor function
    */
    constructor() public {}

    /**
    * @dev Tells the address of the current implementation
    * @return address of the current implementation
    */
    function implementation() public view returns (address impl) {
        bytes32 position = IMPLEMENTATION_POSITION;
        assembly {
            impl := sload(position)
        }
    }

    /**
    * @dev Sets the address of the current implementation
    * @param _newImplementation address representing the new implementation to be set
    */
    function _setImplementation(address _newImplementation) internal {
        bytes32 position = IMPLEMENTATION_POSITION;
        assembly {
        sstore(position, _newImplementation)
        }
    }

    /**
    * @dev Upgrades the implementation address
    * @param _newImplementation representing the address of the new implementation to be set
    */
    function _upgradeTo(address _newImplementation) internal {
        address currentImplementation = implementation();
        require(currentImplementation != _newImplementation);
        _setImplementation(_newImplementation);
        emit Upgraded(_newImplementation);
    }
}

/**
 * @title OwnedUpgradeabilityProxy
 * @dev This contract combines an upgradeability proxy with basic authorization control functionalities
 */
contract OwnedUpgradeabilityProxy is UpgradeabilityProxy {
    /**
    * @dev Event to show ownership has been transferred
    * @param previousOwner representing the address of the previous owner
    * @param newOwner representing the address of the new owner
    */
    event ProxyOwnershipTransferred(address previousOwner, address newOwner);

    // Storage position of the owner of the contract
    bytes32 private constant PROXY_OWNER_POSITION = keccak256("org.govblocks.proxy.owner");

    /**
    * @dev the constructor sets the original owner of the contract to the sender account.
    */
    constructor(address _implementation) public {
        _setUpgradeabilityOwner(msg.sender);
        _upgradeTo(_implementation);
    }

    /**
    * @dev Throws if called by any account other than the owner.
    */
    modifier onlyProxyOwner() {
        require(msg.sender == proxyOwner());
        _;
    }

    /**
    * @dev Tells the address of the owner
    * @return the address of the owner
    */
    function proxyOwner() public view returns (address owner) {
        bytes32 position = PROXY_OWNER_POSITION;
        assembly {
            owner := sload(position)
        }
    }

    /**
    * @dev Allows the current owner to transfer control of the contract to a newOwner.
    * @param _newOwner The address to transfer ownership to.
    */
    function transferProxyOwnership(address _newOwner) public onlyProxyOwner {
        require(_newOwner != address(0));
        _setUpgradeabilityOwner(_newOwner);
        emit ProxyOwnershipTransferred(proxyOwner(), _newOwner);
    }

    /**
    * @dev Allows the proxy owner to upgrade the current version of the proxy.
    * @param _implementation representing the address of the new implementation to be set.
    */
    function upgradeTo(address _implementation) public onlyProxyOwner {
        _upgradeTo(_implementation);
    }

    /**
     * @dev Sets the address of the owner
    */
    function _setUpgradeabilityOwner(address _newProxyOwner) internal {
        bytes32 position = PROXY_OWNER_POSITION;
        assembly {
            sstore(position, _newProxyOwner)
        }
    }
}


contract SOTEMaster is Governed {
    using SafeMath for uint;

    struct EmergencyPause {
        bool pause;
        uint time;
        bytes4 by;
    }

    EmergencyPause[] public emergencyPaused;

    bytes2[] internal allContractNames;
    mapping(address => bool) public contractsActive;
    mapping(bytes2 => address payable) internal allContractVersions;
    mapping(bytes2 => bool) public isProxy;
    mapping(bytes2 => bool) public isUpgradable;

    address public tokenAddress;

    bool internal reentrancyLock;


    bool public masterInitialized;
    address public owner;
    uint public pauseTime;
    bool constructorCheck;

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

    /// @dev to initiate master data
    /// @param _tokenAdd SOTE token address.
    function initiateMaster(address _tokenAdd,address payable _govAdd) external {

        OwnedUpgradeabilityProxy proxy =  OwnedUpgradeabilityProxy(address(uint160(address(this))));
        require(msg.sender == proxy.proxyOwner(),"Sender is not proxy owner.");
        require(!constructorCheck,"Constructor already ran.");
        constructorCheck = true;
        tokenAddress = _tokenAdd;
        owner = msg.sender;
        masterAddress = address(this);
        contractsActive[address(this)] = true;
        pauseTime = 28 days; //4 weeks
        
        // 1. init gov 
        allContractNames.push("GV");
        allContractVersions["GV"] = _govAdd;
        contractsActive[_govAdd] = true;
        isProxy["GV"] = true;
        
        // 2. set masterInitialized
        masterInitialized = true;
        
    }

    function upgradeMultipleImplementations(
        bytes2[] calldata _contractNames,
        address[] calldata _contractAddresses
        ) 
        external 
        onlyAuthorizedToGovern 
    {
        require(_contractNames.length == _contractAddresses.length,"Array length should be equal.");
        for (uint i=0; i < _contractNames.length; i++) {
            require(_contractAddresses[i] != address(0),"null address is not allowed.");
            require(isProxy[_contractNames[i]],"Contract should be proxy.");
            OwnedUpgradeabilityProxy proxy = OwnedUpgradeabilityProxy(allContractVersions[_contractNames[i]]);
            proxy.upgradeTo(_contractAddresses[i]);
        }
    }

    /// @dev Adds new internal contract
    /// @param _type pass 1 if contract is upgradable, 2 if contract is proxy, any other uint if none.
    function addNewInternalContract(
        bytes2 _contractName,
        address payable _contractAddress,
        uint _type
        ) 
    external 
    onlyAuthorizedToGovern {
        require(allContractVersions[_contractName] == address(0),"Contract code is already available.");
        require(_contractAddress != address(0),"NULL address is not allowed.");
        allContractNames.push(_contractName);
        address newInternalContract = _contractAddress; // Using extra varible to get rid of if condition.
        if (_type == 1) {
            isUpgradable[_contractName] = true;
        } else if (_type == 2) {
            newInternalContract = _generateProxy(_contractAddress);
            isProxy[_contractName] = true;
        }
        allContractVersions[_contractName] = address(uint160(newInternalContract));
        contractsActive[newInternalContract] = true;
        Iupgradable up = Iupgradable(allContractVersions[_contractName]);
        up.changeMasterAddress(address(this));
        up.changeDependentContractAddress();
    }

    /**
     * @dev Anyone can close a claim if oraclize fails to close it.
     * @param _claimId id of claim to be closed.
     */ 
    function closeClaim(uint _claimId) external {

        require(canCall(_claimId), "Payout retry time not reached.");
        ClaimsReward cr = ClaimsReward(getLatestAddress("CR"));
        cr.changeClaimStatus(_claimId);
    }

    /**
     * @dev  Handles the oraclize query callback.
     * @param myid ID of oraclize query to be processed
     */ 
    function delegateCallBack(bytes32 myid) external noReentrancy {
        PoolData pd = PoolData(getLatestAddress("PD"));
        uint callTime = pd.getDateUpdOfAPI(myid);
        uint dateAdd = pd.getDateAddOfAPI(myid);
        require(callTime == dateAdd, "Callback already received");

        bytes4 res = pd.getApiIdTypeOf(myid);
        pd.updateDateUpdOfAPI(myid);

        if (isPause()) {

            bytes4 by;
            (, , by) = getLastEmergencyPause();

            require(res == "EP", "Only callback of type EP is allowed during emergency pause");
            require(callTime.add(pauseTime) < now, "Callback was called too soon");
            require(by == "AB", "Emergency paused was not started by Advisory Board");

            addEmergencyPause(false, "AUT");
            return;
        }

        uint id = pd.getIdOfApiId(myid);

        if (res == "COV") {
            Quotation qt = Quotation(getLatestAddress("QT"));
            qt.expireCover(id);
            return;
        }

        if (res == "CLA") {
            require(canCall(id), "Payout retry time not reached");
            ClaimsReward cr = ClaimsReward(getLatestAddress("CR"));
            cr.changeClaimStatus(id);
            return;
        }

        if (res == "MCRF") {
            require(callTime.add(pd.mcrFailTime()) < now, "MCR posting time not reached");
            MCR m1 = MCR(getLatestAddress("MC"));
            m1.addLastMCRData(uint64(id));
            return;
        }

        if (res == "ULT") {
            require(callTime.add(pd.liquidityTradeCallbackTime()) < now, "Liquidity trade time not reached");
            Pool2 p2 = Pool2(getLatestAddress("P2"));
            p2.externalLiquidityTrade();
            return;
        }

        if (res == "MCR" || res == "IARB") {
            return;
        }

        revert("Invalid callback");
    }

    function getOwnerParameters(bytes8 code) external view returns(bytes8 codeVal, address val) {
        codeVal = code;
        QuotationData qd;
        PoolData pd;
        if (code == "MSWALLET") {
            TokenData td;
            td = TokenData(getLatestAddress("TD"));
            val = td.walletAddress();

        } else if (code == "MCRNOTA") {
            
            pd = PoolData(getLatestAddress("PD"));
            val = pd.notariseMCR();

        } else if (code == "DAIFEED") {
            pd = PoolData(getLatestAddress("PD"));
            val = pd.daiFeedAddress();

        } else if (code == "UNISWADD") {
            Pool2 p2;
            p2 = Pool2(getLatestAddress("P2"));
            val = p2.uniswapFactoryAddress();

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

            val = owner;

        } else if (code == "QUOAUTH") {
            
            qd = QuotationData(getLatestAddress("QD"));
            val = qd.authQuoteEngine();

        } else if (code == "KYCAUTH") {
            qd = QuotationData(getLatestAddress("QD"));
            val = qd.kycAuthAddress();

        }
        
    }

    /// @dev Add Emergency pause
    /// @param _pause to set Emergency Pause ON/OFF
    /// @param _by to set who Start/Stop EP
    function addEmergencyPause(bool _pause, bytes4 _by) public {
        require(_by == "AB" || _by == "AUT","Invalid call.");
        require(msg.sender == getLatestAddress("P1") || msg.sender == getLatestAddress("GV"),"Callable by P1 and GV only.");
        emergencyPaused.push(EmergencyPause(_pause, now, _by));
        if (_pause == false) {
            Claims c1 = Claims(allContractVersions["CL"]);
            c1.submitClaimAfterEPOff(); // Process claims submitted while EP was on
            c1.startAllPendingClaimsVoting(); // Resume voting on all pending claims
        }
    }

    ///@dev update time in seconds for which emergency pause is applied.
    function updatePauseTime(uint _time) public {

        require(isInternal(msg.sender),"Not internal call.");
        pauseTime = _time;
    }
    
    /// @dev upgrades All Address at a time
    function upgradeAllAddress() 
    public 
    onlyAuthorizedToGovern
    {
        _changeAllAddress();
    }
    
    function changeAllAddress(uint8 start,uint8 end) public 
    {
        uint i;
        for (i = start; i < end; i++) {
            contractsActive[allContractVersions[allContractNames[i]]] = true;
            Iupgradable up = Iupgradable(allContractVersions[allContractNames[i]]);
            up.changeDependentContractAddress(); 
        }
    }
    
    /// @dev upgrades contract at a time
    function upgradeContract(
        bytes2 _contractsName,
        address payable _contractsAddress
        ) 
    public 
    onlyAuthorizedToGovern
    {
        
        address payable oldAddress = allContractVersions[_contractsName];
        contractsActive[oldAddress] = false;
        allContractVersions[_contractsName] = _contractsAddress;
        contractsActive[_contractsAddress] = true;

        Iupgradable up = Iupgradable(allContractVersions[_contractsName]);
        up.changeMasterAddress(address(this));
    }

    /// @dev upgrades multiple contracts at a time
    function upgradeMultipleContracts(
        bytes2[] memory _contractsName,
        address payable[] memory _contractsAddress
        ) 
    public 
    onlyAuthorizedToGovern
    {
        require(_contractsName.length == _contractsAddress.length, "Array length should be equal.");
        
        for (uint i=0; i<_contractsName.length; i++) {
            address payable newAddress = _contractsAddress[i];
            require(newAddress != address(0),"NULL address is not allowed.");
            require(isUpgradable[_contractsName[i]],"Contract should be upgradable.");
            if (_contractsName[i] == "QT") {
                Quotation qt = Quotation(allContractVersions["QT"]);
                qt.transferAssetsToNewContract(newAddress);

            } else if (_contractsName[i] == "CR") {
                TokenController tc = TokenController(getLatestAddress("TC"));
                tc.addToWhitelist(newAddress);
                tc.removeFromWhitelist(allContractVersions["CR"]);
                ClaimsReward cr = ClaimsReward(allContractVersions["CR"]);
                cr.upgrade(newAddress);

            } else if (_contractsName[i] == "P1") {
                Pool1 p1 = Pool1(allContractVersions["P1"]);
                p1.upgradeCapitalPool(newAddress);


            } else if (_contractsName[i] == "P2") {
                Pool2 p2 = Pool2(allContractVersions["P2"]);
                p2.upgradeInvestmentPool(newAddress);

            }

            address payable oldAddress = allContractVersions[_contractsName[i]];
            contractsActive[oldAddress] = false;
            allContractVersions[_contractsName[i]] = newAddress;
            contractsActive[newAddress] = true;

            Iupgradable up = Iupgradable(allContractVersions[_contractsName[i]]);
            up.changeMasterAddress(address(this));
        }

        _changeAllAddress();
    }

    /// @dev checks whether the address is an internal contract address.
    function isInternal(address _contractAddress) public view returns(bool) {
        return contractsActive[_contractAddress];
    }

    /// @dev checks whether the address is the Owner or not.
    function isOwner(address _address) public view returns(bool) {
        return owner == _address;
    }

    /// @dev Checks whether emergency pause id on/not.
    function isPause() public view returns(bool) {
        uint length = emergencyPaused.length;
        return length > 0 && emergencyPaused[length - 1].pause;
    }

    /// @dev checks whether the address is a member of the mutual or not.
    function isMember(address _add) public view returns(bool) {
        MemberRoles mr = MemberRoles(getLatestAddress("MR"));
        return mr.checkRole(_add, uint(MemberRoles.Role.Member));
    }

    ///@dev Gets the number of emergency pause has been toggled.
    function getEmergencyPausedLength() public view returns(uint len) {
        len = emergencyPaused.length;
    }

    ///@dev Gets last emergency pause details.
    function getLastEmergencyPause() public view returns(bool _pause, uint _time, bytes4 _by) {
        _pause = false;
        _time = 0;
        _by = "";
        uint len = getEmergencyPausedLength();
        if (len > 0) {
            len = len.sub(1);
            _pause = emergencyPaused[len].pause;
            _time = emergencyPaused[len].time;
            _by = emergencyPaused[len].by;
        }
    }

    /// @dev Gets latest version name and address
    /// @return contractsName Latest version's contract names
    /// @return contractsAddress Latest version's contract addresses
    function getVersionData() 
        public 
        view 
        returns (
            bytes2[] memory contractsName,
            address[] memory contractsAddress
        ) 
    {
        contractsName = allContractNames;
        contractsAddress = new address[](allContractNames.length);

        for (uint i = 0; i < allContractNames.length; i++) {
            contractsAddress[i] = allContractVersions[allContractNames[i]];
        }
    }

    /**
     * @dev returns the address of token controller 
     * @return address is returned
     */
    function dAppLocker() public view returns(address _add) {

        _add = getLatestAddress("TC");

    }

    /**
     * @dev returns the address of sote token 
     * @return address is returned
     */
    function dAppToken() public view returns(address _add) {
        _add = tokenAddress;
    }

    /// @dev Gets latest contract address
    /// @param _contractName Contract name to fetch
    function getLatestAddress(bytes2 _contractName) public view returns(address payable contractAddress) {
        contractAddress = allContractVersions[_contractName];
    }

    /// @dev Creates a new version of contract addresses
    /// @param _contractAddresses Array of contract addresses which will be generated
    function addNewVersion(address payable[] memory _contractAddresses) public {

        require(msg.sender == owner && !masterInitialized,"Caller should be owner and should only be called once.");
        require(_contractAddresses.length == allContractNames.length, "array length not same");
        masterInitialized = true;

        MemberRoles mr = MemberRoles(_contractAddresses[14]);   
        // shoud send proxy address for proxy contracts (if not 1st time deploying) 
        // bool isMasterUpgrade = mr.soteMasterAddress() != address(0);

        for (uint i = 0; i < allContractNames.length; i++) {
            require(_contractAddresses[i] != address(0),"NULL address is not allowed.");
            allContractVersions[allContractNames[i]] = _contractAddresses[i];
            contractsActive[_contractAddresses[i]] = true;

        }

        // Need to override owner as owner in MR to avoid inconsistency as owner in MR is some other address. 
        (, address[] memory mrOwner) = mr.members(uint(MemberRoles.Role.Owner));
        owner = mrOwner[0];
    }

    /**
     * @dev to check if the address is authorized to govern or not 
     * @param _add is the address in concern
     * @return the boolean status status for the check
     */
    function checkIsAuthToGoverned(address _add) public view returns(bool) {
        return isAuthorizedToGovern(_add);
    }

    /// @dev Allow AB Members to Start Emergency Pause
    function startEmergencyPause() public  onlyAuthorizedToGovern {
        addEmergencyPause(true, "AB"); //Start Emergency Pause
        Pool1 p1 = Pool1(allContractVersions["P1"]);
        p1.closeEmergencyPause(pauseTime); //oraclize callback of 4 weeks
        Claims c1 = Claims(allContractVersions["CL"]);
        c1.pauseAllPendingClaimsVoting(); //Pause Voting of all pending Claims
    }
    
    /**
     * @dev to update the owner parameters 
     * @param code is the associated code 
     * @param val is value to be set
     */
    function updateOwnerParameters(bytes8 code, address payable val) public onlyAuthorizedToGovern {
        QuotationData qd;
        PoolData pd;
        if (code == "MSWALLET") {
            TokenData td;
            td = TokenData(getLatestAddress("TD"));
            td.changeWalletAddress(val);

        } else if (code == "MCRNOTA") {
            
            pd = PoolData(getLatestAddress("PD"));
            pd.changeNotariseAddress(val);

        } else if (code == "DAIFEED") {
            pd = PoolData(getLatestAddress("PD"));
            pd.changeDAIfeedAddress(val);

        } else if (code == "UNISWADD") {
            Pool2 p2;
            p2 = Pool2(getLatestAddress("P2"));
            p2.changeUniswapFactoryAddress(val);

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

            MemberRoles mr = MemberRoles(getLatestAddress("MR"));
            mr.swapOwner(val);
            owner = val;

        } else if (code == "QUOAUTH") {
            
            qd = QuotationData(getLatestAddress("QD"));
            qd.changeAuthQuoteEngine(val);

        } else if (code == "KYCAUTH") {
            qd = QuotationData(getLatestAddress("QD"));
            qd.setKycAuthAddress(val);

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

    /**
     * @dev to generater proxy 
     * @param _implementationAddress of the proxy
     */
    function _generateProxy(address _implementationAddress) internal returns(address) {
        OwnedUpgradeabilityProxy proxy = new OwnedUpgradeabilityProxy(_implementationAddress);
        return address(proxy);
    }
    

    /// @dev Sets the older versions of contract addresses as inactive and the latest one as active.
    function _changeAllAddress() internal {
        uint i;
        for (i = 0; i < allContractNames.length; i++) {
            
            contractsActive[allContractVersions[allContractNames[i]]] = true;
            Iupgradable up = Iupgradable(allContractVersions[allContractNames[i]]);
            up.changeDependentContractAddress(); 
        }
    }

    function canCall(uint _claimId) internal view returns(bool)
    {
        ClaimsData cd = ClaimsData(getLatestAddress("CD"));
        (, , , uint status, uint dateUpd, ) = cd.getClaim(_claimId);
        if (status == 12) {
            if (dateUpd.add(cd.payoutRetryTime()) > now) {
                return false;
            } 
        }
        return true;
    }
}

// /* 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/ */
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 public 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 Initiates add solution
    * To implement the governance interface
    */
    function addSolution(uint, string calldata, bytes calldata) external {
    }

    /**
    * @dev Opens proposal for voting
    * To implement the governance interface
    */
    function openProposalForVoting(uint) 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
        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 Pauses a proposal
    * To implement govblocks interface
    */
    function pauseProposal(uint) public {
    }

    /**
    * @dev Resumes a proposal
    * To implement govblocks interface
    */
    function resumeProposal(uint) public {
    }

    /**
    * @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);
        }
        (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));
        }

    }


}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_founderAddress","type":"address"},{"internalType":"uint256","name":"_initialSupply","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"}],"name":"BlackListed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"}],"name":"WhiteListed","type":"event"},{"constant":false,"inputs":[{"internalType":"address","name":"_member","type":"address"}],"name":"addToWhiteList","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"burnFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_newOperator","type":"address"}],"name":"changeOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isLockedForMV","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_of","type":"address"},{"internalType":"uint256","name":"_days","type":"uint256"}],"name":"lockForMemberVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"operatorTransfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_member","type":"address"}],"name":"removeFromWhiteList","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whiteListed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]

60c06040526004608081905263534f544560e01b60a09081526200002791600591906200018f565b5060408051808201909152600480825263534f544560e01b602090920191825262000055916006916200018f565b506007805460ff191660121790553480156200007057600080fd5b506040516200120e3803806200120e833981810160405260408110156200009657600080fd5b508051602090910151620000b482826001600160e01b03620000bc16565b505062000234565b6001600160a01b038216620000d057600080fd5b620000ec816004546200017560201b62000ce11790919060201c565b6004556001600160a01b038216600090815260208181526040909120546200011f91839062000ce162000175821b17901c565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6000828201838110156200018857600080fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620001d257805160ff191683800117855562000202565b8280016001018555821562000202579182015b8281111562000202578251825591602001919060010190620001e5565b506200021092915062000214565b5090565b6200023191905b808211156200021057600081556001016200021b565b90565b610fca80620002446000396000f3fe608060405234801561001057600080fd5b50600436106101425760003560e01c80634c47e71d116100b857806398fd371f1161007c57806398fd371f14610405578063a457c2d71461042b578063a9059cbb14610457578063b0e65d0714610483578063dd62ed3e146104af578063fa0fca84146104dd57610142565b80634c47e71d1461035b578063570ca7351461038757806370a08231146103ab57806379cc6790146103d157806395d89b41146103fd57610142565b806323b872dd1161010a57806323b872dd1461026a578063313ce567146102a057806339509351146102be57806340c10f19146102ea57806342966c681461031857806347ee03941461033557610142565b806301bf66481461014757806306394c9b1461018157806306fdde03146101a7578063095ea7b31461022457806318160ddd14610250575b600080fd5b61016d6004803603602081101561015d57600080fd5b50356001600160a01b0316610503565b604080519115158252519081900360200190f35b61016d6004803603602081101561019757600080fd5b50356001600160a01b0316610586565b6101af6105e5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101e95781810151838201526020016101d1565b50505050905090810190601f1680156102165780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61016d6004803603604081101561023a57600080fd5b506001600160a01b038135169060200135610673565b6102586106ef565b60408051918252519081900360200190f35b61016d6004803603606081101561028057600080fd5b506001600160a01b038135811691602081013590911690604001356106f5565b6102a86107ab565b6040805160ff9092168252519081900360200190f35b61016d600480360360408110156102d457600080fd5b506001600160a01b0381351690602001356107b4565b6103166004803603604081101561030057600080fd5b506001600160a01b038135169060200135610862565b005b61016d6004803603602081101561032e57600080fd5b50356108a2565b61016d6004803603602081101561034b57600080fd5b50356001600160a01b03166108b6565b6103166004803603604081101561037157600080fd5b506001600160a01b03813516906020013561093c565b61038f6109ca565b604080516001600160a01b039092168252519081900360200190f35b610258600480360360208110156103c157600080fd5b50356001600160a01b03166109de565b61016d600480360360408110156103e757600080fd5b506001600160a01b0381351690602001356109f9565b6101af610a0e565b6102586004803603602081101561041b57600080fd5b50356001600160a01b0316610a69565b61016d6004803603604081101561044157600080fd5b506001600160a01b038135169060200135610a7b565b61016d6004803603604081101561046d57600080fd5b506001600160a01b038135169060200135610ac4565b61016d6004803603604081101561049957600080fd5b506001600160a01b038135169060200135610b36565b610258600480360360408110156104c557600080fd5b506001600160a01b0381358116916020013516610bad565b61016d600480360360208110156104f357600080fd5b50356001600160a01b0316610bd8565b60075460009061010090046001600160a01b0316156105385760075461010090046001600160a01b0316331461053857600080fd5b6001600160a01b038216600081815260026020526040808220805460ff19169055517f7fd26be6fc92aff63f1f4409b2b2ddeb272a888031d7f55ec830485ec61941869190a2506001919050565b60075460009061010090046001600160a01b0316156105bb5760075461010090046001600160a01b031633146105bb57600080fd5b50600780546001600160a01b03831661010002610100600160a81b03199091161790556001919050565b6005805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561066b5780601f106106405761010080835404028352916020019161066b565b820191906000526020600020905b81548152906001019060200180831161064e57829003601f168201915b505050505081565b60006001600160a01b03831661068857600080fd5b3360008181526001602090815260408083206001600160a01b03881680855290835292819020869055805186815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a350600192915050565b60045490565b6001600160a01b038216600090815260026020526040812054839060ff1661071c57600080fd5b6001600160a01b038516600090815260036020526040902054421161074057600080fd5b6001600160a01b03851660009081526020819052604090205483111561076557600080fd5b6001600160a01b038516600090815260016020908152604080832033845290915290205483111561079557600080fd5b6107a0858585610bed565b506001949350505050565b60075460ff1681565b60006001600160a01b0383166107c957600080fd5b3360009081526001602090815260408083206001600160a01b03871684529091529020546107fd908363ffffffff610ce116565b3360008181526001602090815260408083206001600160a01b0389168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b60075461010090046001600160a01b0316156108945760075461010090046001600160a01b0316331461089457600080fd5b61089e8282610cfa565b5050565b60006108ae3383610d90565b506001919050565b60075460009061010090046001600160a01b0316156108eb5760075461010090046001600160a01b031633146108eb57600080fd5b6001600160a01b038216600081815260026020526040808220805460ff19166001179055517f2e00aa132a0165955a7de5481083fd2933e22d472949147a9c3c69eec84c17009190a2506001919050565b60075461010090046001600160a01b03161561096e5760075461010090046001600160a01b0316331461096e57600080fd5b6001600160a01b038216600090815260036020526040902054610997824263ffffffff610ce116565b111561089e576109ad814263ffffffff610ce116565b6001600160a01b0383166000908152600360205260409020555050565b60075461010090046001600160a01b031681565b6001600160a01b031660009081526020819052604090205490565b6000610a058383610e37565b50600192915050565b6006805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561066b5780601f106106405761010080835404028352916020019161066b565b60036020526000908152604090205481565b60006001600160a01b038316610a9057600080fd5b3360009081526001602090815260408083206001600160a01b03871684529091529020546107fd908363ffffffff610ec916565b6001600160a01b038216600090815260026020526040812054839060ff16610aeb57600080fd5b336000908152600360205260409020544211610b0657600080fd5b33600090815260208190526040902054831115610b2257600080fd5b610b2c8484610ede565b5060019392505050565b60075460009061010090046001600160a01b031615610b6b5760075461010090046001600160a01b03163314610b6b57600080fd5b6001600160a01b038316600090815260208190526040902054821115610b9057600080fd5b600754610a0590849061010090046001600160a01b031684610bed565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60026020526000908152604090205460ff1681565b6001600160a01b038316600090815260208190526040902054610c16908263ffffffff610ec916565b6001600160a01b038085166000908152602081905260408082209390935590841681522054610c4b908263ffffffff610ce116565b6001600160a01b03808416600090815260208181526040808320949094559186168152600182528281203382529091522054610c8d908263ffffffff610ec916565b6001600160a01b0380851660008181526001602090815260408083203384528252918290209490945580518581529051928616939192600080516020610f76833981519152929181900390910190a3505050565b600082820183811015610cf357600080fd5b9392505050565b6001600160a01b038216610d0d57600080fd5b600454610d20908263ffffffff610ce116565b6004556001600160a01b038216600090815260208190526040902054610d4c908263ffffffff610ce116565b6001600160a01b038316600081815260208181526040808320949094558351858152935192939192600080516020610f768339815191529281900390910190a35050565b6001600160a01b038216600090815260208190526040902054811115610db557600080fd5b600454610dc8908263ffffffff610ec916565b6004556001600160a01b038216600090815260208190526040902054610df4908263ffffffff610ec916565b6001600160a01b03831660008181526020818152604080832094909455835185815293519193600080516020610f76833981519152929081900390910190a35050565b6001600160a01b0382166000908152600160209081526040808320338452909152902054811115610e6757600080fd5b6001600160a01b0382166000908152600160209081526040808320338452909152902054610e9b908263ffffffff610ec916565b6001600160a01b038316600090815260016020908152604080832033845290915290205561089e8282610d90565b600082821115610ed857600080fd5b50900390565b33600090815260208190526040902054610efe908263ffffffff610ec916565b33600090815260208190526040808220929092556001600160a01b03841681522054610f30908263ffffffff610ce116565b6001600160a01b03831660008181526020818152604091829020939093558051848152905191923392600080516020610f768339815191529281900390910190a3505056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa265627a7a72315820a30806146fbedf9e86266afacbe1d5468f0edfcf7f45442628176f4ec378966b64736f6c634300051100320000000000000000000000002b14d048faedfafd42428b04d053905ce6173352000000000000000000000000000000000000000000084595161401484a000000

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000002b14d048faedfafd42428b04d053905ce6173352000000000000000000000000000000000000000000084595161401484a000000

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000002b14d048faedfafd42428b04d053905ce6173352
Arg [1] : 000000000000000000000000000000000000000000084595161401484a000000


Deployed ByteCode Sourcemap

50411:11185:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;50411:11185:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;55380:186;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;55380:186:0;-1:-1:-1;;;;;55380:186:0;;:::i;:::-;;;;;;;;;;;;;;;;;;55679:145;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;55679:145:0;-1:-1:-1;;;;;55679:145:0;;:::i;50856:27::-;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;50856:27:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;52993:244;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;52993:244:0;;;;;;;;:::i;51419:91::-;;;:::i;:::-;;;;;;;;;;;;;;;;57984:415;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;57984:415:0;;;;;;;;;;;;;;;;;:::i;50926:26::-;;;:::i;:::-;;;;;;;;;;;;;;;;;;;53711:383;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;53711:383:0;;;;;;;;:::i;56729:108::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;56729:108:0;;;;;;;;:::i;:::-;;55981:118;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;55981:118:0;;:::i;55078:180::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;55078:180:0;-1:-1:-1;;;;;55078:180:0;;:::i;58497:::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;58497:180:0;;;;;;;;:::i;50959:23::-;;;:::i;:::-;;;;-1:-1:-1;;;;;50959:23:0;;;;;;;;;;;;;;51726:106;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;51726:106:0;-1:-1:-1;;;;;51726:106:0;;:::i;56354:132::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;56354:132:0;;;;;;;;:::i;50890:29::-;;;:::i;50765:45::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;50765:45:0;-1:-1:-1;;;;;50765:45:0;;:::i;54573:393::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;54573:393:0;;;;;;;;:::i;57008:281::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;57008:281:0;;;;;;;;:::i;57484:211::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;57484:211:0;;;;;;;;:::i;52166:188::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;52166:188:0;;;;;;;;;;:::i;50712:44::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;50712:44:0;-1:-1:-1;;;;;50712:44:0;;:::i;55380:186::-;51128:8;;55455:4;;51128:8;;;-1:-1:-1;;;;;51128:8:0;:22;51124:72;;51187:8;;;;;-1:-1:-1;;;;;51187:8:0;51173:10;:22;51165:31;;;;;;-1:-1:-1;;;;;55472:20:0;;55495:5;55472:20;;;:11;:20;;;;;;:28;;-1:-1:-1;;55472:28:0;;;55516:20;;;55495:5;55516:20;-1:-1:-1;55554:4:0;55380:186;;;:::o;55679:145::-;51128:8;;55754:4;;51128:8;;;-1:-1:-1;;;;;51128:8:0;:22;51124:72;;51187:8;;;;;-1:-1:-1;;;;;51187:8:0;51173:10;:22;51165:31;;;;;;-1:-1:-1;55771:8:0;:23;;-1:-1:-1;;;;;55771:23:0;;;;-1:-1:-1;;;;;;55771:23:0;;;;;;:8;55679:145;;;:::o;50856:27::-;;;;;;;;;;;;;;;-1:-1:-1;;50856:27:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;52993:244::-;53058:4;-1:-1:-1;;;;;53083:21:0;;53075:30;;;;;;53127:10;53118:20;;;;:8;:20;;;;;;;;-1:-1:-1;;;;;53118:29:0;;;;;;;;;;;;:37;;;53171:36;;;;;;;53118:29;;53127:10;53171:36;;;;;;;;;;;-1:-1:-1;53225:4:0;52993:244;;;;:::o;51419:91::-;51490:12;;51419:91;:::o;57984:415::-;-1:-1:-1;;;;;51044:16:0;;58140:4;51044:16;;;:11;:16;;;;;;58118:2;;51044:16;;51036:25;;;;;;-1:-1:-1;;;;;58170:19:0;;;;;;:13;:19;;;;;;58192:3;-1:-1:-1;58162:34:0;;;;;;-1:-1:-1;;;;;58257:15:0;;:9;:15;;;;;;;;;;;58248:24;;;58240:33;;;;;;-1:-1:-1;;;;;58301:14:0;;;;;;:8;:14;;;;;;;;58316:10;58301:26;;;;;;;;58292:35;;;58284:44;;;;;;58339:30;58353:4;58359:2;58363:5;58339:13;:30::i;:::-;-1:-1:-1;58387:4:0;;57984:415;-1:-1:-1;;;;57984:415:0:o;50926:26::-;;;;;;:::o;53711:383::-;53834:4;-1:-1:-1;;;;;53864:21:0;;53856:30;;;;;;53951:10;53942:20;;;;:8;:20;;;;;;;;-1:-1:-1;;;;;53942:29:0;;;;;;;;;;:45;;53976:10;53942:45;:33;:45;:::i;:::-;53908:10;53899:20;;;;:8;:20;;;;;;;;-1:-1:-1;;;;;53899:29:0;;;;;;;;;;;;:89;;;54004:60;;;;;;53899:29;;54004:60;;;;;;;;;;;-1:-1:-1;54082:4:0;53711:383;;;;:::o;56729:108::-;51128:8;;;;;-1:-1:-1;;;;;51128:8:0;:22;51124:72;;51187:8;;;;;-1:-1:-1;;;;;51187:8:0;51173:10;:22;51165:31;;;;;;56807:22;56813:7;56822:6;56807:5;:22::i;:::-;56729:108;;:::o;55981:118::-;56027:4;56044:25;56050:10;56062:6;56044:5;:25::i;:::-;-1:-1:-1;56087:4:0;55981:118;;;:::o;55078:180::-;51128:8;;55148:4;;51128:8;;;-1:-1:-1;;;;;51128:8:0;:22;51124:72;;51187:8;;;;;-1:-1:-1;;;;;51187:8:0;51173:10;:22;51165:31;;;;;;-1:-1:-1;;;;;55165:20:0;;;;;;:11;:20;;;;;;:27;;-1:-1:-1;;55165:27:0;55188:4;55165:27;;;55208:20;;;55165;55208;-1:-1:-1;55246:4:0;55078:180;;;:::o;58497:::-;51128:8;;;;;-1:-1:-1;;;;;51128:8:0;:22;51124:72;;51187:8;;;;;-1:-1:-1;;;;;51187:8:0;51173:10;:22;51165:31;;;;;;-1:-1:-1;;;;;58601:18:0;;;;;;:13;:18;;;;;;58584:14;:5;58594:3;58584:14;:9;:14;:::i;:::-;:35;58580:89;;;58655:14;:5;58665:3;58655:14;:9;:14;:::i;:::-;-1:-1:-1;;;;;58634:18:0;;;;;;:13;:18;;;;;:35;58497:180;;:::o;50959:23::-;;;;;;-1:-1:-1;;;;;50959:23:0;;:::o;51726:106::-;-1:-1:-1;;;;;51808:16:0;51781:7;51808:16;;;;;;;;;;;;51726:106::o;56354:132::-;56417:4;56434:22;56444:4;56450:5;56434:9;:22::i;:::-;-1:-1:-1;56474:4:0;56354:132;;;;:::o;50890:29::-;;;;;;;;;;;;;;;-1:-1:-1;;50890:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;50765:45;;;;;;;;;;;;;:::o;54573:393::-;54701:4;-1:-1:-1;;;;;54731:21:0;;54723:30;;;;;;54818:10;54809:20;;;;:8;:20;;;;;;;;-1:-1:-1;;;;;54809:29:0;;;;;;;;;;:50;;54843:15;54809:50;:33;:50;:::i;57008:281::-;-1:-1:-1;;;;;51044:16:0;;57085:4;51044:16;;;:11;:16;;;;;;57072:2;;51044:16;;51036:25;;;;;;57126:10;57112:25;;;;:13;:25;;;;;;57140:3;-1:-1:-1;57104:40:0;;;;;;57215:10;57205:9;:21;;;;;;;;;;;57196:30;;;57188:39;;;;;;57238:20;57248:2;57252:5;57238:9;:20::i;:::-;-1:-1:-1;57277:4:0;;57008:281;-1:-1:-1;;;57008:281:0:o;57484:211::-;51128:8;;57568:4;;51128:8;;;-1:-1:-1;;;;;51128:8:0;:22;51124:72;;51187:8;;;;;-1:-1:-1;;;;;51187:8:0;51173:10;:22;51165:31;;;;;;-1:-1:-1;;;;;57602:15:0;;:9;:15;;;;;;;;;;;57593:24;;;57585:33;;;;;;57649:8;;57629:36;;57643:4;;57649:8;;;-1:-1:-1;;;;;57649:8:0;57659:5;57629:13;:36::i;52166:188::-;-1:-1:-1;;;;;52322:15:0;;;52290:7;52322:15;;;:8;:15;;;;;;;;:24;;;;;;;;;;;;;52166:188::o;50712:44::-;;;;;;;;;;;;;;;:::o;59365:353::-;-1:-1:-1;;;;;59515:15:0;;:9;:15;;;;;;;;;;;:26;;59535:5;59515:26;:19;:26;:::i;:::-;-1:-1:-1;;;;;59497:15:0;;;:9;:15;;;;;;;;;;;:44;;;;59568:13;;;;;;;:24;;59586:5;59568:24;:17;:24;:::i;:::-;-1:-1:-1;;;;;59552:13:0;;;:9;:13;;;;;;;;;;;:40;;;;59632:14;;;;;:8;:14;;;;;59647:10;59632:26;;;;;;;:37;;59663:5;59632:37;:30;:37;:::i;:::-;-1:-1:-1;;;;;59603:14:0;;;;;;;:8;:14;;;;;;;;59618:10;59603:26;;;;;;;;:66;;;;59685:25;;;;;;;;;;;59603:14;;-1:-1:-1;;;;;;;;;;;59685:25:0;;;;;;;;;;59365:353;;;:::o;4140:150::-;4198:7;4230:5;;;4254:6;;;;4246:15;;;;;;4281:1;4140:150;-1:-1:-1;;;4140:150:0:o;60065:271::-;-1:-1:-1;;;;;60141:21:0;;60133:30;;;;;;60189:12;;:24;;60206:6;60189:24;:16;:24;:::i;:::-;60174:12;:39;-1:-1:-1;;;;;60245:18:0;;:9;:18;;;;;;;;;;;:30;;60268:6;60245:30;:22;:30;:::i;:::-;-1:-1:-1;;;;;60224:18:0;;:9;:18;;;;;;;;;;;:51;;;;60291:37;;;;;;;60224:18;;:9;;-1:-1:-1;;;;;;;;;;;60291:37:0;;;;;;;;;60065:271;;:::o;60566:280::-;-1:-1:-1;;;;;60652:18:0;;:9;:18;;;;;;;;;;;60642:28;;;60634:37;;;;;;60699:12;;:24;;60716:6;60699:24;:16;:24;:::i;:::-;60684:12;:39;-1:-1:-1;;;;;60755:18:0;;:9;:18;;;;;;;;;;;:30;;60778:6;60755:30;:22;:30;:::i;:::-;-1:-1:-1;;;;;60734:18:0;;:9;:18;;;;;;;;;;;:51;;;;60801:37;;;;;;;60734:9;;-1:-1:-1;;;;;;;;;;;60801:37:0;;;;;;;;;;60566:280;;:::o;61171:422::-;-1:-1:-1;;;;;61259:17:0;;;;;;:8;:17;;;;;;;;61277:10;61259:29;;;;;;;;61250:38;;;61242:47;;;;;;-1:-1:-1;;;;;61503:17:0;;;;;;:8;:17;;;;;;;;61521:10;61503:29;;;;;;;;:50;;61547:5;61503:50;:33;:50;:::i;:::-;-1:-1:-1;;;;;61471:17:0;;;;;;:8;:17;;;;;;;;61489:10;61471:29;;;;;;;:82;61564:21;61480:7;61579:5;61564;:21::i;3914:150::-;3972:7;4005:1;4000;:6;;3992:15;;;;;;-1:-1:-1;4030:5:0;;;3914:150::o;58848:228::-;58948:10;58938:9;:21;;;;;;;;;;;:32;;58964:5;58938:32;:25;:32;:::i;:::-;58924:10;58914:9;:21;;;;;;;;;;;:56;;;;-1:-1:-1;;;;;58997:13:0;;;;;;:24;;59015:5;58997:24;:17;:24;:::i;:::-;-1:-1:-1;;;;;58981:13:0;;:9;:13;;;;;;;;;;;;:40;;;;59037:31;;;;;;;58981:13;;59046:10;;-1:-1:-1;;;;;;;;;;;59037:31:0;;;;;;;;;58848:228;;:::o

Swarm Source

bzzr://a30806146fbedf9e86266afacbe1d5468f0edfcf7f45442628176f4ec378966b
Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.