ETH Price: $3,116.08 (+0.23%)
Gas: 4 Gwei

Transaction Decoder

Block:
19723414 at Apr-24-2024 06:20:23 AM +UTC
Transaction Fee:
0.000921418578095134 ETH $2.87
Gas Used:
109,298 Gas / 8.430333383 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x00c83aeC...80501a7A7
0x04956cAa...E511a72BB
0.099995434549745764 Eth
Nonce: 105
0.09907401597165063 Eth
Nonce: 106
0.000921418578095134
5.268321332831500925 Eth5.268322357277938793 Eth0.000001024446437868

Execution Trace

SkaleToken.transfer( recipient=0x14f12aEeD4e904314C66BB1aA686A40B6DC6F89e, amount=10000000000000000000000 ) => ( True )
  • ERC1820Registry.getInterfaceImplementer( _addr=0x04956cAa50920968990f68e195bE59CE511a72BB, _interfaceHash=29DDB589B1FB5FC7CF394961C1ADF5F8C6454761ADF795E67FE149F658ABE895 ) => ( 0x0000000000000000000000000000000000000000 )
  • AdminUpgradeabilityProxy.35817773( )
    • ContractManager.getContract( name=TokenState ) => ( contractAddress=0x4eE5F270572285776814e32952446e9B7Ee15C86 )
    • AdminUpgradeabilityProxy.fa8dacba( )
    • ERC1820Registry.getInterfaceImplementer( _addr=0x14f12aEeD4e904314C66BB1aA686A40B6DC6F89e, _interfaceHash=B281FC8C12954D22544DB45DE3159A39272895B169A852B314F9CC762E44C53B ) => ( 0x0000000000000000000000000000000000000000 )
      File 1 of 8: SkaleToken
      {"BokkyPooBahsDateTimeLibrary.sol":{"content":"pragma solidity ^0.6.0;\n\n// ----------------------------------------------------------------------------\n// BokkyPooBah\u0027s DateTime Library v1.01\n//\n// A gas-efficient Solidity date and time library\n//\n// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary\n//\n// Tested date range 1970/01/01 to 2345/12/31\n//\n// Conventions:\n// Unit      | Range         | Notes\n// :-------- |:-------------:|:-----\n// timestamp | \u003e= 0          | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC\n// year      | 1970 ... 2345 |\n// month     | 1 ... 12      |\n// day       | 1 ... 31      |\n// hour      | 0 ... 23      |\n// minute    | 0 ... 59      |\n// second    | 0 ... 59      |\n// dayOfWeek | 1 ... 7       | 1 = Monday, ..., 7 = Sunday\n//\n//\n// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018-2019. The MIT Licence.\n// ----------------------------------------------------------------------------\n\nlibrary BokkyPooBahsDateTimeLibrary {\n\n    uint constant SECONDS_PER_DAY = 24 * 60 * 60;\n    uint constant SECONDS_PER_HOUR = 60 * 60;\n    uint constant SECONDS_PER_MINUTE = 60;\n    int constant OFFSET19700101 = 2440588;\n\n    uint constant DOW_MON = 1;\n    uint constant DOW_TUE = 2;\n    uint constant DOW_WED = 3;\n    uint constant DOW_THU = 4;\n    uint constant DOW_FRI = 5;\n    uint constant DOW_SAT = 6;\n    uint constant DOW_SUN = 7;\n\n    // ------------------------------------------------------------------------\n    // Calculate the number of days from 1970/01/01 to year/month/day using\n    // the date conversion algorithm from\n    //   http://aa.usno.navy.mil/faq/docs/JD_Formula.php\n    // and subtracting the offset 2440588 so that 1970/01/01 is day 0\n    //\n    // days = day\n    //      - 32075\n    //      + 1461 * (year + 4800 + (month - 14) / 12) / 4\n    //      + 367 * (month - 2 - (month - 14) / 12 * 12) / 12\n    //      - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4\n    //      - offset\n    // ------------------------------------------------------------------------\n    function _daysFromDate(uint year, uint month, uint day) internal pure returns (uint _days) {\n        require(year \u003e= 1970);\n        int _year = int(year);\n        int _month = int(month);\n        int _day = int(day);\n\n        int __days = _day\n          - 32075\n          + 1461 * (_year + 4800 + (_month - 14) / 12) / 4\n          + 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12\n          - 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4\n          - OFFSET19700101;\n\n        _days = uint(__days);\n    }\n\n    // ------------------------------------------------------------------------\n    // Calculate year/month/day from the number of days since 1970/01/01 using\n    // the date conversion algorithm from\n    //   http://aa.usno.navy.mil/faq/docs/JD_Formula.php\n    // and adding the offset 2440588 so that 1970/01/01 is day 0\n    //\n    // int L = days + 68569 + offset\n    // int N = 4 * L / 146097\n    // L = L - (146097 * N + 3) / 4\n    // year = 4000 * (L + 1) / 1461001\n    // L = L - 1461 * year / 4 + 31\n    // month = 80 * L / 2447\n    // dd = L - 2447 * month / 80\n    // L = month / 11\n    // month = month + 2 - 12 * L\n    // year = 100 * (N - 49) + year + L\n    // ------------------------------------------------------------------------\n    function _daysToDate(uint _days) internal pure returns (uint year, uint month, uint day) {\n        int __days = int(_days);\n\n        int L = __days + 68569 + OFFSET19700101;\n        int N = 4 * L / 146097;\n        L = L - (146097 * N + 3) / 4;\n        int _year = 4000 * (L + 1) / 1461001;\n        L = L - 1461 * _year / 4 + 31;\n        int _month = 80 * L / 2447;\n        int _day = L - 2447 * _month / 80;\n        L = _month / 11;\n        _month = _month + 2 - 12 * L;\n        _year = 100 * (N - 49) + _year + L;\n\n        year = uint(_year);\n        month = uint(_month);\n        day = uint(_day);\n    }\n\n    function timestampFromDate(uint year, uint month, uint day) internal pure returns (uint timestamp) {\n        timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;\n    }\n    function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (uint timestamp) {\n        timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second;\n    }\n    function timestampToDate(uint timestamp) internal pure returns (uint year, uint month, uint day) {\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n    }\n    function timestampToDateTime(uint timestamp) internal pure returns (uint year, uint month, uint day, uint hour, uint minute, uint second) {\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        uint secs = timestamp % SECONDS_PER_DAY;\n        hour = secs / SECONDS_PER_HOUR;\n        secs = secs % SECONDS_PER_HOUR;\n        minute = secs / SECONDS_PER_MINUTE;\n        second = secs % SECONDS_PER_MINUTE;\n    }\n\n    function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) {\n        if (year \u003e= 1970 \u0026\u0026 month \u003e 0 \u0026\u0026 month \u003c= 12) {\n            uint daysInMonth = _getDaysInMonth(year, month);\n            if (day \u003e 0 \u0026\u0026 day \u003c= daysInMonth) {\n                valid = true;\n            }\n        }\n    }\n    function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) {\n        if (isValidDate(year, month, day)) {\n            if (hour \u003c 24 \u0026\u0026 minute \u003c 60 \u0026\u0026 second \u003c 60) {\n                valid = true;\n            }\n        }\n    }\n    function isLeapYear(uint timestamp) internal pure returns (bool leapYear) {\n        uint year;\n        uint month;\n        uint day;\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        leapYear = _isLeapYear(year);\n    }\n    function _isLeapYear(uint year) internal pure returns (bool leapYear) {\n        leapYear = ((year % 4 == 0) \u0026\u0026 (year % 100 != 0)) || (year % 400 == 0);\n    }\n    function isWeekDay(uint timestamp) internal pure returns (bool weekDay) {\n        weekDay = getDayOfWeek(timestamp) \u003c= DOW_FRI;\n    }\n    function isWeekEnd(uint timestamp) internal pure returns (bool weekEnd) {\n        weekEnd = getDayOfWeek(timestamp) \u003e= DOW_SAT;\n    }\n    function getDaysInMonth(uint timestamp) internal pure returns (uint daysInMonth) {\n        uint year;\n        uint month;\n        uint day;\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        daysInMonth = _getDaysInMonth(year, month);\n    }\n    function _getDaysInMonth(uint year, uint month) internal pure returns (uint daysInMonth) {\n        if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {\n            daysInMonth = 31;\n        } else if (month != 2) {\n            daysInMonth = 30;\n        } else {\n            daysInMonth = _isLeapYear(year) ? 29 : 28;\n        }\n    }\n    // 1 = Monday, 7 = Sunday\n    function getDayOfWeek(uint timestamp) internal pure returns (uint dayOfWeek) {\n        uint _days = timestamp / SECONDS_PER_DAY;\n        dayOfWeek = (_days + 3) % 7 + 1;\n    }\n\n    function getYear(uint timestamp) internal pure returns (uint year) {\n        uint month;\n        uint day;\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n    }\n    function getMonth(uint timestamp) internal pure returns (uint month) {\n        uint year;\n        uint day;\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n    }\n    function getDay(uint timestamp) internal pure returns (uint day) {\n        uint year;\n        uint month;\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n    }\n    function getHour(uint timestamp) internal pure returns (uint hour) {\n        uint secs = timestamp % SECONDS_PER_DAY;\n        hour = secs / SECONDS_PER_HOUR;\n    }\n    function getMinute(uint timestamp) internal pure returns (uint minute) {\n        uint secs = timestamp % SECONDS_PER_HOUR;\n        minute = secs / SECONDS_PER_MINUTE;\n    }\n    function getSecond(uint timestamp) internal pure returns (uint second) {\n        second = timestamp % SECONDS_PER_MINUTE;\n    }\n\n    function addYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) {\n        uint year;\n        uint month;\n        uint day;\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        year += _years;\n        uint daysInMonth = _getDaysInMonth(year, month);\n        if (day \u003e daysInMonth) {\n            day = daysInMonth;\n        }\n        newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;\n        require(newTimestamp \u003e= timestamp);\n    }\n    function addMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) {\n        uint year;\n        uint month;\n        uint day;\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        month += _months;\n        year += (month - 1) / 12;\n        month = (month - 1) % 12 + 1;\n        uint daysInMonth = _getDaysInMonth(year, month);\n        if (day \u003e daysInMonth) {\n            day = daysInMonth;\n        }\n        newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;\n        require(newTimestamp \u003e= timestamp);\n    }\n    function addDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) {\n        newTimestamp = timestamp + _days * SECONDS_PER_DAY;\n        require(newTimestamp \u003e= timestamp);\n    }\n    function addHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) {\n        newTimestamp = timestamp + _hours * SECONDS_PER_HOUR;\n        require(newTimestamp \u003e= timestamp);\n    }\n    function addMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) {\n        newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE;\n        require(newTimestamp \u003e= timestamp);\n    }\n    function addSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) {\n        newTimestamp = timestamp + _seconds;\n        require(newTimestamp \u003e= timestamp);\n    }\n\n    function subYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) {\n        uint year;\n        uint month;\n        uint day;\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        year -= _years;\n        uint daysInMonth = _getDaysInMonth(year, month);\n        if (day \u003e daysInMonth) {\n            day = daysInMonth;\n        }\n        newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;\n        require(newTimestamp \u003c= timestamp);\n    }\n    function subMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) {\n        uint year;\n        uint month;\n        uint day;\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        uint yearMonth = year * 12 + (month - 1) - _months;\n        year = yearMonth / 12;\n        month = yearMonth % 12 + 1;\n        uint daysInMonth = _getDaysInMonth(year, month);\n        if (day \u003e daysInMonth) {\n            day = daysInMonth;\n        }\n        newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;\n        require(newTimestamp \u003c= timestamp);\n    }\n    function subDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) {\n        newTimestamp = timestamp - _days * SECONDS_PER_DAY;\n        require(newTimestamp \u003c= timestamp);\n    }\n    function subHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) {\n        newTimestamp = timestamp - _hours * SECONDS_PER_HOUR;\n        require(newTimestamp \u003c= timestamp);\n    }\n    function subMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) {\n        newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE;\n        require(newTimestamp \u003c= timestamp);\n    }\n    function subSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) {\n        newTimestamp = timestamp - _seconds;\n        require(newTimestamp \u003c= timestamp);\n    }\n\n    function diffYears(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _years) {\n        require(fromTimestamp \u003c= toTimestamp);\n        uint fromYear;\n        uint fromMonth;\n        uint fromDay;\n        uint toYear;\n        uint toMonth;\n        uint toDay;\n        (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);\n        (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY);\n        _years = toYear - fromYear;\n    }\n    function diffMonths(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _months) {\n        require(fromTimestamp \u003c= toTimestamp);\n        uint fromYear;\n        uint fromMonth;\n        uint fromDay;\n        uint toYear;\n        uint toMonth;\n        uint toDay;\n        (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);\n        (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY);\n        _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth;\n    }\n    function diffDays(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _days) {\n        require(fromTimestamp \u003c= toTimestamp);\n        _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY;\n    }\n    function diffHours(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _hours) {\n        require(fromTimestamp \u003c= toTimestamp);\n        _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR;\n    }\n    function diffMinutes(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _minutes) {\n        require(fromTimestamp \u003c= toTimestamp);\n        _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE;\n    }\n    function diffSeconds(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _seconds) {\n        require(fromTimestamp \u003c= toTimestamp);\n        _seconds = toTimestamp - fromTimestamp;\n    }\n}\n"},"ConstantsHolder.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    ConstantsHolder.sol - SKALE Manager\n    Copyright (C) 2018-Present SKALE Labs\n    @author Artem Payvin\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\nimport \"./Permissions.sol\";\n\n\n/**\n * @title Contains constants and common variables for Skale Manager system\n * @author Artem Payvin\n */\ncontract ConstantsHolder is Permissions {\n\n    // initial price for creating Node (100 SKL)\n    uint public constant NODE_DEPOSIT = 100 * 1e18;\n\n    uint8 public constant TOTAL_SPACE_ON_NODE = 128;\n\n    // part of Node for Small Skale-chain (1/128 of Node)\n    uint8 public constant SMALL_DIVISOR = 128;\n\n    // part of Node for Medium Skale-chain (1/8 of Node)\n    uint8 public constant MEDIUM_DIVISOR = 8;\n\n    // part of Node for Large Skale-chain (full Node)\n    uint8 public constant LARGE_DIVISOR = 1;\n\n    // part of Node for Medium Test Skale-chain (1/4 of Node)\n    uint8 public constant MEDIUM_TEST_DIVISOR = 4;\n\n    // typically number of Nodes for Skale-chain (16 Nodes)\n    uint public constant NUMBER_OF_NODES_FOR_SCHAIN = 16;\n\n    // number of Nodes for Test Skale-chain (2 Nodes)\n    uint public constant NUMBER_OF_NODES_FOR_TEST_SCHAIN = 2;\n\n    // number of Nodes for Test Skale-chain (4 Nodes)\n    uint public constant NUMBER_OF_NODES_FOR_MEDIUM_TEST_SCHAIN = 4;    \n\n    // number of seconds in one year\n    uint32 public constant SECONDS_TO_YEAR = 31622400;\n\n    // initial number of monitors\n    uint public constant NUMBER_OF_MONITORS = 24;\n\n    uint public constant OPTIMAL_LOAD_PERCENTAGE = 80;\n\n    uint public constant ADJUSTMENT_SPEED = 1000;\n\n    uint public constant COOLDOWN_TIME = 60;\n\n    uint public constant MIN_PRICE = 10**6;\n\n    uint public constant MSR_REDUCING_COEFFICIENT = 2;\n\n    uint public constant DOWNTIME_THRESHOLD_PART = 30;\n\n    uint public constant BOUNTY_LOCKUP_MONTHS = 3;\n\n    // MSR - Minimum staking requirement\n    uint public msr;\n\n    // Reward period - 30 days (each 30 days Node would be granted for bounty)\n    uint32 public rewardPeriod;\n\n    // Allowable latency - 150000 ms by default\n    uint32 public allowableLatency;\n\n    /**\n     * Delta period - 1 hour (1 hour before Reward period became Monitors need\n     * to send Verdicts and 1 hour after Reward period became Node need to come\n     * and get Bounty)\n     */\n    uint32 public deltaPeriod;\n\n    /**\n     * Check time - 2 minutes (every 2 minutes monitors should check metrics\n     * from checked nodes)\n     */\n    uint public checkTime;\n\n    //Need to add minimal allowed parameters for verdicts\n\n    uint public launchTimestamp;\n\n    uint public rotationDelay;\n\n    uint public proofOfUseLockUpPeriodDays;\n\n    uint public proofOfUseDelegationPercentage;\n\n    /**\n     * Set reward and delta periods to new one, run only by owner. This function\n     * only for tests.\n     * @param newRewardPeriod - new Reward period\n     * @param newDeltaPeriod - new Delta period\n     */\n    function setPeriods(uint32 newRewardPeriod, uint32 newDeltaPeriod) external onlyOwner {\n        require(\n            newRewardPeriod \u003e= newDeltaPeriod \u0026\u0026 newRewardPeriod - newDeltaPeriod \u003e= checkTime,\n            \"Incorrect Periods\"\n        );\n        rewardPeriod = newRewardPeriod;\n        deltaPeriod = newDeltaPeriod;\n    }\n\n    /**\n     * Set new check time. This function only for tests.\n     * @param newCheckTime - new check time\n     */\n    function setCheckTime(uint newCheckTime) external onlyOwner {\n        require(rewardPeriod - deltaPeriod \u003e= checkTime, \"Incorrect check time\");\n        checkTime = newCheckTime;\n    }    \n\n    /**\n     * Set latency new one in ms, run only by owner. This function\n     * only for tests.\n     * @param newAllowableLatency - new Allowable Latency\n     */\n    function setLatency(uint32 newAllowableLatency) external onlyOwner {\n        allowableLatency = newAllowableLatency;\n    }\n\n    function setMSR(uint newMSR) external onlyOwner {\n        msr = newMSR;\n    }\n\n    function setLaunchTimestamp(uint timestamp) external onlyOwner {\n        require(now \u003c launchTimestamp, \"Can\u0027t set network launch timestamp because network is already launched\");\n        launchTimestamp = timestamp;\n    }\n\n    function setRotationDelay(uint newDelay) external onlyOwner {\n        rotationDelay = newDelay;\n    }\n\n    function setProofOfUseLockUpPeriod(uint periodDays) external onlyOwner {\n        proofOfUseLockUpPeriodDays = periodDays;\n    }\n\n    function setProofOfUseDelegationPercentage(uint percentage) external onlyOwner {\n        require(percentage \u003c= 100, \"Percentage value is incorrect\");\n        proofOfUseDelegationPercentage = percentage;\n    }\n\n    /**\n     * @dev constructor in Permissions approach\n     * @param contractsAddress needed in Permissions constructor\n     */\n    function initialize(address contractsAddress) public override initializer {\n        Permissions.initialize(contractsAddress);\n\n        msr = 0;\n        rewardPeriod = 86400;\n        allowableLatency = 150000;\n        deltaPeriod = 3600;\n        checkTime = 300;\n        launchTimestamp = uint(-1);\n        rotationDelay = 12 hours;\n        proofOfUseLockUpPeriodDays = 90;\n        proofOfUseDelegationPercentage = 50;\n    }\n}\n"},"ContractManager.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    ContractManager.sol - SKALE Manager\n    Copyright (C) 2018-Present SKALE Labs\n    @author Artem Payvin\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\nimport \"./OEPOwnable.sol\";\nimport \"./OEPAddress.sol\";\n\nimport \"./StringUtils.sol\";\n\n\n/**\n * @title Main contract in upgradeable approach. This contract contains the actual\n * current mapping from contract IDs (in the form of human-readable strings) to addresses.\n * @author Artem Payvin\n */\ncontract ContractManager is OwnableUpgradeSafe {\n    using StringUtils for string;\n    using Address for address;\n\n    // mapping of actual smart contracts addresses\n    mapping (bytes32 =\u003e address) public contracts;\n\n    event ContractUpgraded(string contractsName, address contractsAddress);\n\n    function initialize() external initializer {\n        OwnableUpgradeSafe.__Ownable_init();\n    }\n\n    /**\n     * Adds actual contract to mapping of actual contract addresses\n     * @param contractsName - contracts name in skale manager system\n     * @param newContractsAddress - contracts address in skale manager system\n     */\n    function setContractsAddress(string calldata contractsName, address newContractsAddress) external onlyOwner {\n        // check newContractsAddress is not equal to zero\n        require(newContractsAddress != address(0), \"New address is equal zero\");\n        // create hash of contractsName\n        bytes32 contractId = keccak256(abi.encodePacked(contractsName));\n        // check newContractsAddress is not equal the previous contract\u0027s address\n        require(contracts[contractId] != newContractsAddress, \"Contract is already added\");\n        require(newContractsAddress.isContract(), \"Given contracts address does not contain code\");\n        // add newContractsAddress to mapping of actual contract addresses\n        contracts[contractId] = newContractsAddress;\n        emit ContractUpgraded(contractsName, newContractsAddress);\n    }\n\n    function getContract(string calldata name) external view returns (address contractAddress) {\n        contractAddress = contracts[keccak256(abi.encodePacked(name))];\n        require(contractAddress != address(0), name.strConcat(\" contract has not been found\"));\n    }\n}\n"},"DelegationController.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    DelegationController.sol - SKALE Manager\n    Copyright (C) 2018-Present SKALE Labs\n    @author Dmytro Stebaiev\n    @author Vadim Yavorsky\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\npragma experimental ABIEncoderV2;\n\nimport \"./OCSafeMath.sol\";\n\nimport \"./Permissions.sol\";\nimport \"./SkaleToken.sol\";\nimport \"./Nodes.sol\";\nimport \"./MathUtils.sol\";\nimport \"./FractionUtils.sol\";\n\nimport \"./DelegationPeriodManager.sol\";\nimport \"./Punisher.sol\";\nimport \"./TokenLaunchLocker.sol\";\nimport \"./TokenState.sol\";\nimport \"./ValidatorService.sol\";\nimport \"./PartialDifferences.sol\";\n\n/**\n * @title Delegation Controller\n * @dev This contract performs all delegation functions including delegation\n * requests, undelegation, slashing, etc.\n *\n * Delegators and validators may both perform delegations. Validators who perform\n * delegations to themselves are effectively self-delegating or self-bonding.\n *\n * Delegated tokens may be in one of several states:\n *\n * - PROPOSED: token holder proposes tokens to delegate to a validator\n * - ACCEPTED: token delegations are accepted by a validator and are locked-by-delegation\n * - CANCELED: token holder cancels delegation proposal. Only allowed before the proposal is accepted by the validator\n * - REJECTED: token proposal expires at the UTC start of the next month\n * - DELEGATED: accepted delegations are delegated at the UTC start of the month\n * - UNDELEGATION_REQUESTED: token holder requests delegations to undelegate from the validator\n * - COMPLETED: undelegation request is completed at the end of the delegation period\n */\ncontract DelegationController is Permissions, ILocker {\n    using MathUtils for uint;\n    using PartialDifferences for PartialDifferences.Sequence;\n    using PartialDifferences for PartialDifferences.Value;\n    using FractionUtils for FractionUtils.Fraction;\n\n    enum State {\n        PROPOSED,\n        ACCEPTED,\n        CANCELED,\n        REJECTED,\n        DELEGATED,\n        UNDELEGATION_REQUESTED,\n        COMPLETED\n    }\n\n    struct Delegation {\n        address holder; // address of token owner\n        uint validatorId;\n        uint amount;\n        uint delegationPeriod;\n        uint created; // time of delegation creation\n        uint started; // month when a delegation becomes active\n        uint finished; // first month after a delegation ends\n        string info;\n    }\n\n    struct SlashingLogEvent {\n        FractionUtils.Fraction reducingCoefficient;\n        uint nextMonth;\n    }\n\n    struct SlashingLog {\n        //      month =\u003e slashing event\n        mapping (uint =\u003e SlashingLogEvent) slashes;\n        uint firstMonth;\n        uint lastMonth;\n    }\n\n    struct DelegationExtras {\n        uint lastSlashingMonthBeforeDelegation;\n    }\n\n    struct SlashingEvent {\n        FractionUtils.Fraction reducingCoefficient;\n        uint validatorId;\n        uint month;\n    }\n\n    struct SlashingSignal {\n        address holder;\n        uint penalty;\n    }\n\n    struct LockedInPending {\n        uint amount;\n        uint month;\n    }\n\n    struct FirstDelegationMonth {\n        // month\n        uint value;\n        //validatorId =\u003e month\n        mapping (uint =\u003e uint) byValidator;\n    }\n\n    /**\n     * @dev Emitted when a delegation is proposed to a validator.\n     */\n    event DelegationProposed(\n        uint delegationId\n    );\n\n    /**\n     * @dev Emitted when a delegation is accepted by a validator.\n     */\n    event DelegationAccepted(\n        uint delegationId\n    );\n\n    /**\n     * @dev Emitted when a delegation is cancelled by the delegator.\n     */\n    event DelegationRequestCanceledByUser(\n        uint delegationId\n    );\n\n    /**\n     * @dev Emitted when a delegation is requested to undelegate.\n     */\n    event UndelegationRequested(\n        uint delegationId\n    );\n\n    /// @dev delegations will never be deleted to index in this array may be used like delegation id\n    Delegation[] public delegations;\n\n    // validatorId =\u003e delegationId[]\n    mapping (uint =\u003e uint[]) public delegationsByValidator;\n\n    //        holder =\u003e delegationId[]\n    mapping (address =\u003e uint[]) public delegationsByHolder;\n\n    // delegationId =\u003e extras\n    mapping(uint =\u003e DelegationExtras) private _delegationExtras;\n\n    // validatorId =\u003e sequence\n    mapping (uint =\u003e PartialDifferences.Value) private _delegatedToValidator;\n    // validatorId =\u003e sequence\n    mapping (uint =\u003e PartialDifferences.Sequence) private _effectiveDelegatedToValidator;\n\n    // validatorId =\u003e slashing log\n    mapping (uint =\u003e SlashingLog) private _slashesOfValidator;\n\n    //        holder =\u003e sequence\n    mapping (address =\u003e PartialDifferences.Value) private _delegatedByHolder;\n    //        holder =\u003e   validatorId =\u003e sequence\n    mapping (address =\u003e mapping (uint =\u003e PartialDifferences.Value)) private _delegatedByHolderToValidator;\n    //        holder =\u003e   validatorId =\u003e sequence\n    mapping (address =\u003e mapping (uint =\u003e PartialDifferences.Sequence)) private _effectiveDelegatedByHolderToValidator;\n\n    SlashingEvent[] private _slashes;\n    //        holder =\u003e index in _slashes;\n    mapping (address =\u003e uint) private _firstUnprocessedSlashByHolder;\n\n    //        holder =\u003e   validatorId =\u003e month\n    mapping (address =\u003e FirstDelegationMonth) private _firstDelegationMonth;\n\n    //        holder =\u003e locked in pending\n    mapping (address =\u003e LockedInPending) private _lockedInPendingDelegations;\n\n    /**\n     * @dev Modifier to make a function callable only if delegation exists.\n     */\n    modifier checkDelegationExists(uint delegationId) {\n        require(delegationId \u003c delegations.length, \"Delegation does not exist\");\n        _;\n    }\n\n    function getAndUpdateDelegatedToValidatorNow(uint validatorId) external returns (uint) {\n        return getAndUpdateDelegatedToValidator(validatorId, _getCurrentMonth());\n    }\n\n    function getAndUpdateDelegatedAmount(address holder) external returns (uint) {\n        return _getAndUpdateDelegatedByHolder(holder);\n    }\n\n    function getAndUpdateEffectiveDelegatedByHolderToValidator(address holder, uint validatorId, uint month) external\n        allow(\"Distributor\") returns (uint effectiveDelegated)\n    {\n        SlashingSignal[] memory slashingSignals = _processAllSlashesWithoutSignals(holder);\n        effectiveDelegated = _effectiveDelegatedByHolderToValidator[holder][validatorId]\n            .getAndUpdateValueInSequence(month);\n        _sendSlashingSignals(slashingSignals);\n    }\n\n    /**\n     * @dev Allows a token holder to create a delegation proposal of an `amount`\n     * and `delegationPeriod` to a `validatorId`. Delegation must be accepted\n     * by the validator before the UTC start of the month, otherwise the\n     * delegation will be rejected.\n     *\n     * The token holder may add additional information in each proposal.\n     *\n     * @param validatorId uint ID of validator to receive delegation proposal\n     * @param amount uint amount of proposed delegation\n     * @param delegationPeriod uint period of proposed delegation\n     * @param info string extra information provided by the token holder (if any)\n     */\n    function delegate(\n        uint validatorId,\n        uint amount,\n        uint delegationPeriod,\n        string calldata info\n    )\n        external\n    {\n\n        ValidatorService validatorService = ValidatorService(contractManager.getContract(\"ValidatorService\"));\n        DelegationPeriodManager delegationPeriodManager = DelegationPeriodManager(\n            contractManager.getContract(\"DelegationPeriodManager\"));\n        SkaleToken skaleToken = SkaleToken(contractManager.getContract(\"SkaleToken\"));\n        TokenState tokenState = TokenState(contractManager.getContract(\"TokenState\"));\n\n        require(\n            validatorService.checkMinimumDelegation(validatorId, amount),\n            \"Amount does not meet the validator\u0027s minimum delegation amount\");\n        require(\n            validatorService.isAuthorizedValidator(validatorId),\n            \"Validator is not authorized to accept delegation request\");\n        require(\n            delegationPeriodManager.isDelegationPeriodAllowed(delegationPeriod),\n            \"This delegation period is not allowed\");\n        require(\n            validatorService.isAcceptingNewRequests(validatorId),\n            \"The validator is not currently accepting new requests\");\n\n        SlashingSignal[] memory slashingSignals = _processAllSlashesWithoutSignals(msg.sender);\n\n        uint delegationId = _addDelegation(\n            msg.sender,\n            validatorId,\n            amount,\n            delegationPeriod,\n            info);\n\n        // check that there is enough money\n        uint holderBalance = skaleToken.balanceOf(msg.sender);\n        uint forbiddenForDelegation = tokenState.getAndUpdateForbiddenForDelegationAmount(msg.sender);\n        require(holderBalance \u003e= forbiddenForDelegation, \"Token holder does not have enough tokens to delegate\");\n\n        emit DelegationProposed(delegationId);\n\n        _sendSlashingSignals(slashingSignals);\n    }\n\n    /**\n     * @dev See ILocker.\n     */\n    function getAndUpdateLockedAmount(address wallet) external override returns (uint) {\n        return _getAndUpdateLockedAmount(wallet);\n    }\n\n    /**\n     * @dev See ILocker.\n     */\n    function getAndUpdateForbiddenForDelegationAmount(address wallet) external override returns (uint) {\n        return _getAndUpdateLockedAmount(wallet);\n    }\n\n    /**\n     * @dev Allows a token holder to cancel a delegation proposal.\n     *\n     * Requirements:\n     *\n     * - the sender must be the token holder of the delegation proposal.\n     * - the delegation must still be in a PROPOSED state.\n     *\n     * Emits a DelegationRequestCanceledByUser event.\n     *\n     * @param delegationId uint ID of delegation proposal\n     */\n    function cancelPendingDelegation(uint delegationId) external checkDelegationExists(delegationId) {\n        require(msg.sender == delegations[delegationId].holder, \"Only token holders can cancel delegation request\");\n        require(getState(delegationId) == State.PROPOSED, \"Token holders are only able to cancel PROPOSED delegations\");\n\n        delegations[delegationId].finished = _getCurrentMonth();\n        _subtractFromLockedInPendingDelegations(delegations[delegationId].holder, delegations[delegationId].amount);\n\n        emit DelegationRequestCanceledByUser(delegationId);\n    }\n\n    /**\n     * @dev Allows a validator to accept a proposed delegation.\n     * Successful acceptance of delegations transition the tokens from a\n     * PROPOSED state to ACCEPTED, and tokens are locked for the remainder of the\n     * delegation period.\n     *\n     * Emits a DelegationAccepted event.\n     *\n     * @param delegationId uint ID of delegation proposal\n     */\n    function acceptPendingDelegation(uint delegationId) external checkDelegationExists(delegationId) {\n        ValidatorService validatorService = ValidatorService(contractManager.getContract(\"ValidatorService\"));\n        require(\n            validatorService.checkValidatorAddressToId(msg.sender, delegations[delegationId].validatorId),\n            \"No permissions to accept request\");\n        \n        State currentState = getState(delegationId);\n        if (currentState != State.PROPOSED) {\n            if (currentState == State.ACCEPTED ||\n                currentState == State.DELEGATED ||\n                currentState == State.UNDELEGATION_REQUESTED ||\n                currentState == State.COMPLETED)\n            {\n                revert(\"The delegation has been already accepted\");\n            } else if (currentState == State.CANCELED) {\n                revert(\"The delegation has been cancelled by token holder\");\n            } else if (currentState == State.REJECTED) {\n                revert(\"The delegation request is outdated\");\n            }\n        }\n        require(currentState == State.PROPOSED, \"Cannot set delegation state to accepted\");\n        \n        TokenLaunchLocker tokenLaunchLocker = TokenLaunchLocker(contractManager.getContract(\"TokenLaunchLocker\"));\n\n        SlashingSignal[] memory slashingSignals = _processAllSlashesWithoutSignals(delegations[delegationId].holder);\n\n        _addToAllStatistics(delegationId);\n\n        tokenLaunchLocker.handleDelegationAdd(\n            delegations[delegationId].holder,\n            delegationId,\n            delegations[delegationId].amount,\n            delegations[delegationId].started);\n\n        _sendSlashingSignals(slashingSignals);\n\n        emit DelegationAccepted(delegationId);\n    }\n\n    /**\n     * @dev Allows a delegator to undelegate a specific delegation.\n     *\n     * Requirements:\n     *\n     * - the sender must be the delegator.\n     * - the delegation must be in DELEGATED state.\n     *\n     * Emits an UndelegationRequested event.\n     *\n     * @param delegationId uint ID of delegation to undelegate\n     */\n    function requestUndelegation(uint delegationId) external checkDelegationExists(delegationId) {\n        require(getState(delegationId) == State.DELEGATED, \"Cannot request undelegation\");\n\n        ValidatorService validatorService = ValidatorService(contractManager.getContract(\"ValidatorService\"));\n        require(\n            delegations[delegationId].holder == msg.sender ||\n            (validatorService.validatorAddressExists(msg.sender) \u0026\u0026\n            delegations[delegationId].validatorId == validatorService.getValidatorId(msg.sender)),\n            \"Permission denied to request undelegation\");\n\n        TokenLaunchLocker tokenLaunchLocker = TokenLaunchLocker(contractManager.getContract(\"TokenLaunchLocker\"));\n        DelegationPeriodManager delegationPeriodManager = DelegationPeriodManager(\n            contractManager.getContract(\"DelegationPeriodManager\"));\n\n        processAllSlashes(msg.sender);\n        delegations[delegationId].finished = _calculateDelegationEndMonth(delegationId);\n        uint amountAfterSlashing = _calculateDelegationAmountAfterSlashing(delegationId);\n\n        _removeFromDelegatedToValidator(\n            delegations[delegationId].validatorId,\n            amountAfterSlashing,\n            delegations[delegationId].finished);\n        _removeFromDelegatedByHolder(\n            delegations[delegationId].holder,\n            amountAfterSlashing,\n            delegations[delegationId].finished);\n        _removeFromDelegatedByHolderToValidator(\n            delegations[delegationId].holder,\n            delegations[delegationId].validatorId,\n            amountAfterSlashing,\n            delegations[delegationId].finished);\n        uint effectiveAmount = amountAfterSlashing.mul(delegationPeriodManager.stakeMultipliers(\n            delegations[delegationId].delegationPeriod));\n        _removeFromEffectiveDelegatedToValidator(\n            delegations[delegationId].validatorId,\n            effectiveAmount,\n            delegations[delegationId].finished);\n        _removeFromEffectiveDelegatedByHolderToValidator(\n            delegations[delegationId].holder,\n            delegations[delegationId].validatorId,\n            effectiveAmount,\n            delegations[delegationId].finished);\n\n        tokenLaunchLocker.handleDelegationRemoving(\n            delegations[delegationId].holder,\n            delegationId,\n            delegations[delegationId].finished);\n\n        emit UndelegationRequested(delegationId);\n    }\n\n    /**\n     * @dev Allows the Punisher to confiscate an `amount` of stake from\n     * `validatorId` by slashing. This slashes all delegations of the validator,\n     * which reduces the amount that the validator has staked. This consequence\n     * may force the SKALE Manger to reduce the number of nodes a validator is\n     * operating so the validator can meet the Minimum Staking Requirement.\n     *\n     * See Punisher.\n     *\n     * Emits a SlashingEvent.\n     *\n     * @param validatorId uint validator to slash\n     * @param amount uint amount to slash\n     *\n     */\n    function confiscate(uint validatorId, uint amount) external allow(\"Punisher\") {\n        uint currentMonth = _getCurrentMonth();\n        FractionUtils.Fraction memory coefficient =\n            _delegatedToValidator[validatorId].reduceValue(amount, currentMonth);\n        _effectiveDelegatedToValidator[validatorId].reduceSequence(coefficient, currentMonth);\n        _putToSlashingLog(_slashesOfValidator[validatorId], coefficient, currentMonth);\n        _slashes.push(SlashingEvent({reducingCoefficient: coefficient, validatorId: validatorId, month: currentMonth}));\n    }\n\n    function getAndUpdateEffectiveDelegatedToValidator(uint validatorId, uint month)\n        external allow(\"Distributor\") returns (uint)\n    {\n        return _effectiveDelegatedToValidator[validatorId].getAndUpdateValueInSequence(month);\n    }\n\n    function getAndUpdateDelegatedByHolderToValidatorNow(address holder, uint validatorId) external returns (uint) {\n        return _getAndUpdateDelegatedByHolderToValidator(holder, validatorId, _getCurrentMonth());\n    }\n\n    function getDelegation(uint delegationId)\n        external view checkDelegationExists(delegationId) returns (Delegation memory)\n    {\n        return delegations[delegationId];\n    }\n\n    function getFirstDelegationMonth(address holder, uint validatorId) external view returns(uint) {\n        return _firstDelegationMonth[holder].byValidator[validatorId];\n    }\n\n    function getDelegationsByValidatorLength(uint validatorId) external view returns (uint) {\n        return delegationsByValidator[validatorId].length;\n    }\n\n    function getDelegationsByHolderLength(address holder) external view returns (uint) {\n        return delegationsByHolder[holder].length;\n    }\n\n    function initialize(address contractsAddress) public override initializer {\n        Permissions.initialize(contractsAddress);\n    }\n\n    function getAndUpdateDelegatedToValidator(uint validatorId, uint month)\n        public allow(\"Nodes\") returns (uint)\n    {\n        return _delegatedToValidator[validatorId].getAndUpdateValue(month);\n    }\n\n    function processSlashes(address holder, uint limit) public {\n        _sendSlashingSignals(_processSlashesWithoutSignals(holder, limit));\n    }\n\n    function processAllSlashes(address holder) public {\n        processSlashes(holder, 0);\n    }\n\n    /**\n     * @dev Returns the token state of a given delegation.\n     *\n     * @param delegationId uint ID of the delegation\n     */\n    function getState(uint delegationId) public view checkDelegationExists(delegationId) returns (State state) {\n        if (delegations[delegationId].started == 0) {\n            if (delegations[delegationId].finished == 0) {\n                TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract(\"TimeHelpers\"));\n                if (_getCurrentMonth() == timeHelpers.timestampToMonth(delegations[delegationId].created)) {\n                    return State.PROPOSED;\n                } else {\n                    return State.REJECTED;\n                }\n            } else {\n                return State.CANCELED;\n            }\n        } else {\n            if (_getCurrentMonth() \u003c delegations[delegationId].started) {\n                return State.ACCEPTED;\n            } else {\n                if (delegations[delegationId].finished == 0) {\n                    return State.DELEGATED;\n                } else {\n                    if (_getCurrentMonth() \u003c delegations[delegationId].finished) {\n                        return State.UNDELEGATION_REQUESTED;\n                    } else {\n                        return State.COMPLETED;\n                    }\n                }\n            }\n        }\n    }\n\n    function getLockedInPendingDelegations(address holder) public view returns (uint) {\n        uint currentMonth = _getCurrentMonth();\n        if (_lockedInPendingDelegations[holder].month \u003c currentMonth) {\n            return 0;\n        } else {\n            return _lockedInPendingDelegations[holder].amount;\n        }\n    }\n\n    function hasUnprocessedSlashes(address holder) public view returns (bool) {\n        return _everDelegated(holder) \u0026\u0026 _firstUnprocessedSlashByHolder[holder] \u003c _slashes.length;\n    }\n\n    // private\n\n    function _addDelegation(\n        address holder,\n        uint validatorId,\n        uint amount,\n        uint delegationPeriod,\n        string memory info\n    )\n        private\n        returns (uint delegationId)\n    {\n        delegationId = delegations.length;\n        delegations.push(Delegation(\n            holder,\n            validatorId,\n            amount,\n            delegationPeriod,\n            now,\n            0,\n            0,\n            info\n        ));\n        delegationsByValidator[validatorId].push(delegationId);\n        delegationsByHolder[holder].push(delegationId);\n        _addToLockedInPendingDelegations(delegations[delegationId].holder, delegations[delegationId].amount);\n    }\n\n    function _calculateDelegationEndMonth(uint delegationId) private view returns (uint) {\n        uint currentMonth = _getCurrentMonth();\n        uint started = delegations[delegationId].started;\n\n        if (currentMonth \u003c started) {\n            return started.add(delegations[delegationId].delegationPeriod);\n        } else {\n            uint completedPeriods = currentMonth.sub(started).div(delegations[delegationId].delegationPeriod);\n            return started.add(completedPeriods.add(1).mul(delegations[delegationId].delegationPeriod));\n        }\n    }\n\n    function _addToDelegatedToValidator(uint validatorId, uint amount, uint month) private {\n        _delegatedToValidator[validatorId].addToValue(amount, month);\n    }\n\n    function _addToEffectiveDelegatedToValidator(uint validatorId, uint effectiveAmount, uint month) private {\n        _effectiveDelegatedToValidator[validatorId].addToSequence(effectiveAmount, month);\n    }\n\n    function _addToDelegatedByHolder(address holder, uint amount, uint month) private {\n        _delegatedByHolder[holder].addToValue(amount, month);\n    }\n\n    function _addToDelegatedByHolderToValidator(\n        address holder, uint validatorId, uint amount, uint month) private\n    {\n        _delegatedByHolderToValidator[holder][validatorId].addToValue(amount, month);\n    }\n\n    function _removeFromDelegatedByHolder(address holder, uint amount, uint month) private {\n        _delegatedByHolder[holder].subtractFromValue(amount, month);\n    }\n\n    function _removeFromDelegatedByHolderToValidator(\n        address holder, uint validatorId, uint amount, uint month) private\n    {\n        _delegatedByHolderToValidator[holder][validatorId].subtractFromValue(amount, month);\n    }\n\n    function _addToEffectiveDelegatedByHolderToValidator(\n        address holder,\n        uint validatorId,\n        uint effectiveAmount,\n        uint month)\n        private\n    {\n        _effectiveDelegatedByHolderToValidator[holder][validatorId].addToSequence(effectiveAmount, month);\n    }\n\n    function _removeFromEffectiveDelegatedByHolderToValidator(\n        address holder,\n        uint validatorId,\n        uint effectiveAmount,\n        uint month)\n        private\n    {\n        _effectiveDelegatedByHolderToValidator[holder][validatorId].subtractFromSequence(effectiveAmount, month);\n    }\n\n    function _getAndUpdateDelegatedByHolder(address holder) private returns (uint) {\n        uint currentMonth = _getCurrentMonth();\n        processAllSlashes(holder);\n        return _delegatedByHolder[holder].getAndUpdateValue(currentMonth);\n    }\n\n    function _getAndUpdateDelegatedByHolderToValidator(\n        address holder,\n        uint validatorId,\n        uint month)\n        private returns (uint)\n    {\n        return _delegatedByHolderToValidator[holder][validatorId].getAndUpdateValue(month);\n    }\n\n    function _addToLockedInPendingDelegations(address holder, uint amount) private returns (uint) {\n        uint currentMonth = _getCurrentMonth();\n        if (_lockedInPendingDelegations[holder].month \u003c currentMonth) {\n            _lockedInPendingDelegations[holder].amount = amount;\n            _lockedInPendingDelegations[holder].month = currentMonth;\n        } else {\n            assert(_lockedInPendingDelegations[holder].month == currentMonth);\n            _lockedInPendingDelegations[holder].amount = _lockedInPendingDelegations[holder].amount.add(amount);\n        }\n    }\n\n    function _subtractFromLockedInPendingDelegations(address holder, uint amount) private returns (uint) {\n        uint currentMonth = _getCurrentMonth();\n        require(\n            _lockedInPendingDelegations[holder].month == currentMonth,\n            \"There are no delegation requests this month\");\n        require(_lockedInPendingDelegations[holder].amount \u003e= amount, \"Unlocking amount is too big\");\n        _lockedInPendingDelegations[holder].amount = _lockedInPendingDelegations[holder].amount.sub(amount);\n    }\n\n    function _getCurrentMonth() private view returns (uint) {\n        TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract(\"TimeHelpers\"));\n        return timeHelpers.getCurrentMonth();\n    }\n\n    function _getAndUpdateLockedAmount(address wallet) private returns (uint) {\n        return _getAndUpdateDelegatedByHolder(wallet).add(getLockedInPendingDelegations(wallet));\n    }\n\n    function _updateFirstDelegationMonth(address holder, uint validatorId, uint month) private {\n        if (_firstDelegationMonth[holder].value == 0) {\n            _firstDelegationMonth[holder].value = month;\n            _firstUnprocessedSlashByHolder[holder] = _slashes.length;\n        }\n        if (_firstDelegationMonth[holder].byValidator[validatorId] == 0) {\n            _firstDelegationMonth[holder].byValidator[validatorId] = month;\n        }\n    }\n\n    function _everDelegated(address holder) private view returns (bool) {\n        return _firstDelegationMonth[holder].value \u003e 0;\n    }\n\n    function _removeFromDelegatedToValidator(uint validatorId, uint amount, uint month) private {\n        _delegatedToValidator[validatorId].subtractFromValue(amount, month);\n    }\n\n    function _removeFromEffectiveDelegatedToValidator(uint validatorId, uint effectiveAmount, uint month) private {\n        _effectiveDelegatedToValidator[validatorId].subtractFromSequence(effectiveAmount, month);\n    }\n\n    function _calculateDelegationAmountAfterSlashing(uint delegationId) private view returns (uint) {\n        uint startMonth = _delegationExtras[delegationId].lastSlashingMonthBeforeDelegation;\n        uint validatorId = delegations[delegationId].validatorId;\n        uint amount = delegations[delegationId].amount;\n        if (startMonth == 0) {\n            startMonth = _slashesOfValidator[validatorId].firstMonth;\n            if (startMonth == 0) {\n                return amount;\n            }\n        }\n        for (uint i = startMonth;\n            i \u003e 0 \u0026\u0026 i \u003c delegations[delegationId].finished;\n            i = _slashesOfValidator[validatorId].slashes[i].nextMonth) {\n            if (i \u003e= delegations[delegationId].started) {\n                amount = amount\n                    .mul(_slashesOfValidator[validatorId].slashes[i].reducingCoefficient.numerator)\n                    .div(_slashesOfValidator[validatorId].slashes[i].reducingCoefficient.denominator);\n            }\n        }\n        return amount;\n    }\n\n    function _putToSlashingLog(\n        SlashingLog storage log,\n        FractionUtils.Fraction memory coefficient,\n        uint month)\n        private\n    {\n        if (log.firstMonth == 0) {\n            log.firstMonth = month;\n            log.lastMonth = month;\n            log.slashes[month].reducingCoefficient = coefficient;\n            log.slashes[month].nextMonth = 0;\n        } else {\n            require(log.lastMonth \u003c= month, \"Cannot put slashing event in the past\");\n            if (log.lastMonth == month) {\n                log.slashes[month].reducingCoefficient =\n                    log.slashes[month].reducingCoefficient.multiplyFraction(coefficient);\n            } else {\n                log.slashes[month].reducingCoefficient = coefficient;\n                log.slashes[month].nextMonth = 0;\n                log.slashes[log.lastMonth].nextMonth = month;\n                log.lastMonth = month;\n            }\n        }\n    }\n\n    function _processSlashesWithoutSignals(address holder, uint limit)\n        private returns (SlashingSignal[] memory slashingSignals)\n    {\n        if (hasUnprocessedSlashes(holder)) {\n            uint index = _firstUnprocessedSlashByHolder[holder];\n            uint end = _slashes.length;\n            if (limit \u003e 0 \u0026\u0026 index.add(limit) \u003c end) {\n                end = index.add(limit);\n            }\n            slashingSignals = new SlashingSignal[](end.sub(index));\n            uint begin = index;\n            for (; index \u003c end; ++index) {\n                uint validatorId = _slashes[index].validatorId;\n                uint month = _slashes[index].month;\n                uint oldValue = _getAndUpdateDelegatedByHolderToValidator(holder, validatorId, month);\n                if (oldValue.muchGreater(0)) {\n                    _delegatedByHolderToValidator[holder][validatorId].reduceValueByCoefficientAndUpdateSum(\n                        _delegatedByHolder[holder],\n                        _slashes[index].reducingCoefficient,\n                        month);\n                    _effectiveDelegatedByHolderToValidator[holder][validatorId].reduceSequence(\n                        _slashes[index].reducingCoefficient,\n                        month);\n                    slashingSignals[index.sub(begin)].holder = holder;\n                    slashingSignals[index.sub(begin)].penalty\n                        = oldValue.boundedSub(_getAndUpdateDelegatedByHolderToValidator(holder, validatorId, month));\n                }\n            }\n            _firstUnprocessedSlashByHolder[holder] = end;\n        }\n    }\n\n    function _processAllSlashesWithoutSignals(address holder)\n        private returns (SlashingSignal[] memory slashingSignals)\n    {\n        return _processSlashesWithoutSignals(holder, 0);\n    }\n\n    function _sendSlashingSignals(SlashingSignal[] memory slashingSignals) private {\n        Punisher punisher = Punisher(contractManager.getContract(\"Punisher\"));\n        address previousHolder = address(0);\n        uint accumulatedPenalty = 0;\n        for (uint i = 0; i \u003c slashingSignals.length; ++i) {\n            if (slashingSignals[i].holder != previousHolder) {\n                if (accumulatedPenalty \u003e 0) {\n                    punisher.handleSlash(previousHolder, accumulatedPenalty);\n                }\n                previousHolder = slashingSignals[i].holder;\n                accumulatedPenalty = slashingSignals[i].penalty;\n            } else {\n                accumulatedPenalty = accumulatedPenalty.add(slashingSignals[i].penalty);\n            }\n        }\n        if (accumulatedPenalty \u003e 0) {\n            punisher.handleSlash(previousHolder, accumulatedPenalty);\n        }\n    }\n\n    function _addToAllStatistics(uint delegationId) private {\n        DelegationPeriodManager delegationPeriodManager = DelegationPeriodManager(\n            contractManager.getContract(\"DelegationPeriodManager\"));\n\n        uint currentMonth = _getCurrentMonth();\n        delegations[delegationId].started = currentMonth.add(1);\n        if (_slashesOfValidator[delegations[delegationId].validatorId].lastMonth \u003e 0) {\n            _delegationExtras[delegationId].lastSlashingMonthBeforeDelegation =\n                _slashesOfValidator[delegations[delegationId].validatorId].lastMonth;\n        }\n\n        _addToDelegatedToValidator(\n            delegations[delegationId].validatorId,\n            delegations[delegationId].amount,\n            currentMonth.add(1));\n        _addToDelegatedByHolder(\n            delegations[delegationId].holder,\n            delegations[delegationId].amount,\n            currentMonth.add(1));\n        _addToDelegatedByHolderToValidator(\n            delegations[delegationId].holder,\n            delegations[delegationId].validatorId,\n            delegations[delegationId].amount,\n            currentMonth.add(1));\n        _updateFirstDelegationMonth(\n            delegations[delegationId].holder,\n            delegations[delegationId].validatorId,\n            currentMonth.add(1));\n        uint effectiveAmount = delegations[delegationId].amount.mul(delegationPeriodManager.stakeMultipliers(\n            delegations[delegationId].delegationPeriod));\n        _addToEffectiveDelegatedToValidator(\n            delegations[delegationId].validatorId,\n            effectiveAmount,\n            currentMonth.add(1));\n        _addToEffectiveDelegatedByHolderToValidator(\n            delegations[delegationId].holder,\n            delegations[delegationId].validatorId,\n            effectiveAmount,\n            currentMonth.add(1));\n    }\n}\n"},"DelegationPeriodManager.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    DelegationPeriodManager.sol - SKALE Manager\n    Copyright (C) 2018-Present SKALE Labs\n    @author Dmytro Stebaiev\n    @author Vadim Yavorsky\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\nimport \"../Permissions.sol\";\n\n/**\n * @title Delegation Period Manager\n * @dev This contract handles all delegation offerings. Delegations are held for\n * a specified period (months), and different durations can have different\n * returns or `stakeMultiplier`. Currently, only delegation periods can be added.\n */\ncontract DelegationPeriodManager is Permissions {\n\n    /**\n     * @dev Emitted when a new delegation period is specified.\n     */\n    event DelegationPeriodWasSet(\n        uint length,\n        uint stakeMultiplier\n    );\n\n    mapping (uint =\u003e uint) public stakeMultipliers;\n\n    /**\n     * @dev Creates a new available delegation period and return in the network.\n     * Only the owner may set new delegation period and returns in the network.\n     *\n     * Emits a DelegationPeriodWasSet event.\n     *\n     * @param monthsCount uint delegation duration in months\n     * @param stakeMultiplier uint return for delegation\n     */\n    function setDelegationPeriod(uint monthsCount, uint stakeMultiplier) external onlyOwner {\n        stakeMultipliers[monthsCount] = stakeMultiplier;\n\n        emit DelegationPeriodWasSet(monthsCount, stakeMultiplier);\n    }\n\n    /**\n     * @dev Checks whether given delegation period is allowed.\n     *\n     * @param monthsCount uint delegation duration in months\n     * @return bool True if delegation period is allowed\n     */\n    function isDelegationPeriodAllowed(uint monthsCount) external view returns (bool) {\n        return stakeMultipliers[monthsCount] != 0 ? true : false;\n    }\n\n    /**\n     * @dev Initial delegation period and multiplier settings.\n     */\n    function initialize(address contractsAddress) public override initializer {\n        Permissions.initialize(contractsAddress);\n        stakeMultipliers[3] = 100;  // 3 months at 100\n        stakeMultipliers[6] = 150;  // 6 months at 150\n        stakeMultipliers[12] = 200; // 12 months at 200\n    }\n}"},"ERC777.sol":{"content":"pragma solidity ^0.6.0;\n\nimport \"./OCContext.sol\";\nimport \"./OCIERC777.sol\";\nimport \"./OCIERC777Recipient.sol\";\nimport \"./OCIERC777Sender.sol\";\nimport \"./OCIERC20.sol\";\nimport \"./OCSafeMath.sol\";\n// import \"@openzeppelin/contracts/utils/Address.sol\"; Removed by SKALE\nimport \"./OCIERC1820Registry.sol\";\n\n/* Added by SKALE */\nimport \"./Permissions.sol\";\n/* End of added by SKALE */\n\n/**\n * @dev Implementation of the {IERC777} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n *\n * Support for ERC20 is included in this contract, as specified by the EIP: both\n * the ERC777 and ERC20 interfaces can be safely used when interacting with it.\n * Both {IERC777-Sent} and {IERC20-Transfer} events are emitted on token\n * movements.\n *\n * Additionally, the {IERC777-granularity} value is hard-coded to `1`, meaning that there\n * are no special restrictions in the amount of tokens that created, moved, or\n * destroyed. This makes integration with ERC20 applications seamless.\n */\ncontract ERC777 is Context, IERC777, IERC20 {\n    using SafeMath for uint256;\n    using Address for address;\n\n    IERC1820Registry constant internal _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);\n\n    mapping(address =\u003e uint256) private _balances;\n\n    uint256 private _totalSupply;\n\n    string private _name;\n    string private _symbol;\n\n    // We inline the result of the following hashes because Solidity doesn\u0027t resolve them at compile time.\n    // See https://github.com/ethereum/solidity/issues/4024.\n\n    // keccak256(\"ERC777TokensSender\")\n    bytes32 constant private _TOKENS_SENDER_INTERFACE_HASH =\n        0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895;\n\n    // keccak256(\"ERC777TokensRecipient\")\n    bytes32 constant private _TOKENS_RECIPIENT_INTERFACE_HASH =\n        0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;\n\n    // This isn\u0027t ever read from - it\u0027s only used to respond to the defaultOperators query.\n    address[] private _defaultOperatorsArray;\n\n    // Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators).\n    mapping(address =\u003e bool) private _defaultOperators;\n\n    // For each account, a mapping of its operators and revoked default operators.\n    mapping(address =\u003e mapping(address =\u003e bool)) private _operators;\n    mapping(address =\u003e mapping(address =\u003e bool)) private _revokedDefaultOperators;\n\n    // ERC20-allowances\n    mapping (address =\u003e mapping (address =\u003e uint256)) private _allowances;\n\n    /**\n     * @dev `defaultOperators` may be an empty array.\n     */\n    constructor(\n        string memory name,\n        string memory symbol,\n        address[] memory defaultOperators\n    ) public {\n        _name = name;\n        _symbol = symbol;\n\n        _defaultOperatorsArray = defaultOperators;\n        for (uint256 i = 0; i \u003c _defaultOperatorsArray.length; i++) {\n            _defaultOperators[_defaultOperatorsArray[i]] = true;\n        }\n\n        // register interfaces\n        _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256(\"ERC777Token\"), address(this));\n        _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256(\"ERC20Token\"), address(this));\n    }\n\n    /**\n     * @dev See {IERC777-name}.\n     */\n    function name() public view override returns (string memory) {\n        return _name;\n    }\n\n    /**\n     * @dev See {IERC777-symbol}.\n     */\n    function symbol() public view override returns (string memory) {\n        return _symbol;\n    }\n\n    /**\n     * @dev See {ERC20-decimals}.\n     *\n     * Always returns 18, as per the\n     * [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility).\n     */\n    function decimals() public pure returns (uint8) {\n        return 18;\n    }\n\n    /**\n     * @dev See {IERC777-granularity}.\n     *\n     * This implementation always returns `1`.\n     */\n    function granularity() public view override returns (uint256) {\n        return 1;\n    }\n\n    /**\n     * @dev See {IERC777-totalSupply}.\n     */\n    function totalSupply() public view override(IERC20, IERC777) returns (uint256) {\n        return _totalSupply;\n    }\n\n    /**\n     * @dev Returns the amount of tokens owned by an account (`tokenHolder`).\n     */\n    function balanceOf(address tokenHolder) public view override(IERC20, IERC777) returns (uint256) {\n        return _balances[tokenHolder];\n    }\n\n    /**\n     * @dev See {IERC777-send}.\n     *\n     * Also emits a {IERC20-Transfer} event for ERC20 compatibility.\n     */\n    function send(address recipient, uint256 amount, bytes memory data) public override  {\n        _send(_msgSender(), recipient, amount, data, \"\", true);\n    }\n\n    /**\n     * @dev See {IERC20-transfer}.\n     *\n     * Unlike `send`, `recipient` is _not_ required to implement the {IERC777Recipient}\n     * interface if it is a contract.\n     *\n     * Also emits a {Sent} event.\n     */\n    function transfer(address recipient, uint256 amount) public override returns (bool) {\n        require(recipient != address(0), \"ERC777: transfer to the zero address\");\n\n        address from = _msgSender();\n\n        _callTokensToSend(from, from, recipient, amount, \"\", \"\");\n\n        _move(from, from, recipient, amount, \"\", \"\");\n\n        _callTokensReceived(from, from, recipient, amount, \"\", \"\", false);\n\n        return true;\n    }\n\n    /**\n     * @dev See {IERC777-burn}.\n     *\n     * Also emits a {IERC20-Transfer} event for ERC20 compatibility.\n     */\n    function burn(uint256 amount, bytes memory data) public override  {\n        _burn(_msgSender(), amount, data, \"\");\n    }\n\n    /**\n     * @dev See {IERC777-isOperatorFor}.\n     */\n    function isOperatorFor(\n        address operator,\n        address tokenHolder\n    ) public view override returns (bool) {\n        return operator == tokenHolder ||\n            (_defaultOperators[operator] \u0026\u0026 !_revokedDefaultOperators[tokenHolder][operator]) ||\n            _operators[tokenHolder][operator];\n    }\n\n    /**\n     * @dev See {IERC777-authorizeOperator}.\n     */\n    function authorizeOperator(address operator) public override  {\n        require(_msgSender() != operator, \"ERC777: authorizing self as operator\");\n\n        if (_defaultOperators[operator]) {\n            delete _revokedDefaultOperators[_msgSender()][operator];\n        } else {\n            _operators[_msgSender()][operator] = true;\n        }\n\n        emit AuthorizedOperator(operator, _msgSender());\n    }\n\n    /**\n     * @dev See {IERC777-revokeOperator}.\n     */\n    function revokeOperator(address operator) public override  {\n        require(operator != _msgSender(), \"ERC777: revoking self as operator\");\n\n        if (_defaultOperators[operator]) {\n            _revokedDefaultOperators[_msgSender()][operator] = true;\n        } else {\n            delete _operators[_msgSender()][operator];\n        }\n\n        emit RevokedOperator(operator, _msgSender());\n    }\n\n    /**\n     * @dev See {IERC777-defaultOperators}.\n     */\n    function defaultOperators() public view override returns (address[] memory) {\n        return _defaultOperatorsArray;\n    }\n\n    /**\n     * @dev See {IERC777-operatorSend}.\n     *\n     * Emits {Sent} and {IERC20-Transfer} events.\n     */\n    function operatorSend(\n        address sender,\n        address recipient,\n        uint256 amount,\n        bytes memory data,\n        bytes memory operatorData\n    )\n    public override\n    {\n        require(isOperatorFor(_msgSender(), sender), \"ERC777: caller is not an operator for holder\");\n        _send(sender, recipient, amount, data, operatorData, true);\n    }\n\n    /**\n     * @dev See {IERC777-operatorBurn}.\n     *\n     * Emits {Burned} and {IERC20-Transfer} events.\n     */\n    function operatorBurn(address account, uint256 amount, bytes memory data, bytes memory operatorData) public override {\n        require(isOperatorFor(_msgSender(), account), \"ERC777: caller is not an operator for holder\");\n        _burn(account, amount, data, operatorData);\n    }\n\n    /**\n     * @dev See {IERC20-allowance}.\n     *\n     * Note that operator and allowance concepts are orthogonal: operators may\n     * not have allowance, and accounts with allowance may not be operators\n     * themselves.\n     */\n    function allowance(address holder, address spender) public view override returns (uint256) {\n        return _allowances[holder][spender];\n    }\n\n    /**\n     * @dev See {IERC20-approve}.\n     *\n     * Note that accounts cannot have allowance issued by their operators.\n     */\n    function approve(address spender, uint256 value) public override returns (bool) {\n        address holder = _msgSender();\n        _approve(holder, spender, value);\n        return true;\n    }\n\n   /**\n    * @dev See {IERC20-transferFrom}.\n    *\n    * Note that operator and allowance concepts are orthogonal: operators cannot\n    * call `transferFrom` (unless they have allowance), and accounts with\n    * allowance cannot call `operatorSend` (unless they are operators).\n    *\n    * Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events.\n    */\n    function transferFrom(address holder, address recipient, uint256 amount) public override returns (bool) {\n        require(recipient != address(0), \"ERC777: transfer to the zero address\");\n        require(holder != address(0), \"ERC777: transfer from the zero address\");\n\n        address spender = _msgSender();\n\n        _callTokensToSend(spender, holder, recipient, amount, \"\", \"\");\n\n        _move(spender, holder, recipient, amount, \"\", \"\");\n        _approve(holder, spender, _allowances[holder][spender].sub(amount, \"ERC777: transfer amount exceeds allowance\"));\n\n        _callTokensReceived(spender, holder, recipient, amount, \"\", \"\", false);\n\n        return true;\n    }\n\n    /**\n     * @dev Creates `amount` tokens and assigns them to `account`, increasing\n     * the total supply.\n     *\n     * If a send hook is registered for `account`, the corresponding function\n     * will be called with `operator`, `data` and `operatorData`.\n     *\n     * See {IERC777Sender} and {IERC777Recipient}.\n     *\n     * Emits {Minted} and {IERC20-Transfer} events.\n     *\n     * Requirements\n     *\n     * - `account` cannot be the zero address.\n     * - if `account` is a contract, it must implement the {IERC777Recipient}\n     * interface.\n     */\n    function _mint(\n        address account,\n        uint256 amount,\n        bytes memory userData,\n        bytes memory operatorData\n    )\n    internal virtual\n    {\n        require(account != address(0), \"ERC777: mint to the zero address\");\n\n        address operator = _msgSender();\n\n        _beforeTokenTransfer(operator, address(0), account, amount);\n\n        // Update state variables\n        _totalSupply = _totalSupply.add(amount);\n        _balances[account] = _balances[account].add(amount);\n\n        _callTokensReceived(operator, address(0), account, amount, userData, operatorData, true);\n\n        emit Minted(operator, account, amount, userData, operatorData);\n        emit Transfer(address(0), account, amount);\n    }\n\n    /**\n     * @dev Send tokens\n     * @param from address token holder address\n     * @param to address recipient address\n     * @param amount uint256 amount of tokens to transfer\n     * @param userData bytes extra information provided by the token holder (if any)\n     * @param operatorData bytes extra information provided by the operator (if any)\n     * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient\n     */\n    function _send(\n        address from,\n        address to,\n        uint256 amount,\n        bytes memory userData,\n        bytes memory operatorData,\n        bool requireReceptionAck\n    )\n        internal\n    {\n        require(from != address(0), \"ERC777: send from the zero address\");\n        require(to != address(0), \"ERC777: send to the zero address\");\n\n        address operator = _msgSender();\n\n        _callTokensToSend(operator, from, to, amount, userData, operatorData);\n\n        _move(operator, from, to, amount, userData, operatorData);\n\n        _callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);\n    }\n\n    /**\n     * @dev Burn tokens\n     * @param from address token holder address\n     * @param amount uint256 amount of tokens to burn\n     * @param data bytes extra information provided by the token holder\n     * @param operatorData bytes extra information provided by the operator (if any)\n     */\n    function _burn(\n        address from,\n        uint256 amount,\n        bytes memory data,\n        bytes memory operatorData\n    )\n        internal virtual\n    {\n        require(from != address(0), \"ERC777: burn from the zero address\");\n\n        address operator = _msgSender();\n\n        /* Chaged by SKALE: we swapped these lines to prevent delegation of burning tokens */\n\n        _callTokensToSend(operator, from, address(0), amount, data, operatorData);\n\n        _beforeTokenTransfer(operator, from, address(0), amount);\n\n        /* End of changed by SKALE */\n\n        // Update state variables\n        _balances[from] = _balances[from].sub(amount, \"ERC777: burn amount exceeds balance\");\n        _totalSupply = _totalSupply.sub(amount);\n\n        emit Burned(operator, from, amount, data, operatorData);\n        emit Transfer(from, address(0), amount);\n    }\n\n    function _move(\n        address operator,\n        address from,\n        address to,\n        uint256 amount,\n        bytes memory userData,\n        bytes memory operatorData\n    )\n        private\n    {\n        _beforeTokenTransfer(operator, from, to, amount);\n\n        _balances[from] = _balances[from].sub(amount, \"ERC777: transfer amount exceeds balance\");\n        _balances[to] = _balances[to].add(amount);\n\n        emit Sent(operator, from, to, amount, userData, operatorData);\n        emit Transfer(from, to, amount);\n    }\n\n    /**\n     * @dev See {ERC20-_approve}.\n     *\n     * Note that accounts cannot have allowance issued by their operators.\n     */\n    function _approve(address holder, address spender, uint256 value) internal {\n        require(holder != address(0), \"ERC777: approve from the zero address\");\n        require(spender != address(0), \"ERC777: approve to the zero address\");\n\n        _allowances[holder][spender] = value;\n        emit Approval(holder, spender, value);\n    }\n\n    /**\n     * @dev Call from.tokensToSend() if the interface is registered\n     * @param operator address operator requesting the transfer\n     * @param from address token holder address\n     * @param to address recipient address\n     * @param amount uint256 amount of tokens to transfer\n     * @param userData bytes extra information provided by the token holder (if any)\n     * @param operatorData bytes extra information provided by the operator (if any)\n     */\n    function _callTokensToSend(\n        address operator,\n        address from,\n        address to,\n        uint256 amount,\n        bytes memory userData,\n        bytes memory operatorData\n    )\n        /* Chaged by SKALE from private */ internal /* End of changed by SKALE */\n        /* Added by SKALE */ virtual /* End of added by SKALE */\n    {\n        address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH);\n        if (implementer != address(0)) {\n            IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);\n        }\n    }\n\n    /**\n     * @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but\n     * tokensReceived() was not registered for the recipient\n     * @param operator address operator requesting the transfer\n     * @param from address token holder address\n     * @param to address recipient address\n     * @param amount uint256 amount of tokens to transfer\n     * @param userData bytes extra information provided by the token holder (if any)\n     * @param operatorData bytes extra information provided by the operator (if any)\n     * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient\n     */\n    function _callTokensReceived(\n        address operator,\n        address from,\n        address to,\n        uint256 amount,\n        bytes memory userData,\n        bytes memory operatorData,\n        bool requireReceptionAck\n    )\n        /* Chaged by SKALE from private */ internal /* End of changed by SKALE */\n        /* Added by SKALE */ virtual /* End of added by SKALE */\n    {\n        address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH);\n        if (implementer != address(0)) {\n            IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);\n        } else if (requireReceptionAck) {\n            require(!to.isContract(), \"ERC777: token recipient contract has no implementer for ERC777TokensRecipient\");\n        }\n    }\n\n    /**\n     * @dev Hook that is called before any token transfer. This includes\n     * calls to {send}, {transfer}, {operatorSend}, minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, ``from``\u0027s `tokenId` will be\n     * transferred to `to`.\n     * - when `from` is zero, `tokenId` will be minted for `to`.\n     * - when `to` is zero, ``from``\u0027s `tokenId` will be burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _beforeTokenTransfer(address operator, address from, address to, uint256 tokenId) internal virtual { }\n}\n"},"FractionUtils.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    FractionUtils.sol - SKALE Manager\n    Copyright (C) 2018-Present SKALE Labs\n    @author Dmytro Stebaiev\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\nimport \"./OCSafeMath.sol\";\n\n\nlibrary FractionUtils {\n    using SafeMath for uint;\n\n    struct Fraction {\n        uint numerator;\n        uint denominator;\n    }\n\n    function createFraction(uint numerator, uint denominator) internal pure returns (Fraction memory) {\n        require(denominator \u003e 0, \"Division by zero\");\n        Fraction memory fraction = Fraction({numerator: numerator, denominator: denominator});\n        reduceFraction(fraction);\n        return fraction;\n    }\n\n    function createFraction(uint value) internal pure returns (Fraction memory) {\n        return createFraction(value, 1);\n    }\n\n    function reduceFraction(Fraction memory fraction) internal pure {\n        uint _gcd = gcd(fraction.numerator, fraction.denominator);\n        fraction.numerator = fraction.numerator.div(_gcd);\n        fraction.denominator = fraction.denominator.div(_gcd);\n    }\n\n    function multiplyFraction(Fraction memory a, Fraction memory b) internal pure returns (Fraction memory) {\n        return createFraction(a.numerator.mul(b.numerator), a.denominator.mul(b.denominator));\n    }\n\n    function gcd(uint a, uint b) internal pure returns (uint) {\n        uint _a = a;\n        uint _b = b;\n        if (_b \u003e _a) {\n            (_a, _b) = swap(_a, _b);\n        }\n        while (_b \u003e 0) {\n            _a = _a.mod(_b);\n            (_a, _b) = swap (_a, _b);\n        }\n        return _a;\n    }\n\n    function swap(uint a, uint b) internal pure returns (uint, uint) {\n        return (b, a);\n    }\n}"},"IDelegatableToken.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    IDelegatableToken.sol - SKALE Manager\n    Copyright (C) 2019-Present SKALE Labs\n    @author Dmytro Stebaiev\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\n/**\n * @dev Interface of Delegatable Token operations.\n */\ninterface IDelegatableToken {\n    /**\n     * @dev Updates and returns the amount of locked tokens of a given account (`wallet`).\n     */\n    function getAndUpdateLockedAmount(address wallet) external returns (uint);\n    /**\n     * @dev Updates and returns the amount of delegated tokens of a given account (`wallet`).\n     */\n    function getAndUpdateDelegatedAmount(address wallet) external returns (uint);\n    /**\n     * @dev Updates and returns the amount of slashed tokens of a given account (`wallet`).\n     */\n    function getAndUpdateSlashedAmount(address wallet) external returns (uint);\n}"},"ILocker.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    ILocker.sol - SKALE Manager\n    Copyright (C) 2019-Present SKALE Labs\n    @author Dmytro Stebaiev\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\n/**\n * @dev Interface of Locker functions of the {TokenState} contract.\n *\n * The SKALE Network has three types of locked tokens:\n *\n * - Tokens that are transferrable but are currently locked into delegation with\n * a validator. See {DelegationController};\n *\n * - Tokens that are not transferable from one address to another, but may be\n * delegated to a validator {getAndUpdateLockedAmount}. This lock enforces\n * Proof-of-Use requirements. See {TokenLaunchLocker}; and,\n *\n * - Tokens that are neither transferable nor delegatable\n * {getAndUpdateForbiddenForDelegationAmount}. This lock enforces slashing.\n * See {Punisher}.\n */\ninterface ILocker {\n    /**\n     * @dev Returns the locked amount of untransferable tokens of a given `wallet`\n     */\n    function getAndUpdateLockedAmount(address wallet) external returns (uint);\n\n    /**\n     * @dev Returns the locked amount of untransferable and un-delegatable tokens of a given `wallet`.\n     */\n    function getAndUpdateForbiddenForDelegationAmount(address wallet) external returns (uint);\n}\n"},"MathUtils.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    StringUtils.sol - SKALE Manager\n    Copyright (C) 2018-Present SKALE Labs\n    @author Dmytro Stebaiev\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\n\nlibrary MathUtils {\n    event UnderflowError(\n        uint a,\n        uint b\n    );\n\n    uint constant private _EPS = 1e6;\n\n    function boundedSub(uint256 a, uint256 b) internal returns (uint256) {\n        if (a \u003e= b) {\n            return a - b;\n        } else {\n            emit UnderflowError(a, b);\n            return 0;\n        }\n    }\n\n    function boundedSubWithoutEvent(uint256 a, uint256 b) internal pure returns (uint256) {\n        if (a \u003e= b) {\n            return a - b;\n        } else {\n            return 0;\n        }\n    }\n\n    function muchGreater(uint256 a, uint256 b) internal pure returns (bool) {\n        assert(uint(-1) - _EPS \u003e b);\n        return a \u003e b + _EPS;\n    }\n\n    function approximatelyEqual(uint256 a, uint256 b) internal pure returns (bool) {\n        if (a \u003e b) {\n            return a - b \u003c _EPS;\n        } else {\n            return b - a \u003c _EPS;\n        }\n    }\n}"},"Nodes.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    Nodes.sol - SKALE Manager\n    Copyright (C) 2018-Present SKALE Labs\n    @author Artem Payvin\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\npragma experimental ABIEncoderV2;\n\nimport \"./OCSafeCast.sol\";\n\nimport \"./Permissions.sol\";\nimport \"./ConstantsHolder.sol\";\nimport \"./ValidatorService.sol\";\nimport \"./DelegationController.sol\";\n\n\n/**\n * @title Nodes - contract contains all functionality logic to manage Nodes\n */\ncontract Nodes is Permissions {\n    \n    using SafeCast for uint;\n\n    // All Nodes states\n    enum NodeStatus {Active, Leaving, Left}\n\n    struct Node {\n        string name;\n        bytes4 ip;\n        bytes4 publicIP;\n        uint16 port;\n        bytes32[2] publicKey;\n        uint startBlock;\n        uint lastRewardDate;\n        uint finishTime;\n        NodeStatus status;\n        uint validatorId;\n    }\n\n    // struct to note which Nodes and which number of Nodes owned by user\n    struct CreatedNodes {\n        mapping (uint =\u003e bool) isNodeExist;\n        uint numberOfNodes;\n    }\n\n    struct SpaceManaging {\n        uint8 freeSpace;\n        uint indexInSpaceMap;\n    }\n\n    // TODO: move outside the contract\n    struct NodeCreationParams {\n        string name;\n        bytes4 ip;\n        bytes4 publicIp;\n        uint16 port;\n        bytes32[2] publicKey;\n        uint16 nonce;\n    }\n\n    // array which contain all Nodes\n    Node[] public nodes;\n\n    SpaceManaging[] public spaceOfNodes;\n\n    // mapping for checking which Nodes and which number of Nodes owned by user\n    mapping (address =\u003e CreatedNodes) public nodeIndexes;\n    // mapping for checking is IP address busy\n    mapping (bytes4 =\u003e bool) public nodesIPCheck;\n    // mapping for checking is Name busy\n    mapping (bytes32 =\u003e bool) public nodesNameCheck;\n    // mapping for indication from Name to Index\n    mapping (bytes32 =\u003e uint) public nodesNameToIndex;\n    // mapping for indication from space to Nodes\n    mapping (uint8 =\u003e uint[]) public spaceToNodes;\n\n    mapping (uint =\u003e uint[]) public validatorToNodeIndexes;\n\n    uint public numberOfActiveNodes;\n    uint public numberOfLeavingNodes;\n    uint public numberOfLeftNodes;\n\n    // informs that Node is created\n    event NodeCreated(\n        uint nodeIndex,\n        address owner,\n        string name,\n        bytes4 ip,\n        bytes4 publicIP,\n        uint16 port,\n        uint16 nonce,\n        uint time,\n        uint gasSpend\n    );\n\n    // informs that node is fully finished quitting from the system\n    event ExitCompleted(\n        uint nodeIndex,\n        uint time,\n        uint gasSpend\n    );\n\n    // informs that owner starts the procedure of quitting the Node from the system\n    event ExitInited(\n        uint nodeIndex,\n        uint startLeavingPeriod,\n        uint time,\n        uint gasSpend\n    );\n\n    /**\n     * @dev removeSpaceFromFractionalNode - occupies space from Fractional Node\n     * function could be run only by Schains\n     * @param nodeIndex - index of Node at array of Fractional Nodes\n     * @param space - space which should be occupied\n     */\n    function removeSpaceFromNode(uint nodeIndex, uint8 space)\n        external\n        allowTwo(\"NodeRotation\", \"SchainsInternal\")\n        returns (bool)\n    {\n        if (spaceOfNodes[nodeIndex].freeSpace \u003c space) {\n            return false;\n        }\n        if (space \u003e 0) {\n            _moveNodeToNewSpaceMap(\n                nodeIndex,\n                uint(spaceOfNodes[nodeIndex].freeSpace).sub(space).toUint8()\n            );\n        }\n        return true;\n    }\n\n    /**\n     * @dev adSpaceToFractionalNode - returns space to Fractional Node\n     * function could be run only be Schains\n     * @param nodeIndex - index of Node at array of Fractional Nodes\n     * @param space - space which should be returned\n     */\n    function addSpaceToNode(uint nodeIndex, uint8 space) external allow(\"Schains\") {\n        if (space \u003e 0) {\n            _moveNodeToNewSpaceMap(\n                nodeIndex,\n                uint(spaceOfNodes[nodeIndex].freeSpace).add(space).toUint8()\n            );\n        }\n    }\n\n    /**\n     * @dev changeNodeLastRewardDate - changes Node\u0027s last reward date\n     * function could be run only by SkaleManager\n     * @param nodeIndex - index of Node\n     */\n    function changeNodeLastRewardDate(uint nodeIndex) external allow(\"SkaleManager\") {\n        nodes[nodeIndex].lastRewardDate = block.timestamp;\n    }\n\n    function changeNodeFinishTime(uint nodeIndex, uint time) external allow(\"SkaleManager\") {\n        nodes[nodeIndex].finishTime = time;\n    }\n\n    /**\n     * @dev createNode - creates new Node and add it to the Nodes contract\n     * function could be only run by SkaleManager\n     * @param from - owner of Node\n     * @return nodeIndex - index of Node\n     */\n    function createNode(address from, NodeCreationParams calldata params)\n        external\n        allow(\"SkaleManager\")\n        returns (uint nodeIndex)\n    {\n        // checks that Node has correct data\n        require(params.ip != 0x0 \u0026\u0026 !nodesIPCheck[params.ip], \"IP address is zero or is not available\");\n        require(!nodesNameCheck[keccak256(abi.encodePacked(params.name))], \"Name has already registered\");\n        require(params.port \u003e 0, \"Port is zero\");\n\n        uint validatorId = ValidatorService(\n            contractManager.getContract(\"ValidatorService\")).getValidatorIdByNodeAddress(from);\n\n        // adds Node to Nodes contract\n        nodeIndex = _addNode(\n            from,\n            params.name,\n            params.ip,\n            params.publicIp,\n            params.port,\n            params.publicKey,\n            validatorId);\n\n        emit NodeCreated(\n            nodeIndex,\n            from,\n            params.name,\n            params.ip,\n            params.publicIp,\n            params.port,\n            params.nonce,\n            block.timestamp,\n            gasleft());\n    }\n\n    /**\n     * @dev initExit - initiate a procedure of quitting the system\n     * function could be only run by SkaleManager\n     * @param nodeIndex - index of Node\n     * @return true - if everything OK\n     */\n    function initExit(uint nodeIndex) external allow(\"SkaleManager\") returns (bool) {\n\n        _setNodeLeaving(nodeIndex);\n\n        emit ExitInited(\n            nodeIndex,\n            block.timestamp,\n            block.timestamp,\n            gasleft());\n        return true;\n    }\n\n    /**\n     * @dev completeExit - finish a procedure of quitting the system\n     * function could be run only by SkaleManager\n     * @param nodeIndex - index of Node\n     * @return amount of SKL which be returned\n     */\n    function completeExit(uint nodeIndex) external allow(\"SkaleManager\") returns (bool) {\n        require(isNodeLeaving(nodeIndex), \"Node is not Leaving\");\n\n        _setNodeLeft(nodeIndex);\n        _deleteNode(nodeIndex);\n\n        emit ExitCompleted(\n            nodeIndex,\n            block.timestamp,\n            gasleft());\n        return true;\n    }\n\n    function deleteNodeForValidator(uint validatorId, uint nodeIndex) external allow(\"SkaleManager\") {\n        ValidatorService validatorService = ValidatorService(contractManager.getContract(\"ValidatorService\"));\n        require(validatorService.validatorExists(validatorId), \"Validator with such ID does not exist\");\n        uint[] memory validatorNodes = validatorToNodeIndexes[validatorId];\n        uint position = _findNode(validatorNodes, nodeIndex);\n        if (position \u003c validatorNodes.length) {\n            validatorToNodeIndexes[validatorId][position] =\n                validatorToNodeIndexes[validatorId][validatorNodes.length.sub(1)];\n        }\n        validatorToNodeIndexes[validatorId].pop();\n    }\n\n    function checkPossibilityCreatingNode(address nodeAddress) external allow(\"SkaleManager\") {\n        ValidatorService validatorService = ValidatorService(contractManager.getContract(\"ValidatorService\"));\n        DelegationController delegationController = DelegationController(\n            contractManager.getContract(\"DelegationController\")\n        );\n        uint validatorId = validatorService.getValidatorIdByNodeAddress(nodeAddress);\n        require(validatorService.isAuthorizedValidator(validatorId), \"Validator is not authorized to create a node\");\n        uint[] memory validatorNodes = validatorToNodeIndexes[validatorId];\n        uint delegationsTotal = delegationController.getAndUpdateDelegatedToValidatorNow(validatorId);\n        uint msr = ConstantsHolder(contractManager.getContract(\"ConstantsHolder\")).msr();\n        require(\n            validatorNodes.length.add(1).mul(msr) \u003c= delegationsTotal,\n            \"Validator must meet the Minimum Staking Requirement\");\n    }\n\n    function checkPossibilityToMaintainNode(\n        uint validatorId,\n        uint nodeIndex\n    )\n        external\n        allow(\"Bounty\")\n        returns (bool)\n    {\n        DelegationController delegationController = DelegationController(\n            contractManager.getContract(\"DelegationController\")\n        );\n        ValidatorService validatorService = ValidatorService(contractManager.getContract(\"ValidatorService\"));\n        require(validatorService.validatorExists(validatorId), \"Validator with such ID does not exist\");\n        uint[] memory validatorNodes = validatorToNodeIndexes[validatorId];\n        uint position = _findNode(validatorNodes, nodeIndex);\n        require(position \u003c validatorNodes.length, \"Node does not exist for this Validator\");\n        uint delegationsTotal = delegationController.getAndUpdateDelegatedToValidatorNow(validatorId);\n        uint msr = ConstantsHolder(contractManager.getContract(\"ConstantsHolder\")).msr();\n        return position.add(1).mul(msr) \u003c= delegationsTotal;\n    }\n\n    function getNodesWithFreeSpace(uint8 freeSpace) external view returns (uint[] memory) {\n        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract(\"ConstantsHolder\"));\n        uint[] memory nodesWithFreeSpace = new uint[](countNodesWithFreeSpace(freeSpace));\n        uint cursor = 0;\n        for (uint8 i = freeSpace; i \u003c= constantsHolder.TOTAL_SPACE_ON_NODE(); ++i) {\n            for (uint j = 0; j \u003c spaceToNodes[i].length; j++) {\n                nodesWithFreeSpace[cursor] = spaceToNodes[i][j];\n                ++cursor;\n            }\n        }\n        return nodesWithFreeSpace;\n    }\n\n    /**\n     * @dev isTimeForReward - checks if time for reward has come\n     * @param nodeIndex - index of Node\n     * @return if time for reward has come - true, else - false\n     */\n    function isTimeForReward(uint nodeIndex) external view returns (bool) {\n        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract(\"ConstantsHolder\"));\n        return uint(nodes[nodeIndex].lastRewardDate).add(constantsHolder.rewardPeriod()) \u003c= block.timestamp;\n    }\n\n    /**\n     * @dev isNodeExist - checks existence of Node at this address\n     * @param from - account address\n     * @param nodeIndex - index of Node\n     * @return if exist - true, else - false\n     */\n    function isNodeExist(address from, uint nodeIndex) external view returns (bool) {\n        return nodeIndexes[from].isNodeExist[nodeIndex];\n    }\n\n    /**\n     * @dev getNodeIP - get ip address of Node\n     * @param nodeIndex - index of Node\n     * @return ip address\n     */\n    function getNodeIP(uint nodeIndex) external view returns (bytes4) {\n        require(nodeIndex \u003c nodes.length, \"Node does not exist\");\n        return nodes[nodeIndex].ip;\n    }\n\n    /**\n     * @dev getNodePort - get Node\u0027s port\n     * @param nodeIndex - index of Node\n     * @return port\n     */\n    function getNodePort(uint nodeIndex) external view returns (uint16) {\n        return nodes[nodeIndex].port;\n    }\n\n    function getNodePublicKey(uint nodeIndex) external view returns (bytes32[2] memory) {\n        return nodes[nodeIndex].publicKey;\n    }\n\n    function getNodeFinishTime(uint nodeIndex) external view returns (uint) {\n        return nodes[nodeIndex].finishTime;\n    }\n\n    /**\n     * @dev isNodeLeft - checks if Node status Left\n     * @param nodeIndex - index of Node\n     * @return if Node status Left - true, else - false\n     */\n    function isNodeLeft(uint nodeIndex) external view returns (bool) {\n        return nodes[nodeIndex].status == NodeStatus.Left;\n    }\n\n    /**\n     * @dev getNodeLastRewardDate - get Node last reward date\n     * @param nodeIndex - index of Node\n     * @return Node last reward date\n     */\n    function getNodeLastRewardDate(uint nodeIndex) external view returns (uint) {\n        return nodes[nodeIndex].lastRewardDate;\n    }\n\n    /**\n     * @dev getNodeNextRewardDate - get Node next reward date\n     * @param nodeIndex - index of Node\n     * @return Node next reward date\n     */\n    function getNodeNextRewardDate(uint nodeIndex) external view returns (uint) {\n        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract(\"ConstantsHolder\"));\n        return nodes[nodeIndex].lastRewardDate.add(constantsHolder.rewardPeriod());\n    }\n\n    /**\n     * @dev getNumberOfNodes - get number of Nodes\n     * @return number of Nodes\n     */\n    function getNumberOfNodes() external view returns (uint) {\n        return nodes.length;\n    }\n\n    /**\n     * @dev getNumberOfFullNodes - get number Online Nodes\n     * @return number of active nodes plus number of leaving nodes\n     */\n    function getNumberOnlineNodes() external view returns (uint) {\n        return numberOfActiveNodes.add(numberOfLeavingNodes);\n    }\n\n    /**\n     * @dev getActiveNodeIPs - get array of ips of Active Nodes\n     * @return activeNodeIPs - array of ips of Active Nodes\n     */\n    function getActiveNodeIPs() external view returns (bytes4[] memory activeNodeIPs) {\n        activeNodeIPs = new bytes4[](numberOfActiveNodes);\n        uint indexOfActiveNodeIPs = 0;\n        for (uint indexOfNodes = 0; indexOfNodes \u003c nodes.length; indexOfNodes++) {\n            if (isNodeActive(indexOfNodes)) {\n                activeNodeIPs[indexOfActiveNodeIPs] = nodes[indexOfNodes].ip;\n                indexOfActiveNodeIPs++;\n            }\n        }\n    }\n\n    /**\n     * @dev getActiveNodesByAddress - get array of indexes of Active Nodes, which were\n     * created by msg.sender\n     * @return activeNodesByAddress Array of indexes of Active Nodes, which were created by msg.sender\n     */\n    function getActiveNodesByAddress() external view returns (uint[] memory activeNodesByAddress) {\n        activeNodesByAddress = new uint[](nodeIndexes[msg.sender].numberOfNodes);\n        uint indexOfActiveNodesByAddress = 0;\n        for (uint indexOfNodes = 0; indexOfNodes \u003c nodes.length; indexOfNodes++) {\n            if (nodeIndexes[msg.sender].isNodeExist[indexOfNodes] \u0026\u0026 isNodeActive(indexOfNodes)) {\n                activeNodesByAddress[indexOfActiveNodesByAddress] = indexOfNodes;\n                indexOfActiveNodesByAddress++;\n            }\n        }\n    }\n\n    /**\n     * @dev getActiveNodeIds - get array of indexes of Active Nodes\n     * @return activeNodeIds - array of indexes of Active Nodes\n     */\n    function getActiveNodeIds() external view returns (uint[] memory activeNodeIds) {\n        activeNodeIds = new uint[](numberOfActiveNodes);\n        uint indexOfActiveNodeIds = 0;\n        for (uint indexOfNodes = 0; indexOfNodes \u003c nodes.length; indexOfNodes++) {\n            if (isNodeActive(indexOfNodes)) {\n                activeNodeIds[indexOfActiveNodeIds] = indexOfNodes;\n                indexOfActiveNodeIds++;\n            }\n        }\n    }\n\n    function getValidatorId(uint nodeIndex) external view returns (uint) {\n        require(nodeIndex \u003c nodes.length, \"Node does not exist\");\n        return nodes[nodeIndex].validatorId;\n    }\n\n    function getNodeStatus(uint nodeIndex) external view returns (NodeStatus) {\n        return nodes[nodeIndex].status;\n    }\n\n    function getValidatorNodeIndexes(uint validatorId) external view returns (uint[] memory) {\n        ValidatorService validatorService = ValidatorService(contractManager.getContract(\"ValidatorService\"));\n        require(validatorService.validatorExists(validatorId), \"Validator with such ID does not exist\");\n        return validatorToNodeIndexes[validatorId];\n    }\n\n    /**\n     * @dev constructor in Permissions approach\n     * @param contractsAddress needed in Permissions constructor\n    */\n    function initialize(address contractsAddress) public override initializer {\n        Permissions.initialize(contractsAddress);\n\n        numberOfActiveNodes = 0;\n        numberOfLeavingNodes = 0;\n        numberOfLeftNodes = 0;\n    }\n\n    /**\n     * @dev isNodeActive - checks if Node status Active\n     * @param nodeIndex - index of Node\n     * @return if Node status Active - true, else - false\n     */\n    function isNodeActive(uint nodeIndex) public view returns (bool) {\n        return nodes[nodeIndex].status == NodeStatus.Active;\n    }\n\n    /**\n     * @dev isNodeLeaving - checks if Node status Leaving\n     * @param nodeIndex - index of Node\n     * @return if Node status Leaving - true, else - false\n     */\n    function isNodeLeaving(uint nodeIndex) public view returns (bool) {\n        return nodes[nodeIndex].status == NodeStatus.Leaving;\n    }\n\n    function countNodesWithFreeSpace(uint8 freeSpace) public view returns (uint count) {\n        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract(\"ConstantsHolder\"));\n        count = 0;\n        for (uint8 i = freeSpace; i \u003c= constantsHolder.TOTAL_SPACE_ON_NODE(); ++i) {\n            count = count.add(spaceToNodes[i].length);\n        }\n    }\n\n    function _findNode(uint[] memory validatorNodeIndexes, uint nodeIndex) private pure returns (uint) {\n        uint i;\n        for (i = 0; i \u003c validatorNodeIndexes.length; i++) {\n            if (validatorNodeIndexes[i] == nodeIndex) {\n                return i;\n            }\n        }\n        return validatorNodeIndexes.length;\n    }\n\n    function _moveNodeToNewSpaceMap(uint nodeIndex, uint8 newSpace) private {\n        uint8 previousSpace = spaceOfNodes[nodeIndex].freeSpace;\n        uint indexInArray = spaceOfNodes[nodeIndex].indexInSpaceMap;\n        if (indexInArray \u003c spaceToNodes[previousSpace].length.sub(1)) {\n            uint shiftedIndex = spaceToNodes[previousSpace][spaceToNodes[previousSpace].length.sub(1)];\n            spaceToNodes[previousSpace][indexInArray] = shiftedIndex;\n            spaceOfNodes[shiftedIndex].indexInSpaceMap = indexInArray;\n            spaceToNodes[previousSpace].pop();\n        } else {\n            spaceToNodes[previousSpace].pop();\n        }\n        spaceToNodes[newSpace].push(nodeIndex);\n        spaceOfNodes[nodeIndex].freeSpace = newSpace;\n        spaceOfNodes[nodeIndex].indexInSpaceMap = spaceToNodes[newSpace].length.sub(1);\n    }\n\n    /**\n     * @dev _setNodeLeft - set Node Left\n     * function could be run only by Nodes\n     * @param nodeIndex - index of Node\n     */\n    function _setNodeLeft(uint nodeIndex) private {\n        nodesIPCheck[nodes[nodeIndex].ip] = false;\n        nodesNameCheck[keccak256(abi.encodePacked(nodes[nodeIndex].name))] = false;\n        delete nodesNameToIndex[keccak256(abi.encodePacked(nodes[nodeIndex].name))];\n        if (nodes[nodeIndex].status == NodeStatus.Active) {\n            numberOfActiveNodes--;\n        } else {\n            numberOfLeavingNodes--;\n        }\n        nodes[nodeIndex].status = NodeStatus.Left;\n        numberOfLeftNodes++;\n    }\n\n    /**\n     * @dev _setNodeLeaving - set Node Leaving\n     * function could be run only by Nodes\n     * @param nodeIndex - index of Node\n     */\n    function _setNodeLeaving(uint nodeIndex) private {\n        nodes[nodeIndex].status = NodeStatus.Leaving;\n        numberOfActiveNodes--;\n        numberOfLeavingNodes++;\n    }\n\n    /**\n     * @dev _addNode - adds Node to array\n     * function could be run only by executor\n     * @param from - owner of Node\n     * @param name - Node name\n     * @param ip - Node ip\n     * @param publicIP - Node public ip\n     * @param port - Node public port\n     * @param publicKey - Ethereum public key\n     * @return nodeIndex Index of Node\n     */\n    function _addNode(\n        address from,\n        string memory name,\n        bytes4 ip,\n        bytes4 publicIP,\n        uint16 port,\n        bytes32[2] memory publicKey,\n        uint validatorId\n    )\n        private\n        returns (uint nodeIndex)\n    {\n        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract(\"ConstantsHolder\"));\n        nodes.push(Node({\n            name: name,\n            ip: ip,\n            publicIP: publicIP,\n            port: port,\n            //owner: from,\n            publicKey: publicKey,\n            startBlock: block.number,\n            lastRewardDate: block.timestamp,\n            finishTime: 0,\n            status: NodeStatus.Active,\n            validatorId: validatorId\n        }));\n        nodeIndex = nodes.length.sub(1);\n        validatorToNodeIndexes[validatorId].push(nodeIndex);\n        bytes32 nodeId = keccak256(abi.encodePacked(name));\n        nodesIPCheck[ip] = true;\n        nodesNameCheck[nodeId] = true;\n        nodesNameToIndex[nodeId] = nodeIndex;\n        nodeIndexes[from].isNodeExist[nodeIndex] = true;\n        nodeIndexes[from].numberOfNodes++;\n        spaceOfNodes.push(SpaceManaging({\n            freeSpace: constantsHolder.TOTAL_SPACE_ON_NODE(),\n            indexInSpaceMap: spaceToNodes[constantsHolder.TOTAL_SPACE_ON_NODE()].length\n        }));\n        spaceToNodes[constantsHolder.TOTAL_SPACE_ON_NODE()].push(nodeIndex);\n        numberOfActiveNodes++;\n    }\n\n    function _deleteNode(uint nodeIndex) private {\n        uint8 space = spaceOfNodes[nodeIndex].freeSpace;\n        uint indexInArray = spaceOfNodes[nodeIndex].indexInSpaceMap;\n        if (indexInArray \u003c spaceToNodes[space].length.sub(1)) {\n            uint shiftedIndex = spaceToNodes[space][spaceToNodes[space].length.sub(1)];\n            spaceToNodes[space][indexInArray] = shiftedIndex;\n            spaceOfNodes[shiftedIndex].indexInSpaceMap = indexInArray;\n            spaceToNodes[space].pop();\n        } else {\n            spaceToNodes[space].pop();\n        }\n        delete spaceOfNodes[nodeIndex].freeSpace;\n        delete spaceOfNodes[nodeIndex].indexInSpaceMap;\n    }\n\n}\n"},"OCContext.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\n/*\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with GSN meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address payable) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes memory) {\n        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691\n        return msg.data;\n    }\n}\n"},"OCECDSA.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n    /**\n     * @dev Returns the address that signed a hashed message (`hash`) with\n     * `signature`. This address can then be used for verification purposes.\n     *\n     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n     * this function rejects them by requiring the `s` value to be in the lower\n     * half order, and the `v` value to be either 27 or 28.\n     *\n     * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n     * verification to be secure: it is possible to craft signatures that\n     * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n     * this is by receiving a hash of the original message (which may otherwise\n     * be too long), and then calling {toEthSignedMessageHash} on it.\n     */\n    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n        // Check the signature length\n        if (signature.length != 65) {\n            revert(\"ECDSA: invalid signature length\");\n        }\n\n        // Divide the signature in r, s and v variables\n        bytes32 r;\n        bytes32 s;\n        uint8 v;\n\n        // ecrecover takes the signature parameters, and the only way to get them\n        // currently is to use assembly.\n        // solhint-disable-next-line no-inline-assembly\n        assembly {\n            r := mload(add(signature, 0x20))\n            s := mload(add(signature, 0x40))\n            v := byte(0, mload(add(signature, 0x60)))\n        }\n\n        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n        // the valid range for s in (281): 0 \u003c s \u003c secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most\n        // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n        //\n        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n        // these malleable signatures as well.\n        if (uint256(s) \u003e 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n            revert(\"ECDSA: invalid signature \u0027s\u0027 value\");\n        }\n\n        if (v != 27 \u0026\u0026 v != 28) {\n            revert(\"ECDSA: invalid signature \u0027v\u0027 value\");\n        }\n\n        // If the signature is valid (and not malleable), return the signer address\n        address signer = ecrecover(hash, v, r, s);\n        require(signer != address(0), \"ECDSA: invalid signature\");\n\n        return signer;\n    }\n\n    /**\n     * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n     * replicates the behavior of the\n     * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]\n     * JSON-RPC method.\n     *\n     * See {recover}.\n     */\n    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n        // 32 is the length in bytes of hash,\n        // enforced by the type signature above\n        return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n    }\n}\n"},"OCIERC1820Registry.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\n/**\n * @dev Interface of the global ERC1820 Registry, as defined in the\n * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register\n * implementers for interfaces in this registry, as well as query support.\n *\n * Implementers may be shared by multiple accounts, and can also implement more\n * than a single interface for each account. Contracts can implement interfaces\n * for themselves, but externally-owned accounts (EOA) must delegate this to a\n * contract.\n *\n * {IERC165} interfaces can also be queried via the registry.\n *\n * For an in-depth explanation and source code analysis, see the EIP text.\n */\ninterface IERC1820Registry {\n    /**\n     * @dev Sets `newManager` as the manager for `account`. A manager of an\n     * account is able to set interface implementers for it.\n     *\n     * By default, each account is its own manager. Passing a value of `0x0` in\n     * `newManager` will reset the manager to this initial state.\n     *\n     * Emits a {ManagerChanged} event.\n     *\n     * Requirements:\n     *\n     * - the caller must be the current manager for `account`.\n     */\n    function setManager(address account, address newManager) external;\n\n    /**\n     * @dev Returns the manager for `account`.\n     *\n     * See {setManager}.\n     */\n    function getManager(address account) external view returns (address);\n\n    /**\n     * @dev Sets the `implementer` contract as ``account``\u0027s implementer for\n     * `interfaceHash`.\n     *\n     * `account` being the zero address is an alias for the caller\u0027s address.\n     * The zero address can also be used in `implementer` to remove an old one.\n     *\n     * See {interfaceHash} to learn how these are created.\n     *\n     * Emits an {InterfaceImplementerSet} event.\n     *\n     * Requirements:\n     *\n     * - the caller must be the current manager for `account`.\n     * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not\n     * end in 28 zeroes).\n     * - `implementer` must implement {IERC1820Implementer} and return true when\n     * queried for support, unless `implementer` is the caller. See\n     * {IERC1820Implementer-canImplementInterfaceForAddress}.\n     */\n    function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external;\n\n    /**\n     * @dev Returns the implementer of `interfaceHash` for `account`. If no such\n     * implementer is registered, returns the zero address.\n     *\n     * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28\n     * zeroes), `account` will be queried for support of it.\n     *\n     * `account` being the zero address is an alias for the caller\u0027s address.\n     */\n    function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address);\n\n    /**\n     * @dev Returns the interface hash for an `interfaceName`, as defined in the\n     * corresponding\n     * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].\n     */\n    function interfaceHash(string calldata interfaceName) external pure returns (bytes32);\n\n    /**\n     *  @notice Updates the cache with whether the contract implements an ERC165 interface or not.\n     *  @param account Address of the contract for which to update the cache.\n     *  @param interfaceId ERC165 interface for which to update the cache.\n     */\n    function updateERC165Cache(address account, bytes4 interfaceId) external;\n\n    /**\n     *  @notice Checks whether a contract implements an ERC165 interface or not.\n     *  If the result is not cached a direct lookup on the contract address is performed.\n     *  If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling\n     *  {updateERC165Cache} with the contract address.\n     *  @param account Address of the contract to check.\n     *  @param interfaceId ERC165 interface to check.\n     *  @return True if `account` implements `interfaceId`, false otherwise.\n     */\n    function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);\n\n    /**\n     *  @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.\n     *  @param account Address of the contract to check.\n     *  @param interfaceId ERC165 interface to check.\n     *  @return True if `account` implements `interfaceId`, false otherwise.\n     */\n    function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);\n\n    event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);\n\n    event ManagerChanged(address indexed account, address indexed newManager);\n}\n"},"OCIERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller\u0027s account to `recipient`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller\u0027s tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender\u0027s allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `sender` to `recipient` using the\n     * allowance mechanism. `amount` is then deducted from the caller\u0027s\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"},"OCIERC777.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\n/**\n * @dev Interface of the ERC777Token standard as defined in the EIP.\n *\n * This contract uses the\n * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let\n * token holders and recipients react to token movements by using setting implementers\n * for the associated interfaces in said registry. See {IERC1820Registry} and\n * {ERC1820Implementer}.\n */\ninterface IERC777 {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token, usually a shorter version of the\n     * name.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the smallest part of the token that is not divisible. This\n     * means all token operations (creation, movement and destruction) must have\n     * amounts that are a multiple of this number.\n     *\n     * For most token contracts, this value will equal 1.\n     */\n    function granularity() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by an account (`owner`).\n     */\n    function balanceOf(address owner) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller\u0027s account to `recipient`.\n     *\n     * If send or receive hooks are registered for the caller and `recipient`,\n     * the corresponding functions will be called with `data` and empty\n     * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.\n     *\n     * Emits a {Sent} event.\n     *\n     * Requirements\n     *\n     * - the caller must have at least `amount` tokens.\n     * - `recipient` cannot be the zero address.\n     * - if `recipient` is a contract, it must implement the {IERC777Recipient}\n     * interface.\n     */\n    function send(address recipient, uint256 amount, bytes calldata data) external;\n\n    /**\n     * @dev Destroys `amount` tokens from the caller\u0027s account, reducing the\n     * total supply.\n     *\n     * If a send hook is registered for the caller, the corresponding function\n     * will be called with `data` and empty `operatorData`. See {IERC777Sender}.\n     *\n     * Emits a {Burned} event.\n     *\n     * Requirements\n     *\n     * - the caller must have at least `amount` tokens.\n     */\n    function burn(uint256 amount, bytes calldata data) external;\n\n    /**\n     * @dev Returns true if an account is an operator of `tokenHolder`.\n     * Operators can send and burn tokens on behalf of their owners. All\n     * accounts are their own operator.\n     *\n     * See {operatorSend} and {operatorBurn}.\n     */\n    function isOperatorFor(address operator, address tokenHolder) external view returns (bool);\n\n    /**\n     * @dev Make an account an operator of the caller.\n     *\n     * See {isOperatorFor}.\n     *\n     * Emits an {AuthorizedOperator} event.\n     *\n     * Requirements\n     *\n     * - `operator` cannot be calling address.\n     */\n    function authorizeOperator(address operator) external;\n\n    /**\n     * @dev Revoke an account\u0027s operator status for the caller.\n     *\n     * See {isOperatorFor} and {defaultOperators}.\n     *\n     * Emits a {RevokedOperator} event.\n     *\n     * Requirements\n     *\n     * - `operator` cannot be calling address.\n     */\n    function revokeOperator(address operator) external;\n\n    /**\n     * @dev Returns the list of default operators. These accounts are operators\n     * for all token holders, even if {authorizeOperator} was never called on\n     * them.\n     *\n     * This list is immutable, but individual holders may revoke these via\n     * {revokeOperator}, in which case {isOperatorFor} will return false.\n     */\n    function defaultOperators() external view returns (address[] memory);\n\n    /**\n     * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must\n     * be an operator of `sender`.\n     *\n     * If send or receive hooks are registered for `sender` and `recipient`,\n     * the corresponding functions will be called with `data` and\n     * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.\n     *\n     * Emits a {Sent} event.\n     *\n     * Requirements\n     *\n     * - `sender` cannot be the zero address.\n     * - `sender` must have at least `amount` tokens.\n     * - the caller must be an operator for `sender`.\n     * - `recipient` cannot be the zero address.\n     * - if `recipient` is a contract, it must implement the {IERC777Recipient}\n     * interface.\n     */\n    function operatorSend(\n        address sender,\n        address recipient,\n        uint256 amount,\n        bytes calldata data,\n        bytes calldata operatorData\n    ) external;\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, reducing the total supply.\n     * The caller must be an operator of `account`.\n     *\n     * If a send hook is registered for `account`, the corresponding function\n     * will be called with `data` and `operatorData`. See {IERC777Sender}.\n     *\n     * Emits a {Burned} event.\n     *\n     * Requirements\n     *\n     * - `account` cannot be the zero address.\n     * - `account` must have at least `amount` tokens.\n     * - the caller must be an operator for `account`.\n     */\n    function operatorBurn(\n        address account,\n        uint256 amount,\n        bytes calldata data,\n        bytes calldata operatorData\n    ) external;\n\n    event Sent(\n        address indexed operator,\n        address indexed from,\n        address indexed to,\n        uint256 amount,\n        bytes data,\n        bytes operatorData\n    );\n\n    event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);\n\n    event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);\n\n    event AuthorizedOperator(address indexed operator, address indexed tokenHolder);\n\n    event RevokedOperator(address indexed operator, address indexed tokenHolder);\n}\n"},"OCIERC777Recipient.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\n/**\n * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.\n *\n * Accounts can be notified of {IERC777} tokens being sent to them by having a\n * contract implement this interface (contract holders can be their own\n * implementer) and registering it on the\n * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].\n *\n * See {IERC1820Registry} and {ERC1820Implementer}.\n */\ninterface IERC777Recipient {\n    /**\n     * @dev Called by an {IERC777} token contract whenever tokens are being\n     * moved or created into a registered account (`to`). The type of operation\n     * is conveyed by `from` being the zero address or not.\n     *\n     * This call occurs _after_ the token contract\u0027s state is updated, so\n     * {IERC777-balanceOf}, etc., can be used to query the post-operation state.\n     *\n     * This function may revert to prevent the operation from being executed.\n     */\n    function tokensReceived(\n        address operator,\n        address from,\n        address to,\n        uint256 amount,\n        bytes calldata userData,\n        bytes calldata operatorData\n    ) external;\n}\n"},"OCIERC777Sender.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\n/**\n * @dev Interface of the ERC777TokensSender standard as defined in the EIP.\n *\n * {IERC777} Token holders can be notified of operations performed on their\n * tokens by having a contract implement this interface (contract holders can be\n *  their own implementer) and registering it on the\n * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].\n *\n * See {IERC1820Registry} and {ERC1820Implementer}.\n */\ninterface IERC777Sender {\n    /**\n     * @dev Called by an {IERC777} token contract whenever a registered holder\u0027s\n     * (`from`) tokens are about to be moved or destroyed. The type of operation\n     * is conveyed by `to` being the zero address or not.\n     *\n     * This call occurs _before_ the token contract\u0027s state is updated, so\n     * {IERC777-balanceOf}, etc., can be used to query the pre-operation state.\n     *\n     * This function may revert to prevent the operation from being executed.\n     */\n    function tokensToSend(\n        address operator,\n        address from,\n        address to,\n        uint256 amount,\n        bytes calldata userData,\n        bytes calldata operatorData\n    ) external;\n}\n"},"OCReentrancyGuard.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\ncontract ReentrancyGuard {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot\u0027s contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler\u0027s defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction\u0027s gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant _NOT_ENTERED = 1;\n    uint256 private constant _ENTERED = 2;\n\n    uint256 private _status;\n\n    constructor () internal {\n        _status = _NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and make it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        // On the first call to nonReentrant, _notEntered will be true\n        require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n        // Any calls to nonReentrant after this point will fail\n        _status = _ENTERED;\n\n        _;\n\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _status = _NOT_ENTERED;\n    }\n}\n"},"OCSafeCast.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\n\n/**\n * @dev Wrappers over Solidity\u0027s uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it\u0027s recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n\n    /**\n     * @dev Returns the downcasted uint128 from uint256, reverting on\n     * overflow (when the input is greater than largest uint128).\n     *\n     * Counterpart to Solidity\u0027s `uint128` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 128 bits\n     */\n    function toUint128(uint256 value) internal pure returns (uint128) {\n        require(value \u003c 2**128, \"SafeCast: value doesn\\\u0027t fit in 128 bits\");\n        return uint128(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint64 from uint256, reverting on\n     * overflow (when the input is greater than largest uint64).\n     *\n     * Counterpart to Solidity\u0027s `uint64` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 64 bits\n     */\n    function toUint64(uint256 value) internal pure returns (uint64) {\n        require(value \u003c 2**64, \"SafeCast: value doesn\\\u0027t fit in 64 bits\");\n        return uint64(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint32 from uint256, reverting on\n     * overflow (when the input is greater than largest uint32).\n     *\n     * Counterpart to Solidity\u0027s `uint32` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 32 bits\n     */\n    function toUint32(uint256 value) internal pure returns (uint32) {\n        require(value \u003c 2**32, \"SafeCast: value doesn\\\u0027t fit in 32 bits\");\n        return uint32(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint16 from uint256, reverting on\n     * overflow (when the input is greater than largest uint16).\n     *\n     * Counterpart to Solidity\u0027s `uint16` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 16 bits\n     */\n    function toUint16(uint256 value) internal pure returns (uint16) {\n        require(value \u003c 2**16, \"SafeCast: value doesn\\\u0027t fit in 16 bits\");\n        return uint16(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint8 from uint256, reverting on\n     * overflow (when the input is greater than largest uint8).\n     *\n     * Counterpart to Solidity\u0027s `uint8` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 8 bits.\n     */\n    function toUint8(uint256 value) internal pure returns (uint8) {\n        require(value \u003c 2**8, \"SafeCast: value doesn\\\u0027t fit in 8 bits\");\n        return uint8(value);\n    }\n\n    /**\n     * @dev Converts a signed int256 into an unsigned uint256.\n     *\n     * Requirements:\n     *\n     * - input must be greater than or equal to 0.\n     */\n    function toUint256(int256 value) internal pure returns (uint256) {\n        require(value \u003e= 0, \"SafeCast: value must be positive\");\n        return uint256(value);\n    }\n\n    /**\n     * @dev Returns the downcasted int128 from int256, reverting on\n     * overflow (when the input is less than smallest int128 or\n     * greater than largest int128).\n     *\n     * Counterpart to Solidity\u0027s `int128` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 128 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt128(int256 value) internal pure returns (int128) {\n        require(value \u003e= -2**127 \u0026\u0026 value \u003c 2**127, \"SafeCast: value doesn\\\u0027t fit in 128 bits\");\n        return int128(value);\n    }\n\n    /**\n     * @dev Returns the downcasted int64 from int256, reverting on\n     * overflow (when the input is less than smallest int64 or\n     * greater than largest int64).\n     *\n     * Counterpart to Solidity\u0027s `int64` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 64 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt64(int256 value) internal pure returns (int64) {\n        require(value \u003e= -2**63 \u0026\u0026 value \u003c 2**63, \"SafeCast: value doesn\\\u0027t fit in 64 bits\");\n        return int64(value);\n    }\n\n    /**\n     * @dev Returns the downcasted int32 from int256, reverting on\n     * overflow (when the input is less than smallest int32 or\n     * greater than largest int32).\n     *\n     * Counterpart to Solidity\u0027s `int32` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 32 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt32(int256 value) internal pure returns (int32) {\n        require(value \u003e= -2**31 \u0026\u0026 value \u003c 2**31, \"SafeCast: value doesn\\\u0027t fit in 32 bits\");\n        return int32(value);\n    }\n\n    /**\n     * @dev Returns the downcasted int16 from int256, reverting on\n     * overflow (when the input is less than smallest int16 or\n     * greater than largest int16).\n     *\n     * Counterpart to Solidity\u0027s `int16` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 16 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt16(int256 value) internal pure returns (int16) {\n        require(value \u003e= -2**15 \u0026\u0026 value \u003c 2**15, \"SafeCast: value doesn\\\u0027t fit in 16 bits\");\n        return int16(value);\n    }\n\n    /**\n     * @dev Returns the downcasted int8 from int256, reverting on\n     * overflow (when the input is less than smallest int8 or\n     * greater than largest int8).\n     *\n     * Counterpart to Solidity\u0027s `int8` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 8 bits.\n     *\n     * _Available since v3.1._\n     */\n    function toInt8(int256 value) internal pure returns (int8) {\n        require(value \u003e= -2**7 \u0026\u0026 value \u003c 2**7, \"SafeCast: value doesn\\\u0027t fit in 8 bits\");\n        return int8(value);\n    }\n\n    /**\n     * @dev Converts an unsigned uint256 into a signed int256.\n     *\n     * Requirements:\n     *\n     * - input must be less than or equal to maxInt256.\n     */\n    function toInt256(uint256 value) internal pure returns (int256) {\n        require(value \u003c 2**255, \"SafeCast: value doesn\u0027t fit in an int256\");\n        return int256(value);\n    }\n}\n"},"OCSafeMath.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\n/**\n * @dev Wrappers over Solidity\u0027s arithmetic operations with added overflow\n * checks.\n *\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\n * in bugs, because programmers usually assume that an overflow raises an\n * error, which is the standard behavior in high level programming languages.\n * `SafeMath` restores this intuition by reverting the transaction when an\n * operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it\u0027s recommended to use it always.\n */\nlibrary SafeMath {\n    /**\n     * @dev Returns the addition of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity\u0027s `+` operator.\n     *\n     * Requirements:\n     *\n     * - Addition cannot overflow.\n     */\n    function add(uint256 a, uint256 b) internal pure returns (uint256) {\n        uint256 c = a + b;\n        require(c \u003e= a, \"SafeMath: addition overflow\");\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting on\n     * overflow (when the result is negative).\n     *\n     * Counterpart to Solidity\u0027s `-` operator.\n     *\n     * Requirements:\n     *\n     * - Subtraction cannot overflow.\n     */\n    function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n        return sub(a, b, \"SafeMath: subtraction overflow\");\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n     * overflow (when the result is negative).\n     *\n     * Counterpart to Solidity\u0027s `-` operator.\n     *\n     * Requirements:\n     *\n     * - Subtraction cannot overflow.\n     */\n    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b \u003c= a, errorMessage);\n        uint256 c = a - b;\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity\u0027s `*` operator.\n     *\n     * Requirements:\n     *\n     * - Multiplication cannot overflow.\n     */\n    function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n        // Gas optimization: this is cheaper than requiring \u0027a\u0027 not being zero, but the\n        // benefit is lost if \u0027b\u0027 is also tested.\n        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n        if (a == 0) {\n            return 0;\n        }\n\n        uint256 c = a * b;\n        require(c / a == b, \"SafeMath: multiplication overflow\");\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers. Reverts on\n     * division by zero. The result is rounded towards zero.\n     *\n     * Counterpart to Solidity\u0027s `/` operator. Note: this function uses a\n     * `revert` opcode (which leaves remaining gas untouched) while Solidity\n     * uses an invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b) internal pure returns (uint256) {\n        return div(a, b, \"SafeMath: division by zero\");\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on\n     * division by zero. The result is rounded towards zero.\n     *\n     * Counterpart to Solidity\u0027s `/` operator. Note: this function uses a\n     * `revert` opcode (which leaves remaining gas untouched) while Solidity\n     * uses an invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b \u003e 0, errorMessage);\n        uint256 c = a / b;\n        // assert(a == b * c + a % b); // There is no case in which this doesn\u0027t hold\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * Reverts when dividing by zero.\n     *\n     * Counterpart to Solidity\u0027s `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n        return mod(a, b, \"SafeMath: modulo by zero\");\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * Reverts with custom message when dividing by zero.\n     *\n     * Counterpart to Solidity\u0027s `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b != 0, errorMessage);\n        return a % b;\n    }\n}\n"},"OEPAccessControl.sol":{"content":"pragma solidity ^0.6.0;\n\nimport \"./OEPEnumerableSet.sol\";\nimport \"./OEPAddress.sol\";\nimport \"./OEPContext.sol\";\nimport \"./OEPInitializable.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n *     require(hasRole(MY_ROLE, _msgSender()));\n *     ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role\u0027s admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n */\nabstract contract AccessControlUpgradeSafe is Initializable, ContextUpgradeSafe {\n    function __AccessControl_init() internal initializer {\n        __Context_init_unchained();\n        __AccessControl_init_unchained();\n    }\n\n    function __AccessControl_init_unchained() internal initializer {\n\n\n    }\n\n    using EnumerableSet for EnumerableSet.AddressSet;\n    using Address for address;\n\n    struct RoleData {\n        EnumerableSet.AddressSet members;\n        bytes32 adminRole;\n    }\n\n    mapping (bytes32 =\u003e RoleData) private _roles;\n\n    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call, an admin role\n     * bearer except when using {_setupRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) public view returns (bool) {\n        return _roles[role].members.contains(account);\n    }\n\n    /**\n     * @dev Returns the number of accounts that have `role`. Can be used\n     * together with {getRoleMember} to enumerate all bearers of a role.\n     */\n    function getRoleMemberCount(bytes32 role) public view returns (uint256) {\n        return _roles[role].members.length();\n    }\n\n    /**\n     * @dev Returns one of the accounts that have `role`. `index` must be a\n     * value between 0 and {getRoleMemberCount}, non-inclusive.\n     *\n     * Role bearers are not sorted in any particular way, and their ordering may\n     * change at any point.\n     *\n     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n     * you perform all queries on the same block. See the following\n     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n     * for more information.\n     */\n    function getRoleMember(bytes32 role, uint256 index) public view returns (address) {\n        return _roles[role].members.at(index);\n    }\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role\u0027s admin, use {_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) public view returns (bytes32) {\n        return _roles[role].adminRole;\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``\u0027s admin role.\n     */\n    function grantRole(bytes32 role, address account) public virtual {\n        require(hasRole(_roles[role].adminRole, _msgSender()), \"AccessControl: sender must be an admin to grant\");\n\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``\u0027s admin role.\n     */\n    function revokeRole(bytes32 role, address account) public virtual {\n        require(hasRole(_roles[role].adminRole, _msgSender()), \"AccessControl: sender must be an admin to revoke\");\n\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function\u0027s\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `account`.\n     */\n    function renounceRole(bytes32 role, address account) public virtual {\n        require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event. Note that unlike {grantRole}, this function doesn\u0027t perform any\n     * checks on the calling account.\n     *\n     * [WARNING]\n     * ====\n     * This function should only be called from the constructor when setting\n     * up the initial roles for the system.\n     *\n     * Using this function in any other way is effectively circumventing the admin\n     * system imposed by {AccessControl}.\n     * ====\n     */\n    function _setupRole(bytes32 role, address account) internal virtual {\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Sets `adminRole` as ``role``\u0027s admin role.\n     */\n    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n        _roles[role].adminRole = adminRole;\n    }\n\n    function _grantRole(bytes32 role, address account) private {\n        if (_roles[role].members.add(account)) {\n            emit RoleGranted(role, account, _msgSender());\n        }\n    }\n\n    function _revokeRole(bytes32 role, address account) private {\n        if (_roles[role].members.remove(account)) {\n            emit RoleRevoked(role, account, _msgSender());\n        }\n    }\n\n    uint256[49] private __gap;\n}\n"},"OEPAddress.sol":{"content":"pragma solidity ^0.6.2;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts\n        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned\n        // for accounts without code, i.e. `keccak256(\u0027\u0027)`\n        bytes32 codehash;\n        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;\n        // solhint-disable-next-line no-inline-assembly\n        assembly { codehash := extcodehash(account) }\n        return (codehash != accountHash \u0026\u0026 codehash != 0x0);\n    }\n\n    /**\n     * @dev Replacement for Solidity\u0027s `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value\n        (bool success, ) = recipient.call{ value: amount }(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n}\n"},"OEPContext.sol":{"content":"pragma solidity ^0.6.0;\nimport \"./OEPInitializable.sol\";\n\n/*\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with GSN meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\ncontract ContextUpgradeSafe is Initializable {\n    // Empty internal constructor, to prevent people from mistakenly deploying\n    // an instance of this contract, which should be used via inheritance.\n\n    function __Context_init() internal initializer {\n        __Context_init_unchained();\n    }\n\n    function __Context_init_unchained() internal initializer {\n\n\n    }\n\n\n    function _msgSender() internal view virtual returns (address payable) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes memory) {\n        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691\n        return msg.data;\n    }\n\n    uint256[50] private __gap;\n}\n"},"OEPEnumerableSet.sol":{"content":"pragma solidity ^0.6.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n *     // Add the library methods\n *     using EnumerableSet for EnumerableSet.AddressSet;\n *\n *     // Declare a set state variable\n *     EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`\n * (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n    // To implement this library for multiple types with as little code\n    // repetition as possible, we write it in terms of a generic Set type with\n    // bytes32 values.\n    // The Set implementation uses private functions, and user-facing\n    // implementations (such as AddressSet) are just wrappers around the\n    // underlying Set.\n    // This means that we can only create new EnumerableSets for types that fit\n    // in bytes32.\n\n    struct Set {\n        // Storage of set values\n        bytes32[] _values;\n\n        // Position of the value in the `values` array, plus 1 because index 0\n        // means a value is not in the set.\n        mapping (bytes32 =\u003e uint256) _indexes;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function _add(Set storage set, bytes32 value) private returns (bool) {\n        if (!_contains(set, value)) {\n            set._values.push(value);\n            // The value is stored at length-1, but we add 1 to all indexes\n            // and use 0 as a sentinel value\n            set._indexes[value] = set._values.length;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function _remove(Set storage set, bytes32 value) private returns (bool) {\n        // We read and store the value\u0027s index to prevent multiple reads from the same storage slot\n        uint256 valueIndex = set._indexes[value];\n\n        if (valueIndex != 0) { // Equivalent to contains(set, value)\n            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n            // the array, and then remove the last element (sometimes called as \u0027swap and pop\u0027).\n            // This modifies the order of the array, as noted in {at}.\n\n            uint256 toDeleteIndex = valueIndex - 1;\n            uint256 lastIndex = set._values.length - 1;\n\n            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs\n            // so rarely, we still do the swap anyway to avoid the gas cost of adding an \u0027if\u0027 statement.\n\n            bytes32 lastvalue = set._values[lastIndex];\n\n            // Move the last value to the index where the value to delete is\n            set._values[toDeleteIndex] = lastvalue;\n            // Update the index for the moved value\n            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based\n\n            // Delete the slot where the moved value was stored\n            set._values.pop();\n\n            // Delete the index for the deleted slot\n            delete set._indexes[value];\n\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function _contains(Set storage set, bytes32 value) private view returns (bool) {\n        return set._indexes[value] != 0;\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function _length(Set storage set) private view returns (uint256) {\n        return set._values.length;\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function _at(Set storage set, uint256 index) private view returns (bytes32) {\n        require(set._values.length \u003e index, \"EnumerableSet: index out of bounds\");\n        return set._values[index];\n    }\n\n    // AddressSet\n\n    struct AddressSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(AddressSet storage set, address value) internal returns (bool) {\n        return _add(set._inner, bytes32(uint256(value)));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(AddressSet storage set, address value) internal returns (bool) {\n        return _remove(set._inner, bytes32(uint256(value)));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(AddressSet storage set, address value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(uint256(value)));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(AddressSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function at(AddressSet storage set, uint256 index) internal view returns (address) {\n        return address(uint256(_at(set._inner, index)));\n    }\n\n\n    // UintSet\n\n    struct UintSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(UintSet storage set, uint256 value) internal returns (bool) {\n        return _add(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(UintSet storage set, uint256 value) internal returns (bool) {\n        return _remove(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function length(UintSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n        return uint256(_at(set._inner, index));\n    }\n}\n"},"OEPInitializable.sol":{"content":"pragma solidity \u003e=0.4.24 \u003c0.7.0;\n\n\n/**\n * @title Initializable\n *\n * @dev Helper contract to support initializer functions. To use it, replace\n * the constructor with a function that has the `initializer` modifier.\n * WARNING: Unlike constructors, initializer functions must be manually\n * invoked. This applies both to deploying an Initializable contract, as well\n * as extending an Initializable contract via inheritance.\n * WARNING: When used with inheritance, manual care must be taken to not invoke\n * a parent initializer twice, or ensure that all initializers are idempotent,\n * because this is not dealt with automatically as with constructors.\n */\ncontract Initializable {\n\n  /**\n   * @dev Indicates that the contract has been initialized.\n   */\n  bool private initialized;\n\n  /**\n   * @dev Indicates that the contract is in the process of being initialized.\n   */\n  bool private initializing;\n\n  /**\n   * @dev Modifier to use in the initializer function of a contract.\n   */\n  modifier initializer() {\n    require(initializing || isConstructor() || !initialized, \"Contract instance has already been initialized\");\n\n    bool isTopLevelCall = !initializing;\n    if (isTopLevelCall) {\n      initializing = true;\n      initialized = true;\n    }\n\n    _;\n\n    if (isTopLevelCall) {\n      initializing = false;\n    }\n  }\n\n  /// @dev Returns true if and only if the function is running in the constructor\n  function isConstructor() private view returns (bool) {\n    // extcodesize checks the size of the code stored in an address, and\n    // address returns the current address. Since the code is still not\n    // deployed when running a constructor, any checks on its code size will\n    // yield zero, making it an effective way to detect if a contract is\n    // under construction or not.\n    address self = address(this);\n    uint256 cs;\n    assembly { cs := extcodesize(self) }\n    return cs == 0;\n  }\n\n  // Reserved storage space to allow for layout changes in the future.\n  uint256[50] private ______gap;\n}\n"},"OEPOwnable.sol":{"content":"pragma solidity ^0.6.0;\n\nimport \"./OEPContext.sol\";\nimport \"./OEPInitializable.sol\";\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\ncontract OwnableUpgradeSafe is Initializable, ContextUpgradeSafe {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n\n    function __Ownable_init() internal initializer {\n        __Context_init_unchained();\n        __Ownable_init_unchained();\n    }\n\n    function __Ownable_init_unchained() internal initializer {\n\n\n        address msgSender = _msgSender();\n        _owner = msgSender;\n        emit OwnershipTransferred(address(0), msgSender);\n\n    }\n\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        require(_owner == _msgSender(), \"Ownable: caller is not the owner\");\n        _;\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions anymore. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby removing any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        emit OwnershipTransferred(_owner, address(0));\n        _owner = address(0);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        emit OwnershipTransferred(_owner, newOwner);\n        _owner = newOwner;\n    }\n\n    uint256[49] private __gap;\n}\n"},"PartialDifferences.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    PartialDifferences.sol - SKALE Manager\n    Copyright (C) 2018-Present SKALE Labs\n    @author Dmytro Stebaiev\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\nimport \"./MathUtils.sol\";\nimport \"./FractionUtils.sol\";\n\n/**\n * @title Partial Differences Library\n * @dev This library contains functions to manage Partial Differences data\n * structure. Partial Differences is an array of value differences over time.\n *\n * For example: assuming an array [3, 6, 3, 1, 2], partial differences can\n * represent this array as [_, 3, -3, -2, 1].\n *\n * This data structure allows adding values on an open interval with O(1)\n * complexity.\n *\n * For example: add +5 to [3, 6, 3, 1, 2] starting from the second element (3),\n * instead of performing [3, 6, 3+5, 1+5, 2+5] partial differences allows\n * performing [_, 3, -3+5, -2, 1]. The original array can be restored by\n * adding values from partial differences.\n */\nlibrary PartialDifferences {\n    using SafeMath for uint;\n    using MathUtils for uint;\n\n    struct Sequence {\n             // month =\u003e diff\n        mapping (uint =\u003e uint) addDiff;\n             // month =\u003e diff\n        mapping (uint =\u003e uint) subtractDiff;\n             // month =\u003e value\n        mapping (uint =\u003e uint) value;\n\n        uint firstUnprocessedMonth;\n        uint lastChangedMonth;\n    }\n\n    struct Value {\n             // month =\u003e diff\n        mapping (uint =\u003e uint) addDiff;\n             // month =\u003e diff\n        mapping (uint =\u003e uint) subtractDiff;\n\n        uint value;\n        uint firstUnprocessedMonth;\n        uint lastChangedMonth;\n    }\n\n    // functions for sequence\n\n    function addToSequence(Sequence storage sequence, uint diff, uint month) internal {\n        require(sequence.firstUnprocessedMonth \u003c= month, \"Cannot add to the past\");\n        if (sequence.firstUnprocessedMonth == 0) {\n            sequence.firstUnprocessedMonth = month;\n        }\n        sequence.addDiff[month] = sequence.addDiff[month].add(diff);\n        if (sequence.lastChangedMonth != month) {\n            sequence.lastChangedMonth = month;\n        }\n    }\n\n    function subtractFromSequence(Sequence storage sequence, uint diff, uint month) internal {\n        require(sequence.firstUnprocessedMonth \u003c= month, \"Cannot subtract from the past\");\n        if (sequence.firstUnprocessedMonth == 0) {\n            sequence.firstUnprocessedMonth = month;\n        }\n        sequence.subtractDiff[month] = sequence.subtractDiff[month].add(diff);\n        if (sequence.lastChangedMonth != month) {\n            sequence.lastChangedMonth = month;\n        }\n    }\n\n    function getAndUpdateValueInSequence(Sequence storage sequence, uint month) internal returns (uint) {\n        if (sequence.firstUnprocessedMonth == 0) {\n            return 0;\n        }\n\n        if (sequence.firstUnprocessedMonth \u003c= month) {\n            for (uint i = sequence.firstUnprocessedMonth; i \u003c= month; ++i) {\n                uint nextValue = sequence.value[i.sub(1)].add(sequence.addDiff[i]).boundedSub(sequence.subtractDiff[i]);\n                if (sequence.value[i] != nextValue) {\n                    sequence.value[i] = nextValue;\n                }\n                if (sequence.addDiff[i] \u003e 0) {\n                    delete sequence.addDiff[i];\n                }\n                if (sequence.subtractDiff[i] \u003e 0) {\n                    delete sequence.subtractDiff[i];\n                }\n            }\n            sequence.firstUnprocessedMonth = month.add(1);\n        }\n\n        return sequence.value[month];\n    }\n\n    function reduceSequence(\n        Sequence storage sequence,\n        FractionUtils.Fraction memory reducingCoefficient,\n        uint month) internal\n    {\n        require(month.add(1) \u003e= sequence.firstUnprocessedMonth, \"Can\u0027t reduce value in the past\");\n        require(\n            reducingCoefficient.numerator \u003c= reducingCoefficient.denominator,\n            \"Increasing of values is not implemented\");\n        if (sequence.firstUnprocessedMonth == 0) {\n            return;\n        }\n        uint value = getAndUpdateValueInSequence(sequence, month);\n        if (value.approximatelyEqual(0)) {\n            return;\n        }\n\n        sequence.value[month] = sequence.value[month]\n            .mul(reducingCoefficient.numerator)\n            .div(reducingCoefficient.denominator);\n\n        for (uint i = month.add(1); i \u003c= sequence.lastChangedMonth; ++i) {\n            sequence.subtractDiff[i] = sequence.subtractDiff[i]\n                .mul(reducingCoefficient.numerator)\n                .div(reducingCoefficient.denominator);\n        }\n    }\n\n    // functions for value\n\n    function addToValue(Value storage sequence, uint diff, uint month) internal {\n        require(sequence.firstUnprocessedMonth \u003c= month, \"Cannot add to the past\");\n        if (sequence.firstUnprocessedMonth == 0) {\n            sequence.firstUnprocessedMonth = month;\n            sequence.lastChangedMonth = month;\n        }\n        if (month \u003e sequence.lastChangedMonth) {\n            sequence.lastChangedMonth = month;\n        }\n\n        if (month \u003e= sequence.firstUnprocessedMonth) {\n            sequence.addDiff[month] = sequence.addDiff[month].add(diff);\n        } else {\n            sequence.value = sequence.value.add(diff);\n        }\n    }\n\n    function subtractFromValue(Value storage sequence, uint diff, uint month) internal {\n        require(sequence.firstUnprocessedMonth \u003c= month.add(1), \"Cannot subtract from the past\");\n        if (sequence.firstUnprocessedMonth == 0) {\n            sequence.firstUnprocessedMonth = month;\n            sequence.lastChangedMonth = month;\n        }\n        if (month \u003e sequence.lastChangedMonth) {\n            sequence.lastChangedMonth = month;\n        }\n\n        if (month \u003e= sequence.firstUnprocessedMonth) {\n            sequence.subtractDiff[month] = sequence.subtractDiff[month].add(diff);\n        } else {\n            sequence.value = sequence.value.boundedSub(diff);\n        }\n    }\n\n    function getAndUpdateValue(Value storage sequence, uint month) internal returns (uint) {\n        require(\n            month.add(1) \u003e= sequence.firstUnprocessedMonth,\n            \"Cannot calculate value in the past\");\n        if (sequence.firstUnprocessedMonth == 0) {\n            return 0;\n        }\n\n        if (sequence.firstUnprocessedMonth \u003c= month) {\n            for (uint i = sequence.firstUnprocessedMonth; i \u003c= month; ++i) {\n                uint newValue = sequence.value.add(sequence.addDiff[i]).boundedSub(sequence.subtractDiff[i]);\n                if (sequence.value != newValue) {\n                    sequence.value = newValue;\n                }\n                if (sequence.addDiff[i] \u003e 0) {\n                    delete sequence.addDiff[i];\n                }\n                if (sequence.subtractDiff[i] \u003e 0) {\n                    delete sequence.subtractDiff[i];\n                }\n            }\n            sequence.firstUnprocessedMonth = month.add(1);\n        }\n\n        return sequence.value;\n    }\n\n    function reduceValue(\n        Value storage sequence,\n        uint amount,\n        uint month)\n        internal returns (FractionUtils.Fraction memory)\n    {\n        require(month.add(1) \u003e= sequence.firstUnprocessedMonth, \"Cannot reduce value in the past\");\n        if (sequence.firstUnprocessedMonth == 0) {\n            return FractionUtils.createFraction(0);\n        }\n        uint value = getAndUpdateValue(sequence, month);\n        if (value.approximatelyEqual(0)) {\n            return FractionUtils.createFraction(0);\n        }\n\n        uint _amount = amount;\n        if (value \u003c amount) {\n            _amount = value;\n        }\n\n        FractionUtils.Fraction memory reducingCoefficient =\n            FractionUtils.createFraction(value.boundedSub(_amount), value);\n        reduceValueByCoefficient(sequence, reducingCoefficient, month);\n        return reducingCoefficient;\n    }\n\n    function reduceValueByCoefficient(\n        Value storage sequence,\n        FractionUtils.Fraction memory reducingCoefficient,\n        uint month)\n        internal\n    {\n        reduceValueByCoefficientAndUpdateSumIfNeeded(\n            sequence,\n            sequence,\n            reducingCoefficient,\n            month,\n            false);\n    }\n\n    function reduceValueByCoefficientAndUpdateSum(\n        Value storage sequence,\n        Value storage sumSequence,\n        FractionUtils.Fraction memory reducingCoefficient,\n        uint month) internal\n    {\n        reduceValueByCoefficientAndUpdateSumIfNeeded(\n            sequence,\n            sumSequence,\n            reducingCoefficient,\n            month,\n            true);\n    }\n\n    function reduceValueByCoefficientAndUpdateSumIfNeeded(\n        Value storage sequence,\n        Value storage sumSequence,\n        FractionUtils.Fraction memory reducingCoefficient,\n        uint month,\n        bool hasSumSequence) internal\n    {\n        require(month.add(1) \u003e= sequence.firstUnprocessedMonth, \"Cannot reduce value in the past\");\n        if (hasSumSequence) {\n            require(month.add(1) \u003e= sumSequence.firstUnprocessedMonth, \"Cannot reduce value in the past\");\n        }\n        require(\n            reducingCoefficient.numerator \u003c= reducingCoefficient.denominator,\n            \"Increasing of values is not implemented\");\n        if (sequence.firstUnprocessedMonth == 0) {\n            return;\n        }\n        uint value = getAndUpdateValue(sequence, month);\n        if (value.approximatelyEqual(0)) {\n            return;\n        }\n\n        uint newValue = sequence.value.mul(reducingCoefficient.numerator).div(reducingCoefficient.denominator);\n        if (hasSumSequence) {\n            subtractFromValue(sumSequence, sequence.value.boundedSub(newValue), month);\n        }\n        sequence.value = newValue;\n\n        for (uint i = month.add(1); i \u003c= sequence.lastChangedMonth; ++i) {\n            uint newDiff = sequence.subtractDiff[i]\n                .mul(reducingCoefficient.numerator)\n                .div(reducingCoefficient.denominator);\n            if (hasSumSequence) {\n                sumSequence.subtractDiff[i] = sumSequence.subtractDiff[i]\n                    .boundedSub(sequence.subtractDiff[i].boundedSub(newDiff));\n            }\n            sequence.subtractDiff[i] = newDiff;\n        }\n    }\n\n    function clear(Value storage sequence) internal {\n        for (uint i = sequence.firstUnprocessedMonth; i \u003c= sequence.lastChangedMonth; ++i) {\n            if (sequence.addDiff[i] \u003e 0) {\n                delete sequence.addDiff[i];\n            }\n            if (sequence.subtractDiff[i] \u003e 0) {\n                delete sequence.subtractDiff[i];\n            }\n        }\n        if (sequence.value \u003e 0) {\n            delete sequence.value;\n        }\n        if (sequence.firstUnprocessedMonth \u003e 0) {\n            delete sequence.firstUnprocessedMonth;\n        }\n        if (sequence.lastChangedMonth \u003e 0) {\n            delete sequence.lastChangedMonth;\n        }\n    }\n}"},"Permissions.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    Permissions.sol - SKALE Manager\n    Copyright (C) 2018-Present SKALE Labs\n    @author Artem Payvin\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\nimport \"./OCSafeMath.sol\";\nimport \"./OEPAccessControl.sol\";\n\nimport \"./ContractManager.sol\";\n\n\n/**\n * @title Permissions - connected module for Upgradeable approach, knows ContractManager\n * @author Artem Payvin\n */\ncontract Permissions is AccessControlUpgradeSafe {\n    using SafeMath for uint;\n    using Address for address;\n    \n    ContractManager public contractManager;\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        require(_isOwner(), \"Caller is not the owner\");\n        _;\n    }\n\n    modifier onlyAdmin() {\n        require(_isAdmin(msg.sender), \"Caller is not an admin\");\n        _;\n    }\n\n    /**\n     * @dev allow - throws if called by any account and contract other than the owner\n     * or `contractName` contract\n     * @param contractName - human readable name of contract\n     */\n    modifier allow(string memory contractName) {\n        require(\n            contractManager.contracts(keccak256(abi.encodePacked(contractName))) == msg.sender || _isOwner(),\n            \"Message sender is invalid\");\n        _;\n    }\n\n    modifier allowTwo(string memory contractName1, string memory contractName2) {\n        require(\n            contractManager.contracts(keccak256(abi.encodePacked(contractName1))) == msg.sender ||\n            contractManager.contracts(keccak256(abi.encodePacked(contractName2))) == msg.sender ||\n            _isOwner(),\n            \"Message sender is invalid\");\n        _;\n    }\n\n    modifier allowThree(string memory contractName1, string memory contractName2, string memory contractName3) {\n        require(\n            contractManager.contracts(keccak256(abi.encodePacked(contractName1))) == msg.sender ||\n            contractManager.contracts(keccak256(abi.encodePacked(contractName2))) == msg.sender ||\n            contractManager.contracts(keccak256(abi.encodePacked(contractName3))) == msg.sender ||\n            _isOwner(),\n            \"Message sender is invalid\");\n        _;\n    }\n\n    function initialize(address contractManagerAddress) public virtual initializer {\n        AccessControlUpgradeSafe.__AccessControl_init();\n        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n        _setContractManager(contractManagerAddress);\n    }\n\n    function _isOwner() internal view returns (bool) {\n        return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);\n    }\n\n    function _isAdmin(address account) internal view returns (bool) {\n        address skaleManagerAddress = contractManager.contracts(keccak256(abi.encodePacked(\"SkaleManager\")));\n        if (skaleManagerAddress != address(0)) {\n            AccessControlUpgradeSafe skaleManager = AccessControlUpgradeSafe(skaleManagerAddress);\n            return skaleManager.hasRole(keccak256(\"ADMIN_ROLE\"), account) || _isOwner();\n        } else {\n            return _isOwner();\n        }\n    }\n\n    function _setContractManager(address contractManagerAddress) private {\n        require(contractManagerAddress != address(0), \"ContractManager address is not set\");\n        require(contractManagerAddress.isContract(), \"Address is not contract\");\n        contractManager = ContractManager(contractManagerAddress);\n    }\n}\n"},"Punisher.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    Punisher.sol - SKALE Manager\n    Copyright (C) 2019-Present SKALE Labs\n    @author Dmytro Stebaiev\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\nimport \"./OCSafeMath.sol\";\n\nimport \"./Permissions.sol\";\nimport \"./ILocker.sol\";\n\nimport \"./ValidatorService.sol\";\nimport \"./DelegationController.sol\";\n\n/**\n * @title Punisher\n * @dev This contract handles all slashing and forgiving operations.\n */\ncontract Punisher is Permissions, ILocker {\n\n    /**\n     * @dev Emitted when a slashing condition occurs.\n     */\n    event Slash(\n        uint validatorId,\n        uint amount\n    );\n\n    /**\n     * @dev Emitted when a forgive condition occurs.\n     */\n    event Forgive(\n        address wallet,\n        uint amount\n    );\n\n    //        holder =\u003e tokens\n    mapping (address =\u003e uint) private _locked;\n\n    /**\n     * @dev Executes slashing on a validator and its delegations by an `amount`\n     * of tokens. Currently, SkaleDKG is the only service allowed to execute\n     * slashing.\n     *\n     * Emits a Slash event.\n     *\n     * @param validatorId uint validator to be slashed\n     * @param amount uint slashed amount\n    */\n    function slash(uint validatorId, uint amount) external allow(\"SkaleDKG\") {\n        ValidatorService validatorService = ValidatorService(contractManager.getContract(\"ValidatorService\"));\n        DelegationController delegationController = DelegationController(\n            contractManager.getContract(\"DelegationController\"));\n\n        require(validatorService.validatorExists(validatorId), \"Validator does not exist\");\n\n        delegationController.confiscate(validatorId, amount);\n\n        emit Slash(validatorId, amount);\n    }\n\n    /**\n     * @dev Allows the Owner to forgive a slashing condition.\n     *\n     * Emits a Forgive event.\n     *\n     * @param holder address of the slashed\n     * @param amount uint amount to be forgiven\n     */\n    function forgive(address holder, uint amount) external onlyAdmin {\n        DelegationController delegationController = DelegationController(\n            contractManager.getContract(\"DelegationController\"));\n\n        require(!delegationController.hasUnprocessedSlashes(holder), \"Not all slashes were calculated\");\n\n        if (amount \u003e _locked[holder]) {\n            delete _locked[holder];\n        } else {\n            _locked[holder] = _locked[holder].sub(amount);\n        }\n\n        emit Forgive(holder, amount);\n    }\n\n    /**\n     * @dev See ILocker-getAndUpdateLockedAmount\n     */\n    function getAndUpdateLockedAmount(address wallet) external override returns (uint) {\n        return _getAndUpdateLockedAmount(wallet);\n    }\n\n    /**\n     * @dev See ILocker-getAndUpdateForbiddenForDelegationAmount\n     */\n    function getAndUpdateForbiddenForDelegationAmount(address wallet) external override returns (uint) {\n        return _getAndUpdateLockedAmount(wallet);\n    }\n\n    function handleSlash(address holder, uint amount) external allow(\"DelegationController\") {\n        _locked[holder] = _locked[holder].add(amount);\n    }\n\n    function initialize(address contractManagerAddress) public override initializer {\n        Permissions.initialize(contractManagerAddress);\n    }\n\n    // private\n\n    function _getAndUpdateLockedAmount(address wallet) private returns (uint) {\n        DelegationController delegationController = DelegationController(\n            contractManager.getContract(\"DelegationController\"));\n\n        delegationController.processAllSlashes(wallet);\n        return _locked[wallet];\n    }\n\n}"},"SkaleToken.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    SkaleToken.sol - SKALE Manager\n    Copyright (C) 2018-Present SKALE Labs\n    @author Artem Payvin\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\nimport \"./OCSafeMath.sol\";\nimport \"./OCReentrancyGuard.sol\";\n\nimport \"./ERC777.sol\";\n\nimport \"./Permissions.sol\";\nimport \"./IDelegatableToken.sol\";\nimport \"./Punisher.sol\";\nimport \"./TokenState.sol\";\n\n\n/**\n * @title SkaleToken is ERC777 Token implementation, also this contract in skale\n * manager system\n */\ncontract SkaleToken is ERC777, Permissions, ReentrancyGuard, IDelegatableToken {\n    using SafeMath for uint;\n\n    string public constant NAME = \"SKALE\";\n\n    string public constant SYMBOL = \"SKL\";\n\n    uint public constant DECIMALS = 18;\n\n    uint public constant CAP = 7 * 1e9 * (10 ** DECIMALS); // the maximum amount of tokens that can ever be created\n\n    constructor(address contractsAddress, address[] memory defOps) public\n    ERC777(\"SKALE\", \"SKL\", defOps)\n    {\n        Permissions.initialize(contractsAddress);\n    }\n\n    /**\n     * @dev mint - create some amount of token and transfer it to the specified address\n     * @param account - address where some amount of token would be created\n     * @param amount - amount of tokens to mine\n     * @param userData bytes extra information provided by the token holder (if any)\n     * @param operatorData bytes extra information provided by the operator (if any)\n     * @return returns success of function call.\n     */\n    function mint(\n        address account,\n        uint256 amount,\n        bytes calldata userData,\n        bytes calldata operatorData\n    )\n        external\n        allow(\"SkaleManager\")\n        //onlyAuthorized\n        returns (bool)\n    {\n        require(amount \u003c= CAP.sub(totalSupply()), \"Amount is too big\");\n        _mint(\n            account,\n            amount,\n            userData,\n            operatorData\n        );\n\n        return true;\n    }\n\n    function getAndUpdateDelegatedAmount(address wallet) external override returns (uint) {\n        return DelegationController(contractManager.getContract(\"DelegationController\"))\n            .getAndUpdateDelegatedAmount(wallet);\n    }\n\n    function getAndUpdateSlashedAmount(address wallet) external override returns (uint) {\n        return Punisher(contractManager.getContract(\"Punisher\")).getAndUpdateLockedAmount(wallet);\n    }\n\n    function getAndUpdateLockedAmount(address wallet) public override returns (uint) {\n        return TokenState(contractManager.getContract(\"TokenState\")).getAndUpdateLockedAmount(wallet);\n    }\n\n    // internal\n\n    function _beforeTokenTransfer(\n        address, // operator\n        address from,\n        address, // to\n        uint256 tokenId)\n        internal override\n    {\n        uint locked = getAndUpdateLockedAmount(from);\n        if (locked \u003e 0) {\n            require(balanceOf(from) \u003e= locked.add(tokenId), \"Token should be unlocked for transferring\");\n        }\n    }\n\n    function _callTokensToSend(\n        address operator,\n        address from,\n        address to,\n        uint256 amount,\n        bytes memory userData,\n        bytes memory operatorData\n    ) internal override nonReentrant {\n        super._callTokensToSend(operator, from, to, amount, userData, operatorData);\n    }\n\n    function _callTokensReceived(\n        address operator,\n        address from,\n        address to,\n        uint256 amount,\n        bytes memory userData,\n        bytes memory operatorData,\n        bool requireReceptionAck\n    ) internal override nonReentrant {\n        super._callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);\n    }\n\n    // we have to override _msgData() and _msgSender() functions because of collision in Context and ContextUpgradeSafe\n\n    function _msgData() internal view override(Context, ContextUpgradeSafe) returns (bytes memory) {\n        return Context._msgData();\n    }\n\n    function _msgSender() internal view override(Context, ContextUpgradeSafe) returns (address payable) {\n        return Context._msgSender();\n    }\n}\n"},"StringUtils.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    StringUtils.sol - SKALE Manager\n    Copyright (C) 2018-Present SKALE Labs\n    @author Vadim Yavorsky\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\nimport \"./OCSafeMath.sol\";\n\n\nlibrary StringUtils {\n    using SafeMath for uint;\n\n    function strConcat(string memory a, string memory b) internal pure returns (string memory) {\n        bytes memory _ba = bytes(a);\n        bytes memory _bb = bytes(b);\n\n        string memory ab = new string(_ba.length.add(_bb.length));\n        bytes memory strBytes = bytes(ab);\n        uint k = 0;\n        uint i = 0;\n        for (i = 0; i \u003c _ba.length; i++) {\n            strBytes[k++] = _ba[i];\n        }\n        for (i = 0; i \u003c _bb.length; i++) {\n            strBytes[k++] = _bb[i];\n        }\n        return string(strBytes);\n    }\n\n    function uint2str(uint i) internal pure returns (string memory) {\n        if (i == 0) {\n            return \"0\";\n        }\n        uint j = i;\n        uint _i = i;\n        uint len;\n        while (j != 0) {\n            len++;\n            j /= 10;\n        }\n        bytes memory bstr = new bytes(len);\n        uint k = len.sub(1);\n        while (_i != 0) {\n            bstr[k--] = byte(uint8(48 + _i % 10));\n            _i /= 10;\n        }\n        return string(bstr);\n    }\n}"},"TimeHelpers.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    TimeHelpers.sol - SKALE Manager\n    Copyright (C) 2019-Present SKALE Labs\n    @author Dmytro Stebaiev\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\nimport \"./OCSafeMath.sol\";\n\nimport \"./BokkyPooBahsDateTimeLibrary.sol\";\n\n/**\n * @title TimeHelpers\n * @dev The contract performs time operations.\n *\n * These functions are used to calculate monthly and Proof of Use epochs.\n */\ncontract TimeHelpers {\n    using SafeMath for uint;\n\n    uint constant private _ZERO_YEAR = 2020;\n\n    function calculateProofOfUseLockEndTime(uint month, uint lockUpPeriodDays) external view returns (uint timestamp) {\n        timestamp = BokkyPooBahsDateTimeLibrary.addDays(monthToTimestamp(month), lockUpPeriodDays);\n    }\n\n    function addMonths(uint fromTimestamp, uint n) external pure returns (uint) {\n        return BokkyPooBahsDateTimeLibrary.addMonths(fromTimestamp, n);\n    }\n\n    function getCurrentMonth() external view virtual returns (uint) {\n        return timestampToMonth(now);\n    }\n\n    function timestampToMonth(uint timestamp) public view virtual returns (uint) {\n        uint year;\n        uint month;\n        (year, month, ) = BokkyPooBahsDateTimeLibrary.timestampToDate(timestamp);\n        require(year \u003e= _ZERO_YEAR, \"Timestamp is too far in the past\");\n        month = month.sub(1).add(year.sub(_ZERO_YEAR).mul(12));\n        require(month \u003e 0, \"Timestamp is too far in the past\");\n        return month;\n    }\n\n    function monthToTimestamp(uint month) public view virtual returns (uint timestamp) {\n        uint year = _ZERO_YEAR;\n        uint _month = month;\n        year = year.add(_month.div(12));\n        _month = _month.mod(12);\n        _month = _month.add(1);\n        return BokkyPooBahsDateTimeLibrary.timestampFromDate(year, _month, 1);\n    }\n}\n"},"TokenLaunchLocker.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    TokenLaunchLocker.sol - SKALE Manager\n    Copyright (C) 2019-Present SKALE Labs\n    @author Dmytro Stebaiev\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\n\nimport \"./OCSafeMath.sol\";\n\nimport \"./Permissions.sol\";\nimport \"./ILocker.sol\";\nimport \"./ConstantsHolder.sol\";\nimport \"./MathUtils.sol\";\n\nimport \"./DelegationController.sol\";\nimport \"./TimeHelpers.sol\";\nimport \"./PartialDifferences.sol\";\n\n\ncontract TokenLaunchLocker is Permissions, ILocker {\n    using MathUtils for uint;\n    using PartialDifferences for PartialDifferences.Value;\n\n    /**\n     * @dev Emitted when an `amount` is unlocked.\n     */\n    event Unlocked(\n        address holder,\n        uint amount\n    );\n\n    /**\n     * @dev Emitted when an `amount` is locked.\n     */\n    event Locked(\n        address holder,\n        uint amount\n    );\n\n    struct DelegatedAmountAndMonth {\n        uint delegated;\n        uint month;\n    }\n\n    //        holder =\u003e tokens\n    mapping (address =\u003e uint) private _locked;\n\n    //        holder =\u003e tokens\n    mapping (address =\u003e PartialDifferences.Value) private _delegatedAmount;\n\n    mapping (address =\u003e DelegatedAmountAndMonth) private _totalDelegatedAmount;\n\n    // delegationId =\u003e tokens\n    mapping (uint =\u003e uint) private _delegationAmount;\n\n    function lock(address holder, uint amount) external allow(\"TokenLaunchManager\") {\n        _locked[holder] = _locked[holder].add(amount);\n\n        emit Locked(holder, amount);\n    }\n\n    function handleDelegationAdd(\n        address holder, uint delegationId, uint amount, uint month)\n        external allow(\"DelegationController\")\n    {\n        if (_locked[holder] \u003e 0) {\n            TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract(\"TimeHelpers\"));\n\n            uint currentMonth = timeHelpers.getCurrentMonth();\n            uint fromLocked = amount;\n            uint locked = _locked[holder].boundedSub(_getAndUpdateDelegatedAmount(holder, currentMonth));\n            if (fromLocked \u003e locked) {\n                fromLocked = locked;\n            }\n            if (fromLocked \u003e 0) {\n                require(_delegationAmount[delegationId] == 0, \"Delegation was already added\");\n                _addToDelegatedAmount(holder, fromLocked, month);\n                _addToTotalDelegatedAmount(holder, fromLocked, month);\n                _delegationAmount[delegationId] = fromLocked;\n            }\n        }\n    }\n\n    function handleDelegationRemoving(\n        address holder,\n        uint delegationId,\n        uint month)\n        external allow(\"DelegationController\")\n    {\n        if (_delegationAmount[delegationId] \u003e 0) {\n            if (_locked[holder] \u003e 0) {\n                _removeFromDelegatedAmount(holder, _delegationAmount[delegationId], month);\n            }\n            delete _delegationAmount[delegationId];\n        }\n    }\n\n    function getAndUpdateLockedAmount(address wallet) external override returns (uint) {\n        if (_locked[wallet] \u003e 0) {\n            DelegationController delegationController = DelegationController(\n                contractManager.getContract(\"DelegationController\"));\n            TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract(\"TimeHelpers\"));\n            ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract(\"ConstantsHolder\"));\n\n            uint currentMonth = timeHelpers.getCurrentMonth();\n            if (_totalDelegatedSatisfiesProofOfUserCondition(wallet) \u0026\u0026\n                timeHelpers.calculateProofOfUseLockEndTime(\n                    _totalDelegatedAmount[wallet].month,\n                    constantsHolder.proofOfUseLockUpPeriodDays()\n                ) \u003c= now) {\n                _unlock(wallet);\n                return 0;\n            } else {\n                uint lockedByDelegationController = _getAndUpdateDelegatedAmount(wallet, currentMonth)\n                    .add(delegationController.getLockedInPendingDelegations(wallet));\n                if (_locked[wallet] \u003e lockedByDelegationController) {\n                    return _locked[wallet].boundedSub(lockedByDelegationController);\n                } else {\n                    return 0;\n                }\n            }\n        } else {\n            return 0;\n        }\n    }\n\n    function getAndUpdateForbiddenForDelegationAmount(address) external override returns (uint) {\n        return 0;\n    }\n\n    function initialize(address contractManagerAddress) public override initializer {\n        Permissions.initialize(contractManagerAddress);\n    }\n\n    // private\n\n    function _getAndUpdateDelegatedAmount(address holder, uint currentMonth) private returns (uint) {\n        return _delegatedAmount[holder].getAndUpdateValue(currentMonth);\n    }\n\n    function _addToDelegatedAmount(address holder, uint amount, uint month) private {\n        _delegatedAmount[holder].addToValue(amount, month);\n    }\n\n    function _removeFromDelegatedAmount(address holder, uint amount, uint month) private {\n        _delegatedAmount[holder].subtractFromValue(amount, month);\n    }\n\n    function _addToTotalDelegatedAmount(address holder, uint amount, uint month) private {\n        require(\n            _totalDelegatedAmount[holder].month == 0 || _totalDelegatedAmount[holder].month \u003c= month,\n            \"Can\u0027t add to total delegated in the past\");\n\n        // do not update counter if it is big enough\n        // because it will override month value\n        if (!_totalDelegatedSatisfiesProofOfUserCondition(holder)) {\n            _totalDelegatedAmount[holder].delegated = _totalDelegatedAmount[holder].delegated.add(amount);\n            _totalDelegatedAmount[holder].month = month;\n        }\n    }\n\n    function _unlock(address holder) private {\n        emit Unlocked(holder, _locked[holder]);\n        delete _locked[holder];\n        _deleteDelegatedAmount(holder);\n        _deleteTotalDelegatedAmount(holder);\n    }\n\n    function _deleteDelegatedAmount(address holder) private {\n        _delegatedAmount[holder].clear();\n    }\n\n    function _deleteTotalDelegatedAmount(address holder) private {\n        delete _totalDelegatedAmount[holder].delegated;\n        delete _totalDelegatedAmount[holder].month;\n    }\n\n    function _totalDelegatedSatisfiesProofOfUserCondition(address holder) private view returns (bool) {\n        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract(\"ConstantsHolder\"));\n\n        return _totalDelegatedAmount[holder].delegated.mul(100) \u003e=\n            _locked[holder].mul(constantsHolder.proofOfUseDelegationPercentage());\n    }\n}"},"TokenState.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    TokenState.sol - SKALE Manager\n    Copyright (C) 2019-Present SKALE Labs\n    @author Dmytro Stebaiev\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\npragma experimental ABIEncoderV2;\n\nimport \"./Permissions.sol\";\nimport \"./DelegationController.sol\";\nimport \"./TimeHelpers.sol\";\nimport \"./ILocker.sol\";\n\n\n/**\n * @title Token State\n * @dev This contract manages lockers to control token transferability.\n *\n * See ILocker.\n */\ncontract TokenState is Permissions, ILocker {\n\n    /**\n     * @dev Emitted when a contract is added to the locker.\n     */\n    event LockerWasAdded(\n        string locker\n    );\n\n    /**\n     * @dev Emitted when a contract is removed from the locker.\n     */\n    event LockerWasRemoved(\n        string locker\n    );\n\n    string[] private _lockers;\n\n    /**\n     *  @dev Return and update the total locked amount of a given `holder`.\n     *\n     *  @param holder address of the token holder\n     *  @return total locked amount\n    */\n    function getAndUpdateLockedAmount(address holder) external override returns (uint) {\n        uint locked = 0;\n        for (uint i = 0; i \u003c _lockers.length; ++i) {\n            ILocker locker = ILocker(contractManager.getContract(_lockers[i]));\n            locked = locked.add(locker.getAndUpdateLockedAmount(holder));\n        }\n        return locked;\n    }\n\n    /**\n     * @dev Return and update the total locked and un-delegatable amount of a given `holder`.\n     *\n     * @param holder address of the token holder\n     * @return amount total slashed amount (non-transferable and non-delegatable)\n    */\n    function getAndUpdateForbiddenForDelegationAmount(address holder) external override returns (uint amount) {\n        uint forbidden = 0;\n        for (uint i = 0; i \u003c _lockers.length; ++i) {\n            ILocker locker = ILocker(contractManager.getContract(_lockers[i]));\n            forbidden = forbidden.add(locker.getAndUpdateForbiddenForDelegationAmount(holder));\n        }\n        return forbidden;\n    }\n\n    /**\n     * @dev Allows the Owner to remove a contract from the locker.\n     *\n     * Emits a LockerWasRemoved event.\n     *\n     * @param locker string name of contract to remove from locker\n     */\n    function removeLocker(string calldata locker) external onlyOwner {\n        uint index;\n        bytes32 hash = keccak256(abi.encodePacked(locker));\n        for (index = 0; index \u003c _lockers.length; ++index) {\n            if (keccak256(abi.encodePacked(_lockers[index])) == hash) {\n                break;\n            }\n        }\n        if (index \u003c _lockers.length) {\n            if (index \u003c _lockers.length.sub(1)) {\n                _lockers[index] = _lockers[_lockers.length.sub(1)];\n            }\n            delete _lockers[_lockers.length.sub(1)];\n            _lockers.pop();\n            emit LockerWasRemoved(locker);\n        }\n    }\n\n    function initialize(address contractManagerAddress) public override initializer {\n        Permissions.initialize(contractManagerAddress);\n        addLocker(\"DelegationController\");\n        addLocker(\"Punisher\");\n        addLocker(\"TokenLaunchLocker\");\n    }\n\n    /**\n     * @dev Allows the Owner to add a contract to the Locker.\n     *\n     * Emits a LockerWasAdded event.\n     *\n     * @param locker string name of contract to add to locker\n     */\n    function addLocker(string memory locker) public onlyOwner {\n        _lockers.push(locker);\n        emit LockerWasAdded(locker);\n    }\n}\n"},"ValidatorService.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\n\n/*\n    ValidatorService.sol - SKALE Manager\n    Copyright (C) 2019-Present SKALE Labs\n    @author Dmytro Stebaiev\n    @author Artem Payvin\n    @author Vadim Yavorsky\n\n    SKALE Manager is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    SKALE Manager is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with SKALE Manager.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n*/\n\npragma solidity 0.6.10;\npragma experimental ABIEncoderV2;\n\nimport \"./OCSafeMath.sol\";\nimport \"./OCECDSA.sol\";\n\nimport \"./Permissions.sol\";\nimport \"./ConstantsHolder.sol\";\n\nimport \"./DelegationController.sol\";\n\n/**\n * @title ValidatorService\n * @dev This contract handles all validator operations including registration,\n * node management, validator-specific delegation parameters, and more.\n *\n * Validators register an address, and use this address to accept delegations and\n * register nodes.\n *\n */\ncontract ValidatorService is Permissions {\n\n    using ECDSA for bytes32;\n\n    struct Validator {\n        string name;\n        address validatorAddress;\n        address requestedAddress;\n        string description;\n        uint feeRate;\n        uint registrationTime;\n        uint minimumDelegationAmount;\n        bool acceptNewRequests;\n    }\n\n    /**\n     * @dev Emitted when a validator registers.\n     */\n    event ValidatorRegistered(\n        uint validatorId\n    );\n\n    /**\n     * @dev Emitted when a validator address changes.\n     */\n    event ValidatorAddressChanged(\n        uint validatorId,\n        address newAddress\n    );\n\n    event ValidatorWasEnabled(\n        uint validatorId\n    );\n\n    event ValidatorWasDisabled(\n        uint validatorId\n    );\n\n    /**\n     * @dev Emitted when a node address is linked to a validator.\n     */\n    event NodeAddressWasAdded(\n        uint validatorId,\n        address nodeAddress\n    );\n\n    /**\n     * @dev Emitted when a node address is unlinked from a validator.\n     */\n    event NodeAddressWasRemoved(\n        uint validatorId,\n        address nodeAddress\n    );\n\n    mapping (uint =\u003e Validator) public validators;\n    mapping (uint =\u003e bool) private _trustedValidators;\n    uint[] public trustedValidatorsList;\n    //       address =\u003e validatorId\n    mapping (address =\u003e uint) private _validatorAddressToId;\n    //       address =\u003e validatorId\n    mapping (address =\u003e uint) private _nodeAddressToValidatorId;\n    // validatorId =\u003e nodeAddress[]\n    mapping (uint =\u003e address[]) private _nodeAddresses;\n    uint public numberOfValidators;\n    bool public useWhitelist;\n\n    modifier checkValidatorExists(uint validatorId) {\n        require(validatorExists(validatorId), \"Validator with such ID does not exist\");\n        _;\n    }\n\n    /**\n     * @dev Creates a new validator Id.\n     *\n     * Requirements:\n     *\n     * - sender must not already have registered a validator Id.\n     * - fee rate must be between 0 - 1000‰. Note: per mille!\n     *\n     * Emits ValidatorRegistered event.\n     *\n     * @param name string\n     * @param description string\n     * @param feeRate uint Fee charged on delegations by the validator per mille\n     * @param minimumDelegationAmount uint Minimum delegation amount accepted by the validator\n     */\n    function registerValidator(\n        string calldata name,\n        string calldata description,\n        uint feeRate,\n        uint minimumDelegationAmount\n    )\n        external\n        returns (uint validatorId)\n    {\n        require(!validatorAddressExists(msg.sender), \"Validator with such address already exists\");\n        require(feeRate \u003c 1000, \"Fee rate of validator should be lower than 100%\");\n        validatorId = ++numberOfValidators;\n        validators[validatorId] = Validator(\n            name,\n            msg.sender,\n            address(0),\n            description,\n            feeRate,\n            now,\n            minimumDelegationAmount,\n            true\n        );\n        _setValidatorAddress(validatorId, msg.sender);\n\n        emit ValidatorRegistered(validatorId);\n    }\n\n    function enableValidator(uint validatorId) external checkValidatorExists(validatorId) onlyAdmin {\n        require(!_trustedValidators[validatorId], \"Validator is already enabled\");\n        _trustedValidators[validatorId] = true;\n        trustedValidatorsList.push(validatorId);\n        emit ValidatorWasEnabled(validatorId);\n    }\n\n    function disableValidator(uint validatorId) external checkValidatorExists(validatorId) onlyAdmin {\n        require(_trustedValidators[validatorId], \"Validator is already disabled\");\n        _trustedValidators[validatorId] = false;\n        uint position = _find(trustedValidatorsList, validatorId);\n        if (position \u003c trustedValidatorsList.length) {\n            trustedValidatorsList[position] =\n                trustedValidatorsList[trustedValidatorsList.length.sub(1)];\n        }\n        trustedValidatorsList.pop();\n        emit ValidatorWasDisabled(validatorId);\n    }\n\n    /**\n     * @dev Owner can disable the validator whitelist. Once turned off the\n     * whitelist cannot be re-enabled.\n     */\n    function disableWhitelist() external onlyOwner {\n        useWhitelist = false;\n    }\n\n    /**\n     * @dev Allows a validator to request a new address.\n     *\n     * Requirements:\n     *\n     * - new address must not be null\n     * - new address must not be already registered as a validator\n     *\n     * @param newValidatorAddress address\n     */\n    function requestForNewAddress(address newValidatorAddress) external {\n        require(newValidatorAddress != address(0), \"New address cannot be null\");\n        require(_validatorAddressToId[newValidatorAddress] == 0, \"Address already registered\");\n        // check Validator Exist inside getValidatorId\n        uint validatorId = getValidatorId(msg.sender);\n\n        validators[validatorId].requestedAddress = newValidatorAddress;\n    }\n\n    function confirmNewAddress(uint validatorId)\n        external\n        checkValidatorExists(validatorId)\n    {\n        require(\n            getValidator(validatorId).requestedAddress == msg.sender,\n            \"The validator address cannot be changed because it is not the actual owner\"\n        );\n        delete validators[validatorId].requestedAddress;\n        _setValidatorAddress(validatorId, msg.sender);\n\n        emit ValidatorAddressChanged(validatorId, validators[validatorId].validatorAddress);\n    }\n\n    /**\n     * @dev Links a given node address.\n     *\n     * Requirements:\n     *\n     * - the given signature must be valid.\n     * - the address must not be assigned to a validator.\n     *\n     * Emits NodeAddressWasAdded event.\n     *\n     * @param nodeAddress address\n     * @param sig bytes signature of validator Id by node operator.\n     */\n    function linkNodeAddress(address nodeAddress, bytes calldata sig) external {\n        // check Validator Exist inside getValidatorId\n        uint validatorId = getValidatorId(msg.sender);\n        require(\n            keccak256(abi.encodePacked(validatorId)).toEthSignedMessageHash().recover(sig) == nodeAddress,\n            \"Signature is not pass\"\n        );\n        require(_validatorAddressToId[nodeAddress] == 0, \"Node address is a validator\");\n\n        _addNodeAddress(validatorId, nodeAddress);\n        emit NodeAddressWasAdded(validatorId, nodeAddress);\n    }\n\n    /**\n     * @dev Unlinks a given node address from a validator.\n     *\n     * Emits NodeAddressWasRemoved event.\n     *\n     * @param nodeAddress address\n     */\n    function unlinkNodeAddress(address nodeAddress) external {\n        // check Validator Exist inside getValidatorId\n        uint validatorId = getValidatorId(msg.sender);\n\n        _removeNodeAddress(validatorId, nodeAddress);\n        emit NodeAddressWasRemoved(validatorId, nodeAddress);\n    }\n\n    function setValidatorMDA(uint minimumDelegationAmount) external {\n        // check Validator Exist inside getValidatorId\n        uint validatorId = getValidatorId(msg.sender);\n\n        validators[validatorId].minimumDelegationAmount = minimumDelegationAmount;\n    }\n\n    /**\n     * @dev Allows a validator to set a new validator name.\n     *\n     * @param newName string\n     */\n    function setValidatorName(string calldata newName) external {\n        // check Validator Exist inside getValidatorId\n        uint validatorId = getValidatorId(msg.sender);\n\n        validators[validatorId].name = newName;\n    }\n\n    /**\n     * @dev Allows a validator to set a new validator description.\n     *\n     * @param newDescription string\n     */\n    function setValidatorDescription(string calldata newDescription) external {\n        // check Validator Exist inside getValidatorId\n        uint validatorId = getValidatorId(msg.sender);\n\n        validators[validatorId].description = newDescription;\n    }\n\n    /**\n     * @dev Allows a validator to start accepting new delegation requests.\n     *\n     * Requirements:\n     *\n     * - validator must not have already enabled accepting new requests\n     */\n    function startAcceptingNewRequests() external {\n        // check Validator Exist inside getValidatorId\n        uint validatorId = getValidatorId(msg.sender);\n        require(!isAcceptingNewRequests(validatorId), \"Accepting request is already enabled\");\n\n        validators[validatorId].acceptNewRequests = true;\n    }\n\n    /**\n     * @dev Allows a validator to stop accepting new delegation requests.\n     *\n     * Requirements:\n     *\n     * - validator must not have already stopped accepting new requests\n     */\n    function stopAcceptingNewRequests() external {\n        // check Validator Exist inside getValidatorId\n        uint validatorId = getValidatorId(msg.sender);\n        require(isAcceptingNewRequests(validatorId), \"Accepting request is already disabled\");\n\n        validators[validatorId].acceptNewRequests = false;\n    }\n\n    /**\n     * @dev Returns the amount of validator bond.\n     *\n     * @param validatorId uint ID of validator to return the amount of locked funds\n     * @return bondAmount uint the amount of self-delegated funds by the validator\n    */\n    function getAndUpdateBondAmount(uint validatorId)\n        external\n        returns (uint)\n    {\n        DelegationController delegationController = DelegationController(\n            contractManager.getContract(\"DelegationController\")\n        );\n        return delegationController.getAndUpdateDelegatedByHolderToValidatorNow(\n            getValidator(validatorId).validatorAddress,\n            validatorId\n        );\n    }\n\n    function getMyNodesAddresses() external view returns (address[] memory) {\n        return getNodeAddresses(getValidatorId(msg.sender));\n    }\n\n    /**\n     * @dev Returns a list of trusted validators.\n     *\n     * @return uint[] trusted validators\n     */\n    function getTrustedValidators() external view returns (uint[] memory) {\n        return trustedValidatorsList;\n    }\n\n    function checkMinimumDelegation(uint validatorId, uint amount)\n        external\n        view\n        checkValidatorExists(validatorId)\n        allow(\"DelegationController\")\n        returns (bool)\n    {\n        return validators[validatorId].minimumDelegationAmount \u003c= amount ? true : false;\n    }\n\n    function checkValidatorAddressToId(address validatorAddress, uint validatorId)\n        external\n        view\n        returns (bool)\n    {\n        return getValidatorId(validatorAddress) == validatorId ? true : false;\n    }\n\n    function getValidatorIdByNodeAddress(address nodeAddress) external view returns (uint validatorId) {\n        validatorId = _nodeAddressToValidatorId[nodeAddress];\n        require(validatorId != 0, \"Node address is not assigned to a validator\");\n    }\n\n\n    function isAuthorizedValidator(uint validatorId) external view checkValidatorExists(validatorId) returns (bool) {\n        return _trustedValidators[validatorId] || !useWhitelist;\n    }\n\n    function initialize(address contractManagerAddress) public override initializer {\n        Permissions.initialize(contractManagerAddress);\n        useWhitelist = true;\n    }\n\n    function getNodeAddresses(uint validatorId) public view returns (address[] memory) {\n        return _nodeAddresses[validatorId];\n    }\n\n    function validatorExists(uint validatorId) public view returns (bool) {\n        return validatorId \u003c= numberOfValidators \u0026\u0026 validatorId != 0;\n    }\n\n    function validatorAddressExists(address validatorAddress) public view returns (bool) {\n        return _validatorAddressToId[validatorAddress] != 0;\n    }\n\n    function checkIfValidatorAddressExists(address validatorAddress) public view {\n        require(validatorAddressExists(validatorAddress), \"Validator with given address does not exist\");\n    }\n\n    function getValidator(uint validatorId) public view checkValidatorExists(validatorId) returns (Validator memory) {\n        return validators[validatorId];\n    }\n\n    function getValidatorId(address validatorAddress) public view returns (uint) {\n        checkIfValidatorAddressExists(validatorAddress);\n        return _validatorAddressToId[validatorAddress];\n    }\n\n    function isAcceptingNewRequests(uint validatorId) public view checkValidatorExists(validatorId) returns (bool) {\n        return validators[validatorId].acceptNewRequests;\n    }\n\n    // private\n    function _setValidatorAddress(uint validatorId, address validatorAddress) private {\n        if (_validatorAddressToId[validatorAddress] == validatorId) {\n            return;\n        }\n        require(_validatorAddressToId[validatorAddress] == 0, \"Address is in use by another validator\");\n        address oldAddress = validators[validatorId].validatorAddress;\n        delete _validatorAddressToId[oldAddress];\n        _nodeAddressToValidatorId[validatorAddress] = validatorId;\n        validators[validatorId].validatorAddress = validatorAddress;\n        _validatorAddressToId[validatorAddress] = validatorId;\n    }\n\n    function _addNodeAddress(uint validatorId, address nodeAddress) private {\n        if (_nodeAddressToValidatorId[nodeAddress] == validatorId) {\n            return;\n        }\n        require(_nodeAddressToValidatorId[nodeAddress] == 0, \"Validator cannot override node address\");\n        _nodeAddressToValidatorId[nodeAddress] = validatorId;\n        _nodeAddresses[validatorId].push(nodeAddress);\n    }\n\n    function _removeNodeAddress(uint validatorId, address nodeAddress) private {\n        require(_nodeAddressToValidatorId[nodeAddress] == validatorId,\n            \"Validator does not have permissions to unlink node\");\n        delete _nodeAddressToValidatorId[nodeAddress];\n        for (uint i = 0; i \u003c _nodeAddresses[validatorId].length; ++i) {\n            if (_nodeAddresses[validatorId][i] == nodeAddress) {\n                if (i + 1 \u003c _nodeAddresses[validatorId].length) {\n                    _nodeAddresses[validatorId][i] =\n                        _nodeAddresses[validatorId][_nodeAddresses[validatorId].length.sub(1)];\n                }\n                delete _nodeAddresses[validatorId][_nodeAddresses[validatorId].length.sub(1)];\n                _nodeAddresses[validatorId].pop();\n                break;\n            }\n        }\n    }\n\n    function _find(uint[] memory array, uint index) private pure returns (uint) {\n        uint i;\n        for (i = 0; i \u003c array.length; i++) {\n            if (array[i] == index) {\n                return i;\n            }\n        }\n        return array.length;\n    }\n}\n"}}

      File 2 of 8: ERC1820Registry
      /* ERC1820 Pseudo-introspection Registry Contract
       * This standard defines a universal registry smart contract where any address (contract or regular account) can
       * register which interface it supports and which smart contract is responsible for its implementation.
       *
       * Written in 2019 by Jordi Baylina and Jacques Dafflon
       *
       * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to
       * this software to the public domain worldwide. This software is distributed without any warranty.
       *
       * You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see
       * <http://creativecommons.org/publicdomain/zero/1.0/>.
       *
       *    ███████╗██████╗  ██████╗ ██╗ █████╗ ██████╗  ██████╗
       *    ██╔════╝██╔══██╗██╔════╝███║██╔══██╗╚════██╗██╔═████╗
       *    █████╗  ██████╔╝██║     ╚██║╚█████╔╝ █████╔╝██║██╔██║
       *    ██╔══╝  ██╔══██╗██║      ██║██╔══██╗██╔═══╝ ████╔╝██║
       *    ███████╗██║  ██║╚██████╗ ██║╚█████╔╝███████╗╚██████╔╝
       *    ╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚══════╝ ╚═════╝
       *
       *    ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗   ██╗
       *    ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝
       *    ██████╔╝█████╗  ██║  ███╗██║███████╗   ██║   ██████╔╝ ╚████╔╝
       *    ██╔══██╗██╔══╝  ██║   ██║██║╚════██║   ██║   ██╔══██╗  ╚██╔╝
       *    ██║  ██║███████╗╚██████╔╝██║███████║   ██║   ██║  ██║   ██║
       *    ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝
       *
       */
      pragma solidity 0.5.3;
      // IV is value needed to have a vanity address starting with '0x1820'.
      // IV: 53759
      
      /// @dev The interface a contract MUST implement if it is the implementer of
      /// some (other) interface for any address other than itself.
      interface ERC1820ImplementerInterface {
          /// @notice Indicates whether the contract implements the interface 'interfaceHash' for the address 'addr' or not.
          /// @param interfaceHash keccak256 hash of the name of the interface
          /// @param addr Address for which the contract will implement the interface
          /// @return ERC1820_ACCEPT_MAGIC only if the contract implements 'interfaceHash' for the address 'addr'.
          function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
      }
      
      
      /// @title ERC1820 Pseudo-introspection Registry Contract
      /// @author Jordi Baylina and Jacques Dafflon
      /// @notice This contract is the official implementation of the ERC1820 Registry.
      /// @notice For more details, see https://eips.ethereum.org/EIPS/eip-1820
      contract ERC1820Registry {
          /// @notice ERC165 Invalid ID.
          bytes4 constant internal INVALID_ID = 0xffffffff;
          /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).
          bytes4 constant internal ERC165ID = 0x01ffc9a7;
          /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.
          bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
      
          /// @notice mapping from addresses and interface hashes to their implementers.
          mapping(address => mapping(bytes32 => address)) internal interfaces;
          /// @notice mapping from addresses to their manager.
          mapping(address => address) internal managers;
          /// @notice flag for each address and erc165 interface to indicate if it is cached.
          mapping(address => mapping(bytes4 => bool)) internal erc165Cached;
      
          /// @notice Indicates a contract is the 'implementer' of 'interfaceHash' for 'addr'.
          event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
          /// @notice Indicates 'newManager' is the address of the new manager for 'addr'.
          event ManagerChanged(address indexed addr, address indexed newManager);
      
          /// @notice Query if an address implements an interface and through which contract.
          /// @param _addr Address being queried for the implementer of an interface.
          /// (If '_addr' is the zero address then 'msg.sender' is assumed.)
          /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
          /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
          /// @return The address of the contract which implements the interface '_interfaceHash' for '_addr'
          /// or '0' if '_addr' did not register an implementer for this interface.
          function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {
              address addr = _addr == address(0) ? msg.sender : _addr;
              if (isERC165Interface(_interfaceHash)) {
                  bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
                  return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);
              }
              return interfaces[addr][_interfaceHash];
          }
      
          /// @notice Sets the contract which implements a specific interface for an address.
          /// Only the manager defined for that address can set it.
          /// (Each address is the manager for itself until it sets a new manager.)
          /// @param _addr Address for which to set the interface.
          /// (If '_addr' is the zero address then 'msg.sender' is assumed.)
          /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
          /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
          /// @param _implementer Contract address implementing '_interfaceHash' for '_addr'.
          function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {
              address addr = _addr == address(0) ? msg.sender : _addr;
              require(getManager(addr) == msg.sender, "Not the manager");
      
              require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash");
              if (_implementer != address(0) && _implementer != msg.sender) {
                  require(
                      ERC1820ImplementerInterface(_implementer)
                          .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
                      "Does not implement the interface"
                  );
              }
              interfaces[addr][_interfaceHash] = _implementer;
              emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
          }
      
          /// @notice Sets '_newManager' as manager for '_addr'.
          /// The new manager will be able to call 'setInterfaceImplementer' for '_addr'.
          /// @param _addr Address for which to set the new manager.
          /// @param _newManager Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_addr'.)
          function setManager(address _addr, address _newManager) external {
              require(getManager(_addr) == msg.sender, "Not the manager");
              managers[_addr] = _newManager == _addr ? address(0) : _newManager;
              emit ManagerChanged(_addr, _newManager);
          }
      
          /// @notice Get the manager of an address.
          /// @param _addr Address for which to return the manager.
          /// @return Address of the manager for a given address.
          function getManager(address _addr) public view returns(address) {
              // By default the manager of an address is the same address
              if (managers[_addr] == address(0)) {
                  return _addr;
              } else {
                  return managers[_addr];
              }
          }
      
          /// @notice Compute the keccak256 hash of an interface given its name.
          /// @param _interfaceName Name of the interface.
          /// @return The keccak256 hash of an interface name.
          function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {
              return keccak256(abi.encodePacked(_interfaceName));
          }
      
          /* --- ERC165 Related Functions --- */
          /* --- Developed in collaboration with William Entriken. --- */
      
          /// @notice Updates the cache with whether the contract implements an ERC165 interface or not.
          /// @param _contract Address of the contract for which to update the cache.
          /// @param _interfaceId ERC165 interface for which to update the cache.
          function updateERC165Cache(address _contract, bytes4 _interfaceId) external {
              interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(
                  _contract, _interfaceId) ? _contract : address(0);
              erc165Cached[_contract][_interfaceId] = true;
          }
      
          /// @notice Checks whether a contract implements an ERC165 interface or not.
          //  If the result is not cached a direct lookup on the contract address is performed.
          //  If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
          //  'updateERC165Cache' with the contract address.
          /// @param _contract Address of the contract to check.
          /// @param _interfaceId ERC165 interface to check.
          /// @return True if '_contract' implements '_interfaceId', false otherwise.
          function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {
              if (!erc165Cached[_contract][_interfaceId]) {
                  return implementsERC165InterfaceNoCache(_contract, _interfaceId);
              }
              return interfaces[_contract][_interfaceId] == _contract;
          }
      
          /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
          /// @param _contract Address of the contract to check.
          /// @param _interfaceId ERC165 interface to check.
          /// @return True if '_contract' implements '_interfaceId', false otherwise.
          function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {
              uint256 success;
              uint256 result;
      
              (success, result) = noThrowCall(_contract, ERC165ID);
              if (success == 0 || result == 0) {
                  return false;
              }
      
              (success, result) = noThrowCall(_contract, INVALID_ID);
              if (success == 0 || result != 0) {
                  return false;
              }
      
              (success, result) = noThrowCall(_contract, _interfaceId);
              if (success == 1 && result == 1) {
                  return true;
              }
              return false;
          }
      
          /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.
          /// @param _interfaceHash The hash to check.
          /// @return True if '_interfaceHash' is an ERC165 interface (ending with 28 zeroes), false otherwise.
          function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {
              return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
          }
      
          /// @dev Make a call on a contract without throwing if the function does not exist.
          function noThrowCall(address _contract, bytes4 _interfaceId)
              internal view returns (uint256 success, uint256 result)
          {
              bytes4 erc165ID = ERC165ID;
      
              assembly {
                  let x := mload(0x40)               // Find empty storage location using "free memory pointer"
                  mstore(x, erc165ID)                // Place signature at beginning of empty storage
                  mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature
      
                  success := staticcall(
                      30000,                         // 30k gas
                      _contract,                     // To addr
                      x,                             // Inputs are stored at location x
                      0x24,                          // Inputs are 36 (4 + 32) bytes long
                      x,                             // Store output over input (saves space)
                      0x20                           // Outputs are 32 bytes long
                  )
      
                  result := mload(x)                 // Load the result
              }
          }
      }

      File 3 of 8: AdminUpgradeabilityProxy
      // File: ownership/Ownable.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @title Ownable
       * @dev The Ownable contract has an owner address, and provides basic authorization control
       * functions, this simplifies the implementation of "user permissions".
       *
       * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/ownership/Ownable.sol
       * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
       * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
       * build/artifacts folder) as well as the vanilla Ownable implementation from an openzeppelin version.
       */
      contract OpenZeppelinUpgradesOwnable {
          address private _owner;
      
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      
          /**
           * @dev The Ownable constructor sets the original `owner` of the contract to the sender
           * account.
           */
          constructor () internal {
              _owner = msg.sender;
              emit OwnershipTransferred(address(0), _owner);
          }
      
          /**
           * @return the address of the owner.
           */
          function owner() public view returns (address) {
              return _owner;
          }
      
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(isOwner());
              _;
          }
      
          /**
           * @return true if `msg.sender` is the owner of the contract.
           */
          function isOwner() public view returns (bool) {
              return msg.sender == _owner;
          }
      
          /**
           * @dev Allows the current owner to relinquish control of the contract.
           * @notice Renouncing to ownership will leave the contract without an owner.
           * It will not be possible to call the functions with the `onlyOwner`
           * modifier anymore.
           */
          function renounceOwnership() public onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = address(0);
          }
      
          /**
           * @dev Allows the current owner to transfer control of the contract to a newOwner.
           * @param newOwner The address to transfer ownership to.
           */
          function transferOwnership(address newOwner) public onlyOwner {
              _transferOwnership(newOwner);
          }
      
          /**
           * @dev Transfers control of the contract to a newOwner.
           * @param newOwner The address to transfer ownership to.
           */
          function _transferOwnership(address newOwner) internal {
              require(newOwner != address(0));
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
      
      // File: upgradeability/Proxy.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @title Proxy
       * @dev Implements delegation of calls to other contracts, with proper
       * forwarding of return values and bubbling of failures.
       * It defines a fallback function that delegates all calls to the address
       * returned by the abstract _implementation() internal function.
       */
      contract Proxy {
        /**
         * @dev Fallback function.
         * Implemented entirely in `_fallback`.
         */
        function () payable external {
          _fallback();
        }
      
        /**
         * @return The Address of the implementation.
         */
        function _implementation() internal view returns (address);
      
        /**
         * @dev Delegates execution to an implementation contract.
         * This is a low level function that doesn't return to its internal call site.
         * It will return to the external caller whatever the implementation returns.
         * @param implementation Address to delegate.
         */
        function _delegate(address implementation) internal {
          assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize)
      
            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
      
            // Copy the returned data.
            returndatacopy(0, 0, returndatasize)
      
            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize) }
            default { return(0, returndatasize) }
          }
        }
      
        /**
         * @dev Function that is run as the first thing in the fallback function.
         * Can be redefined in derived contracts to add functionality.
         * Redefinitions must call super._willFallback().
         */
        function _willFallback() internal {
        }
      
        /**
         * @dev fallback implementation.
         * Extracted to enable manual triggering.
         */
        function _fallback() internal {
          _willFallback();
          _delegate(_implementation());
        }
      }
      
      // File: utils/Address.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * Utility library of inline functions on addresses
       *
       * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/utils/Address.sol
       * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
       * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
       * build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version.
       */
      library OpenZeppelinUpgradesAddress {
          /**
           * Returns whether the target address is a contract
           * @dev This function will return false if invoked during the constructor of a contract,
           * as the code is not actually created until after the constructor finishes.
           * @param account address of the account to check
           * @return whether the target address is a contract
           */
          function isContract(address account) internal view returns (bool) {
              uint256 size;
              // XXX Currently there is no better way to check if there is a contract in an address
              // than to check the size of the code at that address.
              // See https://ethereum.stackexchange.com/a/14016/36603
              // for more details about how this works.
              // TODO Check this again before the Serenity release, because all addresses will be
              // contracts then.
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
      }
      
      // File: upgradeability/BaseUpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      
      /**
       * @title BaseUpgradeabilityProxy
       * @dev This contract implements a proxy that allows to change the
       * implementation address to which it will delegate.
       * Such a change is called an implementation upgrade.
       */
      contract BaseUpgradeabilityProxy is Proxy {
        /**
         * @dev Emitted when the implementation is upgraded.
         * @param implementation Address of the new implementation.
         */
        event Upgraded(address indexed implementation);
      
        /**
         * @dev Storage slot with the address of the current implementation.
         * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
         * validated in the constructor.
         */
        bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
      
        /**
         * @dev Returns the current implementation.
         * @return Address of the current implementation
         */
        function _implementation() internal view returns (address impl) {
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            impl := sload(slot)
          }
        }
      
        /**
         * @dev Upgrades the proxy to a new implementation.
         * @param newImplementation Address of the new implementation.
         */
        function _upgradeTo(address newImplementation) internal {
          _setImplementation(newImplementation);
          emit Upgraded(newImplementation);
        }
      
        /**
         * @dev Sets the implementation address of the proxy.
         * @param newImplementation Address of the new implementation.
         */
        function _setImplementation(address newImplementation) internal {
          require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
      
          bytes32 slot = IMPLEMENTATION_SLOT;
      
          assembly {
            sstore(slot, newImplementation)
          }
        }
      }
      
      // File: upgradeability/UpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title UpgradeabilityProxy
       * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing
       * implementation and init data.
       */
      contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
        /**
         * @dev Contract constructor.
         * @param _logic Address of the initial implementation.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, bytes memory _data) public payable {
          assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
          _setImplementation(_logic);
          if(_data.length > 0) {
            (bool success,) = _logic.delegatecall(_data);
            require(success);
          }
        }  
      }
      
      // File: upgradeability/BaseAdminUpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title BaseAdminUpgradeabilityProxy
       * @dev This contract combines an upgradeability proxy with an authorization
       * mechanism for administrative tasks.
       * All external functions in this contract must be guarded by the
       * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
       * feature proposal that would enable this to be done automatically.
       */
      contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
        /**
         * @dev Emitted when the administration has been transferred.
         * @param previousAdmin Address of the previous admin.
         * @param newAdmin Address of the new admin.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
      
        /**
         * @dev Storage slot with the admin of the contract.
         * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
         * validated in the constructor.
         */
      
        bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
      
        /**
         * @dev Modifier to check whether the `msg.sender` is the admin.
         * If it is, it will run the function. Otherwise, it will delegate the call
         * to the implementation.
         */
        modifier ifAdmin() {
          if (msg.sender == _admin()) {
            _;
          } else {
            _fallback();
          }
        }
      
        /**
         * @return The address of the proxy admin.
         */
        function admin() external ifAdmin returns (address) {
          return _admin();
        }
      
        /**
         * @return The address of the implementation.
         */
        function implementation() external ifAdmin returns (address) {
          return _implementation();
        }
      
        /**
         * @dev Changes the admin of the proxy.
         * Only the current admin can call this function.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeAdmin(address newAdmin) external ifAdmin {
          require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
          emit AdminChanged(_admin(), newAdmin);
          _setAdmin(newAdmin);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy.
         * Only the admin can call this function.
         * @param newImplementation Address of the new implementation.
         */
        function upgradeTo(address newImplementation) external ifAdmin {
          _upgradeTo(newImplementation);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy and call a function
         * on the new implementation.
         * This is useful to initialize the proxied contract.
         * @param newImplementation Address of the new implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
          _upgradeTo(newImplementation);
          (bool success,) = newImplementation.delegatecall(data);
          require(success);
        }
      
        /**
         * @return The admin slot.
         */
        function _admin() internal view returns (address adm) {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            adm := sload(slot)
          }
        }
      
        /**
         * @dev Sets the address of the proxy admin.
         * @param newAdmin Address of the new proxy admin.
         */
        function _setAdmin(address newAdmin) internal {
          bytes32 slot = ADMIN_SLOT;
      
          assembly {
            sstore(slot, newAdmin)
          }
        }
      
        /**
         * @dev Only fall back when the sender is not the admin.
         */
        function _willFallback() internal {
          require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
          super._willFallback();
        }
      }
      
      // File: upgradeability/AdminUpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title AdminUpgradeabilityProxy
       * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for 
       * initializing the implementation, admin, and init data.
       */
      contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy {
        /**
         * Contract constructor.
         * @param _logic address of the initial implementation.
         * @param _admin Address of the proxy administrator.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable {
          assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
          _setAdmin(_admin);
        }
      }
      
      // File: upgradeability/ProxyAdmin.sol
      
      pragma solidity ^0.5.0;
      
      
      
      /**
       * @title ProxyAdmin
       * @dev This contract is the admin of a proxy, and is in charge
       * of upgrading it as well as transferring it to another admin.
       */
      contract ProxyAdmin is OpenZeppelinUpgradesOwnable {
      
        /**
         * @dev Returns the current implementation of a proxy.
         * This is needed because only the proxy admin can query it.
         * @return The address of the current implementation of the proxy.
         */
        function getProxyImplementation(AdminUpgradeabilityProxy proxy) public view returns (address) {
          // We need to manually run the static call since the getter cannot be flagged as view
          // bytes4(keccak256("implementation()")) == 0x5c60da1b
          (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
          require(success);
          return abi.decode(returndata, (address));
        }
      
        /**
         * @dev Returns the admin of a proxy. Only the admin can query it.
         * @return The address of the current admin of the proxy.
         */
        function getProxyAdmin(AdminUpgradeabilityProxy proxy) public view returns (address) {
          // We need to manually run the static call since the getter cannot be flagged as view
          // bytes4(keccak256("admin()")) == 0xf851a440
          (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
          require(success);
          return abi.decode(returndata, (address));
        }
      
        /**
         * @dev Changes the admin of a proxy.
         * @param proxy Proxy to change admin.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeProxyAdmin(AdminUpgradeabilityProxy proxy, address newAdmin) public onlyOwner {
          proxy.changeAdmin(newAdmin);
        }
      
        /**
         * @dev Upgrades a proxy to the newest implementation of a contract.
         * @param proxy Proxy to be upgraded.
         * @param implementation the address of the Implementation.
         */
        function upgrade(AdminUpgradeabilityProxy proxy, address implementation) public onlyOwner {
          proxy.upgradeTo(implementation);
        }
      
        /**
         * @dev Upgrades a proxy to the newest implementation of a contract and forwards a function call to it.
         * This is useful to initialize the proxied contract.
         * @param proxy Proxy to be upgraded.
         * @param implementation Address of the Implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeAndCall(AdminUpgradeabilityProxy proxy, address implementation, bytes memory data) payable public onlyOwner {
          proxy.upgradeToAndCall.value(msg.value)(implementation, data);
        }
      }

      File 4 of 8: ContractManager
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          ContractManager.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity 0.8.17;
      import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
      import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
      import "@skalenetwork/skale-manager-interfaces/IContractManager.sol";
      import "./utils/StringUtils.sol";
      import "./thirdparty/openzeppelin/InitializableWithGap.sol";
      /**
       * @title ContractManager
       * @dev Contract contains the actual current mapping from contract IDs
       * (in the form of human-readable strings) to addresses.
       */
      contract ContractManager is InitializableWithGap, OwnableUpgradeable, IContractManager {
          using StringUtils for string;
          using AddressUpgradeable for address;
          string public constant BOUNTY = "Bounty";
          string public constant CONSTANTS_HOLDER = "ConstantsHolder";
          string public constant DELEGATION_PERIOD_MANAGER = "DelegationPeriodManager";
          string public constant PUNISHER = "Punisher";
          string public constant SKALE_TOKEN = "SkaleToken";
          string public constant TIME_HELPERS = "TimeHelpers";
          string public constant TOKEN_STATE = "TokenState";
          string public constant VALIDATOR_SERVICE = "ValidatorService";
          // mapping of actual smart contracts addresses
          mapping (bytes32 => address) public override contracts;
          function initialize() external override initializer {
              OwnableUpgradeable.__Ownable_init();
          }
          /**
           * @dev Allows the Owner to add contract to mapping of contract addresses.
           *
           * Emits a {ContractUpgraded} event.
           *
           * Requirements:
           *
           * - New address is non-zero.
           * - Contract is not already added.
           * - Contract address contains code.
           */
          function setContractsAddress(
              string calldata contractsName,
              address newContractsAddress
          )
              external
              override
              onlyOwner
          {
              // check newContractsAddress is not equal to zero
              require(newContractsAddress != address(0), "New address is equal zero");
              // create hash of contractsName
              bytes32 contractId = keccak256(abi.encodePacked(contractsName));
              // check newContractsAddress is not equal the previous contract's address
              require(contracts[contractId] != newContractsAddress, "Contract is already added");
              require(newContractsAddress.isContract(), "Given contract address does not contain code");
              // add newContractsAddress to mapping of actual contract addresses
              contracts[contractId] = newContractsAddress;
              emit ContractUpgraded(contractsName, newContractsAddress);
          }
          /**
           * @dev Returns contract address.
           *
           * Requirements:
           *
           * - Contract must exist.
           */
          function getDelegationPeriodManager() external view override returns (address) {
              return getContract(DELEGATION_PERIOD_MANAGER);
          }
          function getBounty() external view override returns (address) {
              return getContract(BOUNTY);
          }
          function getValidatorService() external view override returns (address) {
              return getContract(VALIDATOR_SERVICE);
          }
          function getTimeHelpers() external view override returns (address) {
              return getContract(TIME_HELPERS);
          }
          function getConstantsHolder() external view override returns (address) {
              return getContract(CONSTANTS_HOLDER);
          }
          function getSkaleToken() external view override returns (address) {
              return getContract(SKALE_TOKEN);
          }
          function getTokenState() external view override returns (address) {
              return getContract(TOKEN_STATE);
          }
          function getPunisher() external view override returns (address) {
              return getContract(PUNISHER);
          }
          function getContract(string memory name) public view override returns (address contractAddress) {
              contractAddress = contracts[keccak256(abi.encodePacked(name))];
              if (contractAddress == address(0)) {
                  revert(name.strConcat(" contract has not been found"));
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import "../utils/ContextUpgradeable.sol";
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          function __Ownable_init() internal onlyInitializing {
              __Ownable_init_unchained();
          }
          function __Ownable_init_unchained() internal onlyInitializing {
              _transferOwnership(_msgSender());
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              _checkOwner();
              _;
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if the sender is not the owner.
           */
          function _checkOwner() internal view virtual {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _transferOwnership(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _transferOwnership(newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[49] private __gap;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @dev Collection of functions related to the address type
       */
      library AddressUpgradeable {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      /// @solidity memory-safe-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          IContractManager.sol - SKALE Manager Interfaces
          Copyright (C) 2021-Present SKALE Labs
          @author Dmytro Stebaeiv
          SKALE Manager Interfaces is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager Interfaces 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager Interfaces.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IContractManager {
          /**
           * @dev Emitted when contract is upgraded.
           */
          event ContractUpgraded(string contractsName, address contractsAddress);
          function initialize() external;
          function setContractsAddress(string calldata contractsName, address newContractsAddress) external;
          function contracts(bytes32 nameHash) external view returns (address);
          function getDelegationPeriodManager() external view returns (address);
          function getBounty() external view returns (address);
          function getValidatorService() external view returns (address);
          function getTimeHelpers() external view returns (address);
          function getConstantsHolder() external view returns (address);
          function getSkaleToken() external view returns (address);
          function getTokenState() external view returns (address);
          function getPunisher() external view returns (address);
          function getContract(string calldata name) external view returns (address);
      }// SPDX-License-Identifier: AGPL-3.0-only
      /*
          StringUtils.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Vadim Yavorsky
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity 0.8.17;
      library StringUtils {
          function strConcat(string memory a, string memory b) internal pure returns (string memory) {
              bytes memory _ba = bytes(a);
              bytes memory _bb = bytes(b);
              string memory ab = new string(_ba.length + _bb.length);
              bytes memory strBytes = bytes(ab);
              uint k = 0;
              uint i = 0;
              for (i = 0; i < _ba.length; i++) {
                  strBytes[k++] = _ba[i];
              }
              for (i = 0; i < _bb.length; i++) {
                  strBytes[k++] = _bb[i];
              }
              return string(strBytes);
          }
      }// SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity ^0.8.7;
      import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
      contract InitializableWithGap is Initializable {
          uint256[50] private ______gap;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
      pragma solidity ^0.8.0;
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract ContextUpgradeable is Initializable {
          function __Context_init() internal onlyInitializing {
          }
          function __Context_init_unchained() internal onlyInitializing {
          }
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[50] private __gap;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
      pragma solidity ^0.8.2;
      import "../../utils/AddressUpgradeable.sol";
      /**
       * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
       * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
       * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
       * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
       *
       * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
       * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
       * case an upgrade adds a module that needs to be initialized.
       *
       * For example:
       *
       * [.hljs-theme-light.nopadding]
       * ```
       * contract MyToken is ERC20Upgradeable {
       *     function initialize() initializer public {
       *         __ERC20_init("MyToken", "MTK");
       *     }
       * }
       * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
       *     function initializeV2() reinitializer(2) public {
       *         __ERC20Permit_init("MyToken");
       *     }
       * }
       * ```
       *
       * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
       * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
       *
       * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
       * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
       *
       * [CAUTION]
       * ====
       * Avoid leaving a contract uninitialized.
       *
       * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
       * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
       * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
       *
       * [.hljs-theme-light.nopadding]
       * ```
       * /// @custom:oz-upgrades-unsafe-allow constructor
       * constructor() {
       *     _disableInitializers();
       * }
       * ```
       * ====
       */
      abstract contract Initializable {
          /**
           * @dev Indicates that the contract has been initialized.
           * @custom:oz-retyped-from bool
           */
          uint8 private _initialized;
          /**
           * @dev Indicates that the contract is in the process of being initialized.
           */
          bool private _initializing;
          /**
           * @dev Triggered when the contract has been initialized or reinitialized.
           */
          event Initialized(uint8 version);
          /**
           * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
           * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
           */
          modifier initializer() {
              bool isTopLevelCall = !_initializing;
              require(
                  (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                  "Initializable: contract is already initialized"
              );
              _initialized = 1;
              if (isTopLevelCall) {
                  _initializing = true;
              }
              _;
              if (isTopLevelCall) {
                  _initializing = false;
                  emit Initialized(1);
              }
          }
          /**
           * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
           * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
           * used to initialize parent contracts.
           *
           * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
           * initialization step. This is essential to configure modules that are added through upgrades and that require
           * initialization.
           *
           * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
           * a contract, executing them in the right order is up to the developer or operator.
           */
          modifier reinitializer(uint8 version) {
              require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
              _initialized = version;
              _initializing = true;
              _;
              _initializing = false;
              emit Initialized(version);
          }
          /**
           * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
           * {initializer} and {reinitializer} modifiers, directly or indirectly.
           */
          modifier onlyInitializing() {
              require(_initializing, "Initializable: contract is not initializing");
              _;
          }
          /**
           * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
           * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
           * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
           * through proxies.
           */
          function _disableInitializers() internal virtual {
              require(!_initializing, "Initializable: contract is initializing");
              if (_initialized < type(uint8).max) {
                  _initialized = type(uint8).max;
                  emit Initialized(type(uint8).max);
              }
          }
      }
      

      File 5 of 8: AdminUpgradeabilityProxy
      // File: ownership/Ownable.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @title Ownable
       * @dev The Ownable contract has an owner address, and provides basic authorization control
       * functions, this simplifies the implementation of "user permissions".
       *
       * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/ownership/Ownable.sol
       * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
       * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
       * build/artifacts folder) as well as the vanilla Ownable implementation from an openzeppelin version.
       */
      contract OpenZeppelinUpgradesOwnable {
          address private _owner;
      
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      
          /**
           * @dev The Ownable constructor sets the original `owner` of the contract to the sender
           * account.
           */
          constructor () internal {
              _owner = msg.sender;
              emit OwnershipTransferred(address(0), _owner);
          }
      
          /**
           * @return the address of the owner.
           */
          function owner() public view returns (address) {
              return _owner;
          }
      
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(isOwner());
              _;
          }
      
          /**
           * @return true if `msg.sender` is the owner of the contract.
           */
          function isOwner() public view returns (bool) {
              return msg.sender == _owner;
          }
      
          /**
           * @dev Allows the current owner to relinquish control of the contract.
           * @notice Renouncing to ownership will leave the contract without an owner.
           * It will not be possible to call the functions with the `onlyOwner`
           * modifier anymore.
           */
          function renounceOwnership() public onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = address(0);
          }
      
          /**
           * @dev Allows the current owner to transfer control of the contract to a newOwner.
           * @param newOwner The address to transfer ownership to.
           */
          function transferOwnership(address newOwner) public onlyOwner {
              _transferOwnership(newOwner);
          }
      
          /**
           * @dev Transfers control of the contract to a newOwner.
           * @param newOwner The address to transfer ownership to.
           */
          function _transferOwnership(address newOwner) internal {
              require(newOwner != address(0));
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
      
      // File: upgradeability/Proxy.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @title Proxy
       * @dev Implements delegation of calls to other contracts, with proper
       * forwarding of return values and bubbling of failures.
       * It defines a fallback function that delegates all calls to the address
       * returned by the abstract _implementation() internal function.
       */
      contract Proxy {
        /**
         * @dev Fallback function.
         * Implemented entirely in `_fallback`.
         */
        function () payable external {
          _fallback();
        }
      
        /**
         * @return The Address of the implementation.
         */
        function _implementation() internal view returns (address);
      
        /**
         * @dev Delegates execution to an implementation contract.
         * This is a low level function that doesn't return to its internal call site.
         * It will return to the external caller whatever the implementation returns.
         * @param implementation Address to delegate.
         */
        function _delegate(address implementation) internal {
          assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize)
      
            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
      
            // Copy the returned data.
            returndatacopy(0, 0, returndatasize)
      
            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize) }
            default { return(0, returndatasize) }
          }
        }
      
        /**
         * @dev Function that is run as the first thing in the fallback function.
         * Can be redefined in derived contracts to add functionality.
         * Redefinitions must call super._willFallback().
         */
        function _willFallback() internal {
        }
      
        /**
         * @dev fallback implementation.
         * Extracted to enable manual triggering.
         */
        function _fallback() internal {
          _willFallback();
          _delegate(_implementation());
        }
      }
      
      // File: utils/Address.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * Utility library of inline functions on addresses
       *
       * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/utils/Address.sol
       * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
       * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
       * build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version.
       */
      library OpenZeppelinUpgradesAddress {
          /**
           * Returns whether the target address is a contract
           * @dev This function will return false if invoked during the constructor of a contract,
           * as the code is not actually created until after the constructor finishes.
           * @param account address of the account to check
           * @return whether the target address is a contract
           */
          function isContract(address account) internal view returns (bool) {
              uint256 size;
              // XXX Currently there is no better way to check if there is a contract in an address
              // than to check the size of the code at that address.
              // See https://ethereum.stackexchange.com/a/14016/36603
              // for more details about how this works.
              // TODO Check this again before the Serenity release, because all addresses will be
              // contracts then.
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
      }
      
      // File: upgradeability/BaseUpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      
      /**
       * @title BaseUpgradeabilityProxy
       * @dev This contract implements a proxy that allows to change the
       * implementation address to which it will delegate.
       * Such a change is called an implementation upgrade.
       */
      contract BaseUpgradeabilityProxy is Proxy {
        /**
         * @dev Emitted when the implementation is upgraded.
         * @param implementation Address of the new implementation.
         */
        event Upgraded(address indexed implementation);
      
        /**
         * @dev Storage slot with the address of the current implementation.
         * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
         * validated in the constructor.
         */
        bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
      
        /**
         * @dev Returns the current implementation.
         * @return Address of the current implementation
         */
        function _implementation() internal view returns (address impl) {
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            impl := sload(slot)
          }
        }
      
        /**
         * @dev Upgrades the proxy to a new implementation.
         * @param newImplementation Address of the new implementation.
         */
        function _upgradeTo(address newImplementation) internal {
          _setImplementation(newImplementation);
          emit Upgraded(newImplementation);
        }
      
        /**
         * @dev Sets the implementation address of the proxy.
         * @param newImplementation Address of the new implementation.
         */
        function _setImplementation(address newImplementation) internal {
          require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
      
          bytes32 slot = IMPLEMENTATION_SLOT;
      
          assembly {
            sstore(slot, newImplementation)
          }
        }
      }
      
      // File: upgradeability/UpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title UpgradeabilityProxy
       * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing
       * implementation and init data.
       */
      contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
        /**
         * @dev Contract constructor.
         * @param _logic Address of the initial implementation.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, bytes memory _data) public payable {
          assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
          _setImplementation(_logic);
          if(_data.length > 0) {
            (bool success,) = _logic.delegatecall(_data);
            require(success);
          }
        }  
      }
      
      // File: upgradeability/BaseAdminUpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title BaseAdminUpgradeabilityProxy
       * @dev This contract combines an upgradeability proxy with an authorization
       * mechanism for administrative tasks.
       * All external functions in this contract must be guarded by the
       * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
       * feature proposal that would enable this to be done automatically.
       */
      contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
        /**
         * @dev Emitted when the administration has been transferred.
         * @param previousAdmin Address of the previous admin.
         * @param newAdmin Address of the new admin.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
      
        /**
         * @dev Storage slot with the admin of the contract.
         * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
         * validated in the constructor.
         */
      
        bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
      
        /**
         * @dev Modifier to check whether the `msg.sender` is the admin.
         * If it is, it will run the function. Otherwise, it will delegate the call
         * to the implementation.
         */
        modifier ifAdmin() {
          if (msg.sender == _admin()) {
            _;
          } else {
            _fallback();
          }
        }
      
        /**
         * @return The address of the proxy admin.
         */
        function admin() external ifAdmin returns (address) {
          return _admin();
        }
      
        /**
         * @return The address of the implementation.
         */
        function implementation() external ifAdmin returns (address) {
          return _implementation();
        }
      
        /**
         * @dev Changes the admin of the proxy.
         * Only the current admin can call this function.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeAdmin(address newAdmin) external ifAdmin {
          require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
          emit AdminChanged(_admin(), newAdmin);
          _setAdmin(newAdmin);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy.
         * Only the admin can call this function.
         * @param newImplementation Address of the new implementation.
         */
        function upgradeTo(address newImplementation) external ifAdmin {
          _upgradeTo(newImplementation);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy and call a function
         * on the new implementation.
         * This is useful to initialize the proxied contract.
         * @param newImplementation Address of the new implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
          _upgradeTo(newImplementation);
          (bool success,) = newImplementation.delegatecall(data);
          require(success);
        }
      
        /**
         * @return The admin slot.
         */
        function _admin() internal view returns (address adm) {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            adm := sload(slot)
          }
        }
      
        /**
         * @dev Sets the address of the proxy admin.
         * @param newAdmin Address of the new proxy admin.
         */
        function _setAdmin(address newAdmin) internal {
          bytes32 slot = ADMIN_SLOT;
      
          assembly {
            sstore(slot, newAdmin)
          }
        }
      
        /**
         * @dev Only fall back when the sender is not the admin.
         */
        function _willFallback() internal {
          require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
          super._willFallback();
        }
      }
      
      // File: upgradeability/AdminUpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title AdminUpgradeabilityProxy
       * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for 
       * initializing the implementation, admin, and init data.
       */
      contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy {
        /**
         * Contract constructor.
         * @param _logic address of the initial implementation.
         * @param _admin Address of the proxy administrator.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable {
          assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
          _setAdmin(_admin);
        }
      }
      
      // File: upgradeability/ProxyAdmin.sol
      
      pragma solidity ^0.5.0;
      
      
      
      /**
       * @title ProxyAdmin
       * @dev This contract is the admin of a proxy, and is in charge
       * of upgrading it as well as transferring it to another admin.
       */
      contract ProxyAdmin is OpenZeppelinUpgradesOwnable {
      
        /**
         * @dev Returns the current implementation of a proxy.
         * This is needed because only the proxy admin can query it.
         * @return The address of the current implementation of the proxy.
         */
        function getProxyImplementation(AdminUpgradeabilityProxy proxy) public view returns (address) {
          // We need to manually run the static call since the getter cannot be flagged as view
          // bytes4(keccak256("implementation()")) == 0x5c60da1b
          (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
          require(success);
          return abi.decode(returndata, (address));
        }
      
        /**
         * @dev Returns the admin of a proxy. Only the admin can query it.
         * @return The address of the current admin of the proxy.
         */
        function getProxyAdmin(AdminUpgradeabilityProxy proxy) public view returns (address) {
          // We need to manually run the static call since the getter cannot be flagged as view
          // bytes4(keccak256("admin()")) == 0xf851a440
          (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
          require(success);
          return abi.decode(returndata, (address));
        }
      
        /**
         * @dev Changes the admin of a proxy.
         * @param proxy Proxy to change admin.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeProxyAdmin(AdminUpgradeabilityProxy proxy, address newAdmin) public onlyOwner {
          proxy.changeAdmin(newAdmin);
        }
      
        /**
         * @dev Upgrades a proxy to the newest implementation of a contract.
         * @param proxy Proxy to be upgraded.
         * @param implementation the address of the Implementation.
         */
        function upgrade(AdminUpgradeabilityProxy proxy, address implementation) public onlyOwner {
          proxy.upgradeTo(implementation);
        }
      
        /**
         * @dev Upgrades a proxy to the newest implementation of a contract and forwards a function call to it.
         * This is useful to initialize the proxied contract.
         * @param proxy Proxy to be upgraded.
         * @param implementation Address of the Implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeAndCall(AdminUpgradeabilityProxy proxy, address implementation, bytes memory data) payable public onlyOwner {
          proxy.upgradeToAndCall.value(msg.value)(implementation, data);
        }
      }

      File 6 of 8: TokenState
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          TokenState.sol - SKALE Manager
          Copyright (C) 2019-Present SKALE Labs
          @author Dmytro Stebaiev
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity 0.8.17;
      import "@skalenetwork/skale-manager-interfaces/delegation/ITokenState.sol";
      import "@skalenetwork/skale-manager-interfaces/delegation/ILocker.sol";
      import "@skalenetwork/skale-manager-interfaces/delegation/IDelegationController.sol";
      import "../Permissions.sol";
      /**
       * @title Token State
       * @dev This contract manages lockers to control token transferability.
       *
       * The SKALE Network has three types of locked tokens:
       *
       * - Tokens that are transferrable but are currently locked into delegation with
       * a validator.
       *
       * - Tokens that are not transferable from one address to another, but may be
       * delegated to a validator `getAndUpdateLockedAmount`. This lock enforces
       * Proof-of-Use requirements.
       *
       * - Tokens that are neither transferable nor delegatable
       * `getAndUpdateForbiddenForDelegationAmount`. This lock enforces slashing.
       */
      contract TokenState is Permissions, ILocker, ITokenState {
          string[] private _lockers;
          IDelegationController private _delegationController;
          bytes32 public constant LOCKER_MANAGER_ROLE = keccak256("LOCKER_MANAGER_ROLE");
          modifier onlyLockerManager() {
              require(hasRole(LOCKER_MANAGER_ROLE, msg.sender), "LOCKER_MANAGER_ROLE is required");
              _;
          }
          /**
           *  @dev See {ILocker-getAndUpdateLockedAmount}.
           */
          function getAndUpdateLockedAmount(address holder) external override returns (uint) {
              if (address(_delegationController) == address(0)) {
                  _delegationController =
                      IDelegationController(contractManager.getContract("DelegationController"));
              }
              uint locked = 0;
              if (_delegationController.getDelegationsByHolderLength(holder) > 0) {
                  // the holder ever delegated
                  for (uint i = 0; i < _lockers.length; ++i) {
                      ILocker locker = ILocker(contractManager.getContract(_lockers[i]));
                      locked = locked + locker.getAndUpdateLockedAmount(holder);
                  }
              }
              return locked;
          }
          /**
           * @dev See {ILocker-getAndUpdateForbiddenForDelegationAmount}.
           */
          function getAndUpdateForbiddenForDelegationAmount(address holder) external override returns (uint amount) {
              uint forbidden = 0;
              for (uint i = 0; i < _lockers.length; ++i) {
                  ILocker locker = ILocker(contractManager.getContract(_lockers[i]));
                  forbidden = forbidden + locker.getAndUpdateForbiddenForDelegationAmount(holder);
              }
              return forbidden;
          }
          /**
           * @dev Allows the Owner to remove a contract from the locker.
           *
           * Emits a {LockerWasRemoved} event.
           */
          function removeLocker(string calldata locker) external override onlyLockerManager {
              uint index;
              bytes32 hash = keccak256(abi.encodePacked(locker));
              for (index = 0; index < _lockers.length; ++index) {
                  if (keccak256(abi.encodePacked(_lockers[index])) == hash) {
                      break;
                  }
              }
              if (index < _lockers.length) {
                  if (index < _lockers.length - 1) {
                      _lockers[index] = _lockers[_lockers.length - 1];
                  }
                  delete _lockers[_lockers.length - 1];
                  _lockers.pop();
                  emit LockerWasRemoved(locker);
              }
          }
          function initialize(address contractManagerAddress) public override initializer {
              Permissions.initialize(contractManagerAddress);
              _setupRole(LOCKER_MANAGER_ROLE, msg.sender);
              addLocker("DelegationController");
              addLocker("Punisher");
          }
          /**
           * @dev Allows the Owner to add a contract to the Locker.
           *
           * Emits a {LockerWasAdded} event.
           */
          function addLocker(string memory locker) public override onlyLockerManager {
              _lockers.push(locker);
              emit LockerWasAdded(locker);
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          ITokenState.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface ITokenState {
          /**
           * @dev Emitted when a contract is added to the locker.
           */
          event LockerWasAdded(
              string locker
          );
          /**
           * @dev Emitted when a contract is removed from the locker.
           */
          event LockerWasRemoved(
              string locker
          );
          
          function removeLocker(string calldata locker) external;
          function addLocker(string memory locker) external;
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          ILocker.sol - SKALE Manager
          Copyright (C) 2019-Present SKALE Labs
          @author Dmytro Stebaiev
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      /**
       * @dev Interface of the Locker functions.
       */
      interface ILocker {
          /**
           * @dev Returns and updates the total amount of locked tokens of a given 
           * `holder`.
           */
          function getAndUpdateLockedAmount(address wallet) external returns (uint);
          /**
           * @dev Returns and updates the total non-transferrable and un-delegatable
           * amount of a given `holder`.
           */
          function getAndUpdateForbiddenForDelegationAmount(address wallet) external returns (uint);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          IDelegationController.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IDelegationController {
          enum State {
              PROPOSED,
              ACCEPTED,
              CANCELED,
              REJECTED,
              DELEGATED,
              UNDELEGATION_REQUESTED,
              COMPLETED
          }
          struct Delegation {
              address holder; // address of token owner
              uint validatorId;
              uint amount;
              uint delegationPeriod;
              uint created; // time of delegation creation
              uint started; // month when a delegation becomes active
              uint finished; // first month after a delegation ends
              string info;
          }
          /**
           * @dev Emitted when validator was confiscated.
           */
          event Confiscated(
              uint indexed validatorId,
              uint amount
          );
          /**
           * @dev Emitted when validator was confiscated.
           */
          event SlashesProcessed(
              address indexed holder,
              uint limit
          );
          /**
           * @dev Emitted when a delegation is proposed to a validator.
           */
          event DelegationProposed(
              uint delegationId
          );
          /**
           * @dev Emitted when a delegation is accepted by a validator.
           */
          event DelegationAccepted(
              uint delegationId
          );
          /**
           * @dev Emitted when a delegation is cancelled by the delegator.
           */
          event DelegationRequestCanceledByUser(
              uint delegationId
          );
          /**
           * @dev Emitted when a delegation is requested to undelegate.
           */
          event UndelegationRequested(
              uint delegationId
          );
          
          function getAndUpdateDelegatedToValidatorNow(uint validatorId) external returns (uint);
          function getAndUpdateDelegatedAmount(address holder) external returns (uint);
          function getAndUpdateEffectiveDelegatedByHolderToValidator(address holder, uint validatorId, uint month)
              external
              returns (uint effectiveDelegated);
          function delegate(
              uint validatorId,
              uint amount,
              uint delegationPeriod,
              string calldata info
          )
              external;
          function cancelPendingDelegation(uint delegationId) external;
          function acceptPendingDelegation(uint delegationId) external;
          function requestUndelegation(uint delegationId) external;
          function confiscate(uint validatorId, uint amount) external;
          function getAndUpdateEffectiveDelegatedToValidator(uint validatorId, uint month) external returns (uint);
          function getAndUpdateDelegatedByHolderToValidatorNow(address holder, uint validatorId) external returns (uint);
          function processSlashes(address holder, uint limit) external;
          function processAllSlashes(address holder) external;
          function getEffectiveDelegatedValuesByValidator(uint validatorId) external view returns (uint[] memory);
          function getEffectiveDelegatedToValidator(uint validatorId, uint month) external view returns (uint);
          function getDelegatedToValidator(uint validatorId, uint month) external view returns (uint);
          function getDelegation(uint delegationId) external view returns (Delegation memory);
          function getFirstDelegationMonth(address holder, uint validatorId) external view returns(uint);
          function getDelegationsByValidatorLength(uint validatorId) external view returns (uint);
          function getDelegationsByHolderLength(address holder) external view returns (uint);
          function getState(uint delegationId) external view returns (State state);
          function getLockedInPendingDelegations(address holder) external view returns (uint);
          function hasUnprocessedSlashes(address holder) external view returns (bool);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          Permissions.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity 0.8.17;
      import "@skalenetwork/skale-manager-interfaces/IContractManager.sol";
      import "@skalenetwork/skale-manager-interfaces/IPermissions.sol";
      import "./thirdparty/openzeppelin/AccessControlUpgradeableLegacy.sol";
      /**
       * @title Permissions
       * @dev Contract is connected module for Upgradeable approach, knows ContractManager
       */
      contract Permissions is AccessControlUpgradeableLegacy, IPermissions {
          using AddressUpgradeable for address;
          IContractManager public contractManager;
          /**
           * @dev Modifier to make a function callable only when caller is the Owner.
           *
           * Requirements:
           *
           * - The caller must be the owner.
           */
          modifier onlyOwner() {
              require(_isOwner(), "Caller is not the owner");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when caller is an Admin.
           *
           * Requirements:
           *
           * - The caller must be an admin.
           */
          modifier onlyAdmin() {
              require(_isAdmin(msg.sender), "Caller is not an admin");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when caller is the Owner
           * or `contractName` contract.
           *
           * Requirements:
           *
           * - The caller must be the owner or `contractName`.
           */
          modifier allow(string memory contractName) {
              require(
                  contractManager.getContract(contractName) == msg.sender || _isOwner(),
                  "Message sender is invalid");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when caller is the Owner
           * or `contractName1` or `contractName2` contract.
           *
           * Requirements:
           *
           * - The caller must be the owner, `contractName1`, or `contractName2`.
           */
          modifier allowTwo(string memory contractName1, string memory contractName2) {
              require(
                  contractManager.getContract(contractName1) == msg.sender ||
                  contractManager.getContract(contractName2) == msg.sender ||
                  _isOwner(),
                  "Message sender is invalid");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when caller is the Owner
           * or `contractName1`, `contractName2`, or `contractName3` contract.
           *
           * Requirements:
           *
           * - The caller must be the owner, `contractName1`, `contractName2`, or
           * `contractName3`.
           */
          modifier allowThree(string memory contractName1, string memory contractName2, string memory contractName3) {
              require(
                  contractManager.getContract(contractName1) == msg.sender ||
                  contractManager.getContract(contractName2) == msg.sender ||
                  contractManager.getContract(contractName3) == msg.sender ||
                  _isOwner(),
                  "Message sender is invalid");
              _;
          }
          function initialize(address contractManagerAddress) public virtual override initializer {
              AccessControlUpgradeableLegacy.__AccessControl_init();
              _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
              _setContractManager(contractManagerAddress);
          }
          function _isOwner() internal view returns (bool) {
              return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
          }
          function _isAdmin(address account) internal view returns (bool) {
              address skaleManagerAddress = contractManager.contracts(keccak256(abi.encodePacked("SkaleManager")));
              if (skaleManagerAddress != address(0)) {
                  AccessControlUpgradeableLegacy skaleManager = AccessControlUpgradeableLegacy(skaleManagerAddress);
                  return skaleManager.hasRole(keccak256("ADMIN_ROLE"), account) || _isOwner();
              } else {
                  return _isOwner();
              }
          }
          function _setContractManager(address contractManagerAddress) private {
              require(contractManagerAddress != address(0), "ContractManager address is not set");
              require(contractManagerAddress.isContract(), "Address is not contract");
              contractManager = IContractManager(contractManagerAddress);
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          IContractManager.sol - SKALE Manager Interfaces
          Copyright (C) 2021-Present SKALE Labs
          @author Dmytro Stebaeiv
          SKALE Manager Interfaces is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager Interfaces 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager Interfaces.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IContractManager {
          /**
           * @dev Emitted when contract is upgraded.
           */
          event ContractUpgraded(string contractsName, address contractsAddress);
          function initialize() external;
          function setContractsAddress(string calldata contractsName, address newContractsAddress) external;
          function contracts(bytes32 nameHash) external view returns (address);
          function getDelegationPeriodManager() external view returns (address);
          function getBounty() external view returns (address);
          function getValidatorService() external view returns (address);
          function getTimeHelpers() external view returns (address);
          function getConstantsHolder() external view returns (address);
          function getSkaleToken() external view returns (address);
          function getTokenState() external view returns (address);
          function getPunisher() external view returns (address);
          function getContract(string calldata name) external view returns (address);
      }// SPDX-License-Identifier: AGPL-3.0-only
      /*
          IPermissions.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IPermissions {
          function initialize(address contractManagerAddress) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.7;
      import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
      import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
      import "@skalenetwork/skale-manager-interfaces/thirdparty/openzeppelin/IAccessControlUpgradeableLegacy.sol";
      import "./InitializableWithGap.sol";
      /**
       * @dev Contract module that allows children to implement role-based access
       * control mechanisms.
       *
       * Roles are referred to by their `bytes32` identifier. These should be exposed
       * in the external API and be unique. The best way to achieve this is by
       * using `public constant` hash digests:
       *
       * ```
       * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
       * ```
       *
       * Roles can be used to represent a set of permissions. To restrict access to a
       * function call, use {hasRole}:
       *
       * ```
       * function foo() public {
       *     require(hasRole(MY_ROLE, _msgSender()));
       *     ...
       * }
       * ```
       *
       * Roles can be granted and revoked dynamically via the {grantRole} and
       * {revokeRole} functions. Each role has an associated admin role, and only
       * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
       *
       * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
       * that only accounts with this role will be able to grant or revoke other
       * roles. More complex role relationships can be created by using
       * {_setRoleAdmin}.
       */
      abstract contract AccessControlUpgradeableLegacy is InitializableWithGap, ContextUpgradeable, IAccessControlUpgradeableLegacy {
          function __AccessControl_init() internal initializer {
              __Context_init_unchained();
              __AccessControl_init_unchained();
          }
          function __AccessControl_init_unchained() internal initializer {
          }
          using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
          struct RoleData {
              EnumerableSetUpgradeable.AddressSet members;
              bytes32 adminRole;
          }
          mapping (bytes32 => RoleData) private _roles;
          bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
          /**
           * @dev Returns `true` if `account` has been granted `role`.
           */
          function hasRole(bytes32 role, address account) public view override returns (bool) {
              return _roles[role].members.contains(account);
          }
          /**
           * @dev Returns the number of accounts that have `role`. Can be used
           * together with {getRoleMember} to enumerate all bearers of a role.
           */
          function getRoleMemberCount(bytes32 role) public view override returns (uint256) {
              return _roles[role].members.length();
          }
          /**
           * @dev Returns one of the accounts that have `role`. `index` must be a
           * value between 0 and {getRoleMemberCount}, non-inclusive.
           *
           * Role bearers are not sorted in any particular way, and their ordering may
           * change at any point.
           *
           * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
           * you perform all queries on the same block. See the following
           * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
           * for more information.
           */
          function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {
              return _roles[role].members.at(index);
          }
          /**
           * @dev Returns the admin role that controls `role`. See {grantRole} and
           * {revokeRole}.
           *
           * To change a role's admin, use {_setRoleAdmin}.
           */
          function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
              return _roles[role].adminRole;
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function grantRole(bytes32 role, address account) public virtual override {
              require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
              _grantRole(role, account);
          }
          /**
           * @dev Revokes `role` from `account`.
           *
           * If `account` had been granted `role`, emits a {RoleRevoked} event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function revokeRole(bytes32 role, address account) public virtual override {
              require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
              _revokeRole(role, account);
          }
          /**
           * @dev Revokes `role` from the calling account.
           *
           * Roles are often managed via {grantRole} and {revokeRole}: this function's
           * purpose is to provide a mechanism for accounts to lose their privileges
           * if they are compromised (such as when a trusted device is misplaced).
           *
           * If the calling account had been granted `role`, emits a {RoleRevoked}
           * event.
           *
           * Requirements:
           *
           * - the caller must be `account`.
           */
          function renounceRole(bytes32 role, address account) public virtual override {
              require(account == _msgSender(), "AccessControl: can only renounce roles for self");
              _revokeRole(role, account);
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event. Note that unlike {grantRole}, this function doesn't perform any
           * checks on the calling account.
           *
           * [WARNING]
           * ====
           * This function should only be called from the constructor when setting
           * up the initial roles for the system.
           *
           * Using this function in any other way is effectively circumventing the admin
           * system imposed by {AccessControl}.
           * ====
           */
          function _setupRole(bytes32 role, address account) internal virtual {
              _grantRole(role, account);
          }
          /**
           * @dev Sets `adminRole` as ``role``'s admin role.
           */
          function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
              _roles[role].adminRole = adminRole;
          }
          function _grantRole(bytes32 role, address account) private {
              if (_roles[role].members.add(account)) {
                  emit RoleGranted(role, account, _msgSender());
              }
          }
          function _revokeRole(bytes32 role, address account) private {
              if (_roles[role].members.remove(account)) {
                  emit RoleRevoked(role, account, _msgSender());
              }
          }
          uint256[49] private __gap;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Library for managing
       * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
       * types.
       *
       * Sets have the following properties:
       *
       * - Elements are added, removed, and checked for existence in constant time
       * (O(1)).
       * - Elements are enumerated in O(n). No guarantees are made on the ordering.
       *
       * ```
       * contract Example {
       *     // Add the library methods
       *     using EnumerableSet for EnumerableSet.AddressSet;
       *
       *     // Declare a set state variable
       *     EnumerableSet.AddressSet private mySet;
       * }
       * ```
       *
       * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
       * and `uint256` (`UintSet`) are supported.
       *
       * [WARNING]
       * ====
       *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
       *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
       *
       *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
       * ====
       */
      library EnumerableSetUpgradeable {
          // To implement this library for multiple types with as little code
          // repetition as possible, we write it in terms of a generic Set type with
          // bytes32 values.
          // The Set implementation uses private functions, and user-facing
          // implementations (such as AddressSet) are just wrappers around the
          // underlying Set.
          // This means that we can only create new EnumerableSets for types that fit
          // in bytes32.
          struct Set {
              // Storage of set values
              bytes32[] _values;
              // Position of the value in the `values` array, plus 1 because index 0
              // means a value is not in the set.
              mapping(bytes32 => uint256) _indexes;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function _add(Set storage set, bytes32 value) private returns (bool) {
              if (!_contains(set, value)) {
                  set._values.push(value);
                  // The value is stored at length-1, but we add 1 to all indexes
                  // and use 0 as a sentinel value
                  set._indexes[value] = set._values.length;
                  return true;
              } else {
                  return false;
              }
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function _remove(Set storage set, bytes32 value) private returns (bool) {
              // We read and store the value's index to prevent multiple reads from the same storage slot
              uint256 valueIndex = set._indexes[value];
              if (valueIndex != 0) {
                  // Equivalent to contains(set, value)
                  // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                  // the array, and then remove the last element (sometimes called as 'swap and pop').
                  // This modifies the order of the array, as noted in {at}.
                  uint256 toDeleteIndex = valueIndex - 1;
                  uint256 lastIndex = set._values.length - 1;
                  if (lastIndex != toDeleteIndex) {
                      bytes32 lastValue = set._values[lastIndex];
                      // Move the last value to the index where the value to delete is
                      set._values[toDeleteIndex] = lastValue;
                      // Update the index for the moved value
                      set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                  }
                  // Delete the slot where the moved value was stored
                  set._values.pop();
                  // Delete the index for the deleted slot
                  delete set._indexes[value];
                  return true;
              } else {
                  return false;
              }
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function _contains(Set storage set, bytes32 value) private view returns (bool) {
              return set._indexes[value] != 0;
          }
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function _length(Set storage set) private view returns (uint256) {
              return set._values.length;
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function _at(Set storage set, uint256 index) private view returns (bytes32) {
              return set._values[index];
          }
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function _values(Set storage set) private view returns (bytes32[] memory) {
              return set._values;
          }
          // Bytes32Set
          struct Bytes32Set {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _add(set._inner, value);
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _remove(set._inner, value);
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
              return _contains(set._inner, value);
          }
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(Bytes32Set storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
              return _at(set._inner, index);
          }
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
              return _values(set._inner);
          }
          // AddressSet
          struct AddressSet {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(AddressSet storage set, address value) internal returns (bool) {
              return _add(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(AddressSet storage set, address value) internal returns (bool) {
              return _remove(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(AddressSet storage set, address value) internal view returns (bool) {
              return _contains(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(AddressSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(AddressSet storage set, uint256 index) internal view returns (address) {
              return address(uint160(uint256(_at(set._inner, index))));
          }
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function values(AddressSet storage set) internal view returns (address[] memory) {
              bytes32[] memory store = _values(set._inner);
              address[] memory result;
              /// @solidity memory-safe-assembly
              assembly {
                  result := store
              }
              return result;
          }
          // UintSet
          struct UintSet {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(UintSet storage set, uint256 value) internal returns (bool) {
              return _add(set._inner, bytes32(value));
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(UintSet storage set, uint256 value) internal returns (bool) {
              return _remove(set._inner, bytes32(value));
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(UintSet storage set, uint256 value) internal view returns (bool) {
              return _contains(set._inner, bytes32(value));
          }
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function length(UintSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(UintSet storage set, uint256 index) internal view returns (uint256) {
              return uint256(_at(set._inner, index));
          }
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function values(UintSet storage set) internal view returns (uint256[] memory) {
              bytes32[] memory store = _values(set._inner);
              uint256[] memory result;
              /// @solidity memory-safe-assembly
              assembly {
                  result := store
              }
              return result;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
      pragma solidity ^0.8.0;
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract ContextUpgradeable is Initializable {
          function __Context_init() internal onlyInitializing {
          }
          function __Context_init_unchained() internal onlyInitializing {
          }
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[50] private __gap;
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          IAccessControlUpgradeableLegacy.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IAccessControlUpgradeableLegacy {
          /**
           * @dev Emitted when `account` is granted `role`.
           *
           * `sender` is the account that originated the contract call, an admin role
           * bearer except when using {_setupRole}.
           */
          event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Emitted when `account` is revoked `role`.
           *
           * `sender` is the account that originated the contract call:
           *   - if using `revokeRole`, it is the admin role bearer
           *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
           */
          event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
          
          function grantRole(bytes32 role, address account) external;
          function revokeRole(bytes32 role, address account) external;
          function renounceRole(bytes32 role, address account) external;
          function hasRole(bytes32 role, address account) external view returns (bool);
          function getRoleMemberCount(bytes32 role) external view returns (uint256);
          function getRoleMember(bytes32 role, uint256 index) external view returns (address);
          function getRoleAdmin(bytes32 role) external view returns (bytes32);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity ^0.8.7;
      import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
      contract InitializableWithGap is Initializable {
          uint256[50] private ______gap;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
      pragma solidity ^0.8.2;
      import "../../utils/AddressUpgradeable.sol";
      /**
       * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
       * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
       * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
       * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
       *
       * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
       * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
       * case an upgrade adds a module that needs to be initialized.
       *
       * For example:
       *
       * [.hljs-theme-light.nopadding]
       * ```
       * contract MyToken is ERC20Upgradeable {
       *     function initialize() initializer public {
       *         __ERC20_init("MyToken", "MTK");
       *     }
       * }
       * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
       *     function initializeV2() reinitializer(2) public {
       *         __ERC20Permit_init("MyToken");
       *     }
       * }
       * ```
       *
       * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
       * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
       *
       * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
       * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
       *
       * [CAUTION]
       * ====
       * Avoid leaving a contract uninitialized.
       *
       * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
       * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
       * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
       *
       * [.hljs-theme-light.nopadding]
       * ```
       * /// @custom:oz-upgrades-unsafe-allow constructor
       * constructor() {
       *     _disableInitializers();
       * }
       * ```
       * ====
       */
      abstract contract Initializable {
          /**
           * @dev Indicates that the contract has been initialized.
           * @custom:oz-retyped-from bool
           */
          uint8 private _initialized;
          /**
           * @dev Indicates that the contract is in the process of being initialized.
           */
          bool private _initializing;
          /**
           * @dev Triggered when the contract has been initialized or reinitialized.
           */
          event Initialized(uint8 version);
          /**
           * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
           * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
           */
          modifier initializer() {
              bool isTopLevelCall = !_initializing;
              require(
                  (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                  "Initializable: contract is already initialized"
              );
              _initialized = 1;
              if (isTopLevelCall) {
                  _initializing = true;
              }
              _;
              if (isTopLevelCall) {
                  _initializing = false;
                  emit Initialized(1);
              }
          }
          /**
           * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
           * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
           * used to initialize parent contracts.
           *
           * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
           * initialization step. This is essential to configure modules that are added through upgrades and that require
           * initialization.
           *
           * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
           * a contract, executing them in the right order is up to the developer or operator.
           */
          modifier reinitializer(uint8 version) {
              require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
              _initialized = version;
              _initializing = true;
              _;
              _initializing = false;
              emit Initialized(version);
          }
          /**
           * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
           * {initializer} and {reinitializer} modifiers, directly or indirectly.
           */
          modifier onlyInitializing() {
              require(_initializing, "Initializable: contract is not initializing");
              _;
          }
          /**
           * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
           * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
           * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
           * through proxies.
           */
          function _disableInitializers() internal virtual {
              require(!_initializing, "Initializable: contract is initializing");
              if (_initialized < type(uint8).max) {
                  _initialized = type(uint8).max;
                  emit Initialized(type(uint8).max);
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @dev Collection of functions related to the address type
       */
      library AddressUpgradeable {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      /// @solidity memory-safe-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      

      File 7 of 8: AdminUpgradeabilityProxy
      // File: ownership/Ownable.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @title Ownable
       * @dev The Ownable contract has an owner address, and provides basic authorization control
       * functions, this simplifies the implementation of "user permissions".
       *
       * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/ownership/Ownable.sol
       * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
       * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
       * build/artifacts folder) as well as the vanilla Ownable implementation from an openzeppelin version.
       */
      contract OpenZeppelinUpgradesOwnable {
          address private _owner;
      
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      
          /**
           * @dev The Ownable constructor sets the original `owner` of the contract to the sender
           * account.
           */
          constructor () internal {
              _owner = msg.sender;
              emit OwnershipTransferred(address(0), _owner);
          }
      
          /**
           * @return the address of the owner.
           */
          function owner() public view returns (address) {
              return _owner;
          }
      
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(isOwner());
              _;
          }
      
          /**
           * @return true if `msg.sender` is the owner of the contract.
           */
          function isOwner() public view returns (bool) {
              return msg.sender == _owner;
          }
      
          /**
           * @dev Allows the current owner to relinquish control of the contract.
           * @notice Renouncing to ownership will leave the contract without an owner.
           * It will not be possible to call the functions with the `onlyOwner`
           * modifier anymore.
           */
          function renounceOwnership() public onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = address(0);
          }
      
          /**
           * @dev Allows the current owner to transfer control of the contract to a newOwner.
           * @param newOwner The address to transfer ownership to.
           */
          function transferOwnership(address newOwner) public onlyOwner {
              _transferOwnership(newOwner);
          }
      
          /**
           * @dev Transfers control of the contract to a newOwner.
           * @param newOwner The address to transfer ownership to.
           */
          function _transferOwnership(address newOwner) internal {
              require(newOwner != address(0));
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
      
      // File: upgradeability/Proxy.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @title Proxy
       * @dev Implements delegation of calls to other contracts, with proper
       * forwarding of return values and bubbling of failures.
       * It defines a fallback function that delegates all calls to the address
       * returned by the abstract _implementation() internal function.
       */
      contract Proxy {
        /**
         * @dev Fallback function.
         * Implemented entirely in `_fallback`.
         */
        function () payable external {
          _fallback();
        }
      
        /**
         * @return The Address of the implementation.
         */
        function _implementation() internal view returns (address);
      
        /**
         * @dev Delegates execution to an implementation contract.
         * This is a low level function that doesn't return to its internal call site.
         * It will return to the external caller whatever the implementation returns.
         * @param implementation Address to delegate.
         */
        function _delegate(address implementation) internal {
          assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize)
      
            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
      
            // Copy the returned data.
            returndatacopy(0, 0, returndatasize)
      
            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize) }
            default { return(0, returndatasize) }
          }
        }
      
        /**
         * @dev Function that is run as the first thing in the fallback function.
         * Can be redefined in derived contracts to add functionality.
         * Redefinitions must call super._willFallback().
         */
        function _willFallback() internal {
        }
      
        /**
         * @dev fallback implementation.
         * Extracted to enable manual triggering.
         */
        function _fallback() internal {
          _willFallback();
          _delegate(_implementation());
        }
      }
      
      // File: utils/Address.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * Utility library of inline functions on addresses
       *
       * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/utils/Address.sol
       * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
       * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
       * build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version.
       */
      library OpenZeppelinUpgradesAddress {
          /**
           * Returns whether the target address is a contract
           * @dev This function will return false if invoked during the constructor of a contract,
           * as the code is not actually created until after the constructor finishes.
           * @param account address of the account to check
           * @return whether the target address is a contract
           */
          function isContract(address account) internal view returns (bool) {
              uint256 size;
              // XXX Currently there is no better way to check if there is a contract in an address
              // than to check the size of the code at that address.
              // See https://ethereum.stackexchange.com/a/14016/36603
              // for more details about how this works.
              // TODO Check this again before the Serenity release, because all addresses will be
              // contracts then.
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
      }
      
      // File: upgradeability/BaseUpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      
      /**
       * @title BaseUpgradeabilityProxy
       * @dev This contract implements a proxy that allows to change the
       * implementation address to which it will delegate.
       * Such a change is called an implementation upgrade.
       */
      contract BaseUpgradeabilityProxy is Proxy {
        /**
         * @dev Emitted when the implementation is upgraded.
         * @param implementation Address of the new implementation.
         */
        event Upgraded(address indexed implementation);
      
        /**
         * @dev Storage slot with the address of the current implementation.
         * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
         * validated in the constructor.
         */
        bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
      
        /**
         * @dev Returns the current implementation.
         * @return Address of the current implementation
         */
        function _implementation() internal view returns (address impl) {
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            impl := sload(slot)
          }
        }
      
        /**
         * @dev Upgrades the proxy to a new implementation.
         * @param newImplementation Address of the new implementation.
         */
        function _upgradeTo(address newImplementation) internal {
          _setImplementation(newImplementation);
          emit Upgraded(newImplementation);
        }
      
        /**
         * @dev Sets the implementation address of the proxy.
         * @param newImplementation Address of the new implementation.
         */
        function _setImplementation(address newImplementation) internal {
          require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
      
          bytes32 slot = IMPLEMENTATION_SLOT;
      
          assembly {
            sstore(slot, newImplementation)
          }
        }
      }
      
      // File: upgradeability/UpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title UpgradeabilityProxy
       * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing
       * implementation and init data.
       */
      contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
        /**
         * @dev Contract constructor.
         * @param _logic Address of the initial implementation.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, bytes memory _data) public payable {
          assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
          _setImplementation(_logic);
          if(_data.length > 0) {
            (bool success,) = _logic.delegatecall(_data);
            require(success);
          }
        }  
      }
      
      // File: upgradeability/BaseAdminUpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title BaseAdminUpgradeabilityProxy
       * @dev This contract combines an upgradeability proxy with an authorization
       * mechanism for administrative tasks.
       * All external functions in this contract must be guarded by the
       * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
       * feature proposal that would enable this to be done automatically.
       */
      contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
        /**
         * @dev Emitted when the administration has been transferred.
         * @param previousAdmin Address of the previous admin.
         * @param newAdmin Address of the new admin.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
      
        /**
         * @dev Storage slot with the admin of the contract.
         * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
         * validated in the constructor.
         */
      
        bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
      
        /**
         * @dev Modifier to check whether the `msg.sender` is the admin.
         * If it is, it will run the function. Otherwise, it will delegate the call
         * to the implementation.
         */
        modifier ifAdmin() {
          if (msg.sender == _admin()) {
            _;
          } else {
            _fallback();
          }
        }
      
        /**
         * @return The address of the proxy admin.
         */
        function admin() external ifAdmin returns (address) {
          return _admin();
        }
      
        /**
         * @return The address of the implementation.
         */
        function implementation() external ifAdmin returns (address) {
          return _implementation();
        }
      
        /**
         * @dev Changes the admin of the proxy.
         * Only the current admin can call this function.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeAdmin(address newAdmin) external ifAdmin {
          require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
          emit AdminChanged(_admin(), newAdmin);
          _setAdmin(newAdmin);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy.
         * Only the admin can call this function.
         * @param newImplementation Address of the new implementation.
         */
        function upgradeTo(address newImplementation) external ifAdmin {
          _upgradeTo(newImplementation);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy and call a function
         * on the new implementation.
         * This is useful to initialize the proxied contract.
         * @param newImplementation Address of the new implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
          _upgradeTo(newImplementation);
          (bool success,) = newImplementation.delegatecall(data);
          require(success);
        }
      
        /**
         * @return The admin slot.
         */
        function _admin() internal view returns (address adm) {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            adm := sload(slot)
          }
        }
      
        /**
         * @dev Sets the address of the proxy admin.
         * @param newAdmin Address of the new proxy admin.
         */
        function _setAdmin(address newAdmin) internal {
          bytes32 slot = ADMIN_SLOT;
      
          assembly {
            sstore(slot, newAdmin)
          }
        }
      
        /**
         * @dev Only fall back when the sender is not the admin.
         */
        function _willFallback() internal {
          require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
          super._willFallback();
        }
      }
      
      // File: upgradeability/AdminUpgradeabilityProxy.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title AdminUpgradeabilityProxy
       * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for 
       * initializing the implementation, admin, and init data.
       */
      contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy {
        /**
         * Contract constructor.
         * @param _logic address of the initial implementation.
         * @param _admin Address of the proxy administrator.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable {
          assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
          _setAdmin(_admin);
        }
      }
      
      // File: upgradeability/ProxyAdmin.sol
      
      pragma solidity ^0.5.0;
      
      
      
      /**
       * @title ProxyAdmin
       * @dev This contract is the admin of a proxy, and is in charge
       * of upgrading it as well as transferring it to another admin.
       */
      contract ProxyAdmin is OpenZeppelinUpgradesOwnable {
      
        /**
         * @dev Returns the current implementation of a proxy.
         * This is needed because only the proxy admin can query it.
         * @return The address of the current implementation of the proxy.
         */
        function getProxyImplementation(AdminUpgradeabilityProxy proxy) public view returns (address) {
          // We need to manually run the static call since the getter cannot be flagged as view
          // bytes4(keccak256("implementation()")) == 0x5c60da1b
          (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
          require(success);
          return abi.decode(returndata, (address));
        }
      
        /**
         * @dev Returns the admin of a proxy. Only the admin can query it.
         * @return The address of the current admin of the proxy.
         */
        function getProxyAdmin(AdminUpgradeabilityProxy proxy) public view returns (address) {
          // We need to manually run the static call since the getter cannot be flagged as view
          // bytes4(keccak256("admin()")) == 0xf851a440
          (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
          require(success);
          return abi.decode(returndata, (address));
        }
      
        /**
         * @dev Changes the admin of a proxy.
         * @param proxy Proxy to change admin.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeProxyAdmin(AdminUpgradeabilityProxy proxy, address newAdmin) public onlyOwner {
          proxy.changeAdmin(newAdmin);
        }
      
        /**
         * @dev Upgrades a proxy to the newest implementation of a contract.
         * @param proxy Proxy to be upgraded.
         * @param implementation the address of the Implementation.
         */
        function upgrade(AdminUpgradeabilityProxy proxy, address implementation) public onlyOwner {
          proxy.upgradeTo(implementation);
        }
      
        /**
         * @dev Upgrades a proxy to the newest implementation of a contract and forwards a function call to it.
         * This is useful to initialize the proxied contract.
         * @param proxy Proxy to be upgraded.
         * @param implementation Address of the Implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeAndCall(AdminUpgradeabilityProxy proxy, address implementation, bytes memory data) payable public onlyOwner {
          proxy.upgradeToAndCall.value(msg.value)(implementation, data);
        }
      }

      File 8 of 8: DelegationController
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
      pragma solidity ^0.8.2;
      import "../../utils/AddressUpgradeable.sol";
      /**
       * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
       * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
       * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
       * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
       *
       * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
       * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
       * case an upgrade adds a module that needs to be initialized.
       *
       * For example:
       *
       * [.hljs-theme-light.nopadding]
       * ```solidity
       * contract MyToken is ERC20Upgradeable {
       *     function initialize() initializer public {
       *         __ERC20_init("MyToken", "MTK");
       *     }
       * }
       *
       * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
       *     function initializeV2() reinitializer(2) public {
       *         __ERC20Permit_init("MyToken");
       *     }
       * }
       * ```
       *
       * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
       * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
       *
       * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
       * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
       *
       * [CAUTION]
       * ====
       * Avoid leaving a contract uninitialized.
       *
       * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
       * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
       * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
       *
       * [.hljs-theme-light.nopadding]
       * ```
       * /// @custom:oz-upgrades-unsafe-allow constructor
       * constructor() {
       *     _disableInitializers();
       * }
       * ```
       * ====
       */
      abstract contract Initializable {
          /**
           * @dev Indicates that the contract has been initialized.
           * @custom:oz-retyped-from bool
           */
          uint8 private _initialized;
          /**
           * @dev Indicates that the contract is in the process of being initialized.
           */
          bool private _initializing;
          /**
           * @dev Triggered when the contract has been initialized or reinitialized.
           */
          event Initialized(uint8 version);
          /**
           * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
           * `onlyInitializing` functions can be used to initialize parent contracts.
           *
           * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
           * constructor.
           *
           * Emits an {Initialized} event.
           */
          modifier initializer() {
              bool isTopLevelCall = !_initializing;
              require(
                  (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                  "Initializable: contract is already initialized"
              );
              _initialized = 1;
              if (isTopLevelCall) {
                  _initializing = true;
              }
              _;
              if (isTopLevelCall) {
                  _initializing = false;
                  emit Initialized(1);
              }
          }
          /**
           * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
           * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
           * used to initialize parent contracts.
           *
           * A reinitializer may be used after the original initialization step. This is essential to configure modules that
           * are added through upgrades and that require initialization.
           *
           * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
           * cannot be nested. If one is invoked in the context of another, execution will revert.
           *
           * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
           * a contract, executing them in the right order is up to the developer or operator.
           *
           * WARNING: setting the version to 255 will prevent any future reinitialization.
           *
           * Emits an {Initialized} event.
           */
          modifier reinitializer(uint8 version) {
              require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
              _initialized = version;
              _initializing = true;
              _;
              _initializing = false;
              emit Initialized(version);
          }
          /**
           * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
           * {initializer} and {reinitializer} modifiers, directly or indirectly.
           */
          modifier onlyInitializing() {
              require(_initializing, "Initializable: contract is not initializing");
              _;
          }
          /**
           * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
           * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
           * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
           * through proxies.
           *
           * Emits an {Initialized} event the first time it is successfully executed.
           */
          function _disableInitializers() internal virtual {
              require(!_initializing, "Initializable: contract is initializing");
              if (_initialized != type(uint8).max) {
                  _initialized = type(uint8).max;
                  emit Initialized(type(uint8).max);
              }
          }
          /**
           * @dev Returns the highest version that has been initialized. See {reinitializer}.
           */
          function _getInitializedVersion() internal view returns (uint8) {
              return _initialized;
          }
          /**
           * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
           */
          function _isInitializing() internal view returns (bool) {
              return _initializing;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @dev Collection of functions related to the address type
       */
      library AddressUpgradeable {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           *
           * Furthermore, `isContract` will also return true if the target contract within
           * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
           * which only has an effect at the end of a transaction.
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
           * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
           *
           * _Available since v4.8._
           */
          function verifyCallResultFromTarget(
              address target,
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              if (success) {
                  if (returndata.length == 0) {
                      // only check isContract if the call was successful and the return data is empty
                      // otherwise we already know that it was a contract
                      require(isContract(target), "Address: call to non-contract");
                  }
                  return returndata;
              } else {
                  _revert(returndata, errorMessage);
              }
          }
          /**
           * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason or using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  _revert(returndata, errorMessage);
              }
          }
          function _revert(bytes memory returndata, string memory errorMessage) private pure {
              // Look for revert reason and bubble it up if present
              if (returndata.length > 0) {
                  // The easiest way to bubble the revert reason is using memory via assembly
                  /// @solidity memory-safe-assembly
                  assembly {
                      let returndata_size := mload(returndata)
                      revert(add(32, returndata), returndata_size)
                  }
              } else {
                  revert(errorMessage);
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
      pragma solidity ^0.8.0;
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract ContextUpgradeable is Initializable {
          function __Context_init() internal onlyInitializing {
          }
          function __Context_init_unchained() internal onlyInitializing {
          }
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[50] private __gap;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
      // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
      pragma solidity ^0.8.0;
      /**
       * @dev Library for managing
       * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
       * types.
       *
       * Sets have the following properties:
       *
       * - Elements are added, removed, and checked for existence in constant time
       * (O(1)).
       * - Elements are enumerated in O(n). No guarantees are made on the ordering.
       *
       * ```solidity
       * contract Example {
       *     // Add the library methods
       *     using EnumerableSet for EnumerableSet.AddressSet;
       *
       *     // Declare a set state variable
       *     EnumerableSet.AddressSet private mySet;
       * }
       * ```
       *
       * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
       * and `uint256` (`UintSet`) are supported.
       *
       * [WARNING]
       * ====
       * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
       * unusable.
       * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
       *
       * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
       * array of EnumerableSet.
       * ====
       */
      library EnumerableSetUpgradeable {
          // To implement this library for multiple types with as little code
          // repetition as possible, we write it in terms of a generic Set type with
          // bytes32 values.
          // The Set implementation uses private functions, and user-facing
          // implementations (such as AddressSet) are just wrappers around the
          // underlying Set.
          // This means that we can only create new EnumerableSets for types that fit
          // in bytes32.
          struct Set {
              // Storage of set values
              bytes32[] _values;
              // Position of the value in the `values` array, plus 1 because index 0
              // means a value is not in the set.
              mapping(bytes32 => uint256) _indexes;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function _add(Set storage set, bytes32 value) private returns (bool) {
              if (!_contains(set, value)) {
                  set._values.push(value);
                  // The value is stored at length-1, but we add 1 to all indexes
                  // and use 0 as a sentinel value
                  set._indexes[value] = set._values.length;
                  return true;
              } else {
                  return false;
              }
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function _remove(Set storage set, bytes32 value) private returns (bool) {
              // We read and store the value's index to prevent multiple reads from the same storage slot
              uint256 valueIndex = set._indexes[value];
              if (valueIndex != 0) {
                  // Equivalent to contains(set, value)
                  // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                  // the array, and then remove the last element (sometimes called as 'swap and pop').
                  // This modifies the order of the array, as noted in {at}.
                  uint256 toDeleteIndex = valueIndex - 1;
                  uint256 lastIndex = set._values.length - 1;
                  if (lastIndex != toDeleteIndex) {
                      bytes32 lastValue = set._values[lastIndex];
                      // Move the last value to the index where the value to delete is
                      set._values[toDeleteIndex] = lastValue;
                      // Update the index for the moved value
                      set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                  }
                  // Delete the slot where the moved value was stored
                  set._values.pop();
                  // Delete the index for the deleted slot
                  delete set._indexes[value];
                  return true;
              } else {
                  return false;
              }
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function _contains(Set storage set, bytes32 value) private view returns (bool) {
              return set._indexes[value] != 0;
          }
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function _length(Set storage set) private view returns (uint256) {
              return set._values.length;
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function _at(Set storage set, uint256 index) private view returns (bytes32) {
              return set._values[index];
          }
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function _values(Set storage set) private view returns (bytes32[] memory) {
              return set._values;
          }
          // Bytes32Set
          struct Bytes32Set {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _add(set._inner, value);
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _remove(set._inner, value);
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
              return _contains(set._inner, value);
          }
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(Bytes32Set storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
              return _at(set._inner, index);
          }
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
              bytes32[] memory store = _values(set._inner);
              bytes32[] memory result;
              /// @solidity memory-safe-assembly
              assembly {
                  result := store
              }
              return result;
          }
          // AddressSet
          struct AddressSet {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(AddressSet storage set, address value) internal returns (bool) {
              return _add(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(AddressSet storage set, address value) internal returns (bool) {
              return _remove(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(AddressSet storage set, address value) internal view returns (bool) {
              return _contains(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(AddressSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(AddressSet storage set, uint256 index) internal view returns (address) {
              return address(uint160(uint256(_at(set._inner, index))));
          }
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function values(AddressSet storage set) internal view returns (address[] memory) {
              bytes32[] memory store = _values(set._inner);
              address[] memory result;
              /// @solidity memory-safe-assembly
              assembly {
                  result := store
              }
              return result;
          }
          // UintSet
          struct UintSet {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(UintSet storage set, uint256 value) internal returns (bool) {
              return _add(set._inner, bytes32(value));
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(UintSet storage set, uint256 value) internal returns (bool) {
              return _remove(set._inner, bytes32(value));
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(UintSet storage set, uint256 value) internal view returns (bool) {
              return _contains(set._inner, bytes32(value));
          }
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(UintSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(UintSet storage set, uint256 index) internal view returns (uint256) {
              return uint256(_at(set._inner, index));
          }
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function values(UintSet storage set) internal view returns (uint256[] memory) {
              bytes32[] memory store = _values(set._inner);
              uint256[] memory result;
              /// @solidity memory-safe-assembly
              assembly {
                  result := store
              }
              return result;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC777/IERC777.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC777Token standard as defined in the EIP.
       *
       * This contract uses the
       * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let
       * token holders and recipients react to token movements by using setting implementers
       * for the associated interfaces in said registry. See {IERC1820Registry} and
       * {ERC1820Implementer}.
       */
      interface IERC777 {
          /**
           * @dev Emitted when `amount` tokens are created by `operator` and assigned to `to`.
           *
           * Note that some additional user `data` and `operatorData` can be logged in the event.
           */
          event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
          /**
           * @dev Emitted when `operator` destroys `amount` tokens from `account`.
           *
           * Note that some additional user `data` and `operatorData` can be logged in the event.
           */
          event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
          /**
           * @dev Emitted when `operator` is made operator for `tokenHolder`.
           */
          event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
          /**
           * @dev Emitted when `operator` is revoked its operator status for `tokenHolder`.
           */
          event RevokedOperator(address indexed operator, address indexed tokenHolder);
          /**
           * @dev Returns the name of the token.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the symbol of the token, usually a shorter version of the
           * name.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the smallest part of the token that is not divisible. This
           * means all token operations (creation, movement and destruction) must have
           * amounts that are a multiple of this number.
           *
           * For most token contracts, this value will equal 1.
           */
          function granularity() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by an account (`owner`).
           */
          function balanceOf(address owner) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * If send or receive hooks are registered for the caller and `recipient`,
           * the corresponding functions will be called with `data` and empty
           * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
           *
           * Emits a {Sent} event.
           *
           * Requirements
           *
           * - the caller must have at least `amount` tokens.
           * - `recipient` cannot be the zero address.
           * - if `recipient` is a contract, it must implement the {IERC777Recipient}
           * interface.
           */
          function send(address recipient, uint256 amount, bytes calldata data) external;
          /**
           * @dev Destroys `amount` tokens from the caller's account, reducing the
           * total supply.
           *
           * If a send hook is registered for the caller, the corresponding function
           * will be called with `data` and empty `operatorData`. See {IERC777Sender}.
           *
           * Emits a {Burned} event.
           *
           * Requirements
           *
           * - the caller must have at least `amount` tokens.
           */
          function burn(uint256 amount, bytes calldata data) external;
          /**
           * @dev Returns true if an account is an operator of `tokenHolder`.
           * Operators can send and burn tokens on behalf of their owners. All
           * accounts are their own operator.
           *
           * See {operatorSend} and {operatorBurn}.
           */
          function isOperatorFor(address operator, address tokenHolder) external view returns (bool);
          /**
           * @dev Make an account an operator of the caller.
           *
           * See {isOperatorFor}.
           *
           * Emits an {AuthorizedOperator} event.
           *
           * Requirements
           *
           * - `operator` cannot be calling address.
           */
          function authorizeOperator(address operator) external;
          /**
           * @dev Revoke an account's operator status for the caller.
           *
           * See {isOperatorFor} and {defaultOperators}.
           *
           * Emits a {RevokedOperator} event.
           *
           * Requirements
           *
           * - `operator` cannot be calling address.
           */
          function revokeOperator(address operator) external;
          /**
           * @dev Returns the list of default operators. These accounts are operators
           * for all token holders, even if {authorizeOperator} was never called on
           * them.
           *
           * This list is immutable, but individual holders may revoke these via
           * {revokeOperator}, in which case {isOperatorFor} will return false.
           */
          function defaultOperators() external view returns (address[] memory);
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must
           * be an operator of `sender`.
           *
           * If send or receive hooks are registered for `sender` and `recipient`,
           * the corresponding functions will be called with `data` and
           * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
           *
           * Emits a {Sent} event.
           *
           * Requirements
           *
           * - `sender` cannot be the zero address.
           * - `sender` must have at least `amount` tokens.
           * - the caller must be an operator for `sender`.
           * - `recipient` cannot be the zero address.
           * - if `recipient` is a contract, it must implement the {IERC777Recipient}
           * interface.
           */
          function operatorSend(
              address sender,
              address recipient,
              uint256 amount,
              bytes calldata data,
              bytes calldata operatorData
          ) external;
          /**
           * @dev Destroys `amount` tokens from `account`, reducing the total supply.
           * The caller must be an operator of `account`.
           *
           * If a send hook is registered for `account`, the corresponding function
           * will be called with `data` and `operatorData`. See {IERC777Sender}.
           *
           * Emits a {Burned} event.
           *
           * Requirements
           *
           * - `account` cannot be the zero address.
           * - `account` must have at least `amount` tokens.
           * - the caller must be an operator for `account`.
           */
          function operatorBurn(address account, uint256 amount, bytes calldata data, bytes calldata operatorData) external;
          event Sent(
              address indexed operator,
              address indexed from,
              address indexed to,
              uint256 amount,
              bytes data,
              bytes operatorData
          );
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          IDelegationController.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IDelegationController {
          enum State {
              PROPOSED,
              ACCEPTED,
              CANCELED,
              REJECTED,
              DELEGATED,
              UNDELEGATION_REQUESTED,
              COMPLETED
          }
          struct Delegation {
              address holder; // address of token owner
              uint validatorId;
              uint amount;
              uint delegationPeriod;
              uint created; // time of delegation creation
              uint started; // month when a delegation becomes active
              uint finished; // first month after a delegation ends
              string info;
          }
          /**
           * @dev Emitted when validator was confiscated.
           */
          event Confiscated(
              uint indexed validatorId,
              uint amount
          );
          /**
           * @dev Emitted when validator was confiscated.
           */
          event SlashesProcessed(
              address indexed holder,
              uint limit
          );
          /**
           * @dev Emitted when a delegation is proposed to a validator.
           */
          event DelegationProposed(
              uint delegationId
          );
          /**
           * @dev Emitted when a delegation is accepted by a validator.
           */
          event DelegationAccepted(
              uint delegationId
          );
          /**
           * @dev Emitted when a delegation is cancelled by the delegator.
           */
          event DelegationRequestCanceledByUser(
              uint delegationId
          );
          /**
           * @dev Emitted when a delegation is requested to undelegate.
           */
          event UndelegationRequested(
              uint delegationId
          );
          
          function getAndUpdateDelegatedToValidatorNow(uint validatorId) external returns (uint);
          function getAndUpdateDelegatedAmount(address holder) external returns (uint);
          function getAndUpdateEffectiveDelegatedByHolderToValidator(address holder, uint validatorId, uint month)
              external
              returns (uint effectiveDelegated);
          function delegate(
              uint validatorId,
              uint amount,
              uint delegationPeriod,
              string calldata info
          )
              external;
          function cancelPendingDelegation(uint delegationId) external;
          function acceptPendingDelegation(uint delegationId) external;
          function requestUndelegation(uint delegationId) external;
          function confiscate(uint validatorId, uint amount) external;
          function getAndUpdateEffectiveDelegatedToValidator(uint validatorId, uint month) external returns (uint);
          function getAndUpdateDelegatedByHolderToValidatorNow(address holder, uint validatorId) external returns (uint);
          function processSlashes(address holder, uint limit) external;
          function processAllSlashes(address holder) external;
          function getEffectiveDelegatedValuesByValidator(uint validatorId) external view returns (uint[] memory);
          function getEffectiveDelegatedToValidator(uint validatorId, uint month) external view returns (uint);
          function getDelegatedToValidator(uint validatorId, uint month) external view returns (uint);
          function getDelegation(uint delegationId) external view returns (Delegation memory);
          function getFirstDelegationMonth(address holder, uint validatorId) external view returns(uint);
          function getDelegationsByValidatorLength(uint validatorId) external view returns (uint);
          function getDelegationsByHolderLength(address holder) external view returns (uint);
          function getState(uint delegationId) external view returns (State state);
          function getLockedInPendingDelegations(address holder) external view returns (uint);
          function hasUnprocessedSlashes(address holder) external view returns (bool);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          IDelegationPeriodManager.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IDelegationPeriodManager {
          /**
           * @dev Emitted when a new delegation period is specified.
           */
          event DelegationPeriodWasSet(
              uint length,
              uint stakeMultiplier
          );
          
          function setDelegationPeriod(uint monthsCount, uint stakeMultiplier) external;
          function stakeMultipliers(uint monthsCount) external view returns (uint);
          function isDelegationPeriodAllowed(uint monthsCount) external view returns (bool);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          ILocker.sol - SKALE Manager
          Copyright (C) 2019-Present SKALE Labs
          @author Dmytro Stebaiev
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      /**
       * @dev Interface of the Locker functions.
       */
      interface ILocker {
          /**
           * @dev Returns and updates the total amount of locked tokens of a given 
           * `holder`.
           */
          function getAndUpdateLockedAmount(address wallet) external returns (uint);
          /**
           * @dev Returns and updates the total non-transferrable and un-delegatable
           * amount of a given `holder`.
           */
          function getAndUpdateForbiddenForDelegationAmount(address wallet) external returns (uint);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          IPunisher.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IPunisher {
          /**
           * @dev Emitted upon slashing condition.
           */
          event Slash(
              uint validatorId,
              uint amount
          );
          /**
           * @dev Emitted upon forgive condition.
           */
          event Forgive(
              address wallet,
              uint amount
          );
          
          function slash(uint validatorId, uint amount) external;
          function forgive(address holder, uint amount) external;
          function handleSlash(address holder, uint amount) external;
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          ITimeHelpers.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface ITimeHelpers {
          function calculateProofOfUseLockEndTime(uint month, uint lockUpPeriodDays) external view returns (uint timestamp);
          function getCurrentMonth() external view returns (uint);
          function timestampToYear(uint timestamp) external view returns (uint);
          function timestampToMonth(uint timestamp) external view returns (uint);
          function monthToTimestamp(uint month) external view returns (uint timestamp);
          function addDays(uint fromTimestamp, uint n) external pure returns (uint);
          function addMonths(uint fromTimestamp, uint n) external pure returns (uint);
          function addYears(uint fromTimestamp, uint n) external pure returns (uint);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          IValidatorService.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IValidatorService {
          struct Validator {
              string name;
              address validatorAddress;
              address requestedAddress;
              string description;
              uint feeRate;
              uint registrationTime;
              uint minimumDelegationAmount;
              bool acceptNewRequests;
          }
          
          /**
           * @dev Emitted when a validator registers.
           */
          event ValidatorRegistered(
              uint validatorId
          );
          /**
           * @dev Emitted when a validator address changes.
           */
          event ValidatorAddressChanged(
              uint validatorId,
              address newAddress
          );
          /**
           * @dev Emitted when a validator is enabled.
           */
          event ValidatorWasEnabled(
              uint validatorId
          );
          /**
           * @dev Emitted when a validator is disabled.
           */
          event ValidatorWasDisabled(
              uint validatorId
          );
          /**
           * @dev Emitted when a node address is linked to a validator.
           */
          event NodeAddressWasAdded(
              uint validatorId,
              address nodeAddress
          );
          /**
           * @dev Emitted when a node address is unlinked from a validator.
           */
          event NodeAddressWasRemoved(
              uint validatorId,
              address nodeAddress
          );
          /**
           * @dev Emitted when whitelist disabled.
           */
          event WhitelistDisabled(bool status);
          /**
           * @dev Emitted when validator requested new address.
           */
          event RequestNewAddress(uint indexed validatorId, address previousAddress, address newAddress);
          /**
           * @dev Emitted when validator set new minimum delegation amount.
           */
          event SetMinimumDelegationAmount(uint indexed validatorId, uint previousMDA, uint newMDA);
          /**
           * @dev Emitted when validator set new name.
           */
          event SetValidatorName(uint indexed validatorId, string previousName, string newName);
          /**
           * @dev Emitted when validator set new description.
           */
          event SetValidatorDescription(uint indexed validatorId, string previousDescription, string newDescription);
          /**
           * @dev Emitted when validator start or stop accepting new delegation requests.
           */
          event AcceptingNewRequests(uint indexed validatorId, bool status);
          
          function registerValidator(
              string calldata name,
              string calldata description,
              uint feeRate,
              uint minimumDelegationAmount
          )
              external
              returns (uint validatorId);
          function enableValidator(uint validatorId) external;
          function disableValidator(uint validatorId) external;
          function disableWhitelist() external;
          function requestForNewAddress(address newValidatorAddress) external;
          function confirmNewAddress(uint validatorId) external;
          function linkNodeAddress(address nodeAddress, bytes calldata sig) external;
          function unlinkNodeAddress(address nodeAddress) external;
          function setValidatorMDA(uint minimumDelegationAmount) external;
          function setValidatorName(string calldata newName) external;
          function setValidatorDescription(string calldata newDescription) external;
          function startAcceptingNewRequests() external;
          function stopAcceptingNewRequests() external;
          function removeNodeAddress(uint validatorId, address nodeAddress) external;
          function getAndUpdateBondAmount(uint validatorId) external returns (uint);
          function getMyNodesAddresses() external view returns (address[] memory);
          function getTrustedValidators() external view returns (uint[] memory);
          function checkValidatorAddressToId(address validatorAddress, uint validatorId)
              external
              view
              returns (bool);
          function getValidatorIdByNodeAddress(address nodeAddress) external view returns (uint validatorId);
          function checkValidatorCanReceiveDelegation(uint validatorId, uint amount) external view;
          function getNodeAddresses(uint validatorId) external view returns (address[] memory);
          function validatorExists(uint validatorId) external view returns (bool);
          function validatorAddressExists(address validatorAddress) external view returns (bool);
          function checkIfValidatorAddressExists(address validatorAddress) external view;
          function getValidator(uint validatorId) external view returns (Validator memory);
          function getValidatorId(address validatorAddress) external view returns (uint);
          function isAcceptingNewRequests(uint validatorId) external view returns (bool);
          function isAuthorizedValidator(uint validatorId) external view returns (bool);
          function getValidatorIdByNodeAddressWithoutRevert(address nodeAddress) external view returns (uint validatorId);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          IBountyV2.sol - SKALE Manager Interfaces
          Copyright (C) 2021-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager Interfaces is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager Interfaces 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager Interfaces.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IBountyV2 {
          /**
           * @dev Emitted when bounty reduction is turned on or turned off.
           */
          event BountyReduction(bool status);
          /**
           * @dev Emitted when a node creation window was changed.
           */
          event NodeCreationWindowWasChanged(
              uint oldValue,
              uint newValue
          );
          function calculateBounty(uint nodeIndex) external returns (uint);
          function enableBountyReduction() external;
          function disableBountyReduction() external;
          function setNodeCreationWindowSeconds(uint window) external;
          function handleDelegationAdd(uint amount, uint month) external;
          function handleDelegationRemoving(uint amount, uint month) external;
          function estimateBounty(uint nodeIndex) external view returns (uint);
          function getNextRewardTimestamp(uint nodeIndex) external view returns (uint);
          function getEffectiveDelegatedSum() external view returns (uint[] memory);
      }// SPDX-License-Identifier: AGPL-3.0-only
      /*
          IConstantsHolder.sol - SKALE Manager Interfaces
          Copyright (C) 2021-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager Interfaces is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager Interfaces 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager Interfaces.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IConstantsHolder {
          /**
           * @dev Emitted when constants updated.
           */
          event ConstantUpdated(
              bytes32 indexed constantHash,
              uint previousValue,
              uint newValue
          );
          function setPeriods(uint32 newRewardPeriod, uint32 newDeltaPeriod) external;
          function setCheckTime(uint newCheckTime) external;
          function setLatency(uint32 newAllowableLatency) external;
          function setMSR(uint newMSR) external;
          function setLaunchTimestamp(uint timestamp) external;
          function setRotationDelay(uint newDelay) external;
          function setProofOfUseLockUpPeriod(uint periodDays) external;
          function setProofOfUseDelegationPercentage(uint percentage) external;
          function setLimitValidatorsPerDelegator(uint newLimit) external;
          function setSchainCreationTimeStamp(uint timestamp) external;
          function setMinimalSchainLifetime(uint lifetime) external;
          function setComplaintTimeLimit(uint timeLimit) external;
          function setMinNodeBalance(uint newMinNodeBalance) external;
          function reinitialize() external;
          function msr() external view returns (uint);
          function launchTimestamp() external view returns (uint);
          function rotationDelay() external view returns (uint);
          function limitValidatorsPerDelegator() external view returns (uint);
          function schainCreationTimeStamp() external view returns (uint);
          function minimalSchainLifetime() external view returns (uint);
          function complaintTimeLimit() external view returns (uint);
          function minNodeBalance() external view returns (uint);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          IContractManager.sol - SKALE Manager Interfaces
          Copyright (C) 2021-Present SKALE Labs
          @author Dmytro Stebaeiv
          SKALE Manager Interfaces is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager Interfaces 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager Interfaces.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IContractManager {
          /**
           * @dev Emitted when contract is upgraded.
           */
          event ContractUpgraded(string contractsName, address contractsAddress);
          function initialize() external;
          function setContractsAddress(string calldata contractsName, address newContractsAddress) external;
          function contracts(bytes32 nameHash) external view returns (address);
          function getDelegationPeriodManager() external view returns (address);
          function getBounty() external view returns (address);
          function getValidatorService() external view returns (address);
          function getTimeHelpers() external view returns (address);
          function getConstantsHolder() external view returns (address);
          function getSkaleToken() external view returns (address);
          function getTokenState() external view returns (address);
          function getPunisher() external view returns (address);
          function getContract(string calldata name) external view returns (address);
      }// SPDX-License-Identifier: AGPL-3.0-only
      /*
          IPermissions.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IPermissions {
          function initialize(address contractManagerAddress) external;
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          IAccessControlUpgradeableLegacy.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity >=0.6.10 <0.9.0;
      interface IAccessControlUpgradeableLegacy {
          /**
           * @dev Emitted when `account` is granted `role`.
           *
           * `sender` is the account that originated the contract call, an admin role
           * bearer except when using {_setupRole}.
           */
          event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Emitted when `account` is revoked `role`.
           *
           * `sender` is the account that originated the contract call:
           *   - if using `revokeRole`, it is the admin role bearer
           *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
           */
          event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
          
          function grantRole(bytes32 role, address account) external;
          function revokeRole(bytes32 role, address account) external;
          function renounceRole(bytes32 role, address account) external;
          function hasRole(bytes32 role, address account) external view returns (bool);
          function getRoleMemberCount(bytes32 role) external view returns (uint256);
          function getRoleMember(bytes32 role, uint256 index) external view returns (address);
          function getRoleAdmin(bytes32 role) external view returns (bytes32);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          DelegationController.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Dmytro Stebaiev
          @author Vadim Yavorsky
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity 0.8.17;
      import { IERC777 } from "@openzeppelin/contracts/token/ERC777/IERC777.sol";
      import { IDelegationController } from "@skalenetwork/skale-manager-interfaces/delegation/IDelegationController.sol";
      import { IDelegationPeriodManager }
      from "@skalenetwork/skale-manager-interfaces/delegation/IDelegationPeriodManager.sol";
      import { IPunisher } from "@skalenetwork/skale-manager-interfaces/delegation/IPunisher.sol";
      import { IValidatorService } from "@skalenetwork/skale-manager-interfaces/delegation/IValidatorService.sol";
      import { ILocker } from "@skalenetwork/skale-manager-interfaces/delegation/ILocker.sol";
      import { ITimeHelpers } from "@skalenetwork/skale-manager-interfaces/delegation/ITimeHelpers.sol";
      import { IBountyV2 } from "@skalenetwork/skale-manager-interfaces/IBountyV2.sol";
      import { IConstantsHolder } from "@skalenetwork/skale-manager-interfaces/IConstantsHolder.sol";
      import { Permissions } from "../Permissions.sol";
      import { FractionUtils } from "../utils/FractionUtils.sol";
      import { MathUtils } from "../utils/MathUtils.sol";
      import { PartialDifferences } from "./PartialDifferences.sol";
      /**
       * @title Delegation Controller
       * @dev This contract performs all delegation functions including delegation
       * requests, and undelegation, etc.
       *
       * Delegators and validators may both perform delegations. Validators who perform
       * delegations to themselves are effectively self-delegating or self-bonding.
       *
       * IMPORTANT: Undelegation may be requested at any time, but undelegation is only
       * performed at the completion of the current delegation period.
       *
       * Delegated tokens may be in one of several states:
       *
       * - PROPOSED: token holder proposes tokens to delegate to a validator.
       * - ACCEPTED: token delegations are accepted by a validator and are locked-by-delegation.
       * - CANCELED: token holder cancels delegation proposal. Only allowed before the proposal is accepted by the validator.
       * - REJECTED: token proposal expires at the UTC start of the next month.
       * - DELEGATED: accepted delegations are delegated at the UTC start of the month.
       * - UNDELEGATION_REQUESTED: token holder requests delegations to undelegate from the validator.
       * - COMPLETED: undelegation request is completed at the end of the delegation period.
       */
      contract DelegationController is Permissions, ILocker, IDelegationController {
          using MathUtils for uint;
          using PartialDifferences for PartialDifferences.Sequence;
          using PartialDifferences for PartialDifferences.Value;
          using FractionUtils for FractionUtils.Fraction;
          struct SlashingLogEvent {
              FractionUtils.Fraction reducingCoefficient;
              uint256 nextMonth;
          }
          struct SlashingLog {
              //      month => slashing event
              mapping (uint256 => SlashingLogEvent) slashes;
              uint256 firstMonth;
              uint256 lastMonth;
          }
          struct DelegationExtras {
              uint256 lastSlashingMonthBeforeDelegation;
          }
          struct SlashingEvent {
              FractionUtils.Fraction reducingCoefficient;
              uint256 validatorId;
              uint256 month;
          }
          struct SlashingSignal {
              address holder;
              uint256 penalty;
          }
          struct LockedInPending {
              uint256 amount;
              uint256 month;
          }
          struct FirstDelegationMonth {
              // month
              uint256 value;
              //validatorId => month
              mapping (uint256 => uint256) byValidator;
          }
          struct ValidatorsStatistics {
              // number of validators
              uint256 number;
              //validatorId => amount of delegations
              mapping (uint256 => uint256) delegated;
          }
          uint256 public constant UNDELEGATION_PROHIBITION_WINDOW_SECONDS = 3 * 24 * 60 * 60;
          /// @dev delegations will never be deleted to index in this array may be used like delegation id
          Delegation[] public delegations;
          // validatorId => delegationId[]
          mapping (uint256 => uint256[]) public delegationsByValidator;
          //        holder => delegationId[]
          mapping (address => uint256[]) public delegationsByHolder;
          // delegationId => extras
          mapping(uint256 => DelegationExtras) private _delegationExtras;
          // validatorId => sequence
          mapping (uint256 => PartialDifferences.Value) private _delegatedToValidator;
          // validatorId => sequence
          mapping (uint256 => PartialDifferences.Sequence) private _effectiveDelegatedToValidator;
          // validatorId => slashing log
          mapping (uint256 => SlashingLog) private _slashesOfValidator;
          //        holder => sequence
          mapping (address => PartialDifferences.Value) private _delegatedByHolder;
          //        holder =>   validatorId => sequence
          mapping (address => mapping (uint256 => PartialDifferences.Value)) private _delegatedByHolderToValidator;
          //        holder =>   validatorId => sequence
          mapping (address => mapping (uint256 => PartialDifferences.Sequence))
          private _effectiveDelegatedByHolderToValidator;
          SlashingEvent[] private _slashes;
          //        holder => index in _slashes;
          mapping (address => uint256) private _firstUnprocessedSlashByHolder;
          //        holder =>   validatorId => month
          mapping (address => FirstDelegationMonth) private _firstDelegationMonth;
          //        holder => locked in pending
          mapping (address => LockedInPending) private _lockedInPendingDelegations;
          mapping (address => ValidatorsStatistics) private _numberOfValidatorsPerDelegator;
          /**
           * @dev Modifier to make a function callable only if delegation exists.
           */
          modifier checkDelegationExists(uint256 delegationId) {
              require(delegationId < delegations.length, "Delegation does not exist");
              _;
          }
          function initialize(address contractsAddress) public override initializer {
              Permissions.initialize(contractsAddress);
          }
          /**
           * @dev Update and return a validator's delegations.
           */
          function getAndUpdateDelegatedToValidatorNow(uint256 validatorId) external override returns (uint256 amount) {
              return _getAndUpdateDelegatedToValidator(validatorId, _getCurrentMonth());
          }
          /**
           * @dev Update and return the amount delegated.
           */
          function getAndUpdateDelegatedAmount(address holder) external override returns (uint256 amount) {
              return _getAndUpdateDelegatedByHolder(holder);
          }
          /**
           * @dev Update and return the effective amount delegated (minus slash) for
           * the given month.
           */
          function getAndUpdateEffectiveDelegatedByHolderToValidator(address holder, uint256 validatorId, uint256 month)
              external
              override
              allow("Distributor")
              returns (uint256 effectiveDelegated)
          {
              SlashingSignal[] memory slashingSignals = _processAllSlashesWithoutSignals(holder);
              effectiveDelegated = _effectiveDelegatedByHolderToValidator[holder][validatorId]
                  .getAndUpdateValueInSequence(month);
              _sendSlashingSignals(slashingSignals);
          }
          /**
           * @dev Allows a token holder to create a delegation proposal of an `amount`
           * and `delegationPeriod` to a `validatorId`. Delegation must be accepted
           * by the validator before the UTC start of the month, otherwise the
           * delegation will be rejected.
           *
           * The token holder may add additional information in each proposal.
           *
           * Emits a {DelegationProposed} event.
           *
           * Requirements:
           *
           * - Holder must have sufficient delegatable tokens.
           * - Delegation must be above the validator's minimum delegation amount.
           * - Delegation period must be allowed.
           * - Validator must be authorized if trusted list is enabled.
           * - Validator must be accepting new delegation requests.
           */
          function delegate(
              uint256 validatorId,
              uint256 amount,
              uint256 delegationPeriod,
              string calldata info
          )
              external
              override
          {
              require(
                  _getDelegationPeriodManager().isDelegationPeriodAllowed(delegationPeriod),
                  "This delegation period is not allowed");
              _getValidatorService().checkValidatorCanReceiveDelegation(validatorId, amount);
              _checkIfDelegationIsAllowed(msg.sender, validatorId);
              SlashingSignal[] memory slashingSignals = _processAllSlashesWithoutSignals(msg.sender);
              uint256 delegationId = _addDelegation({
                  holder: msg.sender,
                  validatorId: validatorId,
                  amount: amount,
                  delegationPeriod: delegationPeriod,
                  info: info
              });
              // check that there is enough money
              uint256 holderBalance = IERC777(contractManager.getSkaleToken()).balanceOf(msg.sender);
              uint256 forbiddenForDelegation = ILocker(contractManager.getTokenState())
                  .getAndUpdateForbiddenForDelegationAmount(msg.sender);
              require(holderBalance >= forbiddenForDelegation, "Token holder does not have enough tokens to delegate");
              emit DelegationProposed(delegationId);
              _sendSlashingSignals(slashingSignals);
          }
          /**
           * @dev See {ILocker-getAndUpdateLockedAmount}.
           */
          function getAndUpdateLockedAmount(address wallet) external override returns (uint256 amount) {
              return _getAndUpdateLockedAmount(wallet);
          }
          /**
           * @dev See {ILocker-getAndUpdateForbiddenForDelegationAmount}.
           */
          function getAndUpdateForbiddenForDelegationAmount(address wallet) external override returns (uint256 amount) {
              return _getAndUpdateLockedAmount(wallet);
          }
          /**
           * @dev Allows token holder to cancel a delegation proposal.
           *
           * Emits a {DelegationRequestCanceledByUser} event.
           *
           * Requirements:
           *
           * - `msg.sender` must be the token holder of the delegation proposal.
           * - Delegation state must be PROPOSED.
           */
          function cancelPendingDelegation(uint256 delegationId) external override checkDelegationExists(delegationId) {
              require(msg.sender == delegations[delegationId].holder, "Only token holders can cancel delegation request");
              require(getState(delegationId) == State.PROPOSED, "Token holders are only able to cancel PROPOSED delegations");
              delegations[delegationId].finished = _getCurrentMonth();
              _subtractFromLockedInPendingDelegations(delegations[delegationId].holder, delegations[delegationId].amount);
              emit DelegationRequestCanceledByUser(delegationId);
          }
          /**
           * @dev Allows a validator to accept a proposed delegation.
           * Successful acceptance of delegations transition the tokens from a
           * PROPOSED state to ACCEPTED, and tokens are locked for the remainder of the
           * delegation period.
           *
           * Emits a {DelegationAccepted} event.
           *
           * Requirements:
           *
           * - Validator must be recipient of proposal.
           * - Delegation state must be PROPOSED.
           */
          function acceptPendingDelegation(uint256 delegationId) external override checkDelegationExists(delegationId) {
              require(
                  _getValidatorService().checkValidatorAddressToId(msg.sender, delegations[delegationId].validatorId),
                  "No permissions to accept request");
              _accept(delegationId);
          }
          /**
           * @dev Allows delegator to undelegate a specific delegation.
           *
           * Emits UndelegationRequested event.
           *
           * Requirements:
           *
           * - `msg.sender` must be the delegator or the validator.
           * - Delegation state must be DELEGATED.
           */
          function requestUndelegation(uint256 delegationId) external override checkDelegationExists(delegationId) {
              require(getState(delegationId) == State.DELEGATED, "Cannot request undelegation");
              IValidatorService validatorService = _getValidatorService();
              require(
                  delegations[delegationId].holder == msg.sender ||
                  (validatorService.validatorAddressExists(msg.sender) &&
                  delegations[delegationId].validatorId == validatorService.getValidatorId(msg.sender)),
                  "Permission denied to request undelegation");
              _removeValidatorFromValidatorsPerDelegators(
                  delegations[delegationId].holder,
                  delegations[delegationId].validatorId);
              processAllSlashes(msg.sender);
              delegations[delegationId].finished = _calculateDelegationEndMonth(delegationId);
              require(
                  block.timestamp + UNDELEGATION_PROHIBITION_WINDOW_SECONDS
                      < _getTimeHelpers().monthToTimestamp(delegations[delegationId].finished),
                  "Undelegation requests must be sent 3 days before the end of delegation period"
              );
              _subtractFromAllStatistics(delegationId);
              emit UndelegationRequested(delegationId);
          }
          /**
           * @dev Allows Punisher contract to slash an `amount` of stake from
           * a validator. This slashes an amount of delegations of the validator,
           * which reduces the amount that the validator has staked. This consequence
           * may force the SKALE Manager to reduce the number of nodes a validator is
           * operating so the validator can meet the Minimum Staking Requirement.
           *
           * Emits a {SlashingEvent}.
           *
           * See {Punisher}.
           */
          function confiscate(uint256 validatorId, uint256 amount) external override allow("Punisher") {
              uint256 currentMonth = _getCurrentMonth();
              FractionUtils.Fraction memory coefficient =
                  _delegatedToValidator[validatorId].reduceValue(amount, currentMonth);
              uint256 initialEffectiveDelegated =
                  _effectiveDelegatedToValidator[validatorId].getAndUpdateValueInSequence(currentMonth);
              uint256[] memory initialSubtractions = new uint256[](0);
              if (currentMonth < _effectiveDelegatedToValidator[validatorId].lastChangedMonth) {
                  initialSubtractions = new uint256[](
                      _effectiveDelegatedToValidator[validatorId].lastChangedMonth - currentMonth
                  );
                  for (uint256 i = 0; i < initialSubtractions.length; ++i) {
                      initialSubtractions[i] = _effectiveDelegatedToValidator[validatorId]
                          .subtractDiff[currentMonth + i + 1];
                  }
              }
              _effectiveDelegatedToValidator[validatorId].reduceSequence(coefficient, currentMonth);
              _putToSlashingLog(_slashesOfValidator[validatorId], coefficient, currentMonth);
              _slashes.push(SlashingEvent({reducingCoefficient: coefficient, validatorId: validatorId, month: currentMonth}));
              IBountyV2 bounty = _getBounty();
              bounty.handleDelegationRemoving(
                  initialEffectiveDelegated -
                      _effectiveDelegatedToValidator[validatorId].getAndUpdateValueInSequence(currentMonth),
                  currentMonth
              );
              for (uint256 i = 0; i < initialSubtractions.length; ++i) {
                  bounty.handleDelegationAdd(
                      initialSubtractions[i] -
                          _effectiveDelegatedToValidator[validatorId].subtractDiff[currentMonth + i + 1],
                      currentMonth + i + 1
                  );
              }
              emit Confiscated(validatorId, amount);
          }
          /**
           * @dev Allows Distributor contract to return and update the effective
           * amount delegated (minus slash) to a validator for a given month.
           */
          function getAndUpdateEffectiveDelegatedToValidator(uint256 validatorId, uint256 month)
              external
              override
              allowTwo("Bounty", "Distributor")
              returns (uint256 amount)
          {
              return _effectiveDelegatedToValidator[validatorId].getAndUpdateValueInSequence(month);
          }
          /**
           * @dev Return and update the amount delegated to a validator for the
           * current month.
           */
          function getAndUpdateDelegatedByHolderToValidatorNow(address holder, uint256 validatorId)
              external
              override
              returns (uint256 amount)
          {
              return _getAndUpdateDelegatedByHolderToValidator(holder, validatorId, _getCurrentMonth());
          }
          function getEffectiveDelegatedValuesByValidator(
              uint256 validatorId
          )
              external
              view
              override
              returns (uint256[] memory amounts)
          {
              return _effectiveDelegatedToValidator[validatorId].getValuesInSequence();
          }
          function getEffectiveDelegatedToValidator(
              uint256 validatorId,
              uint256 month
          )
              external
              view
              override
              returns (uint256 amount)
          {
              return _effectiveDelegatedToValidator[validatorId].getValueInSequence(month);
          }
          function getDelegatedToValidator(
              uint256 validatorId,
              uint256 month
          )
              external
              view
              override
              returns (uint256 amount)
          {
              return _delegatedToValidator[validatorId].getValue(month);
          }
          /**
           * @dev Return Delegation struct.
           */
          function getDelegation(uint256 delegationId)
              external
              view
              override
              checkDelegationExists(delegationId)
              returns (Delegation memory delegation)
          {
              return delegations[delegationId];
          }
          /**
           * @dev Returns the first delegation month.
           */
          function getFirstDelegationMonth(
              address holder,
              uint256 validatorId
          )
              external
              view
              override
              returns(uint256 month)
          {
              return _firstDelegationMonth[holder].byValidator[validatorId];
          }
          /**
           * @dev Returns a validator's total number of delegations.
           */
          function getDelegationsByValidatorLength(uint256 validatorId) external view override returns (uint256 length) {
              return delegationsByValidator[validatorId].length;
          }
          /**
           * @dev Returns a holder's total number of delegations.
           */
          function getDelegationsByHolderLength(address holder) external view override returns (uint256 length) {
              return delegationsByHolder[holder].length;
          }
          /**
           * @dev Process slashes up to the given limit.
           */
          function processSlashes(address holder, uint256 limit) public override {
              _sendSlashingSignals(_processSlashesWithoutSignals(holder, limit));
              emit SlashesProcessed(holder, limit);
          }
          /**
           * @dev Process all slashes.
           */
          function processAllSlashes(address holder) public override {
              processSlashes(holder, 0);
          }
          /**
           * @dev Returns the token state of a given delegation.
           */
          function getState(uint256 delegationId)
              public
              view
              override
              checkDelegationExists(delegationId)
              returns (State state)
          {
              if (delegations[delegationId].started == 0) {
                  if (delegations[delegationId].finished == 0) {
                      if (_getCurrentMonth() == _getTimeHelpers().timestampToMonth(delegations[delegationId].created)) {
                          return State.PROPOSED;
                      } else {
                          return State.REJECTED;
                      }
                  } else {
                      return State.CANCELED;
                  }
              } else {
                  if (_getCurrentMonth() < delegations[delegationId].started) {
                      return State.ACCEPTED;
                  } else {
                      if (delegations[delegationId].finished == 0) {
                          return State.DELEGATED;
                      } else {
                          if (_getCurrentMonth() < delegations[delegationId].finished) {
                              return State.UNDELEGATION_REQUESTED;
                          } else {
                              return State.COMPLETED;
                          }
                      }
                  }
              }
          }
          /**
           * @dev Returns the amount of tokens in PENDING delegation state.
           */
          function getLockedInPendingDelegations(address holder) public view override returns (uint256 amount) {
              uint256 currentMonth = _getCurrentMonth();
              if (_lockedInPendingDelegations[holder].month < currentMonth) {
                  return 0;
              } else {
                  return _lockedInPendingDelegations[holder].amount;
              }
          }
          /**
           * @dev Checks whether there are any unprocessed slashes.
           */
          function hasUnprocessedSlashes(address holder) public view override returns (bool hasUnprocessed) {
              return _everDelegated(holder) && _firstUnprocessedSlashByHolder[holder] < _slashes.length;
          }
          // private
          /**
           * @dev Allows Nodes contract to get and update the amount delegated
           * to validator for a given month.
           */
          function _getAndUpdateDelegatedToValidator(uint256 validatorId, uint256 month)
              private returns (uint256 amount)
          {
              return _delegatedToValidator[validatorId].getAndUpdateValue(month);
          }
          /**
           * @dev Adds a new delegation proposal.
           */
          function _addDelegation(
              address holder,
              uint256 validatorId,
              uint256 amount,
              uint256 delegationPeriod,
              string memory info
          )
              private
              returns (uint256 delegationId)
          {
              delegationId = delegations.length;
              delegations.push(Delegation({
                  holder: holder,
                  validatorId: validatorId,
                  amount: amount,
                  delegationPeriod: delegationPeriod,
                  created: block.timestamp,
                  started: 0,
                  finished: 0,
                  info: info
              }));
              delegationsByValidator[validatorId].push(delegationId);
              delegationsByHolder[holder].push(delegationId);
              _addToLockedInPendingDelegations(delegations[delegationId].holder, delegations[delegationId].amount);
          }
          function _addToDelegatedToValidator(uint256 validatorId, uint256 amount, uint256 month) private {
              _delegatedToValidator[validatorId].addToValue(amount, month);
          }
          function _addToEffectiveDelegatedToValidator(uint256 validatorId, uint256 effectiveAmount, uint256 month) private {
              _effectiveDelegatedToValidator[validatorId].addToSequence(effectiveAmount, month);
          }
          function _addToDelegatedByHolder(address holder, uint256 amount, uint256 month) private {
              _delegatedByHolder[holder].addToValue(amount, month);
          }
          function _addToDelegatedByHolderToValidator(
              address holder, uint256 validatorId, uint256 amount, uint256 month) private
          {
              _delegatedByHolderToValidator[holder][validatorId].addToValue(amount, month);
          }
          function _addValidatorToValidatorsPerDelegators(address holder, uint256 validatorId) private {
              if (_numberOfValidatorsPerDelegator[holder].delegated[validatorId] == 0) {
                  _numberOfValidatorsPerDelegator[holder].number += 1;
              }
              _numberOfValidatorsPerDelegator[holder].delegated[validatorId] += 1;
          }
          function _removeFromDelegatedByHolder(address holder, uint256 amount, uint256 month) private {
              _delegatedByHolder[holder].subtractFromValue(amount, month);
          }
          function _removeFromDelegatedByHolderToValidator(
              address holder, uint256 validatorId, uint256 amount, uint256 month) private
          {
              _delegatedByHolderToValidator[holder][validatorId].subtractFromValue(amount, month);
          }
          function _removeValidatorFromValidatorsPerDelegators(address holder, uint256 validatorId) private {
              if (_numberOfValidatorsPerDelegator[holder].delegated[validatorId] == 1) {
                  _numberOfValidatorsPerDelegator[holder].number -= 1;
              }
              _numberOfValidatorsPerDelegator[holder].delegated[validatorId] -= 1;
          }
          function _addToEffectiveDelegatedByHolderToValidator(
              address holder,
              uint256 validatorId,
              uint256 effectiveAmount,
              uint256 month)
              private
          {
              _effectiveDelegatedByHolderToValidator[holder][validatorId].addToSequence(effectiveAmount, month);
          }
          function _removeFromEffectiveDelegatedByHolderToValidator(
              address holder,
              uint256 validatorId,
              uint256 effectiveAmount,
              uint256 month)
              private
          {
              _effectiveDelegatedByHolderToValidator[holder][validatorId].subtractFromSequence(effectiveAmount, month);
          }
          function _getAndUpdateDelegatedByHolder(address holder) private returns (uint256 amount) {
              uint256 currentMonth = _getCurrentMonth();
              processAllSlashes(holder);
              return _delegatedByHolder[holder].getAndUpdateValue(currentMonth);
          }
          function _getAndUpdateDelegatedByHolderToValidator(
              address holder,
              uint256 validatorId,
              uint256 month)
              private returns (uint256 amount)
          {
              return _delegatedByHolderToValidator[holder][validatorId].getAndUpdateValue(month);
          }
          function _addToLockedInPendingDelegations(address holder, uint256 amount) private {
              uint256 currentMonth = _getCurrentMonth();
              if (_lockedInPendingDelegations[holder].month < currentMonth) {
                  _lockedInPendingDelegations[holder].amount = amount;
                  _lockedInPendingDelegations[holder].month = currentMonth;
              } else {
                  assert(_lockedInPendingDelegations[holder].month == currentMonth);
                  _lockedInPendingDelegations[holder].amount = _lockedInPendingDelegations[holder].amount + amount;
              }
          }
          function _subtractFromLockedInPendingDelegations(address holder, uint256 amount) private {
              uint256 currentMonth = _getCurrentMonth();
              assert(_lockedInPendingDelegations[holder].month == currentMonth);
              _lockedInPendingDelegations[holder].amount = _lockedInPendingDelegations[holder].amount - amount;
          }
          /**
           * @dev See {ILocker-getAndUpdateLockedAmount}.
           */
          function _getAndUpdateLockedAmount(address wallet) private returns (uint256 amount) {
              return _getAndUpdateDelegatedByHolder(wallet) + getLockedInPendingDelegations(wallet);
          }
          function _updateFirstDelegationMonth(address holder, uint256 validatorId, uint256 month) private {
              if (_firstDelegationMonth[holder].value == 0) {
                  _firstDelegationMonth[holder].value = month;
                  _firstUnprocessedSlashByHolder[holder] = _slashes.length;
              }
              if (_firstDelegationMonth[holder].byValidator[validatorId] == 0) {
                  _firstDelegationMonth[holder].byValidator[validatorId] = month;
              }
          }
          function _removeFromDelegatedToValidator(uint256 validatorId, uint256 amount, uint256 month) private {
              _delegatedToValidator[validatorId].subtractFromValue(amount, month);
          }
          function _removeFromEffectiveDelegatedToValidator(
              uint256 validatorId,
              uint256 effectiveAmount,
              uint256 month
          )
              private
          {
              _effectiveDelegatedToValidator[validatorId].subtractFromSequence(effectiveAmount, month);
          }
          function _putToSlashingLog(
              SlashingLog storage log,
              FractionUtils.Fraction memory coefficient,
              uint256 month)
              private
          {
              if (log.firstMonth == 0) {
                  log.firstMonth = month;
                  log.lastMonth = month;
                  log.slashes[month].reducingCoefficient = coefficient;
                  log.slashes[month].nextMonth = 0;
              } else {
                  require(log.lastMonth <= month, "Cannot put slashing event in the past");
                  if (log.lastMonth == month) {
                      log.slashes[month].reducingCoefficient =
                          log.slashes[month].reducingCoefficient.multiplyFraction(coefficient);
                  } else {
                      log.slashes[month].reducingCoefficient = coefficient;
                      log.slashes[month].nextMonth = 0;
                      log.slashes[log.lastMonth].nextMonth = month;
                      log.lastMonth = month;
                  }
              }
          }
          function _processSlashesWithoutSignals(address holder, uint256 limit)
              private returns (SlashingSignal[] memory slashingSignals)
          {
              if (hasUnprocessedSlashes(holder)) {
                  uint256 index = _firstUnprocessedSlashByHolder[holder];
                  uint256 end = _slashes.length;
                  if (limit > 0 && (index + limit) < end) {
                      end = index + limit;
                  }
                  slashingSignals = new SlashingSignal[](end - index);
                  uint256 begin = index;
                  for (; index < end; ++index) {
                      uint256 validatorId = _slashes[index].validatorId;
                      uint256 month = _slashes[index].month;
                      uint256 oldValue = _getAndUpdateDelegatedByHolderToValidator(holder, validatorId, month);
                      if (oldValue.muchGreater(0)) {
                          _delegatedByHolderToValidator[holder][validatorId].reduceValueByCoefficientAndUpdateSum(
                              _delegatedByHolder[holder],
                              _slashes[index].reducingCoefficient,
                              month);
                          _effectiveDelegatedByHolderToValidator[holder][validatorId].reduceSequence(
                              _slashes[index].reducingCoefficient,
                              month);
                          slashingSignals[index - begin].holder = holder;
                          slashingSignals[index - begin].penalty
                              = oldValue.boundedSub(_getAndUpdateDelegatedByHolderToValidator(holder, validatorId, month));
                      }
                  }
                  _firstUnprocessedSlashByHolder[holder] = end;
              }
          }
          function _processAllSlashesWithoutSignals(address holder)
              private returns (SlashingSignal[] memory slashingSignals)
          {
              return _processSlashesWithoutSignals(holder, 0);
          }
          function _sendSlashingSignals(SlashingSignal[] memory slashingSignals) private {
              IPunisher punisher = IPunisher(contractManager.getPunisher());
              address previousHolder = address(0);
              uint256 accumulatedPenalty = 0;
              for (uint256 i = 0; i < slashingSignals.length; ++i) {
                  if (slashingSignals[i].holder != previousHolder) {
                      if (accumulatedPenalty > 0) {
                          punisher.handleSlash(previousHolder, accumulatedPenalty);
                      }
                      previousHolder = slashingSignals[i].holder;
                      accumulatedPenalty = slashingSignals[i].penalty;
                  } else {
                      accumulatedPenalty = accumulatedPenalty + slashingSignals[i].penalty;
                  }
              }
              if (accumulatedPenalty > 0) {
                  punisher.handleSlash(previousHolder, accumulatedPenalty);
              }
          }
          function _addToAllStatistics(uint256 delegationId) private {
              uint256 currentMonth = _getCurrentMonth();
              delegations[delegationId].started = currentMonth + 1;
              if (_slashesOfValidator[delegations[delegationId].validatorId].lastMonth > 0) {
                  _delegationExtras[delegationId].lastSlashingMonthBeforeDelegation =
                      _slashesOfValidator[delegations[delegationId].validatorId].lastMonth;
              }
              _addToDelegatedToValidator(
                  delegations[delegationId].validatorId,
                  delegations[delegationId].amount,
                  currentMonth + 1);
              _addToDelegatedByHolder(
                  delegations[delegationId].holder,
                  delegations[delegationId].amount,
                  currentMonth + 1);
              _addToDelegatedByHolderToValidator(
                  delegations[delegationId].holder,
                  delegations[delegationId].validatorId,
                  delegations[delegationId].amount,
                  currentMonth + 1);
              _updateFirstDelegationMonth(
                  delegations[delegationId].holder,
                  delegations[delegationId].validatorId,
                  currentMonth + 1);
              uint256 effectiveAmount = delegations[delegationId].amount *
                  _getDelegationPeriodManager().stakeMultipliers(delegations[delegationId].delegationPeriod);
              _addToEffectiveDelegatedToValidator(
                  delegations[delegationId].validatorId,
                  effectiveAmount,
                  currentMonth + 1);
              _addToEffectiveDelegatedByHolderToValidator(
                  delegations[delegationId].holder,
                  delegations[delegationId].validatorId,
                  effectiveAmount,
                  currentMonth + 1);
              _addValidatorToValidatorsPerDelegators(
                  delegations[delegationId].holder,
                  delegations[delegationId].validatorId
              );
          }
          function _subtractFromAllStatistics(uint256 delegationId) private {
              uint256 amountAfterSlashing = _calculateDelegationAmountAfterSlashing(delegationId);
              _removeFromDelegatedToValidator(
                  delegations[delegationId].validatorId,
                  amountAfterSlashing,
                  delegations[delegationId].finished);
              _removeFromDelegatedByHolder(
                  delegations[delegationId].holder,
                  amountAfterSlashing,
                  delegations[delegationId].finished);
              _removeFromDelegatedByHolderToValidator(
                  delegations[delegationId].holder,
                  delegations[delegationId].validatorId,
                  amountAfterSlashing,
                  delegations[delegationId].finished);
              uint256 effectiveAmount = amountAfterSlashing *
                      _getDelegationPeriodManager().stakeMultipliers(delegations[delegationId].delegationPeriod);
              _removeFromEffectiveDelegatedToValidator(
                  delegations[delegationId].validatorId,
                  effectiveAmount,
                  delegations[delegationId].finished);
              _removeFromEffectiveDelegatedByHolderToValidator(
                  delegations[delegationId].holder,
                  delegations[delegationId].validatorId,
                  effectiveAmount,
                  delegations[delegationId].finished);
              _getBounty().handleDelegationRemoving(
                  effectiveAmount,
                  delegations[delegationId].finished);
          }
          function _accept(uint256 delegationId) private {
              _checkIfDelegationIsAllowed(delegations[delegationId].holder, delegations[delegationId].validatorId);
              State currentState = getState(delegationId);
              if (currentState != State.PROPOSED) {
                  if (currentState == State.ACCEPTED ||
                      currentState == State.DELEGATED ||
                      currentState == State.UNDELEGATION_REQUESTED ||
                      currentState == State.COMPLETED)
                  {
                      revert("The delegation has been already accepted");
                  } else if (currentState == State.CANCELED) {
                      revert("The delegation has been cancelled by token holder");
                  } else if (currentState == State.REJECTED) {
                      revert("The delegation request is outdated");
                  }
              }
              require(currentState == State.PROPOSED, "Cannot set delegation state to accepted");
              SlashingSignal[] memory slashingSignals = _processAllSlashesWithoutSignals(delegations[delegationId].holder);
              _addToAllStatistics(delegationId);
              uint256 amount = delegations[delegationId].amount;
              uint256 effectiveAmount = amount *
                  _getDelegationPeriodManager().stakeMultipliers(delegations[delegationId].delegationPeriod);
              _getBounty().handleDelegationAdd(
                  effectiveAmount,
                  delegations[delegationId].started
              );
              _sendSlashingSignals(slashingSignals);
              emit DelegationAccepted(delegationId);
          }
          function _getCurrentMonth() private view returns (uint256 month) {
              return _getTimeHelpers().getCurrentMonth();
          }
          /**
           * @dev Checks whether the holder has performed a delegation.
           */
          function _everDelegated(address holder) private view returns (bool delegated) {
              return _firstDelegationMonth[holder].value > 0;
          }
          /**
           * @dev Returns the month when a delegation ends.
           */
          function _calculateDelegationEndMonth(uint256 delegationId) private view returns (uint256 month) {
              uint256 currentMonth = _getCurrentMonth();
              uint256 started = delegations[delegationId].started;
              if (currentMonth < started) {
                  return started + delegations[delegationId].delegationPeriod;
              } else {
                  uint256 completedPeriods = (currentMonth - started) / delegations[delegationId].delegationPeriod;
                  return started + (completedPeriods + 1) * delegations[delegationId].delegationPeriod;
              }
          }
          /**
           * @dev Returns the delegated amount after a slashing event.
           */
          function _calculateDelegationAmountAfterSlashing(uint256 delegationId) private view returns (uint256 amount) {
              uint256 startMonth = _delegationExtras[delegationId].lastSlashingMonthBeforeDelegation;
              uint256 validatorId = delegations[delegationId].validatorId;
              amount = delegations[delegationId].amount;
              if (startMonth == 0) {
                  startMonth = _slashesOfValidator[validatorId].firstMonth;
                  if (startMonth == 0) {
                      return amount;
                  }
              }
              for (uint256 i = startMonth;
                  i > 0 && i < delegations[delegationId].finished;
                  i = _slashesOfValidator[validatorId].slashes[i].nextMonth) {
                  if (i >= delegations[delegationId].started) {
                      amount = amount
                          * _slashesOfValidator[validatorId].slashes[i].reducingCoefficient.numerator
                          / _slashesOfValidator[validatorId].slashes[i].reducingCoefficient.denominator;
                  }
              }
              return amount;
          }
          /**
           * @dev Checks whether delegation to a validator is allowed.
           *
           * Requirements:
           *
           * - Delegator must not have reached the validator limit.
           * - Delegation must be made in or after the first delegation month.
           */
          function _checkIfDelegationIsAllowed(address holder, uint256 validatorId) private view {
              require(
                  _numberOfValidatorsPerDelegator[holder].delegated[validatorId] > 0 ||
                      _numberOfValidatorsPerDelegator[holder].number < _getConstantsHolder().limitValidatorsPerDelegator(),
                  "Limit of validators is reached"
              );
          }
          function _getDelegationPeriodManager() private view returns (IDelegationPeriodManager delegationPeriodManager) {
              return IDelegationPeriodManager(contractManager.getDelegationPeriodManager());
          }
          function _getBounty() private view returns (IBountyV2 bountyV2) {
              return IBountyV2(contractManager.getBounty());
          }
          function _getValidatorService() private view returns (IValidatorService validatorService) {
              return IValidatorService(contractManager.getValidatorService());
          }
          function _getTimeHelpers() private view returns (ITimeHelpers timeHelpers) {
              return ITimeHelpers(contractManager.getTimeHelpers());
          }
          function _getConstantsHolder() private view returns (IConstantsHolder constantsHolder) {
              return IConstantsHolder(contractManager.getConstantsHolder());
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          PartialDifferences.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Dmytro Stebaiev
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity 0.8.17;
      import { MathUtils } from "../utils/MathUtils.sol";
      import { FractionUtils } from "../utils/FractionUtils.sol";
      /**
       * @title Partial Differences Library
       * @dev This library contains functions to manage Partial Differences data
       * structure. Partial Differences is an array of value differences over time.
       *
       * For example: assuming an array [3, 6, 3, 1, 2], partial differences can
       * represent this array as [_, 3, -3, -2, 1].
       *
       * This data structure allows adding values on an open interval with O(1)
       * complexity.
       *
       * For example: add +5 to [3, 6, 3, 1, 2] starting from the second element (3),
       * instead of performing [3, 6, 3+5, 1+5, 2+5] partial differences allows
       * performing [_, 3, -3+5, -2, 1]. The original array can be restored by
       * adding values from partial differences.
       */
      library PartialDifferences {
          using MathUtils for uint;
          struct Sequence {
                   // month => diff
              mapping (uint256 => uint256) addDiff;
                   // month => diff
              mapping (uint256 => uint256) subtractDiff;
                   // month => value
              mapping (uint256 => uint256) value;
              uint256 firstUnprocessedMonth;
              uint256 lastChangedMonth;
          }
          struct Value {
                   // month => diff
              mapping (uint256 => uint256) addDiff;
                   // month => diff
              mapping (uint256 => uint256) subtractDiff;
              uint256 value;
              uint256 firstUnprocessedMonth;
              uint256 lastChangedMonth;
          }
          // functions for sequence
          function addToSequence(Sequence storage sequence, uint256 diff, uint256 month) internal {
              require(sequence.firstUnprocessedMonth <= month, "Cannot add to the past");
              if (sequence.firstUnprocessedMonth == 0) {
                  sequence.firstUnprocessedMonth = month;
              }
              sequence.addDiff[month] = sequence.addDiff[month] + diff;
              if (sequence.lastChangedMonth != month) {
                  sequence.lastChangedMonth = month;
              }
          }
          function subtractFromSequence(Sequence storage sequence, uint256 diff, uint256 month) internal {
              require(sequence.firstUnprocessedMonth <= month, "Cannot subtract from the past");
              if (sequence.firstUnprocessedMonth == 0) {
                  sequence.firstUnprocessedMonth = month;
              }
              sequence.subtractDiff[month] = sequence.subtractDiff[month] + diff;
              if (sequence.lastChangedMonth != month) {
                  sequence.lastChangedMonth = month;
              }
          }
          function getAndUpdateValueInSequence(Sequence storage sequence, uint256 month) internal returns (uint256 value) {
              if (sequence.firstUnprocessedMonth == 0) {
                  return 0;
              }
              if (sequence.firstUnprocessedMonth <= month) {
                  for (uint256 i = sequence.firstUnprocessedMonth; i <= month; ++i) {
                      uint256 nextValue = (sequence.value[i - 1] + sequence.addDiff[i]).boundedSub(sequence.subtractDiff[i]);
                      if (sequence.value[i] != nextValue) {
                          sequence.value[i] = nextValue;
                      }
                      if (sequence.addDiff[i] > 0) {
                          delete sequence.addDiff[i];
                      }
                      if (sequence.subtractDiff[i] > 0) {
                          delete sequence.subtractDiff[i];
                      }
                  }
                  sequence.firstUnprocessedMonth = month + 1;
              }
              return sequence.value[month];
          }
          function reduceSequence(
              Sequence storage sequence,
              FractionUtils.Fraction memory reducingCoefficient,
              uint256 month) internal
          {
              require(month + 1 >= sequence.firstUnprocessedMonth, "Cannot reduce value in the past");
              require(
                  reducingCoefficient.numerator <= reducingCoefficient.denominator,
                  "Increasing of values is not implemented");
              if (sequence.firstUnprocessedMonth == 0) {
                  return;
              }
              uint256 value = getAndUpdateValueInSequence(sequence, month);
              if (value.approximatelyEqual(0)) {
                  return;
              }
              sequence.value[month] = sequence.value[month]
                  * reducingCoefficient.numerator
                  / reducingCoefficient.denominator;
              for (uint256 i = month + 1; i <= sequence.lastChangedMonth; ++i) {
                  sequence.subtractDiff[i] = sequence.subtractDiff[i]
                      * reducingCoefficient.numerator
                      / reducingCoefficient.denominator;
              }
          }
          // functions for value
          function addToValue(Value storage sequence, uint256 diff, uint256 month) internal {
              require(sequence.firstUnprocessedMonth <= month, "Cannot add to the past");
              if (sequence.firstUnprocessedMonth == 0) {
                  sequence.firstUnprocessedMonth = month;
                  sequence.lastChangedMonth = month;
              }
              if (month > sequence.lastChangedMonth) {
                  sequence.lastChangedMonth = month;
              }
              if (month >= sequence.firstUnprocessedMonth) {
                  sequence.addDiff[month] = sequence.addDiff[month] + diff;
              } else {
                  sequence.value = sequence.value + diff;
              }
          }
          function subtractFromValue(Value storage sequence, uint256 diff, uint256 month) internal {
              require(sequence.firstUnprocessedMonth <= month + 1, "Cannot subtract from the past");
              if (sequence.firstUnprocessedMonth == 0) {
                  sequence.firstUnprocessedMonth = month;
                  sequence.lastChangedMonth = month;
              }
              if (month > sequence.lastChangedMonth) {
                  sequence.lastChangedMonth = month;
              }
              if (month >= sequence.firstUnprocessedMonth) {
                  sequence.subtractDiff[month] = sequence.subtractDiff[month] + diff;
              } else {
                  sequence.value = sequence.value.boundedSub(diff);
              }
          }
          function getAndUpdateValue(Value storage sequence, uint256 month) internal returns (uint256 value) {
              require(
                  month + 1 >= sequence.firstUnprocessedMonth,
                  "Cannot calculate value in the past");
              if (sequence.firstUnprocessedMonth == 0) {
                  return 0;
              }
              if (sequence.firstUnprocessedMonth <= month) {
                  value = sequence.value;
                  for (uint256 i = sequence.firstUnprocessedMonth; i <= month; ++i) {
                      value = (value + sequence.addDiff[i]).boundedSub(sequence.subtractDiff[i]);
                      if (sequence.addDiff[i] > 0) {
                          delete sequence.addDiff[i];
                      }
                      if (sequence.subtractDiff[i] > 0) {
                          delete sequence.subtractDiff[i];
                      }
                  }
                  if (sequence.value != value) {
                      sequence.value = value;
                  }
                  sequence.firstUnprocessedMonth = month + 1;
              }
              return sequence.value;
          }
          function reduceValue(
              Value storage sequence,
              uint256 amount,
              uint256 month)
              internal returns (FractionUtils.Fraction memory reducingCoefficient)
          {
              require(month + 1 >= sequence.firstUnprocessedMonth, "Cannot reduce value in the past");
              if (sequence.firstUnprocessedMonth == 0) {
                  return FractionUtils.createFraction(0);
              }
              uint256 value = getAndUpdateValue(sequence, month);
              if (value.approximatelyEqual(0)) {
                  return FractionUtils.createFraction(0);
              }
              uint256 _amount = amount;
              if (value < amount) {
                  _amount = value;
              }
              reducingCoefficient =
                  FractionUtils.createFraction(value.boundedSub(_amount), value);
              reduceValueByCoefficient(sequence, reducingCoefficient, month);
              return reducingCoefficient;
          }
          function reduceValueByCoefficient(
              Value storage sequence,
              FractionUtils.Fraction memory reducingCoefficient,
              uint256 month)
              internal
          {
              reduceValueByCoefficientAndUpdateSumIfNeeded({
                  sequence: sequence,
                  sumSequence: sequence,
                  reducingCoefficient: reducingCoefficient,
                  month: month,
                  hasSumSequence: false
              });
          }
          function reduceValueByCoefficientAndUpdateSum(
              Value storage sequence,
              Value storage sumSequence,
              FractionUtils.Fraction memory reducingCoefficient,
              uint256 month) internal
          {
              reduceValueByCoefficientAndUpdateSumIfNeeded({
                  sequence: sequence,
                  sumSequence: sumSequence,
                  reducingCoefficient: reducingCoefficient,
                  month: month,
                  hasSumSequence: true
              });
          }
          function reduceValueByCoefficientAndUpdateSumIfNeeded(
              Value storage sequence,
              Value storage sumSequence,
              FractionUtils.Fraction memory reducingCoefficient,
              uint256 month,
              bool hasSumSequence) internal
          {
              require(month + 1 >= sequence.firstUnprocessedMonth, "Cannot reduce value in the past");
              if (hasSumSequence) {
                  require(month + 1 >= sumSequence.firstUnprocessedMonth, "Cannot reduce value in the past");
              }
              require(
                  reducingCoefficient.numerator <= reducingCoefficient.denominator,
                  "Increasing of values is not implemented");
              if (sequence.firstUnprocessedMonth == 0) {
                  return;
              }
              uint256 value = getAndUpdateValue(sequence, month);
              if (value.approximatelyEqual(0)) {
                  return;
              }
              uint256 newValue = sequence.value * reducingCoefficient.numerator / reducingCoefficient.denominator;
              if (hasSumSequence) {
                  subtractFromValue(sumSequence, sequence.value.boundedSub(newValue), month);
              }
              sequence.value = newValue;
              for (uint256 i = month + 1; i <= sequence.lastChangedMonth; ++i) {
                  uint256 newDiff = sequence.subtractDiff[i]
                      * reducingCoefficient.numerator
                      / reducingCoefficient.denominator;
                  if (hasSumSequence) {
                      sumSequence.subtractDiff[i] = sumSequence.subtractDiff[i]
                          .boundedSub(sequence.subtractDiff[i].boundedSub(newDiff));
                  }
                  sequence.subtractDiff[i] = newDiff;
              }
          }
          function getValueInSequence(Sequence storage sequence, uint256 month) internal view returns (uint256 value) {
              if (sequence.firstUnprocessedMonth == 0) {
                  return 0;
              }
              if (sequence.firstUnprocessedMonth <= month) {
                  value = sequence.value[sequence.firstUnprocessedMonth - 1];
                  for (uint256 i = sequence.firstUnprocessedMonth; i <= month; ++i) {
                      value = value + sequence.addDiff[i] - sequence.subtractDiff[i];
                  }
                  return value;
              } else {
                  return sequence.value[month];
              }
          }
          function getValuesInSequence(Sequence storage sequence) internal view returns (uint256[] memory values) {
              if (sequence.firstUnprocessedMonth == 0) {
                  return values;
              }
              uint256 begin = sequence.firstUnprocessedMonth - 1;
              uint256 end = sequence.lastChangedMonth + 1;
              if (end <= begin) {
                  end = begin + 1;
              }
              values = new uint256[](end - begin);
              values[0] = sequence.value[sequence.firstUnprocessedMonth - 1];
              for (uint256 i = 0; i + 1 < values.length; ++i) {
                  uint256 month = sequence.firstUnprocessedMonth + i;
                  values[i + 1] = values[i] + sequence.addDiff[month] - sequence.subtractDiff[month];
              }
          }
          function getValue(Value storage sequence, uint256 month) internal view returns (uint256 value) {
              require(
                  month + 1 >= sequence.firstUnprocessedMonth,
                  "Cannot calculate value in the past");
              if (sequence.firstUnprocessedMonth == 0) {
                  return 0;
              }
              if (sequence.firstUnprocessedMonth <= month) {
                  value = sequence.value;
                  for (uint256 i = sequence.firstUnprocessedMonth; i <= month; ++i) {
                      value = value + sequence.addDiff[i] - sequence.subtractDiff[i];
                  }
                  return value;
              } else {
                  return sequence.value;
              }
          }
          function getValues(Value storage sequence) internal view returns (uint256[] memory values) {
              if (sequence.firstUnprocessedMonth == 0) {
                  return values;
              }
              uint256 begin = sequence.firstUnprocessedMonth - 1;
              uint256 end = sequence.lastChangedMonth + 1;
              if (end <= begin) {
                  end = begin + 1;
              }
              values = new uint256[](end - begin);
              values[0] = sequence.value;
              for (uint256 i = 0; i + 1 < values.length; ++i) {
                  uint256 month = sequence.firstUnprocessedMonth + i;
                  values[i + 1] = values[i] + sequence.addDiff[month] - sequence.subtractDiff[month];
              }
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          Permissions.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Artem Payvin
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity 0.8.17;
      import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
      import { IContractManager } from "@skalenetwork/skale-manager-interfaces/IContractManager.sol";
      import { IPermissions } from "@skalenetwork/skale-manager-interfaces/IPermissions.sol";
      import { AccessControlUpgradeableLegacy } from "./thirdparty/openzeppelin/AccessControlUpgradeableLegacy.sol";
      /**
       * @title Permissions
       * @dev Contract is connected module for Upgradeable approach, knows ContractManager
       */
      contract Permissions is AccessControlUpgradeableLegacy, IPermissions {
          using AddressUpgradeable for address;
          IContractManager public contractManager;
          /**
           * @dev Modifier to make a function callable only when caller is the Owner.
           *
           * Requirements:
           *
           * - The caller must be the owner.
           */
          modifier onlyOwner() {
              require(_isOwner(), "Caller is not the owner");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when caller is an Admin.
           *
           * Requirements:
           *
           * - The caller must be an admin.
           */
          modifier onlyAdmin() {
              require(_isAdmin(msg.sender), "Caller is not an admin");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when caller is the Owner
           * or `contractName` contract.
           *
           * Requirements:
           *
           * - The caller must be the owner or `contractName`.
           */
          modifier allow(string memory contractName) {
              require(
                  contractManager.getContract(contractName) == msg.sender || _isOwner(),
                  "Message sender is invalid");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when caller is the Owner
           * or `contractName1` or `contractName2` contract.
           *
           * Requirements:
           *
           * - The caller must be the owner, `contractName1`, or `contractName2`.
           */
          modifier allowTwo(string memory contractName1, string memory contractName2) {
              require(
                  contractManager.getContract(contractName1) == msg.sender ||
                  contractManager.getContract(contractName2) == msg.sender ||
                  _isOwner(),
                  "Message sender is invalid");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when caller is the Owner
           * or `contractName1`, `contractName2`, or `contractName3` contract.
           *
           * Requirements:
           *
           * - The caller must be the owner, `contractName1`, `contractName2`, or
           * `contractName3`.
           */
          modifier allowThree(string memory contractName1, string memory contractName2, string memory contractName3) {
              require(
                  contractManager.getContract(contractName1) == msg.sender ||
                  contractManager.getContract(contractName2) == msg.sender ||
                  contractManager.getContract(contractName3) == msg.sender ||
                  _isOwner(),
                  "Message sender is invalid");
              _;
          }
          function initialize(address contractManagerAddress) public virtual override initializer {
              AccessControlUpgradeableLegacy.__AccessControl_init();
              _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
              _setContractManager(contractManagerAddress);
          }
          function _isOwner() internal view returns (bool owner) {
              return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
          }
          function _isAdmin(address account) internal view returns (bool admin) {
              address skaleManagerAddress = contractManager.contracts(keccak256(abi.encodePacked("SkaleManager")));
              if (skaleManagerAddress != address(0)) {
                  AccessControlUpgradeableLegacy skaleManager = AccessControlUpgradeableLegacy(skaleManagerAddress);
                  return skaleManager.hasRole(keccak256("ADMIN_ROLE"), account) || _isOwner();
              } else {
                  return _isOwner();
              }
          }
          function _setContractManager(address contractManagerAddress) private {
              require(contractManagerAddress != address(0), "ContractManager address is not set");
              require(contractManagerAddress.isContract(), "Address is not contract");
              contractManager = IContractManager(contractManagerAddress);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.7;
      import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
      import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
      import "@skalenetwork/skale-manager-interfaces/thirdparty/openzeppelin/IAccessControlUpgradeableLegacy.sol";
      import "./InitializableWithGap.sol";
      /**
       * @dev Contract module that allows children to implement role-based access
       * control mechanisms.
       *
       * Roles are referred to by their `bytes32` identifier. These should be exposed
       * in the external API and be unique. The best way to achieve this is by
       * using `public constant` hash digests:
       *
       * ```
       * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
       * ```
       *
       * Roles can be used to represent a set of permissions. To restrict access to a
       * function call, use {hasRole}:
       *
       * ```
       * function foo() public {
       *     require(hasRole(MY_ROLE, _msgSender()));
       *     ...
       * }
       * ```
       *
       * Roles can be granted and revoked dynamically via the {grantRole} and
       * {revokeRole} functions. Each role has an associated admin role, and only
       * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
       *
       * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
       * that only accounts with this role will be able to grant or revoke other
       * roles. More complex role relationships can be created by using
       * {_setRoleAdmin}.
       */
      abstract contract AccessControlUpgradeableLegacy is InitializableWithGap, ContextUpgradeable, IAccessControlUpgradeableLegacy {
          function __AccessControl_init() internal initializer {
              __Context_init_unchained();
              __AccessControl_init_unchained();
          }
          function __AccessControl_init_unchained() internal initializer {
          }
          using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
          struct RoleData {
              EnumerableSetUpgradeable.AddressSet members;
              bytes32 adminRole;
          }
          mapping (bytes32 => RoleData) private _roles;
          bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
          /**
           * @dev Returns `true` if `account` has been granted `role`.
           */
          function hasRole(bytes32 role, address account) public view override returns (bool) {
              return _roles[role].members.contains(account);
          }
          /**
           * @dev Returns the number of accounts that have `role`. Can be used
           * together with {getRoleMember} to enumerate all bearers of a role.
           */
          function getRoleMemberCount(bytes32 role) public view override returns (uint256) {
              return _roles[role].members.length();
          }
          /**
           * @dev Returns one of the accounts that have `role`. `index` must be a
           * value between 0 and {getRoleMemberCount}, non-inclusive.
           *
           * Role bearers are not sorted in any particular way, and their ordering may
           * change at any point.
           *
           * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
           * you perform all queries on the same block. See the following
           * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
           * for more information.
           */
          function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {
              return _roles[role].members.at(index);
          }
          /**
           * @dev Returns the admin role that controls `role`. See {grantRole} and
           * {revokeRole}.
           *
           * To change a role's admin, use {_setRoleAdmin}.
           */
          function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
              return _roles[role].adminRole;
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function grantRole(bytes32 role, address account) public virtual override {
              require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
              _grantRole(role, account);
          }
          /**
           * @dev Revokes `role` from `account`.
           *
           * If `account` had been granted `role`, emits a {RoleRevoked} event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function revokeRole(bytes32 role, address account) public virtual override {
              require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
              _revokeRole(role, account);
          }
          /**
           * @dev Revokes `role` from the calling account.
           *
           * Roles are often managed via {grantRole} and {revokeRole}: this function's
           * purpose is to provide a mechanism for accounts to lose their privileges
           * if they are compromised (such as when a trusted device is misplaced).
           *
           * If the calling account had been granted `role`, emits a {RoleRevoked}
           * event.
           *
           * Requirements:
           *
           * - the caller must be `account`.
           */
          function renounceRole(bytes32 role, address account) public virtual override {
              require(account == _msgSender(), "AccessControl: can only renounce roles for self");
              _revokeRole(role, account);
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event. Note that unlike {grantRole}, this function doesn't perform any
           * checks on the calling account.
           *
           * [WARNING]
           * ====
           * This function should only be called from the constructor when setting
           * up the initial roles for the system.
           *
           * Using this function in any other way is effectively circumventing the admin
           * system imposed by {AccessControl}.
           * ====
           */
          function _setupRole(bytes32 role, address account) internal virtual {
              _grantRole(role, account);
          }
          /**
           * @dev Sets `adminRole` as ``role``'s admin role.
           */
          function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
              _roles[role].adminRole = adminRole;
          }
          function _grantRole(bytes32 role, address account) private {
              if (_roles[role].members.add(account)) {
                  emit RoleGranted(role, account, _msgSender());
              }
          }
          function _revokeRole(bytes32 role, address account) private {
              if (_roles[role].members.remove(account)) {
                  emit RoleRevoked(role, account, _msgSender());
              }
          }
          uint256[49] private __gap;
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity ^0.8.7;
      import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
      contract InitializableWithGap is Initializable {
          uint256[50] private ______gap;
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          FractionUtils.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Dmytro Stebaiev
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity 0.8.17;
      library FractionUtils {
          struct Fraction {
              uint256 numerator;
              uint256 denominator;
          }
          function createFraction(uint256 numerator, uint256 denominator) internal pure returns (Fraction memory fraction) {
              require(denominator > 0, "Division by zero");
              fraction = Fraction({numerator: numerator, denominator: denominator});
              reduceFraction(fraction);
              return fraction;
          }
          function createFraction(uint256 value) internal pure returns (Fraction memory fraction) {
              return createFraction(value, 1);
          }
          function reduceFraction(Fraction memory fraction) internal pure {
              uint256 _gcd = gcd(fraction.numerator, fraction.denominator);
              fraction.numerator = fraction.numerator / _gcd;
              fraction.denominator = fraction.denominator / _gcd;
          }
          // numerator - is limited by 7*10^27, we could multiply it numerator * numerator - it would less than 2^256-1
          function multiplyFraction(Fraction memory a, Fraction memory b) internal pure returns (Fraction memory fraction) {
              return createFraction(a.numerator * b.numerator, a.denominator * b.denominator);
          }
          function gcd(uint256 a, uint256 b) internal pure returns (uint256 value) {
              uint256 _a = a;
              uint256 _b = b;
              if (_b > _a) {
                  (_a, _b) = swap(_a, _b);
              }
              while (_b > 0) {
                  _a = _a % _b;
                  (_a, _b) = swap (_a, _b);
              }
              return _a;
          }
          function swap(uint256 a, uint256 b) internal pure returns (uint256 left, uint256 right) {
              return (b, a);
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      /*
          MathUtils.sol - SKALE Manager
          Copyright (C) 2018-Present SKALE Labs
          @author Dmytro Stebaiev
          SKALE Manager is free software: you can redistribute it and/or modify
          it under the terms of the GNU Affero General Public License as published
          by the Free Software Foundation, either version 3 of the License, or
          (at your option) any later version.
          SKALE Manager 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 Affero General Public License for more details.
          You should have received a copy of the GNU Affero General Public License
          along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
      */
      pragma solidity 0.8.17;
      library MathUtils {
          uint256 constant private _EPS = 1e6;
          event UnderflowError(
              uint256 a,
              uint256 b
          );
          function boundedSub(uint256 a, uint256 b) internal returns (uint256 value) {
              if (a >= b) {
                  return a - b;
              } else {
                  emit UnderflowError(a, b);
                  return 0;
              }
          }
          function boundedSubWithoutEvent(uint256 a, uint256 b) internal pure returns (uint256 value) {
              if (a >= b) {
                  return a - b;
              } else {
                  return 0;
              }
          }
          function muchGreater(uint256 a, uint256 b) internal pure returns (bool result) {
              assert(type(uint).max - _EPS > b);
              return a > b + _EPS;
          }
          function approximatelyEqual(uint256 a, uint256 b) internal pure returns (bool result) {
              if (a > b) {
                  return a - b < _EPS;
              } else {
                  return b - a < _EPS;
              }
          }
      }