ETH Price: $2,643.71 (-0.29%)

Contract

0x1F5b389e9a505aa1465507C40E1cfF67F536eC9d
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040108158332020-09-07 17:19:071503 days ago1599499147IN
 Create: DelegationController
0 ETH0.77518583146

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DelegationController

Compiler Version
v0.6.10+commit.00c0fcaf

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, GNU AGPLv3 license

Contract Source Code (Solidity Multiple files format)

File 1 of 45: DelegationController.sol
// 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.6.10;
pragma experimental ABIEncoderV2;

import "./IERC777.sol";

import "./Permissions.sol";
import "./Nodes.sol";
import "./MathUtils.sol";
import "./FractionUtils.sol";

import "./DelegationPeriodManager.sol";
import "./Punisher.sol";
import "./TokenLaunchLocker.sol";
import "./TokenState.sol";
import "./ValidatorService.sol";
import "./PartialDifferences.sol";

/**
 * @title Delegation Controller
 * @dev This contract performs all delegation functions including delegation
 * requests, undelegation, slashing, etc.
 *
 * Delegators and validators may both perform delegations. Validators who perform
 * delegations to themselves are effectively self-delegating or self-bonding.
 *
 * 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 {
    using MathUtils for uint;
    using PartialDifferences for PartialDifferences.Sequence;
    using PartialDifferences for PartialDifferences.Value;
    using FractionUtils for FractionUtils.Fraction;

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

    struct SlashingLogEvent {
        FractionUtils.Fraction reducingCoefficient;
        uint nextMonth;
    }

    struct SlashingLog {
        //      month => slashing event
        mapping (uint => SlashingLogEvent) slashes;
        uint firstMonth;
        uint lastMonth;
    }

    struct DelegationExtras {
        uint lastSlashingMonthBeforeDelegation;
    }

    struct SlashingEvent {
        FractionUtils.Fraction reducingCoefficient;
        uint validatorId;
        uint month;
    }

    struct SlashingSignal {
        address holder;
        uint penalty;
    }

    struct LockedInPending {
        uint amount;
        uint month;
    }

    struct FirstDelegationMonth {
        // month
        uint value;
        //validatorId => month
        mapping (uint => uint) byValidator;
    }

    struct ValidatorsStatistics {
        // number of validators
        uint number;
        //validatorId => bool - is Delegated or not
        mapping (uint => uint) delegated;
    }

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

    /// @dev delegations will never be deleted to index in this array may be used like delegation id
    Delegation[] public delegations;

    // validatorId => delegationId[]
    mapping (uint => uint[]) public delegationsByValidator;

    //        holder => delegationId[]
    mapping (address => uint[]) public delegationsByHolder;

    // delegationId => extras
    mapping(uint => DelegationExtras) private _delegationExtras;

    // validatorId => sequence
    mapping (uint => PartialDifferences.Value) private _delegatedToValidator;
    // validatorId => sequence
    mapping (uint => PartialDifferences.Sequence) private _effectiveDelegatedToValidator;

    // validatorId => slashing log
    mapping (uint => SlashingLog) private _slashesOfValidator;

    //        holder => sequence
    mapping (address => PartialDifferences.Value) private _delegatedByHolder;
    //        holder =>   validatorId => sequence
    mapping (address => mapping (uint => PartialDifferences.Value)) private _delegatedByHolderToValidator;
    //        holder =>   validatorId => sequence
    mapping (address => mapping (uint => PartialDifferences.Sequence)) private _effectiveDelegatedByHolderToValidator;

    SlashingEvent[] private _slashes;
    //        holder => index in _slashes;
    mapping (address => uint) 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(uint delegationId) {
        require(delegationId < delegations.length, "Delegation does not exist");
        _;
    }

    function getAndUpdateDelegatedToValidatorNow(uint validatorId) external returns (uint) {
        return getAndUpdateDelegatedToValidator(validatorId, _getCurrentMonth());
    }

    function getAndUpdateDelegatedAmount(address holder) external returns (uint) {
        return _getAndUpdateDelegatedByHolder(holder);
    }

    function getAndUpdateEffectiveDelegatedByHolderToValidator(address holder, uint validatorId, uint month) external
        allow("Distributor") returns (uint 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.
     *
     * @param validatorId uint ID of validator to receive delegation proposal
     * @param amount uint amount of proposed delegation
     * @param delegationPeriod uint period of proposed delegation
     * @param info string extra information provided by the token holder (if any)
     */
    function delegate(
        uint validatorId,
        uint amount,
        uint delegationPeriod,
        string calldata info
    )
        external
    {
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        DelegationPeriodManager delegationPeriodManager = DelegationPeriodManager(
            contractManager.getContract("DelegationPeriodManager"));
        IERC777 skaleToken = IERC777(contractManager.getContract("SkaleToken"));
        TokenState tokenState = TokenState(contractManager.getContract("TokenState"));

        require(
            validatorService.checkMinimumDelegation(validatorId, amount),
            "Amount does not meet the validator's minimum delegation amount");
        require(
            validatorService.isAuthorizedValidator(validatorId),
            "Validator is not authorized to accept delegation request");
        require(
            delegationPeriodManager.isDelegationPeriodAllowed(delegationPeriod),
            "This delegation period is not allowed");
        require(
            validatorService.isAcceptingNewRequests(validatorId),
            "The validator is not currently accepting new requests");
        _checkIfDelegationIsAllowed(msg.sender, validatorId);

        SlashingSignal[] memory slashingSignals = _processAllSlashesWithoutSignals(msg.sender);

        uint delegationId = _addDelegation(
            msg.sender,
            validatorId,
            amount,
            delegationPeriod,
            info);

        // check that there is enough money
        uint holderBalance = skaleToken.balanceOf(msg.sender);
        uint forbiddenForDelegation = tokenState.getAndUpdateForbiddenForDelegationAmount(msg.sender);
        require(holderBalance >= forbiddenForDelegation, "Token holder does not have enough tokens to delegate");

        emit DelegationProposed(delegationId);

        _sendSlashingSignals(slashingSignals);
    }

    /**
     * @dev See ILocker.
     */
    function getAndUpdateLockedAmount(address wallet) external override returns (uint) {
        return _getAndUpdateLockedAmount(wallet);
    }

    /**
     * @dev See ILocker.
     */
    function getAndUpdateForbiddenForDelegationAmount(address wallet) external override returns (uint) {
        return _getAndUpdateLockedAmount(wallet);
    }

    /**
     * @dev Allows a token holder to cancel a delegation proposal.
     *
     * Requirements:
     *
     * - the sender must be the token holder of the delegation proposal.
     * - the delegation must still be in a PROPOSED state.
     *
     * Emits a DelegationRequestCanceledByUser event.
     *
     * @param delegationId uint ID of delegation proposal
     */
    function cancelPendingDelegation(uint delegationId) external 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.
     *
     * @param delegationId uint ID of delegation proposal
     */
    function acceptPendingDelegation(uint delegationId) external checkDelegationExists(delegationId) {
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        require(
            validatorService.checkValidatorAddressToId(msg.sender, delegations[delegationId].validatorId),
            "No permissions to accept request");
        _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");
        
        TokenLaunchLocker tokenLaunchLocker = TokenLaunchLocker(contractManager.getContract("TokenLaunchLocker"));

        SlashingSignal[] memory slashingSignals = _processAllSlashesWithoutSignals(delegations[delegationId].holder);

        _addToAllStatistics(delegationId);

        tokenLaunchLocker.handleDelegationAdd(
            delegations[delegationId].holder,
            delegationId,
            delegations[delegationId].amount,
            delegations[delegationId].started);

        _sendSlashingSignals(slashingSignals);

        emit DelegationAccepted(delegationId);
    }

    /**
     * @dev Allows a delegator to undelegate a specific delegation.
     *
     * Requirements:
     *
     * - the sender must be the delegator.
     * - the delegation must be in DELEGATED state.
     *
     * Emits an UndelegationRequested event.
     *
     * @param delegationId uint ID of delegation to undelegate
     */
    function requestUndelegation(uint delegationId) external checkDelegationExists(delegationId) {
        require(getState(delegationId) == State.DELEGATED, "Cannot request undelegation");
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        require(
            delegations[delegationId].holder == msg.sender ||
            (validatorService.validatorAddressExists(msg.sender) &&
            delegations[delegationId].validatorId == validatorService.getValidatorId(msg.sender)),
            "Permission denied to request undelegation");
        TokenLaunchLocker tokenLaunchLocker = TokenLaunchLocker(contractManager.getContract("TokenLaunchLocker"));
        DelegationPeriodManager delegationPeriodManager = DelegationPeriodManager(
            contractManager.getContract("DelegationPeriodManager"));
        _removeValidatorFromValidatorsPerDelegators(
            delegations[delegationId].holder,
            delegations[delegationId].validatorId
        );
        processAllSlashes(msg.sender);
        delegations[delegationId].finished = _calculateDelegationEndMonth(delegationId);
        uint 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);
        uint effectiveAmount = amountAfterSlashing.mul(delegationPeriodManager.stakeMultipliers(
            delegations[delegationId].delegationPeriod));
        _removeFromEffectiveDelegatedToValidator(
            delegations[delegationId].validatorId,
            effectiveAmount,
            delegations[delegationId].finished);
        _removeFromEffectiveDelegatedByHolderToValidator(
            delegations[delegationId].holder,
            delegations[delegationId].validatorId,
            effectiveAmount,
            delegations[delegationId].finished);
        tokenLaunchLocker.handleDelegationRemoving(
            delegations[delegationId].holder,
            delegationId,
            delegations[delegationId].finished);
        emit UndelegationRequested(delegationId);
    }

    /**
     * @dev Allows the Punisher to confiscate an `amount` of stake from
     * `validatorId` by slashing. This slashes all delegations of the validator,
     * which reduces the amount that the validator has staked. This consequence
     * may force the SKALE Manger to reduce the number of nodes a validator is
     * operating so the validator can meet the Minimum Staking Requirement.
     *
     * See Punisher.
     *
     * Emits a SlashingEvent.
     *
     * @param validatorId uint validator to slash
     * @param amount uint amount to slash
     *
     */
    function confiscate(uint validatorId, uint amount) external allow("Punisher") {
        uint currentMonth = _getCurrentMonth();
        FractionUtils.Fraction memory coefficient =
            _delegatedToValidator[validatorId].reduceValue(amount, currentMonth);
        _effectiveDelegatedToValidator[validatorId].reduceSequence(coefficient, currentMonth);
        _putToSlashingLog(_slashesOfValidator[validatorId], coefficient, currentMonth);
        _slashes.push(SlashingEvent({reducingCoefficient: coefficient, validatorId: validatorId, month: currentMonth}));
    }

    function getAndUpdateEffectiveDelegatedToValidator(uint validatorId, uint month)
        external allow("Distributor") returns (uint)
    {
        return _effectiveDelegatedToValidator[validatorId].getAndUpdateValueInSequence(month);
    }

    function getAndUpdateDelegatedByHolderToValidatorNow(address holder, uint validatorId) external returns (uint) {
        return _getAndUpdateDelegatedByHolderToValidator(holder, validatorId, _getCurrentMonth());
    }

    function getDelegation(uint delegationId)
        external view checkDelegationExists(delegationId) returns (Delegation memory)
    {
        return delegations[delegationId];
    }

    function getFirstDelegationMonth(address holder, uint validatorId) external view returns(uint) {
        return _firstDelegationMonth[holder].byValidator[validatorId];
    }

    function getDelegationsByValidatorLength(uint validatorId) external view returns (uint) {
        return delegationsByValidator[validatorId].length;
    }

    function getDelegationsByHolderLength(address holder) external view returns (uint) {
        return delegationsByHolder[holder].length;
    }

    function initialize(address contractsAddress) public override initializer {
        Permissions.initialize(contractsAddress);
    }

    function getAndUpdateDelegatedToValidator(uint validatorId, uint month)
        public allow("Nodes") returns (uint)
    {
        return _delegatedToValidator[validatorId].getAndUpdateValue(month);
    }

    function processSlashes(address holder, uint limit) public {
        _sendSlashingSignals(_processSlashesWithoutSignals(holder, limit));
    }

    function processAllSlashes(address holder) public {
        processSlashes(holder, 0);
    }

    /**
     * @dev Returns the token state of a given delegation.
     *
     * @param delegationId uint ID of the delegation
     */
    function getState(uint delegationId) public view checkDelegationExists(delegationId) returns (State state) {
        if (delegations[delegationId].started == 0) {
            if (delegations[delegationId].finished == 0) {
                TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract("TimeHelpers"));
                if (_getCurrentMonth() == timeHelpers.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;
                    }
                }
            }
        }
    }

    function getLockedInPendingDelegations(address holder) public view returns (uint) {
        uint currentMonth = _getCurrentMonth();
        if (_lockedInPendingDelegations[holder].month < currentMonth) {
            return 0;
        } else {
            return _lockedInPendingDelegations[holder].amount;
        }
    }

    function hasUnprocessedSlashes(address holder) public view returns (bool) {
        return _everDelegated(holder) && _firstUnprocessedSlashByHolder[holder] < _slashes.length;
    }    

    // private

    function _addDelegation(
        address holder,
        uint validatorId,
        uint amount,
        uint delegationPeriod,
        string memory info
    )
        private
        returns (uint delegationId)
    {
        delegationId = delegations.length;
        delegations.push(Delegation(
            holder,
            validatorId,
            amount,
            delegationPeriod,
            now,
            0,
            0,
            info
        ));
        delegationsByValidator[validatorId].push(delegationId);
        delegationsByHolder[holder].push(delegationId);
        _addToLockedInPendingDelegations(delegations[delegationId].holder, delegations[delegationId].amount);
    }

    function _calculateDelegationEndMonth(uint delegationId) private view returns (uint) {
        uint currentMonth = _getCurrentMonth();
        uint started = delegations[delegationId].started;

        if (currentMonth < started) {
            return started.add(delegations[delegationId].delegationPeriod);
        } else {
            uint completedPeriods = currentMonth.sub(started).div(delegations[delegationId].delegationPeriod);
            return started.add(completedPeriods.add(1).mul(delegations[delegationId].delegationPeriod));
        }
    }

    function _addToDelegatedToValidator(uint validatorId, uint amount, uint month) private {
        _delegatedToValidator[validatorId].addToValue(amount, month);
    }

    function _addToEffectiveDelegatedToValidator(uint validatorId, uint effectiveAmount, uint month) private {
        _effectiveDelegatedToValidator[validatorId].addToSequence(effectiveAmount, month);
    }

    function _addToDelegatedByHolder(address holder, uint amount, uint month) private {
        _delegatedByHolder[holder].addToValue(amount, month);
    }

    function _addToDelegatedByHolderToValidator(
        address holder, uint validatorId, uint amount, uint month) private
    {
        _delegatedByHolderToValidator[holder][validatorId].addToValue(amount, month);
    }

    function _addValidatorToValidatorsPerDelegators(address holder, uint validatorId) private {
        if (_numberOfValidatorsPerDelegator[holder].delegated[validatorId] == 0) {
            _numberOfValidatorsPerDelegator[holder].number = _numberOfValidatorsPerDelegator[holder].number.add(1);
        }
        _numberOfValidatorsPerDelegator[holder].
            delegated[validatorId] = _numberOfValidatorsPerDelegator[holder].delegated[validatorId].add(1);
    }

    function _removeFromDelegatedByHolder(address holder, uint amount, uint month) private {
        _delegatedByHolder[holder].subtractFromValue(amount, month);
    }

    function _removeFromDelegatedByHolderToValidator(
        address holder, uint validatorId, uint amount, uint month) private
    {
        _delegatedByHolderToValidator[holder][validatorId].subtractFromValue(amount, month);
    }

    function _removeValidatorFromValidatorsPerDelegators(address holder, uint validatorId) private {
        if (_numberOfValidatorsPerDelegator[holder].delegated[validatorId] == 1) {
            _numberOfValidatorsPerDelegator[holder].number = _numberOfValidatorsPerDelegator[holder].number.sub(1);
        }
        _numberOfValidatorsPerDelegator[holder].
            delegated[validatorId] = _numberOfValidatorsPerDelegator[holder].delegated[validatorId].sub(1);
    }

    function _addToEffectiveDelegatedByHolderToValidator(
        address holder,
        uint validatorId,
        uint effectiveAmount,
        uint month)
        private
    {
        _effectiveDelegatedByHolderToValidator[holder][validatorId].addToSequence(effectiveAmount, month);
    }

    function _removeFromEffectiveDelegatedByHolderToValidator(
        address holder,
        uint validatorId,
        uint effectiveAmount,
        uint month)
        private
    {
        _effectiveDelegatedByHolderToValidator[holder][validatorId].subtractFromSequence(effectiveAmount, month);
    }

    function _getAndUpdateDelegatedByHolder(address holder) private returns (uint) {
        uint currentMonth = _getCurrentMonth();
        processAllSlashes(holder);
        return _delegatedByHolder[holder].getAndUpdateValue(currentMonth);
    }

    function _getAndUpdateDelegatedByHolderToValidator(
        address holder,
        uint validatorId,
        uint month)
        private returns (uint)
    {
        return _delegatedByHolderToValidator[holder][validatorId].getAndUpdateValue(month);
    }

    function _addToLockedInPendingDelegations(address holder, uint amount) private returns (uint) {
        uint 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.add(amount);
        }
    }

    function _subtractFromLockedInPendingDelegations(address holder, uint amount) private returns (uint) {
        uint currentMonth = _getCurrentMonth();
        require(
            _lockedInPendingDelegations[holder].month == currentMonth,
            "There are no delegation requests this month");
        require(_lockedInPendingDelegations[holder].amount >= amount, "Unlocking amount is too big");
        _lockedInPendingDelegations[holder].amount = _lockedInPendingDelegations[holder].amount.sub(amount);
    }

    function _getCurrentMonth() private view returns (uint) {
        TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract("TimeHelpers"));
        return timeHelpers.getCurrentMonth();
    }

    function _getAndUpdateLockedAmount(address wallet) private returns (uint) {
        return _getAndUpdateDelegatedByHolder(wallet).add(getLockedInPendingDelegations(wallet));
    }

    function _updateFirstDelegationMonth(address holder, uint validatorId, uint 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 _everDelegated(address holder) private view returns (bool) {
        return _firstDelegationMonth[holder].value > 0;
    }

    function _removeFromDelegatedToValidator(uint validatorId, uint amount, uint month) private {
        _delegatedToValidator[validatorId].subtractFromValue(amount, month);
    }

    function _removeFromEffectiveDelegatedToValidator(uint validatorId, uint effectiveAmount, uint month) private {
        _effectiveDelegatedToValidator[validatorId].subtractFromSequence(effectiveAmount, month);
    }

    function _calculateDelegationAmountAfterSlashing(uint delegationId) private view returns (uint) {
        uint startMonth = _delegationExtras[delegationId].lastSlashingMonthBeforeDelegation;
        uint validatorId = delegations[delegationId].validatorId;
        uint amount = delegations[delegationId].amount;
        if (startMonth == 0) {
            startMonth = _slashesOfValidator[validatorId].firstMonth;
            if (startMonth == 0) {
                return amount;
            }
        }
        for (uint i = startMonth;
            i > 0 && i < delegations[delegationId].finished;
            i = _slashesOfValidator[validatorId].slashes[i].nextMonth) {
            if (i >= delegations[delegationId].started) {
                amount = amount
                    .mul(_slashesOfValidator[validatorId].slashes[i].reducingCoefficient.numerator)
                    .div(_slashesOfValidator[validatorId].slashes[i].reducingCoefficient.denominator);
            }
        }
        return amount;
    }

    function _putToSlashingLog(
        SlashingLog storage log,
        FractionUtils.Fraction memory coefficient,
        uint 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, uint limit)
        private returns (SlashingSignal[] memory slashingSignals)
    {
        if (hasUnprocessedSlashes(holder)) {
            uint index = _firstUnprocessedSlashByHolder[holder];
            uint end = _slashes.length;
            if (limit > 0 && index.add(limit) < end) {
                end = index.add(limit);
            }
            slashingSignals = new SlashingSignal[](end.sub(index));
            uint begin = index;
            for (; index < end; ++index) {
                uint validatorId = _slashes[index].validatorId;
                uint month = _slashes[index].month;
                uint 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.sub(begin)].holder = holder;
                    slashingSignals[index.sub(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 {
        Punisher punisher = Punisher(contractManager.getContract("Punisher"));
        address previousHolder = address(0);
        uint accumulatedPenalty = 0;
        for (uint 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.add(slashingSignals[i].penalty);
            }
        }
        if (accumulatedPenalty > 0) {
            punisher.handleSlash(previousHolder, accumulatedPenalty);
        }
    }

    function _addToAllStatistics(uint delegationId) private {
        DelegationPeriodManager delegationPeriodManager = DelegationPeriodManager(
            contractManager.getContract("DelegationPeriodManager"));

        uint currentMonth = _getCurrentMonth();
        delegations[delegationId].started = currentMonth.add(1);
        if (_slashesOfValidator[delegations[delegationId].validatorId].lastMonth > 0) {
            _delegationExtras[delegationId].lastSlashingMonthBeforeDelegation =
                _slashesOfValidator[delegations[delegationId].validatorId].lastMonth;
        }

        _addToDelegatedToValidator(
            delegations[delegationId].validatorId,
            delegations[delegationId].amount,
            currentMonth.add(1));
        _addToDelegatedByHolder(
            delegations[delegationId].holder,
            delegations[delegationId].amount,
            currentMonth.add(1));
        _addToDelegatedByHolderToValidator(
            delegations[delegationId].holder,
            delegations[delegationId].validatorId,
            delegations[delegationId].amount,
            currentMonth.add(1));
        _updateFirstDelegationMonth(
            delegations[delegationId].holder,
            delegations[delegationId].validatorId,
            currentMonth.add(1));
        uint effectiveAmount = delegations[delegationId].amount.mul(delegationPeriodManager.stakeMultipliers(
            delegations[delegationId].delegationPeriod));
        _addToEffectiveDelegatedToValidator(
            delegations[delegationId].validatorId,
            effectiveAmount,
            currentMonth.add(1));
        _addToEffectiveDelegatedByHolderToValidator(
            delegations[delegationId].holder,
            delegations[delegationId].validatorId,
            effectiveAmount,
            currentMonth.add(1));
        _addValidatorToValidatorsPerDelegators(
            delegations[delegationId].holder,
            delegations[delegationId].validatorId
        );
    }

    function _checkIfDelegationIsAllowed(address holder, uint validatorId) private view returns (bool) {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        require(
            _numberOfValidatorsPerDelegator[holder].delegated[validatorId] > 0 ||
                (
                    _numberOfValidatorsPerDelegator[holder].delegated[validatorId] == 0 &&
                    _numberOfValidatorsPerDelegator[holder].number < constantsHolder.limitValidatorsPerDelegator()
                ),
            "Limit of validators is reached"
        );
    }
}

File 2 of 45: AccessControl.sol
pragma solidity ^0.6.0;

import "./EnumerableSet.sol";
import "./Address.sol";
import "./Context.sol";
import "./Initializable.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 AccessControlUpgradeSafe is Initializable, ContextUpgradeSafe {
    function __AccessControl_init() internal initializer {
        __Context_init_unchained();
        __AccessControl_init_unchained();
    }

    function __AccessControl_init_unchained() internal initializer {


    }

    using EnumerableSet for EnumerableSet.AddressSet;
    using Address for address;

    struct RoleData {
        EnumerableSet.AddressSet members;
        bytes32 adminRole;
    }

    mapping (bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

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

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view 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 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 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 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 {
        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 {
        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 {
        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;
}

File 3 of 45: Address.sol
pragma solidity ^0.6.2;

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

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

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

File 4 of 45: BokkyPooBahsDateTimeLibrary.sol
pragma solidity ^0.6.0;

// ----------------------------------------------------------------------------
// BokkyPooBah's DateTime Library v1.01
//
// A gas-efficient Solidity date and time library
//
// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary
//
// Tested date range 1970/01/01 to 2345/12/31
//
// Conventions:
// Unit      | Range         | Notes
// :-------- |:-------------:|:-----
// timestamp | >= 0          | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC
// year      | 1970 ... 2345 |
// month     | 1 ... 12      |
// day       | 1 ... 31      |
// hour      | 0 ... 23      |
// minute    | 0 ... 59      |
// second    | 0 ... 59      |
// dayOfWeek | 1 ... 7       | 1 = Monday, ..., 7 = Sunday
//
//
// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018-2019. The MIT Licence.
// ----------------------------------------------------------------------------

library BokkyPooBahsDateTimeLibrary {

    uint constant SECONDS_PER_DAY = 24 * 60 * 60;
    uint constant SECONDS_PER_HOUR = 60 * 60;
    uint constant SECONDS_PER_MINUTE = 60;
    int constant OFFSET19700101 = 2440588;

    uint constant DOW_MON = 1;
    uint constant DOW_TUE = 2;
    uint constant DOW_WED = 3;
    uint constant DOW_THU = 4;
    uint constant DOW_FRI = 5;
    uint constant DOW_SAT = 6;
    uint constant DOW_SUN = 7;

    // ------------------------------------------------------------------------
    // Calculate the number of days from 1970/01/01 to year/month/day using
    // the date conversion algorithm from
    //   http://aa.usno.navy.mil/faq/docs/JD_Formula.php
    // and subtracting the offset 2440588 so that 1970/01/01 is day 0
    //
    // days = day
    //      - 32075
    //      + 1461 * (year + 4800 + (month - 14) / 12) / 4
    //      + 367 * (month - 2 - (month - 14) / 12 * 12) / 12
    //      - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4
    //      - offset
    // ------------------------------------------------------------------------
    function _daysFromDate(uint year, uint month, uint day) internal pure returns (uint _days) {
        require(year >= 1970);
        int _year = int(year);
        int _month = int(month);
        int _day = int(day);

        int __days = _day
          - 32075
          + 1461 * (_year + 4800 + (_month - 14) / 12) / 4
          + 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12
          - 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4
          - OFFSET19700101;

        _days = uint(__days);
    }

    // ------------------------------------------------------------------------
    // Calculate year/month/day from the number of days since 1970/01/01 using
    // the date conversion algorithm from
    //   http://aa.usno.navy.mil/faq/docs/JD_Formula.php
    // and adding the offset 2440588 so that 1970/01/01 is day 0
    //
    // int L = days + 68569 + offset
    // int N = 4 * L / 146097
    // L = L - (146097 * N + 3) / 4
    // year = 4000 * (L + 1) / 1461001
    // L = L - 1461 * year / 4 + 31
    // month = 80 * L / 2447
    // dd = L - 2447 * month / 80
    // L = month / 11
    // month = month + 2 - 12 * L
    // year = 100 * (N - 49) + year + L
    // ------------------------------------------------------------------------
    function _daysToDate(uint _days) internal pure returns (uint year, uint month, uint day) {
        int __days = int(_days);

        int L = __days + 68569 + OFFSET19700101;
        int N = 4 * L / 146097;
        L = L - (146097 * N + 3) / 4;
        int _year = 4000 * (L + 1) / 1461001;
        L = L - 1461 * _year / 4 + 31;
        int _month = 80 * L / 2447;
        int _day = L - 2447 * _month / 80;
        L = _month / 11;
        _month = _month + 2 - 12 * L;
        _year = 100 * (N - 49) + _year + L;

        year = uint(_year);
        month = uint(_month);
        day = uint(_day);
    }

    function timestampFromDate(uint year, uint month, uint day) internal pure returns (uint timestamp) {
        timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;
    }
    function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (uint timestamp) {
        timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second;
    }
    function timestampToDate(uint timestamp) internal pure returns (uint year, uint month, uint day) {
        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
    }
    function timestampToDateTime(uint timestamp) internal pure returns (uint year, uint month, uint day, uint hour, uint minute, uint second) {
        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
        uint secs = timestamp % SECONDS_PER_DAY;
        hour = secs / SECONDS_PER_HOUR;
        secs = secs % SECONDS_PER_HOUR;
        minute = secs / SECONDS_PER_MINUTE;
        second = secs % SECONDS_PER_MINUTE;
    }

    function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) {
        if (year >= 1970 && month > 0 && month <= 12) {
            uint daysInMonth = _getDaysInMonth(year, month);
            if (day > 0 && day <= daysInMonth) {
                valid = true;
            }
        }
    }
    function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) {
        if (isValidDate(year, month, day)) {
            if (hour < 24 && minute < 60 && second < 60) {
                valid = true;
            }
        }
    }
    function isLeapYear(uint timestamp) internal pure returns (bool leapYear) {
        uint year;
        uint month;
        uint day;
        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
        leapYear = _isLeapYear(year);
    }
    function _isLeapYear(uint year) internal pure returns (bool leapYear) {
        leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
    }
    function isWeekDay(uint timestamp) internal pure returns (bool weekDay) {
        weekDay = getDayOfWeek(timestamp) <= DOW_FRI;
    }
    function isWeekEnd(uint timestamp) internal pure returns (bool weekEnd) {
        weekEnd = getDayOfWeek(timestamp) >= DOW_SAT;
    }
    function getDaysInMonth(uint timestamp) internal pure returns (uint daysInMonth) {
        uint year;
        uint month;
        uint day;
        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
        daysInMonth = _getDaysInMonth(year, month);
    }
    function _getDaysInMonth(uint year, uint month) internal pure returns (uint daysInMonth) {
        if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
            daysInMonth = 31;
        } else if (month != 2) {
            daysInMonth = 30;
        } else {
            daysInMonth = _isLeapYear(year) ? 29 : 28;
        }
    }
    // 1 = Monday, 7 = Sunday
    function getDayOfWeek(uint timestamp) internal pure returns (uint dayOfWeek) {
        uint _days = timestamp / SECONDS_PER_DAY;
        dayOfWeek = (_days + 3) % 7 + 1;
    }

    function getYear(uint timestamp) internal pure returns (uint year) {
        uint month;
        uint day;
        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
    }
    function getMonth(uint timestamp) internal pure returns (uint month) {
        uint year;
        uint day;
        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
    }
    function getDay(uint timestamp) internal pure returns (uint day) {
        uint year;
        uint month;
        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
    }
    function getHour(uint timestamp) internal pure returns (uint hour) {
        uint secs = timestamp % SECONDS_PER_DAY;
        hour = secs / SECONDS_PER_HOUR;
    }
    function getMinute(uint timestamp) internal pure returns (uint minute) {
        uint secs = timestamp % SECONDS_PER_HOUR;
        minute = secs / SECONDS_PER_MINUTE;
    }
    function getSecond(uint timestamp) internal pure returns (uint second) {
        second = timestamp % SECONDS_PER_MINUTE;
    }

    function addYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) {
        uint year;
        uint month;
        uint day;
        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
        year += _years;
        uint daysInMonth = _getDaysInMonth(year, month);
        if (day > daysInMonth) {
            day = daysInMonth;
        }
        newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
        require(newTimestamp >= timestamp);
    }
    function addMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) {
        uint year;
        uint month;
        uint day;
        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
        month += _months;
        year += (month - 1) / 12;
        month = (month - 1) % 12 + 1;
        uint daysInMonth = _getDaysInMonth(year, month);
        if (day > daysInMonth) {
            day = daysInMonth;
        }
        newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
        require(newTimestamp >= timestamp);
    }
    function addDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) {
        newTimestamp = timestamp + _days * SECONDS_PER_DAY;
        require(newTimestamp >= timestamp);
    }
    function addHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) {
        newTimestamp = timestamp + _hours * SECONDS_PER_HOUR;
        require(newTimestamp >= timestamp);
    }
    function addMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) {
        newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE;
        require(newTimestamp >= timestamp);
    }
    function addSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) {
        newTimestamp = timestamp + _seconds;
        require(newTimestamp >= timestamp);
    }

    function subYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) {
        uint year;
        uint month;
        uint day;
        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
        year -= _years;
        uint daysInMonth = _getDaysInMonth(year, month);
        if (day > daysInMonth) {
            day = daysInMonth;
        }
        newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
        require(newTimestamp <= timestamp);
    }
    function subMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) {
        uint year;
        uint month;
        uint day;
        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
        uint yearMonth = year * 12 + (month - 1) - _months;
        year = yearMonth / 12;
        month = yearMonth % 12 + 1;
        uint daysInMonth = _getDaysInMonth(year, month);
        if (day > daysInMonth) {
            day = daysInMonth;
        }
        newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
        require(newTimestamp <= timestamp);
    }
    function subDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) {
        newTimestamp = timestamp - _days * SECONDS_PER_DAY;
        require(newTimestamp <= timestamp);
    }
    function subHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) {
        newTimestamp = timestamp - _hours * SECONDS_PER_HOUR;
        require(newTimestamp <= timestamp);
    }
    function subMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) {
        newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE;
        require(newTimestamp <= timestamp);
    }
    function subSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) {
        newTimestamp = timestamp - _seconds;
        require(newTimestamp <= timestamp);
    }

    function diffYears(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _years) {
        require(fromTimestamp <= toTimestamp);
        uint fromYear;
        uint fromMonth;
        uint fromDay;
        uint toYear;
        uint toMonth;
        uint toDay;
        (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);
        (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY);
        _years = toYear - fromYear;
    }
    function diffMonths(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _months) {
        require(fromTimestamp <= toTimestamp);
        uint fromYear;
        uint fromMonth;
        uint fromDay;
        uint toYear;
        uint toMonth;
        uint toDay;
        (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);
        (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY);
        _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth;
    }
    function diffDays(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _days) {
        require(fromTimestamp <= toTimestamp);
        _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY;
    }
    function diffHours(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _hours) {
        require(fromTimestamp <= toTimestamp);
        _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR;
    }
    function diffMinutes(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _minutes) {
        require(fromTimestamp <= toTimestamp);
        _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE;
    }
    function diffSeconds(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _seconds) {
        require(fromTimestamp <= toTimestamp);
        _seconds = toTimestamp - fromTimestamp;
    }
}

File 5 of 45: Bounty.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    Bounty.sol - SKALE Manager
    Copyright (C) 2020-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;

import "./ConstantsHolder.sol";
import "./Nodes.sol";
import "./Permissions.sol";


contract Bounty is Permissions {

    uint public constant STAGE_LENGTH = 31558150; // 1 year
    uint public constant YEAR1_BOUNTY = 3850e5 * 1e18;
    uint public constant YEAR2_BOUNTY = 3465e5 * 1e18;
    uint public constant YEAR3_BOUNTY = 3080e5 * 1e18;
    uint public constant YEAR4_BOUNTY = 2695e5 * 1e18;
    uint public constant YEAR5_BOUNTY = 2310e5 * 1e18;
    uint public constant YEAR6_BOUNTY = 1925e5 * 1e18;
    uint public constant BOUNTY = 96250000 * 1e18;

    uint private _nextStage;
    uint private _stagePool;
    bool public bountyReduction;

    uint private _nodesPerRewardPeriod;
    uint private _nodesRemainingPerRewardPeriod;
    uint private _rewardPeriodFinished;

    function getBounty(
        uint nodeIndex,
        uint downtime,
        uint latency
    )
        external
        allow("SkaleManager")
        returns (uint)
    {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));

        _refillStagePool(constantsHolder);

        if (_rewardPeriodFinished <= now) {
            _updateNodesPerRewardPeriod(constantsHolder, nodes);
        }

        uint bounty = _calculateMaximumBountyAmount(_stagePool, _nextStage, nodeIndex, constantsHolder, nodes);

        bounty = _reduceBounty(
            bounty,
            nodeIndex,
            downtime,
            latency,
            nodes,
            constantsHolder
        );

        _stagePool = _stagePool.sub(bounty);
        _nodesRemainingPerRewardPeriod = _nodesRemainingPerRewardPeriod.sub(1);

        return bounty;
    }

    function enableBountyReduction() external onlyOwner {
        bountyReduction = true;
    }

    function disableBountyReduction() external onlyOwner {
        bountyReduction = false;
    }

    function calculateNormalBounty(uint nodeIndex) external view returns (uint) {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));

        uint stagePoolSize;
        uint nextStage;
        (stagePoolSize, nextStage) = _getStagePoolSize(constantsHolder);

        return _calculateMaximumBountyAmount(
            stagePoolSize,
            nextStage,
            nodeIndex,
            constantsHolder,
            nodes
        );
    }

    function initialize(address contractManagerAddress) public override initializer {
        Permissions.initialize(contractManagerAddress);
        _nextStage = 0;
        _stagePool = 0;
        _rewardPeriodFinished = 0;
        bountyReduction = false;
    }

    // private

    function _calculateMaximumBountyAmount(
        uint stagePoolSize,
        uint nextStage,
        uint nodeIndex,
        ConstantsHolder constantsHolder,
        Nodes nodes
    )
        private
        view
        returns (uint)
    {
        if (nodes.isNodeLeft(nodeIndex)) {
            return 0;
        }

        if (now < constantsHolder.launchTimestamp()) {
            // network is not launched
            // bounty is turned off
            return 0;
        }

        uint numberOfRewards = _getStageBeginningTimestamp(nextStage, constantsHolder)
            .sub(now)
            .div(constantsHolder.rewardPeriod());

        uint numberOfRewardsPerAllNodes = numberOfRewards.mul(_nodesPerRewardPeriod);

        return stagePoolSize.div(
            numberOfRewardsPerAllNodes.add(_nodesRemainingPerRewardPeriod)
        );
    }

    function _getStageBeginningTimestamp(uint stage, ConstantsHolder constantsHolder) private view returns (uint) {
        return constantsHolder.launchTimestamp().add(stage.mul(STAGE_LENGTH));
    }

    function _getStagePoolSize(ConstantsHolder constantsHolder) private view returns (uint stagePool, uint nextStage) {
        stagePool = _stagePool;
        for (nextStage = _nextStage; now >= _getStageBeginningTimestamp(nextStage, constantsHolder); ++nextStage) {
            stagePool += _getStageReward(_nextStage);
        }
    }

    function _refillStagePool(ConstantsHolder constantsHolder) private {
        (_stagePool, _nextStage) = _getStagePoolSize(constantsHolder);
    }

    function _updateNodesPerRewardPeriod(ConstantsHolder constantsHolder, Nodes nodes) private {
        _nodesPerRewardPeriod = nodes.getNumberOnlineNodes();
        _nodesRemainingPerRewardPeriod = _nodesPerRewardPeriod;
        _rewardPeriodFinished = now.add(uint(constantsHolder.rewardPeriod()));
    }

    function _getStageReward(uint stage) private pure returns (uint) {
        if (stage >= 6) {
            return BOUNTY.div(2 ** stage.sub(6).div(3));
        } else {
            if (stage == 0) {
                return YEAR1_BOUNTY;
            } else if (stage == 1) {
                return YEAR2_BOUNTY;
            } else if (stage == 2) {
                return YEAR3_BOUNTY;
            } else if (stage == 3) {
                return YEAR4_BOUNTY;
            } else if (stage == 4) {
                return YEAR5_BOUNTY;
            } else {
                return YEAR6_BOUNTY;
            }
        }
    }

    function _reduceBounty(
        uint bounty,
        uint nodeIndex,
        uint downtime,
        uint latency,
        Nodes nodes,
        ConstantsHolder constants
    )
        private
        returns (uint reducedBounty)
    {
        if (!bountyReduction) {
            return bounty;
        }

        reducedBounty = _reduceBountyByDowntime(bounty, nodeIndex, downtime, nodes, constants);

        if (latency > constants.allowableLatency()) {
            // reduce bounty because latency is too big
            reducedBounty = reducedBounty.mul(constants.allowableLatency()).div(latency);
        }

        if (!nodes.checkPossibilityToMaintainNode(nodes.getValidatorId(nodeIndex), nodeIndex)) {
            reducedBounty = reducedBounty.div(constants.MSR_REDUCING_COEFFICIENT());
        }
    }

    function _reduceBountyByDowntime(
        uint bounty,
        uint nodeIndex,
        uint downtime,
        Nodes nodes,
        ConstantsHolder constants
    )
        private
        view
        returns (uint reducedBounty)
    {
        reducedBounty = bounty;
        uint getBountyDeadline = uint(nodes.getNodeLastRewardDate(nodeIndex))
            .add(constants.rewardPeriod())
            .add(constants.deltaPeriod());
        uint numberOfExpiredIntervals;
        if (now > getBountyDeadline) {
            numberOfExpiredIntervals = now.sub(getBountyDeadline).div(constants.checkTime());
        } else {
            numberOfExpiredIntervals = 0;
        }
        uint normalDowntime = uint(constants.rewardPeriod())
            .sub(constants.deltaPeriod())
            .div(constants.checkTime())
            .div(constants.DOWNTIME_THRESHOLD_PART());
        uint totalDowntime = downtime.add(numberOfExpiredIntervals);
        if (totalDowntime > normalDowntime) {
            // reduce bounty because downtime is too big
            uint penalty = bounty
                .mul(totalDowntime)
                .div(
                    uint(constants.rewardPeriod()).sub(constants.deltaPeriod())
                        .div(constants.checkTime())
                );
            if (bounty > penalty) {
                reducedBounty = bounty.sub(penalty);
            } else {
                reducedBounty = 0;
            }
        }
    }
}

File 6 of 45: ConstantsHolder.sol
// SPDX-License-Identifier: AGPL-3.0-only

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

import "./Permissions.sol";


/**
 * @title Contains constants and common variables for Skale Manager system
 * @author Artem Payvin
 */
contract ConstantsHolder is Permissions {

    // initial price for creating Node (100 SKL)
    uint public constant NODE_DEPOSIT = 100 * 1e18;

    uint8 public constant TOTAL_SPACE_ON_NODE = 128;

    // part of Node for Small Skale-chain (1/128 of Node)
    uint8 public constant SMALL_DIVISOR = 128;

    // part of Node for Medium Skale-chain (1/8 of Node)
    uint8 public constant MEDIUM_DIVISOR = 8;

    // part of Node for Large Skale-chain (full Node)
    uint8 public constant LARGE_DIVISOR = 1;

    // part of Node for Medium Test Skale-chain (1/4 of Node)
    uint8 public constant MEDIUM_TEST_DIVISOR = 4;

    // typically number of Nodes for Skale-chain (16 Nodes)
    uint public constant NUMBER_OF_NODES_FOR_SCHAIN = 16;

    // number of Nodes for Test Skale-chain (2 Nodes)
    uint public constant NUMBER_OF_NODES_FOR_TEST_SCHAIN = 2;

    // number of Nodes for Test Skale-chain (4 Nodes)
    uint public constant NUMBER_OF_NODES_FOR_MEDIUM_TEST_SCHAIN = 4;    

    // number of seconds in one year
    uint32 public constant SECONDS_TO_YEAR = 31622400;

    // initial number of monitors
    uint public constant NUMBER_OF_MONITORS = 24;

    uint public constant OPTIMAL_LOAD_PERCENTAGE = 80;

    uint public constant ADJUSTMENT_SPEED = 1000;

    uint public constant COOLDOWN_TIME = 60;

    uint public constant MIN_PRICE = 10**6;

    uint public constant MSR_REDUCING_COEFFICIENT = 2;

    uint public constant DOWNTIME_THRESHOLD_PART = 30;

    uint public constant BOUNTY_LOCKUP_MONTHS = 2;

    // MSR - Minimum staking requirement
    uint public msr;

    // Reward period - 30 days (each 30 days Node would be granted for bounty)
    uint32 public rewardPeriod;

    // Allowable latency - 150000 ms by default
    uint32 public allowableLatency;

    /**
     * Delta period - 1 hour (1 hour before Reward period became Monitors need
     * to send Verdicts and 1 hour after Reward period became Node need to come
     * and get Bounty)
     */
    uint32 public deltaPeriod;

    /**
     * Check time - 2 minutes (every 2 minutes monitors should check metrics
     * from checked nodes)
     */
    uint public checkTime;

    //Need to add minimal allowed parameters for verdicts

    uint public launchTimestamp;

    uint public rotationDelay;

    uint public proofOfUseLockUpPeriodDays;

    uint public proofOfUseDelegationPercentage;

    uint public limitValidatorsPerDelegator;

    uint256 public firstDelegationsMonth; // deprecated

    // date when schains will be allowed for creation
    uint public schainCreationTimeStamp;

    uint public minimalSchainLifetime;

    /**
     * Set reward and delta periods to new one, run only by owner. This function
     * only for tests.
     * @param newRewardPeriod - new Reward period
     * @param newDeltaPeriod - new Delta period
     */
    function setPeriods(uint32 newRewardPeriod, uint32 newDeltaPeriod) external onlyOwner {
        require(
            newRewardPeriod >= newDeltaPeriod && newRewardPeriod - newDeltaPeriod >= checkTime,
            "Incorrect Periods"
        );
        rewardPeriod = newRewardPeriod;
        deltaPeriod = newDeltaPeriod;
    }

    /**
     * Set new check time. This function only for tests.
     * @param newCheckTime - new check time
     */
    function setCheckTime(uint newCheckTime) external onlyOwner {
        require(rewardPeriod - deltaPeriod >= checkTime, "Incorrect check time");
        checkTime = newCheckTime;
    }    

    /**
     * Set latency new one in ms, run only by owner. This function
     * only for tests.
     * @param newAllowableLatency - new Allowable Latency
     */
    function setLatency(uint32 newAllowableLatency) external onlyOwner {
        allowableLatency = newAllowableLatency;
    }

    function setMSR(uint newMSR) external onlyOwner {
        msr = newMSR;
    }

    function setLaunchTimestamp(uint timestamp) external onlyOwner {
        require(now < launchTimestamp, "Can't set network launch timestamp because network is already launched");
        launchTimestamp = timestamp;
    }

    function setRotationDelay(uint newDelay) external onlyOwner {
        rotationDelay = newDelay;
    }

    function setProofOfUseLockUpPeriod(uint periodDays) external onlyOwner {
        proofOfUseLockUpPeriodDays = periodDays;
    }

    function setProofOfUseDelegationPercentage(uint percentage) external onlyOwner {
        require(percentage <= 100, "Percentage value is incorrect");
        proofOfUseDelegationPercentage = percentage;
    }

    function setLimitValidatorsPerDelegator(uint newLimit) external onlyOwner {
        limitValidatorsPerDelegator = newLimit;
    }

    function setSchainCreationTimeStamp(uint timestamp) external onlyOwner {
        schainCreationTimeStamp = timestamp;
    }

    function setMinimalSchainLifetime(uint lifetime) external onlyOwner {
        minimalSchainLifetime = lifetime;
    }

    /**
     * @dev constructor in Permissions approach
     * @param contractsAddress needed in Permissions constructor
     */
    function initialize(address contractsAddress) public override initializer {
        Permissions.initialize(contractsAddress);

        msr = 0;
        rewardPeriod = 2592000;
        allowableLatency = 150000;
        deltaPeriod = 3600;
        checkTime = 300;
        launchTimestamp = uint(-1);
        rotationDelay = 12 hours;
        proofOfUseLockUpPeriodDays = 90;
        proofOfUseDelegationPercentage = 50;
        limitValidatorsPerDelegator = 20;
        firstDelegationsMonth = 0;
    }
}

File 7 of 45: Context.sol
pragma solidity ^0.6.0;
import "./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 GSN 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.
 */
contract ContextUpgradeSafe is Initializable {
    // Empty internal constructor, to prevent people from mistakenly deploying
    // an instance of this contract, which should be used via inheritance.

    function __Context_init() internal initializer {
        __Context_init_unchained();
    }

    function __Context_init_unchained() internal initializer {


    }


    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }

    uint256[50] private __gap;
}

File 8 of 45: ContractManager.sol
// 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.6.10;

import "./Ownable.sol";
import "./Address.sol";

import "./StringUtils.sol";


/**
 * @title Main contract in upgradeable approach. This contract contains the actual
 * current mapping from contract IDs (in the form of human-readable strings) to addresses.
 * @author Artem Payvin
 */
contract ContractManager is OwnableUpgradeSafe {
    using StringUtils for string;
    using Address for address;

    // mapping of actual smart contracts addresses
    mapping (bytes32 => address) public contracts;

    event ContractUpgraded(string contractsName, address contractsAddress);

    function initialize() external initializer {
        OwnableUpgradeSafe.__Ownable_init();
    }

    /**
     * Adds actual contract to mapping of actual contract addresses
     * @param contractsName - contracts name in skale manager system
     * @param newContractsAddress - contracts address in skale manager system
     */
    function setContractsAddress(string calldata contractsName, address newContractsAddress) external 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 contracts address does not contain code");
        // add newContractsAddress to mapping of actual contract addresses
        contracts[contractId] = newContractsAddress;
        emit ContractUpgraded(contractsName, newContractsAddress);
    }

    function getContract(string calldata name) external view returns (address contractAddress) {
        contractAddress = contracts[keccak256(abi.encodePacked(name))];
        require(contractAddress != address(0), name.strConcat(" contract has not been found"));
    }
}

File 9 of 45: Decryption.sol
// SPDX-License-Identifier: AGPL-3.0-only

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


contract Decryption {

    function encrypt(uint256 secretNumber, bytes32 key) external pure returns (bytes32 ciphertext) {
        return bytes32(secretNumber) ^ key;
    }

    function decrypt(bytes32 ciphertext, bytes32 key) external pure returns (uint256 secretNumber) {
        return uint256(ciphertext ^ key);
    }
}

File 10 of 45: DelegationPeriodManager.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    DelegationPeriodManager.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.6.10;

import "./Permissions.sol";

/**
 * @title Delegation Period Manager
 * @dev This contract handles all delegation offerings. Delegations are held for
 * a specified period (months), and different durations can have different
 * returns or `stakeMultiplier`. Currently, only delegation periods can be added.
 */
contract DelegationPeriodManager is Permissions {

    /**
     * @dev Emitted when a new delegation period is specified.
     */
    event DelegationPeriodWasSet(
        uint length,
        uint stakeMultiplier
    );

    mapping (uint => uint) public stakeMultipliers;

    /**
     * @dev Creates a new available delegation period and return in the network.
     * Only the owner may set new delegation period and returns in the network.
     *
     * Emits a DelegationPeriodWasSet event.
     *
     * @param monthsCount uint delegation duration in months
     * @param stakeMultiplier uint return for delegation
     */
    function setDelegationPeriod(uint monthsCount, uint stakeMultiplier) external onlyOwner {
        stakeMultipliers[monthsCount] = stakeMultiplier;

        emit DelegationPeriodWasSet(monthsCount, stakeMultiplier);
    }

    /**
     * @dev Checks whether given delegation period is allowed.
     *
     * @param monthsCount uint delegation duration in months
     * @return bool True if delegation period is allowed
     */
    function isDelegationPeriodAllowed(uint monthsCount) external view returns (bool) {
        return stakeMultipliers[monthsCount] != 0 ? true : false;
    }

    /**
     * @dev Initial delegation period and multiplier settings.
     */
    function initialize(address contractsAddress) public override initializer {
        Permissions.initialize(contractsAddress);
        stakeMultipliers[3] = 100;  // 3 months at 100
        // stakeMultipliers[6] = 150;  // 6 months at 150
        // stakeMultipliers[12] = 200; // 12 months at 200
    }
}

File 11 of 45: Distributor.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    Distributor.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;
pragma experimental ABIEncoderV2;

import "./IERC1820Registry.sol";
import "./IERC777Recipient.sol";
import "./IERC20.sol";

import "./Permissions.sol";
import "./ConstantsHolder.sol";
import "./MathUtils.sol";

import "./ValidatorService.sol";
import "./DelegationController.sol";
import "./DelegationPeriodManager.sol";
import "./TimeHelpers.sol";


contract Distributor is Permissions, IERC777Recipient {
    using MathUtils for uint;

    /**
     * @dev Emitted when a bounty is withdrawn by the token holder.
     */
    event WithdrawBounty(
        address holder,
        uint validatorId,
        address destination,
        uint amount
    );

    /**
     * @dev Emitted when a validator fee is withdrawn by the validator.
     */
    event WithdrawFee(
        uint validatorId,
        address destination,
        uint amount
    );

    /**
     * @dev Emitted when a bounty is distributed.
     */
    event BountyWasPaid(
        uint validatorId,
        uint amount
    );

    IERC1820Registry private _erc1820;

    // validatorId =>        month => token
    mapping (uint => mapping (uint => uint)) private _bountyPaid;
    // validatorId =>        month => token
    mapping (uint => mapping (uint => uint)) private _feePaid;
    //        holder =>   validatorId => month
    mapping (address => mapping (uint => uint)) private _firstUnwithdrawnMonth;
    // validatorId => month
    mapping (uint => uint) private _firstUnwithdrawnMonthForValidator;

    function getAndUpdateEarnedBountyAmount(uint validatorId) external returns (uint earned, uint endMonth) {
        return getAndUpdateEarnedBountyAmountOf(msg.sender, validatorId);
    }

    function withdrawBounty(uint validatorId, address to) external {
        TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract("TimeHelpers"));
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));

        require(now >= timeHelpers.addMonths(
                constantsHolder.launchTimestamp(),
                constantsHolder.BOUNTY_LOCKUP_MONTHS()
            ), "Bounty is locked");

        uint bounty;
        uint endMonth;
        (bounty, endMonth) = getAndUpdateEarnedBountyAmountOf(msg.sender, validatorId);

        _firstUnwithdrawnMonth[msg.sender][validatorId] = endMonth;

        IERC20 skaleToken = IERC20(contractManager.getContract("SkaleToken"));
        require(skaleToken.transfer(to, bounty), "Failed to transfer tokens");

        emit WithdrawBounty(
            msg.sender,
            validatorId,
            to,
            bounty
        );
    }

    function withdrawFee(address to) external {
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        IERC20 skaleToken = IERC20(contractManager.getContract("SkaleToken"));
        TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract("TimeHelpers"));
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));

        require(now >= timeHelpers.addMonths(
                constantsHolder.launchTimestamp(),
                constantsHolder.BOUNTY_LOCKUP_MONTHS()
            ), "Bounty is locked");
        // check Validator Exist inside getValidatorId
        uint validatorId = validatorService.getValidatorId(msg.sender);

        uint fee;
        uint endMonth;
        (fee, endMonth) = getEarnedFeeAmountOf(validatorId);

        _firstUnwithdrawnMonthForValidator[validatorId] = endMonth;

        require(skaleToken.transfer(to, fee), "Failed to transfer tokens");

        emit WithdrawFee(
            validatorId,
            to,
            fee
        );
    }

    function tokensReceived(
        address,
        address,
        address to,
        uint256 amount,
        bytes calldata userData,
        bytes calldata
    )
        external override
        allow("SkaleToken")
    {
        require(to == address(this), "Receiver is incorrect");
        require(userData.length == 32, "Data length is incorrect");
        uint validatorId = abi.decode(userData, (uint));
        _distributeBounty(amount, validatorId);
    }

    function getEarnedFeeAmount() external view returns (uint earned, uint endMonth) {
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        return getEarnedFeeAmountOf(validatorService.getValidatorId(msg.sender));
    }

    function initialize(address contractsAddress) public override initializer {
        Permissions.initialize(contractsAddress);
        _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
        _erc1820.setInterfaceImplementer(address(this), keccak256("ERC777TokensRecipient"), address(this));
    }

    function getAndUpdateEarnedBountyAmountOf(address wallet, uint validatorId)
        public returns (uint earned, uint endMonth)
    {
        DelegationController delegationController = DelegationController(
            contractManager.getContract("DelegationController"));
        TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract("TimeHelpers"));

        uint currentMonth = timeHelpers.getCurrentMonth();

        uint startMonth = _firstUnwithdrawnMonth[wallet][validatorId];
        if (startMonth == 0) {
            startMonth = delegationController.getFirstDelegationMonth(wallet, validatorId);
            if (startMonth == 0) {
                return (0, 0);
            }
        }

        earned = 0;
        endMonth = currentMonth;
        if (endMonth > startMonth.add(12)) {
            endMonth = startMonth.add(12);
        }
        for (uint i = startMonth; i < endMonth; ++i) {
            uint effectiveDelegatedToValidator =
                delegationController.getAndUpdateEffectiveDelegatedToValidator(validatorId, i);
            if (effectiveDelegatedToValidator.muchGreater(0)) {
                earned = earned.add(
                    _bountyPaid[validatorId][i].mul(
                        delegationController.getAndUpdateEffectiveDelegatedByHolderToValidator(wallet, validatorId, i))
                            .div(effectiveDelegatedToValidator)
                    );
            }
        }
    }

    function getEarnedFeeAmountOf(uint validatorId) public view returns (uint earned, uint endMonth) {
        TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract("TimeHelpers"));

        uint currentMonth = timeHelpers.getCurrentMonth();

        uint startMonth = _firstUnwithdrawnMonthForValidator[validatorId];
        if (startMonth == 0) {
            return (0, 0);
        }

        earned = 0;
        endMonth = currentMonth;
        if (endMonth > startMonth.add(12)) {
            endMonth = startMonth.add(12);
        }
        for (uint i = startMonth; i < endMonth; ++i) {
            earned = earned.add(_feePaid[validatorId][i]);
        }
    }

    // private

    function _distributeBounty(uint amount, uint validatorId) private {
        TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract("TimeHelpers"));
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));

        uint currentMonth = timeHelpers.getCurrentMonth();
        uint feeRate = validatorService.getValidator(validatorId).feeRate;

        uint fee = amount.mul(feeRate).div(1000);
        uint bounty = amount.sub(fee);
        _bountyPaid[validatorId][currentMonth] = _bountyPaid[validatorId][currentMonth].add(bounty);
        _feePaid[validatorId][currentMonth] = _feePaid[validatorId][currentMonth].add(fee);

        if (_firstUnwithdrawnMonthForValidator[validatorId] == 0) {
            _firstUnwithdrawnMonthForValidator[validatorId] = currentMonth;
        }

        emit BountyWasPaid(validatorId, amount);
    }
}

File 12 of 45: ECDH.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    ECDH.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;
import "./SafeMath.sol";


contract ECDH {
    using SafeMath for uint256;

    uint256 constant private _GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798;
    uint256 constant private _GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8;
    uint256 constant private _N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
    uint256 constant private _A = 0;

    function publicKey(uint256 privKey) external pure returns (uint256 qx, uint256 qy) {
        uint256 x;
        uint256 y;
        uint256 z;
        (x, y, z) = ecMul(
            privKey,
            _GX,
            _GY,
            1
        );
        z = inverse(z);
        qx = mulmod(x, z, _N);
        qy = mulmod(y, z, _N);
    }

    function deriveKey(
        uint256 privKey,
        uint256 pubX,
        uint256 pubY
    )
        external
        pure
        returns (uint256 qx, uint256 qy)
    {
        uint256 x;
        uint256 y;
        uint256 z;
        (x, y, z) = ecMul(
            privKey,
            pubX,
            pubY,
            1
        );
        z = inverse(z);
        qx = mulmod(x, z, _N);
        qy = mulmod(y, z, _N);
    }

    function jAdd(
        uint256 x1,
        uint256 z1,
        uint256 x2,
        uint256 z2
    )
        public
        pure
        returns (uint256 x3, uint256 z3)
    {
        (x3, z3) = (addmod(mulmod(z2, x1, _N), mulmod(x2, z1, _N), _N), mulmod(z1, z2, _N));
    }

    function jSub(
        uint256 x1,
        uint256 z1,
        uint256 x2,
        uint256 z2
    )
        public
        pure
        returns (uint256 x3, uint256 z3)
    {
        (x3, z3) = (addmod(mulmod(z2, x1, _N), mulmod(_N.sub(x2), z1, _N), _N), mulmod(z1, z2, _N));
    }

    function jMul(
        uint256 x1,
        uint256 z1,
        uint256 x2,
        uint256 z2
    )
        public
        pure
        returns (uint256 x3, uint256 z3)
    {
        (x3, z3) = (mulmod(x1, x2, _N), mulmod(z1, z2, _N));
    }

    function jDiv(
        uint256 x1,
        uint256 z1,
        uint256 x2,
        uint256 z2
    )
        public
        pure
        returns (uint256 x3, uint256 z3)
    {
        (x3, z3) = (mulmod(x1, z2, _N), mulmod(z1, x2, _N));
    }

    function inverse(uint256 a) public pure returns (uint256 invA) {
        uint256 t = 0;
        uint256 newT = 1;
        uint256 r = _N;
        uint256 newR = a;
        uint256 q;
        while (newR != 0) {
            q = r.div(newR);
            (t, newT) = (newT, addmod(t, (_N.sub(mulmod(q, newT, _N))), _N));
            (r, newR) = (newR, r % newR);
        }
        return t;
    }

    function ecAdd(
        uint256 x1,
        uint256 y1,
        uint256 z1,
        uint256 x2,
        uint256 y2,
        uint256 z2
    )
        public
        pure
        returns (uint256 x3, uint256 y3, uint256 z3)
    {
        uint256 ln;
        uint256 lz;
        uint256 da;
        uint256 db;

        if ((x1 == 0) && (y1 == 0)) {
            return (x2, y2, z2);
        }

        if ((x2 == 0) && (y2 == 0)) {
            return (x1, y1, z1);
        }

        if ((x1 == x2) && (y1 == y2)) {
            (ln, lz) = jMul(x1, z1, x1, z1);
            (ln, lz) = jMul(ln,lz,3,1);
            (ln, lz) = jAdd(ln,lz,_A,1);
            (da, db) = jMul(y1,z1,2,1);
        } else {
            (ln, lz) = jSub(y2,z2,y1,z1);
            (da, db) = jSub(x2,z2,x1,z1);
        }
        (ln, lz) = jDiv(ln,lz,da,db);

        (x3, da) = jMul(ln,lz,ln,lz);
        (x3, da) = jSub(x3,da,x1,z1);
        (x3, da) = jSub(x3,da,x2,z2);

        (y3, db) = jSub(x1,z1,x3,da);
        (y3, db) = jMul(y3,db,ln,lz);
        (y3, db) = jSub(y3,db,y1,z1);

        if (da != db) {
            x3 = mulmod(x3, db, _N);
            y3 = mulmod(y3, da, _N);
            z3 = mulmod(da, db, _N);
        } else {
            z3 = da;
        }
    }

    function ecDouble(
        uint256 x1,
        uint256 y1,
        uint256 z1
    )
        public
        pure
        returns (uint256 x3, uint256 y3, uint256 z3)
    {
        (x3, y3, z3) = ecAdd(
            x1,
            y1,
            z1,
            x1,
            y1,
            z1
        );
    }

    function ecMul(
        uint256 d,
        uint256 x1,
        uint256 y1,
        uint256 z1
    )
        public
        pure
        returns (uint256 x3, uint256 y3, uint256 z3)
    {
        uint256 remaining = d;
        uint256 px = x1;
        uint256 py = y1;
        uint256 pz = z1;
        uint256 acx = 0;
        uint256 acy = 0;
        uint256 acz = 1;

        if (d == 0) {
            return (0, 0, 1);
        }

        while (remaining != 0) {
            if ((remaining & 1) != 0) {
                (acx, acy, acz) = ecAdd(
                    acx,
                    acy,
                    acz,
                    px,
                    py,
                    pz
                );
            }
            remaining = remaining.div(2);
            (px, py, pz) = ecDouble(px, py, pz);
        }

        (x3, y3, z3) = (acx, acy, acz);
    }
}

File 13 of 45: ECDSA.sol
pragma solidity ^0.6.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        // Check the signature length
        if (signature.length != 65) {
            revert("ECDSA: invalid signature length");
        }

        // Divide the signature in r, s and v variables
        bytes32 r;
        bytes32 s;
        uint8 v;

        // ecrecover takes the signature parameters, and the only way to get them
        // currently is to use assembly.
        // solhint-disable-next-line no-inline-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }

        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            revert("ECDSA: invalid signature 's' value");
        }

        if (v != 27 && v != 28) {
            revert("ECDSA: invalid signature 'v' value");
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");

        return signer;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * replicates the behavior of the
     * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
     * JSON-RPC method.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }
}

File 14 of 45: EnumerableSet.sol
pragma solidity ^0.6.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.0.0, only sets of type `address` (`AddressSet`) and `uint256`
 * (`UintSet`) are supported.
 */
library EnumerableSet {
    // 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;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            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] = toDeleteIndex + 1; // All indexes are 1-based

            // 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) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

    // 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(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(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(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(uint256(_at(set._inner, index)));
    }


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

File 15 of 45: FieldOperations.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    FieldOperations.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.6.10;

import "./SafeMath.sol";

import "./Precompiled.sol";


library Fp2Operations {
    using SafeMath for uint;

    struct Fp2Point {
        uint a;
        uint b;
    }

    uint constant public P = 21888242871839275222246405745257275088696311157297823662689037894645226208583;

    function addFp2(Fp2Point memory value1, Fp2Point memory value2) internal pure returns (Fp2Point memory) {
        return Fp2Point({ a: addmod(value1.a, value2.a, P), b: addmod(value1.b, value2.b, P) });
    }

    function scalarMulFp2(Fp2Point memory value, uint scalar) internal pure returns (Fp2Point memory) {
        return Fp2Point({ a: mulmod(scalar, value.a, P), b: mulmod(scalar, value.b, P) });
    }

    function minusFp2(Fp2Point memory diminished, Fp2Point memory subtracted) internal pure
        returns (Fp2Point memory difference)
    {
        uint p = P;
        if (diminished.a >= subtracted.a) {
            difference.a = addmod(diminished.a, p - (subtracted.a), p);
        } else {
            difference.a = p - (addmod(subtracted.a, p - (diminished.a), p));
        }
        if (diminished.b >= subtracted.b) {
            difference.b = addmod(diminished.b, p - (subtracted.b), p);
        } else {
            difference.b = p - (addmod(subtracted.b, p - (diminished.b), p));
        }
    }

    function mulFp2(
        Fp2Point memory value1,
        Fp2Point memory value2
    )
        internal
        pure
        returns (Fp2Point memory result)
    {
        uint p = P;
        Fp2Point memory point = Fp2Point({
            a: mulmod(value1.a, value2.a, p),
            b: mulmod(value1.b, value2.b, p)});
        result.a = addmod(
            point.a,
            mulmod(p - 1, point.b, p),
            p);
        result.b = addmod(
            mulmod(
                addmod(value1.a, value1.b, p),
                addmod(value2.a, value2.b, p),
                p),
            p - addmod(point.a, point.b, p),
            p);
    }

    function squaredFp2(Fp2Point memory value) internal pure returns (Fp2Point memory) {
        uint p = P;
        uint ab = mulmod(value.a, value.b, p);
        uint mult = mulmod(addmod(value.a, value.b, p), addmod(value.a, mulmod(p - 1, value.b, p), p), p);
        return Fp2Point({ a: mult, b: addmod(ab, ab, p) });
    }

    function inverseFp2(Fp2Point memory value) internal view returns (Fp2Point memory result) {
        uint p = P;
        uint t0 = mulmod(value.a, value.a, p);
        uint t1 = mulmod(value.b, value.b, p);
        uint t2 = mulmod(p - 1, t1, p);
        if (t0 >= t2) {
            t2 = addmod(t0, p - t2, p);
        } else {
            t2 = p - addmod(t2, p - t0, p);
        }
        uint t3 = Precompiled.bigModExp(t2, p - 2, p);
        result.a = mulmod(value.a, t3, p);
        result.b = p - mulmod(value.b, t3, p);
    }

    function isEqual(
        Fp2Point memory value1,
        Fp2Point memory value2
    )
        internal
        pure
        returns (bool)
    {
        return value1.a == value2.a && value1.b == value2.b;
    }
}


library G2Operations {
    using SafeMath for uint;
    using Fp2Operations for Fp2Operations.Fp2Point;

    struct G2Point {
        Fp2Operations.Fp2Point x;
        Fp2Operations.Fp2Point y;
    }

    function getTWISTB() internal pure returns (Fp2Operations.Fp2Point memory) {
        // Current solidity version does not support Constants of non-value type
        // so we implemented this function
        return Fp2Operations.Fp2Point({
            a: 19485874751759354771024239261021720505790618469301721065564631296452457478373,
            b: 266929791119991161246907387137283842545076965332900288569378510910307636690
        });
    }

    function getG2() internal pure returns (G2Point memory) {
        // Current solidity version does not support Constants of non-value type
        // so we implemented this function
        return G2Point({
            x: Fp2Operations.Fp2Point({
                a: 10857046999023057135944570762232829481370756359578518086990519993285655852781,
                b: 11559732032986387107991004021392285783925812861821192530917403151452391805634
            }),
            y: Fp2Operations.Fp2Point({
                a: 8495653923123431417604973247489272438418190587263600148770280649306958101930,
                b: 4082367875863433681332203403145435568316851327593401208105741076214120093531
            })
        });
    }

    function getG1() internal pure returns (Fp2Operations.Fp2Point memory) {
        // Current solidity version does not support Constants of non-value type
        // so we implemented this function
        return Fp2Operations.Fp2Point({
            a: 1,
            b: 2
        });
    }

    function getG2Zero() internal pure returns (G2Point memory) {
        // Current solidity version does not support Constants of non-value type
        // so we implemented this function
        return G2Point({
            x: Fp2Operations.Fp2Point({
                a: 0,
                b: 0
            }),
            y: Fp2Operations.Fp2Point({
                a: 1,
                b: 0
            })
        });
    }

    function isG1Point(uint x, uint y) internal pure returns (bool) {
        uint p = Fp2Operations.P;
        return mulmod(y, y, p) == 
            addmod(mulmod(mulmod(x, x, p), x, p), 3, p);
    }
    function isG1(Fp2Operations.Fp2Point memory point) internal pure returns (bool) {
        return isG1Point(point.a, point.b);
    }

    function isG2Point(Fp2Operations.Fp2Point memory x, Fp2Operations.Fp2Point memory y) internal pure returns (bool) {
        if (isG2ZeroPoint(x, y)) {
            return true;
        }
        Fp2Operations.Fp2Point memory squaredY = y.squaredFp2();
        Fp2Operations.Fp2Point memory res = squaredY.minusFp2(
                x.squaredFp2().mulFp2(x)
            ).minusFp2(getTWISTB());
        return res.a == 0 && res.b == 0;
    }

    function isG2(G2Point memory value) internal pure returns (bool) {
        return isG2Point(value.x, value.y);
    }

    function isG2ZeroPoint(
        Fp2Operations.Fp2Point memory x,
        Fp2Operations.Fp2Point memory y
    )
        internal
        pure
        returns (bool)
    {
        return x.a == 0 && x.b == 0 && y.a == 1 && y.b == 0;
    }

    function isG2Zero(G2Point memory value) internal pure returns (bool) {
        return value.x.a == 0 && value.x.b == 0 && value.y.a == 1 && value.y.b == 0;
        // return isG2ZeroPoint(value.x, value.y);
    }

    function addG2(
        G2Point memory value1,
        G2Point memory value2
    )
        internal
        view
        returns (G2Point memory sum)
    {
        if (isG2Zero(value1)) {
            return value2;
        }
        if (isG2Zero(value2)) {
            return value1;
        }
        if (isEqual(value1, value2)) {
            return doubleG2(value1);
        }

        Fp2Operations.Fp2Point memory s = value2.y.minusFp2(value1.y).mulFp2(value2.x.minusFp2(value1.x).inverseFp2());
        sum.x = s.squaredFp2().minusFp2(value1.x.addFp2(value2.x));
        sum.y = value1.y.addFp2(s.mulFp2(sum.x.minusFp2(value1.x)));
        uint p = Fp2Operations.P;
        sum.y.a = p - sum.y.a;
        sum.y.b = p - sum.y.b;
    }

    function toUS(G2Point memory value) internal pure returns (G2Point memory) {
        return G2Point({
            x: value.x.mulFp2(Fp2Operations.Fp2Point({ a: 1, b: 0 }).squaredFp2()),
            y: value.y.mulFp2(
                Fp2Operations.Fp2Point({ a: 1, b: 0 }).mulFp2(Fp2Operations.Fp2Point({ a: 1, b: 0 }).squaredFp2())
            )
        });
    }

    function isEqual(
        G2Point memory value1,
        G2Point memory value2
    )
        internal
        pure
        returns (bool)
    {
        return value1.x.isEqual(value2.x) && value1.y.isEqual(value2.y);
    }

    function doubleG2(G2Point memory value)
        internal
        view
        returns (G2Point memory result)
    {
        if (isG2Zero(value)) {
            return value;
        } else {
            Fp2Operations.Fp2Point memory s =
                value.x.squaredFp2().scalarMulFp2(3).mulFp2(value.y.scalarMulFp2(2).inverseFp2());
            result.x = s.squaredFp2().minusFp2(value.x.addFp2(value.x));
            result.y = value.y.addFp2(s.mulFp2(result.x.minusFp2(value.x)));
            uint p = Fp2Operations.P;
            result.y.a = p - result.y.a;
            result.y.b = p - result.y.b;
        }
    }

    function mulG2(
        G2Point memory value,
        uint scalar
    )
        internal
        view
        returns (G2Point memory result)
    {
        uint step = scalar;
        result = G2Point({
            x: Fp2Operations.Fp2Point({
                a: 0,
                b: 0
            }),
            y: Fp2Operations.Fp2Point({
                a: 1,
                b: 0
            })
        });
        G2Point memory tmp = value;
        uint gs = gasleft();
        while (step > 0) {
            if (step % 2 == 1) {
                result = addG2(result, tmp);
            }
            gs = gasleft();
            tmp = doubleG2(tmp);
            step >>= 1;
        }
    }
}

File 16 of 45: FractionUtils.sol
// 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.6.10;

import "./SafeMath.sol";


library FractionUtils {
    using SafeMath for uint;

    struct Fraction {
        uint numerator;
        uint denominator;
    }

    function createFraction(uint numerator, uint denominator) internal pure returns (Fraction memory) {
        require(denominator > 0, "Division by zero");
        Fraction memory fraction = Fraction({numerator: numerator, denominator: denominator});
        reduceFraction(fraction);
        return fraction;
    }

    function createFraction(uint value) internal pure returns (Fraction memory) {
        return createFraction(value, 1);
    }

    function reduceFraction(Fraction memory fraction) internal pure {
        uint _gcd = gcd(fraction.numerator, fraction.denominator);
        fraction.numerator = fraction.numerator.div(_gcd);
        fraction.denominator = fraction.denominator.div(_gcd);
    }

    function multiplyFraction(Fraction memory a, Fraction memory b) internal pure returns (Fraction memory) {
        return createFraction(a.numerator.mul(b.numerator), a.denominator.mul(b.denominator));
    }

    function gcd(uint a, uint b) internal pure returns (uint) {
        uint _a = a;
        uint _b = b;
        if (_b > _a) {
            (_a, _b) = swap(_a, _b);
        }
        while (_b > 0) {
            _a = _a.mod(_b);
            (_a, _b) = swap (_a, _b);
        }
        return _a;
    }

    function swap(uint a, uint b) internal pure returns (uint, uint) {
        return (b, a);
    }
}

File 17 of 45: IERC1820Registry.sol
pragma solidity ^0.6.0;

/**
 * @dev Interface of the global ERC1820 Registry, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
 * implementers for interfaces in this registry, as well as query support.
 *
 * Implementers may be shared by multiple accounts, and can also implement more
 * than a single interface for each account. Contracts can implement interfaces
 * for themselves, but externally-owned accounts (EOA) must delegate this to a
 * contract.
 *
 * {IERC165} interfaces can also be queried via the registry.
 *
 * For an in-depth explanation and source code analysis, see the EIP text.
 */
interface IERC1820Registry {
    /**
     * @dev Sets `newManager` as the manager for `account`. A manager of an
     * account is able to set interface implementers for it.
     *
     * By default, each account is its own manager. Passing a value of `0x0` in
     * `newManager` will reset the manager to this initial state.
     *
     * Emits a {ManagerChanged} event.
     *
     * Requirements:
     *
     * - the caller must be the current manager for `account`.
     */
    function setManager(address account, address newManager) external;

    /**
     * @dev Returns the manager for `account`.
     *
     * See {setManager}.
     */
    function getManager(address account) external view returns (address);

    /**
     * @dev Sets the `implementer` contract as ``account``'s implementer for
     * `interfaceHash`.
     *
     * `account` being the zero address is an alias for the caller's address.
     * The zero address can also be used in `implementer` to remove an old one.
     *
     * See {interfaceHash} to learn how these are created.
     *
     * Emits an {InterfaceImplementerSet} event.
     *
     * Requirements:
     *
     * - the caller must be the current manager for `account`.
     * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
     * end in 28 zeroes).
     * - `implementer` must implement {IERC1820Implementer} and return true when
     * queried for support, unless `implementer` is the caller. See
     * {IERC1820Implementer-canImplementInterfaceForAddress}.
     */
    function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external;

    /**
     * @dev Returns the implementer of `interfaceHash` for `account`. If no such
     * implementer is registered, returns the zero address.
     *
     * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
     * zeroes), `account` will be queried for support of it.
     *
     * `account` being the zero address is an alias for the caller's address.
     */
    function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address);

    /**
     * @dev Returns the interface hash for an `interfaceName`, as defined in the
     * corresponding
     * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
     */
    function interfaceHash(string calldata interfaceName) external pure returns (bytes32);

    /**
     *  @notice Updates the cache with whether the contract implements an ERC165 interface or not.
     *  @param account Address of the contract for which to update the cache.
     *  @param interfaceId ERC165 interface for which to update the cache.
     */
    function updateERC165Cache(address account, bytes4 interfaceId) external;

    /**
     *  @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 account Address of the contract to check.
     *  @param interfaceId ERC165 interface to check.
     *  @return True if `account` implements `interfaceId`, false otherwise.
     */
    function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);

    /**
     *  @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
     *  @param account Address of the contract to check.
     *  @param interfaceId ERC165 interface to check.
     *  @return True if `account` implements `interfaceId`, false otherwise.
     */
    function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);

    event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);

    event ManagerChanged(address indexed account, address indexed newManager);
}

File 18 of 45: IERC20.sol
pragma solidity ^0.6.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

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

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

File 19 of 45: IERC777.sol
pragma solidity ^0.6.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 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
    );

    event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);

    event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);

    event AuthorizedOperator(address indexed operator, address indexed tokenHolder);

    event RevokedOperator(address indexed operator, address indexed tokenHolder);
}

File 20 of 45: IERC777Recipient.sol
pragma solidity ^0.6.0;

/**
 * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.
 *
 * Accounts can be notified of {IERC777} tokens being sent to them by having a
 * contract implement this interface (contract holders can be their own
 * implementer) and registering it on the
 * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].
 *
 * See {IERC1820Registry} and {ERC1820Implementer}.
 */
interface IERC777Recipient {
    /**
     * @dev Called by an {IERC777} token contract whenever tokens are being
     * moved or created into a registered account (`to`). The type of operation
     * is conveyed by `from` being the zero address or not.
     *
     * This call occurs _after_ the token contract's state is updated, so
     * {IERC777-balanceOf}, etc., can be used to query the post-operation state.
     *
     * This function may revert to prevent the operation from being executed.
     */
    function tokensReceived(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes calldata userData,
        bytes calldata operatorData
    ) external;
}

File 21 of 45: ILocker.sol
// 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;

/**
 * @dev Interface of Locker functions of the {TokenState} contract.
 *
 * The SKALE Network has three types of locked tokens:
 *
 * - Tokens that are transferrable but are currently locked into delegation with
 * a validator. See {DelegationController};
 *
 * - 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. See {TokenLaunchLocker}; and,
 *
 * - Tokens that are neither transferable nor delegatable
 * {getAndUpdateForbiddenForDelegationAmount}. This lock enforces slashing.
 * See {Punisher}.
 */
interface ILocker {
    /**
     * @dev Returns the locked amount of untransferable tokens of a given `wallet`
     */
    function getAndUpdateLockedAmount(address wallet) external returns (uint);

    /**
     * @dev Returns the locked amount of untransferable and un-delegatable tokens of a given `wallet`.
     */
    function getAndUpdateForbiddenForDelegationAmount(address wallet) external returns (uint);
}

File 22 of 45: IMintableToken.sol
// SPDX-License-Identifier: AGPL-3.0-only

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

interface IMintableToken {
    function mint(
        address account,
        uint256 amount,
        bytes calldata userData,
        bytes calldata operatorData
    )
        external
        returns (bool);
}

File 23 of 45: Initializable.sol
pragma solidity >=0.4.24 <0.7.0;


/**
 * @title Initializable
 *
 * @dev Helper contract to support initializer functions. To use it, replace
 * the constructor with a function that has the `initializer` modifier.
 * WARNING: Unlike constructors, initializer functions must be manually
 * invoked. This applies both to deploying an Initializable contract, as well
 * as extending an Initializable contract via inheritance.
 * WARNING: When used with inheritance, manual care must be taken to not invoke
 * a parent initializer twice, or ensure that all initializers are idempotent,
 * because this is not dealt with automatically as with constructors.
 */
contract Initializable {

  /**
   * @dev Indicates that the contract has been initialized.
   */
  bool private initialized;

  /**
   * @dev Indicates that the contract is in the process of being initialized.
   */
  bool private initializing;

  /**
   * @dev Modifier to use in the initializer function of a contract.
   */
  modifier initializer() {
    require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");

    bool isTopLevelCall = !initializing;
    if (isTopLevelCall) {
      initializing = true;
      initialized = true;
    }

    _;

    if (isTopLevelCall) {
      initializing = false;
    }
  }

  /// @dev Returns true if and only if the function is running in the constructor
  function isConstructor() private view returns (bool) {
    // extcodesize checks the size of the code stored in an address, and
    // address returns the current address. Since the code is still not
    // deployed when running a constructor, any checks on its code size will
    // yield zero, making it an effective way to detect if a contract is
    // under construction or not.
    address self = address(this);
    uint256 cs;
    assembly { cs := extcodesize(self) }
    return cs == 0;
  }

  // Reserved storage space to allow for layout changes in the future.
  uint256[50] private ______gap;
}

File 24 of 45: ISkaleDKG.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    ISkaleDKG.sol - SKALE Manager
    Copyright (C) 2019-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;

interface ISkaleDKG {
    function openChannel(bytes32 schainId) external;
    function deleteChannel(bytes32 schainId) external;
    function isLastDKGSuccesful(bytes32 groupIndex) external view returns (bool);
    function isChannelOpened(bytes32 schainId) external view returns (bool);
}

File 25 of 45: KeyStorage.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    KeyStorage.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;
pragma experimental ABIEncoderV2;
import "./Decryption.sol";
import "./Permissions.sol";
import "./SchainsInternal.sol";
import "./ECDH.sol";
import "./Precompiled.sol";
import "./FieldOperations.sol";

contract KeyStorage is Permissions {
    using Fp2Operations for Fp2Operations.Fp2Point;
    using G2Operations for G2Operations.G2Point;

    struct BroadcastedData {
        KeyShare[] secretKeyContribution;
        G2Operations.G2Point[] verificationVector;
    }

    struct KeyShare {
        bytes32[2] publicKey;
        bytes32 share;
    }

    // Unused variable!!
    mapping(bytes32 => mapping(uint => BroadcastedData)) private _data;
    // 
    
    mapping(bytes32 => G2Operations.G2Point) private _publicKeysInProgress;
    mapping(bytes32 => G2Operations.G2Point) private _schainsPublicKeys;

    // Unused variable
    mapping(bytes32 => G2Operations.G2Point[]) private _schainsNodesPublicKeys;
    //

    mapping(bytes32 => G2Operations.G2Point[]) private _previousSchainsPublicKeys;

    function deleteKey(bytes32 groupIndex) external allow("SkaleDKG") {
        _previousSchainsPublicKeys[groupIndex].push(_schainsPublicKeys[groupIndex]);
        delete _schainsPublicKeys[groupIndex];
    }

    function initPublicKeyInProgress(bytes32 groupIndex) external allow("SkaleDKG") {
        _publicKeysInProgress[groupIndex] = G2Operations.getG2Zero();
    }

    function adding(bytes32 groupIndex, G2Operations.G2Point memory value) external allow("SkaleDKG") {
        require(value.isG2(), "Incorrect g2 point");
        _publicKeysInProgress[groupIndex] = value.addG2(_publicKeysInProgress[groupIndex]);
    }

    function finalizePublicKey(bytes32 groupIndex) external allow("SkaleDKG") {
        if (!_isSchainsPublicKeyZero(groupIndex)) {
            _previousSchainsPublicKeys[groupIndex].push(_schainsPublicKeys[groupIndex]);
        }
        _schainsPublicKeys[groupIndex] = _publicKeysInProgress[groupIndex];
        delete _publicKeysInProgress[groupIndex];
    }

    function getCommonPublicKey(bytes32 groupIndex) external view returns (G2Operations.G2Point memory) {
        return _schainsPublicKeys[groupIndex];
    }

    function getPreviousPublicKey(bytes32 groupIndex) external view returns (G2Operations.G2Point memory) {
        uint length = _previousSchainsPublicKeys[groupIndex].length;
        if (length == 0) {
            return G2Operations.getG2Zero();
        }
        return _previousSchainsPublicKeys[groupIndex][length - 1];
    }

    function getAllPreviousPublicKeys(bytes32 groupIndex) external view returns (G2Operations.G2Point[] memory) {
        return _previousSchainsPublicKeys[groupIndex];
    }

    function initialize(address contractsAddress) public override initializer {
        Permissions.initialize(contractsAddress);
    }

    function _isSchainsPublicKeyZero(bytes32 schainId) private view returns (bool) {
        return _schainsPublicKeys[schainId].x.a == 0 &&
            _schainsPublicKeys[schainId].x.b == 0 &&
            _schainsPublicKeys[schainId].y.a == 0 &&
            _schainsPublicKeys[schainId].y.b == 0;
    }

    function _getData() private view returns (BroadcastedData memory) {
        return _data[keccak256(abi.encodePacked("UnusedFunction"))][0];
    }

    function _getNodesPublicKey() private view returns (G2Operations.G2Point memory) {
        return _schainsNodesPublicKeys[keccak256(abi.encodePacked("UnusedFunction"))][0];
    }
}

File 26 of 45: MathUtils.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    StringUtils.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.6.10;


library MathUtils {
    event UnderflowError(
        uint a,
        uint b
    );

    uint constant private _EPS = 1e6;

    function boundedSub(uint256 a, uint256 b) internal returns (uint256) {
        if (a >= b) {
            return a - b;
        } else {
            emit UnderflowError(a, b);
            return 0;
        }
    }

    function boundedSubWithoutEvent(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a >= b) {
            return a - b;
        } else {
            return 0;
        }
    }

    function muchGreater(uint256 a, uint256 b) internal pure returns (bool) {
        assert(uint(-1) - _EPS > b);
        return a > b + _EPS;
    }

    function approximatelyEqual(uint256 a, uint256 b) internal pure returns (bool) {
        if (a > b) {
            return a - b < _EPS;
        } else {
            return b - a < _EPS;
        }
    }
}

File 27 of 45: Monitors.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    Monitors.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;
pragma experimental ABIEncoderV2;

import "./SafeCast.sol";

import "./ConstantsHolder.sol";
import "./Nodes.sol";

contract Monitors is Permissions {

    using StringUtils for string;
    using SafeCast for uint;

    struct Verdict {
        uint toNodeIndex;
        uint32 downtime;
        uint32 latency;
    }

    struct CheckedNode {
        uint nodeIndex;
        uint time;
    }

    struct CheckedNodeWithIp {
        uint nodeIndex;
        uint time;
        bytes4 ip;
    }

    mapping (bytes32 => CheckedNode[]) public checkedNodes;
    mapping (bytes32 => uint[][]) public verdicts;

    mapping (bytes32 => uint[]) public groupsForMonitors;

    mapping (bytes32 => uint) public lastVerdictBlocks;
    mapping (bytes32 => uint) public lastBountyBlocks;


    event MonitorCreated(
        uint nodeIndex,
        bytes32 monitorIndex,
        uint numberOfMonitors,
        uint[] nodesInGroup,
        uint time,
        uint gasSpend
    );

    event VerdictWasSent(
        uint indexed fromMonitorIndex,
        uint indexed toNodeIndex,
        uint32 downtime,
        uint32 latency,
        bool status,
        uint previousBlockEvent,
        uint time,
        uint gasSpend
    );

    event MetricsWereCalculated(
        uint forNodeIndex,
        uint32 averageDowntime,
        uint32 averageLatency,
        uint time,
        uint gasSpend
    );

    event PeriodsWereSet(
        uint rewardPeriod,
        uint deltaPeriod,
        uint time,
        uint gasSpend
    );


    event MonitorRotated(
        bytes32 monitorIndex,
        uint newNode
    );

    /**
     * addMonitor - setup monitors of node
     */
    function addMonitor(uint nodeIndex) external allow("SkaleManager") {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        bytes32 monitorIndex = keccak256(abi.encodePacked(nodeIndex));
        _generateGroup(monitorIndex, nodeIndex, constantsHolder.NUMBER_OF_MONITORS());
        CheckedNode memory checkedNode = _getCheckedNodeData(nodeIndex);
        for (uint i = 0; i < groupsForMonitors[monitorIndex].length; i++) {
            bytes32 index = keccak256(abi.encodePacked(groupsForMonitors[monitorIndex][i]));
            addCheckedNode(index, checkedNode);
        }

        emit MonitorCreated(
            nodeIndex,
            monitorIndex,
            groupsForMonitors[monitorIndex].length,
            groupsForMonitors[monitorIndex],
            block.timestamp,
            gasleft()
        );
    }

    function deleteMonitor(uint nodeIndex) external allow("SkaleManager") {
        bytes32 monitorIndex = keccak256(abi.encodePacked(nodeIndex));
        while (verdicts[keccak256(abi.encodePacked(nodeIndex))].length > 0) {
            verdicts[keccak256(abi.encodePacked(nodeIndex))].pop();
        }
        uint[] memory nodesInGroup = groupsForMonitors[monitorIndex];
        uint index;
        bytes32 monitoringIndex;
        for (uint i = 0; i < nodesInGroup.length; i++) {
            monitoringIndex = keccak256(abi.encodePacked(nodesInGroup[i]));
            (index, ) = _find(monitoringIndex, nodeIndex);
            if (index < checkedNodes[monitoringIndex].length) {
                if (index != checkedNodes[monitoringIndex].length.sub(1)) {
                    checkedNodes[monitoringIndex][index] =
                        checkedNodes[monitoringIndex][checkedNodes[monitoringIndex].length.sub(1)];
                }
                checkedNodes[monitoringIndex].pop();
            }
        }
        delete groupsForMonitors[monitorIndex];
    }

    function removeCheckedNodes(uint nodeIndex) external allow("SkaleManager") {
        bytes32 monitorIndex = keccak256(abi.encodePacked(nodeIndex));
        delete checkedNodes[monitorIndex];
    }

    function sendVerdict(uint fromMonitorIndex, Verdict calldata verdict) external allow("SkaleManager") {
        uint index;
        uint time;
        bytes32 monitorIndex = keccak256(abi.encodePacked(fromMonitorIndex));
        (index, time) = _find(monitorIndex, verdict.toNodeIndex);
        require(time > 0, "Checked Node does not exist in MonitorsArray");
        if (time <= block.timestamp) {
            if (index != checkedNodes[monitorIndex].length.sub(1)) {
                checkedNodes[monitorIndex][index] = 
                    checkedNodes[monitorIndex][checkedNodes[monitorIndex].length.sub(1)];
            }
            delete checkedNodes[monitorIndex][checkedNodes[monitorIndex].length.sub(1)];
            checkedNodes[monitorIndex].pop();
            ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
            bool receiveVerdict = time.add(constantsHolder.deltaPeriod()) > block.timestamp;
            if (receiveVerdict) {
                verdicts[keccak256(abi.encodePacked(verdict.toNodeIndex))].push(
                    [uint(verdict.downtime), uint(verdict.latency)]
                );
            }
            _emitVerdictsEvent(fromMonitorIndex, verdict, receiveVerdict);
        }
    }

    function calculateMetrics(uint nodeIndex)
        external
        allow("SkaleManager")
        returns (uint averageDowntime, uint averageLatency)
    {
        bytes32 monitorIndex = keccak256(abi.encodePacked(nodeIndex));
        uint lengthOfArray = getLengthOfMetrics(monitorIndex);
        uint[] memory downtimeArray = new uint[](lengthOfArray);
        uint[] memory latencyArray = new uint[](lengthOfArray);
        for (uint i = 0; i < lengthOfArray; i++) {
            downtimeArray[i] = verdicts[monitorIndex][i][0];
            latencyArray[i] = verdicts[monitorIndex][i][1];
        }
        if (lengthOfArray > 0) {
            averageDowntime = _median(downtimeArray);
            averageLatency = _median(latencyArray);
        }
        delete verdicts[monitorIndex];
    }

    function setLastBountyBlock(uint nodeIndex) external allow("SkaleManager") {
        lastBountyBlocks[keccak256(abi.encodePacked(nodeIndex))] = block.number;
    }

    function getCheckedArray(bytes32 monitorIndex)
        external
        view
        returns (CheckedNodeWithIp[] memory checkedNodesWithIp)
    {
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));
        checkedNodesWithIp = new CheckedNodeWithIp[](checkedNodes[monitorIndex].length);
        for (uint i = 0; i < checkedNodes[monitorIndex].length; ++i) {
            checkedNodesWithIp[i].nodeIndex = checkedNodes[monitorIndex][i].nodeIndex;
            checkedNodesWithIp[i].time = checkedNodes[monitorIndex][i].time;
            checkedNodesWithIp[i].ip = nodes.getNodeIP(checkedNodes[monitorIndex][i].nodeIndex);
        }
    }

    function getLastBountyBlock(uint nodeIndex) external view returns (uint) {
        return lastBountyBlocks[keccak256(abi.encodePacked(nodeIndex))];
    }

    function getNodesInGroup(bytes32 monitorIndex) external view returns (uint[] memory) {
        return groupsForMonitors[monitorIndex];
    }

    function getNumberOfNodesInGroup(bytes32 monitorIndex) external view returns (uint) {
        return groupsForMonitors[monitorIndex].length;
    }

    function initialize(address newContractsAddress) public override initializer {
        Permissions.initialize(newContractsAddress);
    }

    /**
     *  Add checked node or update existing one if it is already exits
     */
    function addCheckedNode(bytes32 monitorIndex, CheckedNode memory checkedNode) public allow("SkaleManager") {
        for (uint i = 0; i < checkedNodes[monitorIndex].length; ++i) {
            if (checkedNodes[monitorIndex][i].nodeIndex == checkedNode.nodeIndex) {
                checkedNodes[monitorIndex][i] = checkedNode;
                return;
            }
        }
        checkedNodes[monitorIndex].push(checkedNode);
    }

    function getLastReceivedVerdictBlock(uint nodeIndex) public view returns (uint) {
        return lastVerdictBlocks[keccak256(abi.encodePacked(nodeIndex))];
    }

    function getLengthOfMetrics(bytes32 monitorIndex) public view returns (uint) {
        return verdicts[monitorIndex].length;
    }

    function _generateGroup(bytes32 monitorIndex, uint nodeIndex, uint numberOfNodes)
        private
    {
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));
        uint[] memory activeNodes = nodes.getActiveNodeIds();
        uint numberOfNodesInGroup;
        uint availableAmount = activeNodes.length.sub((nodes.isNodeActive(nodeIndex)) ? 1 : 0);
        if (numberOfNodes > availableAmount) {
            numberOfNodesInGroup = availableAmount;
        } else {
            numberOfNodesInGroup = numberOfNodes;
        }
        uint ignoringTail = 0;
        uint random = uint(keccak256(abi.encodePacked(uint(blockhash(block.number.sub(1))), monitorIndex)));
        for (uint i = 0; i < numberOfNodesInGroup; ++i) {
            uint index = random % (activeNodes.length.sub(ignoringTail));
            if (activeNodes[index] == nodeIndex) {
                _swap(activeNodes, index, activeNodes.length.sub(ignoringTail).sub(1));
                ++ignoringTail;
                index = random % (activeNodes.length.sub(ignoringTail));
            }
            groupsForMonitors[monitorIndex].push(activeNodes[index]);
            _swap(activeNodes, index, activeNodes.length.sub(ignoringTail).sub(1));
            ++ignoringTail;
        }
    }

    function _median(uint[] memory values) private pure returns (uint) {
        if (values.length < 1) {
            revert("Can't calculate _median of empty array");
        }
        _quickSort(values, 0, values.length.sub(1));
        return values[values.length.div(2)];
    }

    function _swap(uint[] memory array, uint index1, uint index2) private pure {
        uint buffer = array[index1];
        array[index1] = array[index2];
        array[index2] = buffer;
    }

    function _find(bytes32 monitorIndex, uint nodeIndex) private view returns (uint index, uint time) {
        index = checkedNodes[monitorIndex].length;
        time = 0;
        for (uint i = 0; i < checkedNodes[monitorIndex].length; i++) {
            uint checkedNodeNodeIndex;
            uint checkedNodeTime;
            checkedNodeNodeIndex = checkedNodes[monitorIndex][i].nodeIndex;
            checkedNodeTime = checkedNodes[monitorIndex][i].time;
            if (checkedNodeNodeIndex == nodeIndex && (time == 0 || checkedNodeTime < time))
            {
                index = i;
                time = checkedNodeTime;
            }
        }
    }

    function _quickSort(uint[] memory array, uint left, uint right) private pure {
        uint leftIndex = left;
        uint rightIndex = right;
        uint middle = array[right.add(left).div(2)];
        while (leftIndex <= rightIndex) {
            while (array[leftIndex] < middle) {
                leftIndex++;
                }
            while (middle < array[rightIndex]) {
                rightIndex--;
                }
            if (leftIndex <= rightIndex) {
                (array[leftIndex], array[rightIndex]) = (array[rightIndex], array[leftIndex]);
                leftIndex++;
                rightIndex = (rightIndex > 0 ? rightIndex.sub(1) : 0);
            }
        }
        if (left < rightIndex)
            _quickSort(array, left, rightIndex);
        if (leftIndex < right)
            _quickSort(array, leftIndex, right);
    }

    function _getCheckedNodeData(uint nodeIndex) private view returns (CheckedNode memory checkedNode) {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));

        checkedNode.nodeIndex = nodeIndex;
        checkedNode.time = nodes.getNodeNextRewardDate(nodeIndex).sub(constantsHolder.deltaPeriod());
    }

    function _emitVerdictsEvent(
        uint fromMonitorIndex,
        Verdict memory verdict,
        bool receiveVerdict
    )
        private
    {
        uint previousBlockEvent = getLastReceivedVerdictBlock(verdict.toNodeIndex);
        lastVerdictBlocks[keccak256(abi.encodePacked(verdict.toNodeIndex))] = block.number;

        emit VerdictWasSent(
                fromMonitorIndex,
                verdict.toNodeIndex,
                verdict.downtime,
                verdict.latency,
                receiveVerdict,
                previousBlockEvent,
                block.timestamp,
                gasleft()
            );
    }
}

File 28 of 45: NodeRotation.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    NodeRotation.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.6.10;
pragma experimental ABIEncoderV2;

import "./Permissions.sol";
import "./ConstantsHolder.sol";
import "./SchainsInternal.sol";
import "./Schains.sol";
import "./Nodes.sol";
import "./ISkaleDKG.sol";


contract NodeRotation is Permissions {
    using StringUtils for string;
    using StringUtils for uint;

    /**
     * nodeIndex - index of Node which is in process of rotation(left from schain)
     * newNodeIndex - index of Node which is rotated(added to schain)
     * freezeUntil - time till which Node should be turned on
     * rotationCounter - how many rotations were on this schain
     */
    struct Rotation {
        uint nodeIndex;
        uint newNodeIndex;
        uint freezeUntil;
        uint rotationCounter;
    }

    struct LeavingHistory {
        bytes32 schainIndex;
        uint finishedRotation;
    }

    mapping (bytes32 => Rotation) public rotations;

    mapping (uint => LeavingHistory[]) public leavingHistory;

    mapping (bytes32 => bool) public waitForNewNode;


    function exitFromSchain(uint nodeIndex) external allow("SkaleManager") returns (bool) {
        SchainsInternal schainsInternal = SchainsInternal(contractManager.getContract("SchainsInternal"));
        bytes32 schainId = schainsInternal.getActiveSchain(nodeIndex);
        require(_checkRotation(schainId), "No any free Nodes for rotating");
        rotateNode(nodeIndex, schainId, true);
        return schainsInternal.getActiveSchain(nodeIndex) == bytes32(0) ? true : false;
    }

    function freezeSchains(uint nodeIndex) external allow("SkaleManager") {
        SchainsInternal schainsInternal = SchainsInternal(contractManager.getContract("SchainsInternal"));
        bytes32[] memory schains = schainsInternal.getActiveSchains(nodeIndex);
        for (uint i = 0; i < schains.length; i++) {
            Rotation memory rotation = rotations[schains[i]];
            if (rotation.nodeIndex == nodeIndex && now < rotation.freezeUntil) {
                continue;
            }
            string memory schainName = schainsInternal.getSchainName(schains[i]);
            string memory revertMessage = "Node cannot rotate on Schain ";
            revertMessage = revertMessage.strConcat(schainName);
            revertMessage = revertMessage.strConcat(", occupied by Node ");
            revertMessage = revertMessage.strConcat(rotation.nodeIndex.uint2str());
            string memory dkgRevert = "DKG proccess did not finish on schain ";
            ISkaleDKG skaleDKG = ISkaleDKG(contractManager.getContract("SkaleDKG"));
            require(
                skaleDKG.isLastDKGSuccesful(keccak256(abi.encodePacked(schainName))),
                dkgRevert.strConcat(schainName));
            require(rotation.freezeUntil < now, revertMessage);
            _startRotation(schains[i], nodeIndex);
        }
    }


    function removeRotation(bytes32 schainIndex) external allow("Schains") {
        delete rotations[schainIndex];
    }

    function skipRotationDelay(bytes32 schainIndex) external onlyOwner {
        rotations[schainIndex].freezeUntil = now;
    }

    function getRotation(bytes32 schainIndex) external view returns (Rotation memory) {
        return rotations[schainIndex];
    }

    function getLeavingHistory(uint nodeIndex) external view returns (LeavingHistory[] memory) {
        return leavingHistory[nodeIndex];
    }

    function isRotationInProgress(bytes32 schainIndex) external view returns (bool) {
        return rotations[schainIndex].freezeUntil >= now && !waitForNewNode[schainIndex];
    }

    function initialize(address newContractsAddress) public override initializer {
        Permissions.initialize(newContractsAddress);
    }

    function rotateNode(
        uint nodeIndex,
        bytes32 schainId,
        bool shouldDelay
    )
        public
        allowTwo("SkaleDKG", "SkaleManager")
        returns (uint newNode)
    {
        SchainsInternal schainsInternal = SchainsInternal(contractManager.getContract("SchainsInternal"));
        Schains schains = Schains(contractManager.getContract("Schains"));
        schainsInternal.removeNodeFromSchain(nodeIndex, schainId);
        newNode = selectNodeToGroup(schainId);
        uint8 space = schainsInternal.getSchainsPartOfNode(schainId);
        schains.addSpace(nodeIndex, space);
        _finishRotation(schainId, nodeIndex, newNode, shouldDelay);
    }

    /**
     * @dev selectNodeToGroup - pseudo-randomly select new Node for Schain
     * @param schainId - hash of name of Schain
     * @return nodeIndex - global index of Node
     */
    function selectNodeToGroup(bytes32 schainId)
        public
        allowThree("SkaleManager", "Schains", "SkaleDKG")
        returns (uint)
    {
        SchainsInternal schainsInternal = SchainsInternal(contractManager.getContract("SchainsInternal"));
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));
        require(schainsInternal.isSchainActive(schainId), "Group is not active");
        uint8 space = schainsInternal.getSchainsPartOfNode(schainId);
        uint[] memory possibleNodes = schainsInternal.isEnoughNodes(schainId);
        require(possibleNodes.length > 0, "No any free Nodes for rotation");
        uint nodeIndex;
        uint random = uint(keccak256(abi.encodePacked(uint(blockhash(block.number - 1)), schainId)));
        do {
            uint index = random % possibleNodes.length;
            nodeIndex = possibleNodes[index];
            random = uint(keccak256(abi.encodePacked(random, nodeIndex)));
        } while (schainsInternal.checkException(schainId, nodeIndex));
        require(nodes.removeSpaceFromNode(nodeIndex, space), "Could not remove space from nodeIndex");
        schainsInternal.addSchainForNode(nodeIndex, schainId);
        schainsInternal.setException(schainId, nodeIndex);
        schainsInternal.setNodeInGroup(schainId, nodeIndex);
        return nodeIndex;
    }


    function _startRotation(bytes32 schainIndex, uint nodeIndex) private {
        ConstantsHolder constants = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        rotations[schainIndex].nodeIndex = nodeIndex;
        rotations[schainIndex].newNodeIndex = nodeIndex;
        rotations[schainIndex].freezeUntil = now.add(constants.rotationDelay());
        waitForNewNode[schainIndex] = true;
    }

    function _finishRotation(
        bytes32 schainIndex,
        uint nodeIndex,
        uint newNodeIndex,
        bool shouldDelay)
        private
    {
        ConstantsHolder constants = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        leavingHistory[nodeIndex].push(
            LeavingHistory(schainIndex, shouldDelay ? now.add(constants.rotationDelay()) : now)
        );
        rotations[schainIndex].newNodeIndex = newNodeIndex;
        rotations[schainIndex].rotationCounter++;
        delete waitForNewNode[schainIndex];
        ISkaleDKG(contractManager.getContract("SkaleDKG")).openChannel(schainIndex);
    }

    function _checkRotation(bytes32 schainId ) private view returns (bool) {
        SchainsInternal schainsInternal = SchainsInternal(contractManager.getContract("SchainsInternal"));
        require(schainsInternal.isSchainExist(schainId), "Schain does not exist for rotation");
        return schainsInternal.isAnyFreeNode(schainId);
    }


}

File 29 of 45: Nodes.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    Nodes.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;
pragma experimental ABIEncoderV2;

import "./SafeCast.sol";

import "./Permissions.sol";
import "./ConstantsHolder.sol";
import "./ValidatorService.sol";
import "./DelegationController.sol";


/**
 * @title Nodes - contract contains all functionality logic to manage Nodes
 */
contract Nodes is Permissions {
    
    using SafeCast for uint;

    // All Nodes states
    enum NodeStatus {Active, Leaving, Left, In_Maintenance}

    struct Node {
        string name;
        bytes4 ip;
        bytes4 publicIP;
        uint16 port;
        bytes32[2] publicKey;
        uint startBlock;
        uint lastRewardDate;
        uint finishTime;
        NodeStatus status;
        uint validatorId;
    }

    // struct to note which Nodes and which number of Nodes owned by user
    struct CreatedNodes {
        mapping (uint => bool) isNodeExist;
        uint numberOfNodes;
    }

    struct SpaceManaging {
        uint8 freeSpace;
        uint indexInSpaceMap;
    }

    // TODO: move outside the contract
    struct NodeCreationParams {
        string name;
        bytes4 ip;
        bytes4 publicIp;
        uint16 port;
        bytes32[2] publicKey;
        uint16 nonce;
    }

    // array which contain all Nodes
    Node[] public nodes;

    SpaceManaging[] public spaceOfNodes;

    // mapping for checking which Nodes and which number of Nodes owned by user
    mapping (address => CreatedNodes) public nodeIndexes;
    // mapping for checking is IP address busy
    mapping (bytes4 => bool) public nodesIPCheck;
    // mapping for checking is Name busy
    mapping (bytes32 => bool) public nodesNameCheck;
    // mapping for indication from Name to Index
    mapping (bytes32 => uint) public nodesNameToIndex;
    // mapping for indication from space to Nodes
    mapping (uint8 => uint[]) public spaceToNodes;

    mapping (uint => uint[]) public validatorToNodeIndexes;

    uint public numberOfActiveNodes;
    uint public numberOfLeavingNodes;
    uint public numberOfLeftNodes;

    // informs that Node is created
    event NodeCreated(
        uint nodeIndex,
        address owner,
        string name,
        bytes4 ip,
        bytes4 publicIP,
        uint16 port,
        uint16 nonce,
        uint time,
        uint gasSpend
    );

    // informs that node is fully finished quitting from the system
    event ExitCompleted(
        uint nodeIndex,
        uint time,
        uint gasSpend
    );

    // informs that owner starts the procedure of quitting the Node from the system
    event ExitInited(
        uint nodeIndex,
        uint startLeavingPeriod,
        uint time,
        uint gasSpend
    );

    modifier checkNodeExists(uint nodeIndex) {
        require(nodeIndex < nodes.length, "Node with such index does not exist");
        _;
    }

    /**
     * @dev removeSpaceFromFractionalNode - occupies space from Fractional Node
     * function could be run only by Schains
     * @param nodeIndex - index of Node at array of Fractional Nodes
     * @param space - space which should be occupied
     */
    function removeSpaceFromNode(uint nodeIndex, uint8 space)
        external
        checkNodeExists(nodeIndex)
        allowTwo("NodeRotation", "SchainsInternal")
        returns (bool)
    {
        if (spaceOfNodes[nodeIndex].freeSpace < space) {
            return false;
        }
        if (space > 0) {
            _moveNodeToNewSpaceMap(
                nodeIndex,
                uint(spaceOfNodes[nodeIndex].freeSpace).sub(space).toUint8()
            );
        }
        return true;
    }

    /**
     * @dev adSpaceToFractionalNode - returns space to Fractional Node
     * function could be run only be Schains
     * @param nodeIndex - index of Node at array of Fractional Nodes
     * @param space - space which should be returned
     */
    function addSpaceToNode(uint nodeIndex, uint8 space)
        external
        checkNodeExists(nodeIndex)
        allow("Schains")
    {
        if (space > 0) {
            _moveNodeToNewSpaceMap(
                nodeIndex,
                uint(spaceOfNodes[nodeIndex].freeSpace).add(space).toUint8()
            );
        }
    }

    /**
     * @dev changeNodeLastRewardDate - changes Node's last reward date
     * function could be run only by SkaleManager
     * @param nodeIndex - index of Node
     */
    function changeNodeLastRewardDate(uint nodeIndex)
        external
        checkNodeExists(nodeIndex)
        allow("SkaleManager")
    {
        nodes[nodeIndex].lastRewardDate = block.timestamp;
    }

    function changeNodeFinishTime(uint nodeIndex, uint time)
        external
        checkNodeExists(nodeIndex)
        allow("SkaleManager")
    {
        nodes[nodeIndex].finishTime = time;
    }

    /**
     * @dev createNode - creates new Node and add it to the Nodes contract
     * function could be only run by SkaleManager
     * @param from - owner of Node
     */
    //  * @return nodeIndex - index of Node
    function createNode(address from, NodeCreationParams calldata params)
        external
        allow("SkaleManager")
        // returns (uint nodeIndex)
    {
        // checks that Node has correct data
        require(params.ip != 0x0 && !nodesIPCheck[params.ip], "IP address is zero or is not available");
        require(!nodesNameCheck[keccak256(abi.encodePacked(params.name))], "Name has already registered");
        require(params.port > 0, "Port is zero");

        uint validatorId = ValidatorService(
            contractManager.getContract("ValidatorService")).getValidatorIdByNodeAddress(from);

        // adds Node to Nodes contract
        uint nodeIndex = _addNode(
            from,
            params.name,
            params.ip,
            params.publicIp,
            params.port,
            params.publicKey,
            validatorId);

        emit NodeCreated(
            nodeIndex,
            from,
            params.name,
            params.ip,
            params.publicIp,
            params.port,
            params.nonce,
            block.timestamp,
            gasleft());
    }

    /**
     * @dev initExit - initiate a procedure of quitting the system
     * function could be only run by SkaleManager
     * @param nodeIndex - index of Node
     * @return true - if everything OK
     */
    function initExit(uint nodeIndex)
        external
        checkNodeExists(nodeIndex)
        allow("SkaleManager")
        returns (bool)
    {
        _setNodeLeaving(nodeIndex);

        emit ExitInited(
            nodeIndex,
            block.timestamp,
            block.timestamp,
            gasleft());
        return true;
    }

    /**
     * @dev completeExit - finish a procedure of quitting the system
     * function could be run only by SkaleManager
     * @param nodeIndex - index of Node
     * @return amount of SKL which be returned
     */
    function completeExit(uint nodeIndex)
        external
        checkNodeExists(nodeIndex)
        allow("SkaleManager")
        returns (bool)
    {
        require(isNodeLeaving(nodeIndex), "Node is not Leaving");

        _setNodeLeft(nodeIndex);
        _deleteNode(nodeIndex);

        emit ExitCompleted(
            nodeIndex,
            block.timestamp,
            gasleft());
        return true;
    }

    function deleteNodeForValidator(uint validatorId, uint nodeIndex)
        external
        checkNodeExists(nodeIndex)
        allow("SkaleManager")
    {
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        require(validatorService.validatorExists(validatorId), "Validator with such ID does not exist");
        uint[] memory validatorNodes = validatorToNodeIndexes[validatorId];
        uint position = _findNode(validatorNodes, nodeIndex);
        if (position < validatorNodes.length) {
            validatorToNodeIndexes[validatorId][position] =
                validatorToNodeIndexes[validatorId][validatorNodes.length.sub(1)];
        }
        validatorToNodeIndexes[validatorId].pop();
    }

    function checkPossibilityCreatingNode(address nodeAddress) external allow("SkaleManager") {
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        DelegationController delegationController = DelegationController(
            contractManager.getContract("DelegationController")
        );
        uint validatorId = validatorService.getValidatorIdByNodeAddress(nodeAddress);
        require(validatorService.isAuthorizedValidator(validatorId), "Validator is not authorized to create a node");
        uint[] memory validatorNodes = validatorToNodeIndexes[validatorId];
        uint delegationsTotal = delegationController.getAndUpdateDelegatedToValidatorNow(validatorId);
        uint msr = ConstantsHolder(contractManager.getContract("ConstantsHolder")).msr();
        require(
            validatorNodes.length.add(1).mul(msr) <= delegationsTotal,
            "Validator must meet the Minimum Staking Requirement");
    }

    function checkPossibilityToMaintainNode(
        uint validatorId,
        uint nodeIndex
    )
        external
        checkNodeExists(nodeIndex)
        allow("Bounty")
        returns (bool)
    {
        DelegationController delegationController = DelegationController(
            contractManager.getContract("DelegationController")
        );
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        require(validatorService.validatorExists(validatorId), "Validator with such ID does not exist");
        uint[] memory validatorNodes = validatorToNodeIndexes[validatorId];
        uint position = _findNode(validatorNodes, nodeIndex);
        require(position < validatorNodes.length, "Node does not exist for this Validator");
        uint delegationsTotal = delegationController.getAndUpdateDelegatedToValidatorNow(validatorId);
        uint msr = ConstantsHolder(contractManager.getContract("ConstantsHolder")).msr();
        return position.add(1).mul(msr) <= delegationsTotal;
    }

    function setNodeInMaintenance(uint nodeIndex) external {
        require(nodes[nodeIndex].status == NodeStatus.Active, "Node is not Active");
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        uint validatorId = getValidatorId(nodeIndex);
        bool permitted = (_isOwner() || isNodeExist(msg.sender, nodeIndex));
        if (!permitted) {
            permitted = validatorService.getValidatorId(msg.sender) == validatorId;
        }
        require(permitted, "Sender is not permitted to call this function");
        nodes[nodeIndex].status = NodeStatus.In_Maintenance;
    }

    function removeNodeFromInMaintenance(uint nodeIndex) external {
        require(nodes[nodeIndex].status == NodeStatus.In_Maintenance, "Node is not In Maintence");
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        uint validatorId = getValidatorId(nodeIndex);
        bool permitted = (_isOwner() || isNodeExist(msg.sender, nodeIndex));
        if (!permitted) {
            permitted = validatorService.getValidatorId(msg.sender) == validatorId;
        }
        require(permitted, "Sender is not permitted to call this function");
        nodes[nodeIndex].status = NodeStatus.Active;
    }

    function getNodesWithFreeSpace(uint8 freeSpace) external view returns (uint[] memory) {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        uint[] memory nodesWithFreeSpace = new uint[](countNodesWithFreeSpace(freeSpace));
        uint cursor = 0;
        uint totalSpace = constantsHolder.TOTAL_SPACE_ON_NODE();
        for (uint8 i = freeSpace; i <= totalSpace; ++i) {
            for (uint j = 0; j < spaceToNodes[i].length; j++) {
                nodesWithFreeSpace[cursor] = spaceToNodes[i][j];
                ++cursor;
            }
        }
        return nodesWithFreeSpace;
    }

    /**
     * @dev isTimeForReward - checks if time for reward has come
     * @param nodeIndex - index of Node
     * @return if time for reward has come - true, else - false
     */
    function isTimeForReward(uint nodeIndex)
        external
        view
        checkNodeExists(nodeIndex)
        returns (bool)
    {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        return uint(nodes[nodeIndex].lastRewardDate).add(constantsHolder.rewardPeriod()) <= block.timestamp;
    }

    /**
     * @dev getNodeIP - get ip address of Node
     * @param nodeIndex - index of Node
     * @return ip address
     */
    function getNodeIP(uint nodeIndex)
        external
        view
        checkNodeExists(nodeIndex)
        returns (bytes4)
    {
        require(nodeIndex < nodes.length, "Node does not exist");
        return nodes[nodeIndex].ip;
    }

    /**
     * @dev getNodePort - get Node's port
     * @param nodeIndex - index of Node
     * @return port
     */
    function getNodePort(uint nodeIndex)
        external
        view
        checkNodeExists(nodeIndex)
        returns (uint16)
    {
        return nodes[nodeIndex].port;
    }

    function getNodePublicKey(uint nodeIndex)
        external
        view
        checkNodeExists(nodeIndex)
        returns (bytes32[2] memory)
    {
        return nodes[nodeIndex].publicKey;
    }

    function getNodeFinishTime(uint nodeIndex)
        external
        view
        checkNodeExists(nodeIndex)
        returns (uint)
    {
        return nodes[nodeIndex].finishTime;
    }

    /**
     * @dev isNodeLeft - checks if Node status Left
     * @param nodeIndex - index of Node
     * @return if Node status Left - true, else - false
     */
    function isNodeLeft(uint nodeIndex)
        external
        view
        checkNodeExists(nodeIndex)
        returns (bool)
    {
        return nodes[nodeIndex].status == NodeStatus.Left;
    }

    function isNodeInMaintenance(uint nodeIndex)
        external
        view
        checkNodeExists(nodeIndex)
        returns (bool)
    {
        return nodes[nodeIndex].status == NodeStatus.In_Maintenance;
    }

    /**
     * @dev getNodeLastRewardDate - get Node last reward date
     * @param nodeIndex - index of Node
     * @return Node last reward date
     */
    function getNodeLastRewardDate(uint nodeIndex)
        external
        view
        checkNodeExists(nodeIndex)
        returns (uint)
    {
        return nodes[nodeIndex].lastRewardDate;
    }

    /**
     * @dev getNodeNextRewardDate - get Node next reward date
     * @param nodeIndex - index of Node
     * @return Node next reward date
     */
    function getNodeNextRewardDate(uint nodeIndex)
        external
        view
        checkNodeExists(nodeIndex)
        returns (uint)
    {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        return nodes[nodeIndex].lastRewardDate.add(constantsHolder.rewardPeriod());
    }

    /**
     * @dev getNumberOfNodes - get number of Nodes
     * @return number of Nodes
     */
    function getNumberOfNodes() external view returns (uint) {
        return nodes.length;
    }

    /**
     * @dev getNumberOfFullNodes - get number Online Nodes
     * @return number of active nodes plus number of leaving nodes
     */
    function getNumberOnlineNodes() external view returns (uint) {
        return numberOfActiveNodes.add(numberOfLeavingNodes);
    }

    /**
     * @dev getActiveNodeIPs - get array of ips of Active Nodes
     * @return activeNodeIPs - array of ips of Active Nodes
     */
    function getActiveNodeIPs() external view returns (bytes4[] memory activeNodeIPs) {
        activeNodeIPs = new bytes4[](numberOfActiveNodes);
        uint indexOfActiveNodeIPs = 0;
        for (uint indexOfNodes = 0; indexOfNodes < nodes.length; indexOfNodes++) {
            if (isNodeActive(indexOfNodes)) {
                activeNodeIPs[indexOfActiveNodeIPs] = nodes[indexOfNodes].ip;
                indexOfActiveNodeIPs++;
            }
        }
    }

    /**
     * @dev getActiveNodesByAddress - get array of indexes of Active Nodes, which were
     * created by msg.sender
     * @return activeNodesByAddress Array of indexes of Active Nodes, which were created by msg.sender
     */
    function getActiveNodesByAddress() external view returns (uint[] memory activeNodesByAddress) {
        activeNodesByAddress = new uint[](nodeIndexes[msg.sender].numberOfNodes);
        uint indexOfActiveNodesByAddress = 0;
        for (uint indexOfNodes = 0; indexOfNodes < nodes.length; indexOfNodes++) {
            if (nodeIndexes[msg.sender].isNodeExist[indexOfNodes] && isNodeActive(indexOfNodes)) {
                activeNodesByAddress[indexOfActiveNodesByAddress] = indexOfNodes;
                indexOfActiveNodesByAddress++;
            }
        }
    }

    /**
     * @dev getActiveNodeIds - get array of indexes of Active Nodes
     * @return activeNodeIds - array of indexes of Active Nodes
     */
    function getActiveNodeIds() external view returns (uint[] memory activeNodeIds) {
        activeNodeIds = new uint[](numberOfActiveNodes);
        uint indexOfActiveNodeIds = 0;
        for (uint indexOfNodes = 0; indexOfNodes < nodes.length; indexOfNodes++) {
            if (isNodeActive(indexOfNodes)) {
                activeNodeIds[indexOfActiveNodeIds] = indexOfNodes;
                indexOfActiveNodeIds++;
            }
        }
    }

    function getNodeStatus(uint nodeIndex)
        external
        view
        checkNodeExists(nodeIndex)
        returns (NodeStatus)
    {
        return nodes[nodeIndex].status;
    }

    function getValidatorNodeIndexes(uint validatorId) external view returns (uint[] memory) {
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        require(validatorService.validatorExists(validatorId), "Validator with such ID does not exist");
        return validatorToNodeIndexes[validatorId];
    }

    /**
     * @dev constructor in Permissions approach
     * @param contractsAddress needed in Permissions constructor
    */
    function initialize(address contractsAddress) public override initializer {
        Permissions.initialize(contractsAddress);

        numberOfActiveNodes = 0;
        numberOfLeavingNodes = 0;
        numberOfLeftNodes = 0;
    }

    function getValidatorId(uint nodeIndex)
        public
        view
        checkNodeExists(nodeIndex)
        returns (uint)
    {
        return nodes[nodeIndex].validatorId;
    }

    /**
     * @dev isNodeExist - checks existence of Node at this address
     * @param from - account address
     * @param nodeIndex - index of Node
     * @return if exist - true, else - false
     */
    function isNodeExist(address from, uint nodeIndex)
        public
        view
        checkNodeExists(nodeIndex)
        returns (bool)
    {
        return nodeIndexes[from].isNodeExist[nodeIndex];
    }

    /**
     * @dev isNodeActive - checks if Node status Active
     * @param nodeIndex - index of Node
     * @return if Node status Active - true, else - false
     */
    function isNodeActive(uint nodeIndex)
        public
        view
        checkNodeExists(nodeIndex)
        returns (bool)
    {
        return nodes[nodeIndex].status == NodeStatus.Active;
    }

    /**
     * @dev isNodeLeaving - checks if Node status Leaving
     * @param nodeIndex - index of Node
     * @return if Node status Leaving - true, else - false
     */
    function isNodeLeaving(uint nodeIndex)
        public
        view
        checkNodeExists(nodeIndex)
        returns (bool)
    {
        return nodes[nodeIndex].status == NodeStatus.Leaving;
    }

    function countNodesWithFreeSpace(uint8 freeSpace) public view returns (uint count) {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        count = 0;
        uint totalSpace = constantsHolder.TOTAL_SPACE_ON_NODE();
        for (uint8 i = freeSpace; i <= totalSpace; ++i) {
            count = count.add(spaceToNodes[i].length);
        }
    }

    function _findNode(uint[] memory validatorNodeIndexes, uint nodeIndex) private pure returns (uint) {
        uint i;
        for (i = 0; i < validatorNodeIndexes.length; i++) {
            if (validatorNodeIndexes[i] == nodeIndex) {
                return i;
            }
        }
        return validatorNodeIndexes.length;
    }

    function _moveNodeToNewSpaceMap(uint nodeIndex, uint8 newSpace) private {
        uint8 previousSpace = spaceOfNodes[nodeIndex].freeSpace;
        uint indexInArray = spaceOfNodes[nodeIndex].indexInSpaceMap;
        if (indexInArray < spaceToNodes[previousSpace].length.sub(1)) {
            uint shiftedIndex = spaceToNodes[previousSpace][spaceToNodes[previousSpace].length.sub(1)];
            spaceToNodes[previousSpace][indexInArray] = shiftedIndex;
            spaceOfNodes[shiftedIndex].indexInSpaceMap = indexInArray;
            spaceToNodes[previousSpace].pop();
        } else {
            spaceToNodes[previousSpace].pop();
        }
        spaceToNodes[newSpace].push(nodeIndex);
        spaceOfNodes[nodeIndex].freeSpace = newSpace;
        spaceOfNodes[nodeIndex].indexInSpaceMap = spaceToNodes[newSpace].length.sub(1);
    }

    /**
     * @dev _setNodeLeft - set Node Left
     * function could be run only by Nodes
     * @param nodeIndex - index of Node
     */
    function _setNodeLeft(uint nodeIndex) private {
        nodesIPCheck[nodes[nodeIndex].ip] = false;
        nodesNameCheck[keccak256(abi.encodePacked(nodes[nodeIndex].name))] = false;
        delete nodesNameToIndex[keccak256(abi.encodePacked(nodes[nodeIndex].name))];
        if (nodes[nodeIndex].status == NodeStatus.Active) {
            numberOfActiveNodes--;
        } else {
            numberOfLeavingNodes--;
        }
        nodes[nodeIndex].status = NodeStatus.Left;
        numberOfLeftNodes++;
    }

    /**
     * @dev _setNodeLeaving - set Node Leaving
     * function could be run only by Nodes
     * @param nodeIndex - index of Node
     */
    function _setNodeLeaving(uint nodeIndex) private {
        nodes[nodeIndex].status = NodeStatus.Leaving;
        numberOfActiveNodes--;
        numberOfLeavingNodes++;
    }

    /**
     * @dev _addNode - adds Node to array
     * function could be run only by executor
     * @param from - owner of Node
     * @param name - Node name
     * @param ip - Node ip
     * @param publicIP - Node public ip
     * @param port - Node public port
     * @param publicKey - Ethereum public key
     * @return nodeIndex Index of Node
     */
    function _addNode(
        address from,
        string memory name,
        bytes4 ip,
        bytes4 publicIP,
        uint16 port,
        bytes32[2] memory publicKey,
        uint validatorId
    )
        private
        returns (uint nodeIndex)
    {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        nodes.push(Node({
            name: name,
            ip: ip,
            publicIP: publicIP,
            port: port,
            //owner: from,
            publicKey: publicKey,
            startBlock: block.number,
            lastRewardDate: block.timestamp,
            finishTime: 0,
            status: NodeStatus.Active,
            validatorId: validatorId
        }));
        nodeIndex = nodes.length.sub(1);
        validatorToNodeIndexes[validatorId].push(nodeIndex);
        bytes32 nodeId = keccak256(abi.encodePacked(name));
        nodesIPCheck[ip] = true;
        nodesNameCheck[nodeId] = true;
        nodesNameToIndex[nodeId] = nodeIndex;
        nodeIndexes[from].isNodeExist[nodeIndex] = true;
        nodeIndexes[from].numberOfNodes++;
        spaceOfNodes.push(SpaceManaging({
            freeSpace: constantsHolder.TOTAL_SPACE_ON_NODE(),
            indexInSpaceMap: spaceToNodes[constantsHolder.TOTAL_SPACE_ON_NODE()].length
        }));
        spaceToNodes[constantsHolder.TOTAL_SPACE_ON_NODE()].push(nodeIndex);
        numberOfActiveNodes++;
    }

    function _deleteNode(uint nodeIndex) private {
        uint8 space = spaceOfNodes[nodeIndex].freeSpace;
        uint indexInArray = spaceOfNodes[nodeIndex].indexInSpaceMap;
        if (indexInArray < spaceToNodes[space].length.sub(1)) {
            uint shiftedIndex = spaceToNodes[space][spaceToNodes[space].length.sub(1)];
            spaceToNodes[space][indexInArray] = shiftedIndex;
            spaceOfNodes[shiftedIndex].indexInSpaceMap = indexInArray;
            spaceToNodes[space].pop();
        } else {
            spaceToNodes[space].pop();
        }
        delete spaceOfNodes[nodeIndex].freeSpace;
        delete spaceOfNodes[nodeIndex].indexInSpaceMap;
    }

}

File 30 of 45: Ownable.sol
pragma solidity ^0.6.0;

import "./Context.sol";
import "./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.
 */
contract OwnableUpgradeSafe is Initializable, ContextUpgradeSafe {
    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 initializer {
        __Context_init_unchained();
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal initializer {


        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);

    }


    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        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 {
        emit OwnershipTransferred(_owner, address(0));
        _owner = 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");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }

    uint256[49] private __gap;
}

File 31 of 45: PartialDifferences.sol
// 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.6.10;

import "./MathUtils.sol";
import "./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 SafeMath for uint;
    using MathUtils for uint;

    struct Sequence {
             // month => diff
        mapping (uint => uint) addDiff;
             // month => diff
        mapping (uint => uint) subtractDiff;
             // month => value
        mapping (uint => uint) value;

        uint firstUnprocessedMonth;
        uint lastChangedMonth;
    }

    struct Value {
             // month => diff
        mapping (uint => uint) addDiff;
             // month => diff
        mapping (uint => uint) subtractDiff;

        uint value;
        uint firstUnprocessedMonth;
        uint lastChangedMonth;
    }

    // functions for sequence

    function addToSequence(Sequence storage sequence, uint diff, uint month) internal {
        require(sequence.firstUnprocessedMonth <= month, "Cannot add to the past");
        if (sequence.firstUnprocessedMonth == 0) {
            sequence.firstUnprocessedMonth = month;
        }
        sequence.addDiff[month] = sequence.addDiff[month].add(diff);
        if (sequence.lastChangedMonth != month) {
            sequence.lastChangedMonth = month;
        }
    }

    function subtractFromSequence(Sequence storage sequence, uint diff, uint month) internal {
        require(sequence.firstUnprocessedMonth <= month, "Cannot subtract from the past");
        if (sequence.firstUnprocessedMonth == 0) {
            sequence.firstUnprocessedMonth = month;
        }
        sequence.subtractDiff[month] = sequence.subtractDiff[month].add(diff);
        if (sequence.lastChangedMonth != month) {
            sequence.lastChangedMonth = month;
        }
    }

    function getAndUpdateValueInSequence(Sequence storage sequence, uint month) internal returns (uint) {
        if (sequence.firstUnprocessedMonth == 0) {
            return 0;
        }

        if (sequence.firstUnprocessedMonth <= month) {
            for (uint i = sequence.firstUnprocessedMonth; i <= month; ++i) {
                uint nextValue = sequence.value[i.sub(1)].add(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.add(1);
        }

        return sequence.value[month];
    }

    function reduceSequence(
        Sequence storage sequence,
        FractionUtils.Fraction memory reducingCoefficient,
        uint month) internal
    {
        require(month.add(1) >= sequence.firstUnprocessedMonth, "Can't reduce value in the past");
        require(
            reducingCoefficient.numerator <= reducingCoefficient.denominator,
            "Increasing of values is not implemented");
        if (sequence.firstUnprocessedMonth == 0) {
            return;
        }
        uint value = getAndUpdateValueInSequence(sequence, month);
        if (value.approximatelyEqual(0)) {
            return;
        }

        sequence.value[month] = sequence.value[month]
            .mul(reducingCoefficient.numerator)
            .div(reducingCoefficient.denominator);

        for (uint i = month.add(1); i <= sequence.lastChangedMonth; ++i) {
            sequence.subtractDiff[i] = sequence.subtractDiff[i]
                .mul(reducingCoefficient.numerator)
                .div(reducingCoefficient.denominator);
        }
    }

    // functions for value

    function addToValue(Value storage sequence, uint diff, uint 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].add(diff);
        } else {
            sequence.value = sequence.value.add(diff);
        }
    }

    function subtractFromValue(Value storage sequence, uint diff, uint month) internal {
        require(sequence.firstUnprocessedMonth <= month.add(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].add(diff);
        } else {
            sequence.value = sequence.value.boundedSub(diff);
        }
    }

    function getAndUpdateValue(Value storage sequence, uint month) internal returns (uint) {
        require(
            month.add(1) >= sequence.firstUnprocessedMonth,
            "Cannot calculate value in the past");
        if (sequence.firstUnprocessedMonth == 0) {
            return 0;
        }

        if (sequence.firstUnprocessedMonth <= month) {
            for (uint i = sequence.firstUnprocessedMonth; i <= month; ++i) {
                uint newValue = sequence.value.add(sequence.addDiff[i]).boundedSub(sequence.subtractDiff[i]);
                if (sequence.value != newValue) {
                    sequence.value = newValue;
                }
                if (sequence.addDiff[i] > 0) {
                    delete sequence.addDiff[i];
                }
                if (sequence.subtractDiff[i] > 0) {
                    delete sequence.subtractDiff[i];
                }
            }
            sequence.firstUnprocessedMonth = month.add(1);
        }

        return sequence.value;
    }

    function reduceValue(
        Value storage sequence,
        uint amount,
        uint month)
        internal returns (FractionUtils.Fraction memory)
    {
        require(month.add(1) >= sequence.firstUnprocessedMonth, "Cannot reduce value in the past");
        if (sequence.firstUnprocessedMonth == 0) {
            return FractionUtils.createFraction(0);
        }
        uint value = getAndUpdateValue(sequence, month);
        if (value.approximatelyEqual(0)) {
            return FractionUtils.createFraction(0);
        }

        uint _amount = amount;
        if (value < amount) {
            _amount = value;
        }

        FractionUtils.Fraction memory reducingCoefficient =
            FractionUtils.createFraction(value.boundedSub(_amount), value);
        reduceValueByCoefficient(sequence, reducingCoefficient, month);
        return reducingCoefficient;
    }

    function reduceValueByCoefficient(
        Value storage sequence,
        FractionUtils.Fraction memory reducingCoefficient,
        uint month)
        internal
    {
        reduceValueByCoefficientAndUpdateSumIfNeeded(
            sequence,
            sequence,
            reducingCoefficient,
            month,
            false);
    }

    function reduceValueByCoefficientAndUpdateSum(
        Value storage sequence,
        Value storage sumSequence,
        FractionUtils.Fraction memory reducingCoefficient,
        uint month) internal
    {
        reduceValueByCoefficientAndUpdateSumIfNeeded(
            sequence,
            sumSequence,
            reducingCoefficient,
            month,
            true);
    }

    function reduceValueByCoefficientAndUpdateSumIfNeeded(
        Value storage sequence,
        Value storage sumSequence,
        FractionUtils.Fraction memory reducingCoefficient,
        uint month,
        bool hasSumSequence) internal
    {
        require(month.add(1) >= sequence.firstUnprocessedMonth, "Cannot reduce value in the past");
        if (hasSumSequence) {
            require(month.add(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;
        }
        uint value = getAndUpdateValue(sequence, month);
        if (value.approximatelyEqual(0)) {
            return;
        }

        uint newValue = sequence.value.mul(reducingCoefficient.numerator).div(reducingCoefficient.denominator);
        if (hasSumSequence) {
            subtractFromValue(sumSequence, sequence.value.boundedSub(newValue), month);
        }
        sequence.value = newValue;

        for (uint i = month.add(1); i <= sequence.lastChangedMonth; ++i) {
            uint newDiff = sequence.subtractDiff[i]
                .mul(reducingCoefficient.numerator)
                .div(reducingCoefficient.denominator);
            if (hasSumSequence) {
                sumSequence.subtractDiff[i] = sumSequence.subtractDiff[i]
                    .boundedSub(sequence.subtractDiff[i].boundedSub(newDiff));
            }
            sequence.subtractDiff[i] = newDiff;
        }
    }

    function clear(Value storage sequence) internal {
        for (uint i = sequence.firstUnprocessedMonth; i <= sequence.lastChangedMonth; ++i) {
            if (sequence.addDiff[i] > 0) {
                delete sequence.addDiff[i];
            }
            if (sequence.subtractDiff[i] > 0) {
                delete sequence.subtractDiff[i];
            }
        }
        if (sequence.value > 0) {
            delete sequence.value;
        }
        if (sequence.firstUnprocessedMonth > 0) {
            delete sequence.firstUnprocessedMonth;
        }
        if (sequence.lastChangedMonth > 0) {
            delete sequence.lastChangedMonth;
        }
    }
}

File 32 of 45: Permissions.sol
// 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.6.10;

import "./SafeMath.sol";
import "./AccessControl.sol";

import "./ContractManager.sol";


/**
 * @title Permissions - connected module for Upgradeable approach, knows ContractManager
 * @author Artem Payvin
 */
contract Permissions is AccessControlUpgradeSafe {
    using SafeMath for uint;
    using Address for address;
    
    ContractManager public contractManager;

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(_isOwner(), "Caller is not the owner");
        _;
    }

    modifier onlyAdmin() {
        require(_isAdmin(msg.sender), "Caller is not an admin");
        _;
    }

    /**
     * @dev allow - throws if called by any account and contract other than the owner
     * or `contractName` contract
     * @param contractName - human readable name of contract
     */
    modifier allow(string memory contractName) {
        require(
            contractManager.contracts(keccak256(abi.encodePacked(contractName))) == msg.sender || _isOwner(),
            "Message sender is invalid");
        _;
    }

    modifier allowTwo(string memory contractName1, string memory contractName2) {
        require(
            contractManager.contracts(keccak256(abi.encodePacked(contractName1))) == msg.sender ||
            contractManager.contracts(keccak256(abi.encodePacked(contractName2))) == msg.sender ||
            _isOwner(),
            "Message sender is invalid");
        _;
    }

    modifier allowThree(string memory contractName1, string memory contractName2, string memory contractName3) {
        require(
            contractManager.contracts(keccak256(abi.encodePacked(contractName1))) == msg.sender ||
            contractManager.contracts(keccak256(abi.encodePacked(contractName2))) == msg.sender ||
            contractManager.contracts(keccak256(abi.encodePacked(contractName3))) == msg.sender ||
            _isOwner(),
            "Message sender is invalid");
        _;
    }

    function initialize(address contractManagerAddress) public virtual initializer {
        AccessControlUpgradeSafe.__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)) {
            AccessControlUpgradeSafe skaleManager = AccessControlUpgradeSafe(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 = ContractManager(contractManagerAddress);
    }
}

File 33 of 45: Precompiled.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    Precompiled.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.6.10;


library Precompiled {

    function bigModExp(uint base, uint power, uint modulus) internal view returns (uint) {
        uint[6] memory inputToBigModExp;
        inputToBigModExp[0] = 32;
        inputToBigModExp[1] = 32;
        inputToBigModExp[2] = 32;
        inputToBigModExp[3] = base;
        inputToBigModExp[4] = power;
        inputToBigModExp[5] = modulus;
        uint[1] memory out;
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            success := staticcall(not(0), 5, inputToBigModExp, mul(6, 0x20), out, 0x20)
        }
        require(success, "BigModExp failed");
        return out[0];
    }

    function bn256ScalarMul(uint x, uint y, uint k) internal view returns (uint , uint ) {
        uint[3] memory inputToMul;
        uint[2] memory output;
        inputToMul[0] = x;
        inputToMul[1] = y;
        inputToMul[2] = k;
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            success := staticcall(not(0), 7, inputToMul, 0x60, output, 0x40)
        }
        require(success, "Multiplication failed");
        return (output[0], output[1]);
    }

    function bn256Pairing(
        uint x1,
        uint y1,
        uint a1,
        uint b1,
        uint c1,
        uint d1,
        uint x2,
        uint y2,
        uint a2,
        uint b2,
        uint c2,
        uint d2)
        internal view returns (bool)
    {
        bool success;
        uint[12] memory inputToPairing;
        inputToPairing[0] = x1;
        inputToPairing[1] = y1;
        inputToPairing[2] = a1;
        inputToPairing[3] = b1;
        inputToPairing[4] = c1;
        inputToPairing[5] = d1;
        inputToPairing[6] = x2;
        inputToPairing[7] = y2;
        inputToPairing[8] = a2;
        inputToPairing[9] = b2;
        inputToPairing[10] = c2;
        inputToPairing[11] = d2;
        uint[1] memory out;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            success := staticcall(not(0), 8, inputToPairing, mul(12, 0x20), out, 0x20)
        }
        require(success, "Pairing check failed");
        return out[0] != 0;
    }
}

File 34 of 45: Punisher.sol
// SPDX-License-Identifier: AGPL-3.0-only

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

import "./Permissions.sol";
import "./ILocker.sol";

import "./ValidatorService.sol";
import "./DelegationController.sol";

/**
 * @title Punisher
 * @dev This contract handles all slashing and forgiving operations.
 */
contract Punisher is Permissions, ILocker {

    /**
     * @dev Emitted when a slashing condition occurs.
     */
    event Slash(
        uint validatorId,
        uint amount
    );

    /**
     * @dev Emitted when a forgive condition occurs.
     */
    event Forgive(
        address wallet,
        uint amount
    );

    //        holder => tokens
    mapping (address => uint) private _locked;

    /**
     * @dev Executes slashing on a validator and its delegations by an `amount`
     * of tokens. Currently, SkaleDKG is the only service allowed to execute
     * slashing.
     *
     * Emits a Slash event.
     *
     * @param validatorId uint validator to be slashed
     * @param amount uint slashed amount
    */
    function slash(uint validatorId, uint amount) external allow("SkaleDKG") {
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        DelegationController delegationController = DelegationController(
            contractManager.getContract("DelegationController"));

        require(validatorService.validatorExists(validatorId), "Validator does not exist");

        delegationController.confiscate(validatorId, amount);

        emit Slash(validatorId, amount);
    }

    /**
     * @dev Allows the Owner to forgive a slashing condition.
     *
     * Emits a Forgive event.
     *
     * @param holder address of the slashed
     * @param amount uint amount to be forgiven
     */
    function forgive(address holder, uint amount) external onlyAdmin {
        DelegationController delegationController = DelegationController(
            contractManager.getContract("DelegationController"));

        require(!delegationController.hasUnprocessedSlashes(holder), "Not all slashes were calculated");

        if (amount > _locked[holder]) {
            delete _locked[holder];
        } else {
            _locked[holder] = _locked[holder].sub(amount);
        }

        emit Forgive(holder, amount);
    }

    /**
     * @dev See ILocker-getAndUpdateLockedAmount
     */
    function getAndUpdateLockedAmount(address wallet) external override returns (uint) {
        return _getAndUpdateLockedAmount(wallet);
    }

    /**
     * @dev See ILocker-getAndUpdateForbiddenForDelegationAmount
     */
    function getAndUpdateForbiddenForDelegationAmount(address wallet) external override returns (uint) {
        return _getAndUpdateLockedAmount(wallet);
    }

    function handleSlash(address holder, uint amount) external allow("DelegationController") {
        _locked[holder] = _locked[holder].add(amount);
    }

    function initialize(address contractManagerAddress) public override initializer {
        Permissions.initialize(contractManagerAddress);
    }

    // private

    function _getAndUpdateLockedAmount(address wallet) private returns (uint) {
        DelegationController delegationController = DelegationController(
            contractManager.getContract("DelegationController"));

        delegationController.processAllSlashes(wallet);
        return _locked[wallet];
    }

}

File 35 of 45: SafeCast.sol
pragma solidity ^0.6.0;


/**
 * @dev Wrappers over Solidity's uintXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and then downcasting.
 */
library SafeCast {

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        require(value < 2**255, "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 36 of 45: SafeMath.sol
pragma solidity ^0.6.0;

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

        return c;
    }

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

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

        return c;
    }

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

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

        return c;
    }

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

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

        return c;
    }

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

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

File 37 of 45: Schains.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    Schains.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;
pragma experimental ABIEncoderV2;

import "./Permissions.sol";
import "./SchainsInternal.sol";
import "./ConstantsHolder.sol";
import "./KeyStorage.sol";
import "./SkaleVerifier.sol";
import "./FieldOperations.sol";
import "./NodeRotation.sol";
import "./ISkaleDKG.sol";


/**
 * @title Schains - contract contains all functionality logic to manage Schains
 */
contract Schains is Permissions {
    using StringUtils for string;
    using StringUtils for uint;

    struct SchainParameters {
        uint lifetime;
        uint8 typeOfSchain;
        uint16 nonce;
        string name;
    }

    // informs that Schain is created
    event SchainCreated(
        string name,
        address owner,
        uint partOfNode,
        uint lifetime,
        uint numberOfNodes,
        uint deposit,
        uint16 nonce,
        bytes32 schainId,
        uint time,
        uint gasSpend
    );

    event SchainDeleted(
        address owner,
        string name,
        bytes32 indexed schainId
    );

    event NodeRotated(
        bytes32 schainId,
        uint oldNode,
        uint newNode
    );

    event NodeAdded(
        bytes32 schainId,
        uint newNode
    );

    // informs that Schain based on some Nodes
    event SchainNodes(
        string name,
        bytes32 schainId,
        uint[] nodesInGroup,
        uint time,
        uint gasSpend
    );

    bytes32 public constant SCHAIN_CREATOR_ROLE = keccak256("SCHAIN_CREATOR_ROLE");

    /**
     * @dev addSchain - create Schain in the system
     * function could be run only by executor
     * @param from - owner of Schain
     * @param deposit - received amoung of SKL
     * @param data - Schain's data
     */
    function addSchain(address from, uint deposit, bytes calldata data) external allow("SkaleManager") {
        SchainParameters memory schainParameters = _fallbackSchainParametersDataConverter(data);
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        uint schainCreationTimeStamp = constantsHolder.schainCreationTimeStamp();
        uint minSchainLifetime = constantsHolder.minimalSchainLifetime();
        require(now >= schainCreationTimeStamp, "It is not a time for creating Schain");
        require(
            schainParameters.lifetime >= minSchainLifetime,
            "Minimal schain lifetime should be satisfied"
        );
        require(
            getSchainPrice(schainParameters.typeOfSchain, schainParameters.lifetime) <= deposit,
            "Not enough money to create Schain");

        _addSchain(from, deposit, schainParameters);
    }

    function addSchainByFoundation(
        uint lifetime,
        uint8 typeOfSchain,
        uint16 nonce,
        string calldata name
    )
        external
    {
        require(hasRole(SCHAIN_CREATOR_ROLE, msg.sender), "Sender is not authorized to create schian");

        SchainParameters memory schainParameters = SchainParameters({
            lifetime: lifetime,
            typeOfSchain: typeOfSchain,
            nonce: nonce,
            name: name
        });

        _addSchain(msg.sender, 0, schainParameters);
    }

    /**
     * @dev deleteSchain - removes Schain from the system
     * function could be run only by executor
     * @param from - owner of Schain
     * @param name - Schain name
     */
    function deleteSchain(address from, string calldata name) external allow("SkaleManager") {
        NodeRotation nodeRotation = NodeRotation(contractManager.getContract("NodeRotation"));
        SchainsInternal schainsInternal = SchainsInternal(contractManager.getContract("SchainsInternal"));
        bytes32 schainId = keccak256(abi.encodePacked(name));
        require(
            schainsInternal.isOwnerAddress(from, schainId),
            "Message sender is not an owner of Schain"
        );
        address nodesAddress = contractManager.getContract("Nodes");

        // removes Schain from Nodes
        uint[] memory nodesInGroup = schainsInternal.getNodesInGroup(schainId);
        uint8 partOfNode = schainsInternal.getSchainsPartOfNode(schainId);
        for (uint i = 0; i < nodesInGroup.length; i++) {
            uint schainIndex = schainsInternal.findSchainAtSchainsForNode(
                nodesInGroup[i],
                schainId
            );
            if (schainsInternal.checkHoleForSchain(schainId, i)) {
                continue;
            }
            require(
                schainIndex < schainsInternal.getLengthOfSchainsForNode(nodesInGroup[i]),
                "Some Node does not contain given Schain");
            schainsInternal.removeNodeFromSchain(nodesInGroup[i], schainId);
            schainsInternal.removeNodeFromExceptions(schainId, nodesInGroup[i]);
            if (!Nodes(nodesAddress).isNodeLeft(nodesInGroup[i])) {
                this.addSpace(nodesInGroup[i], partOfNode);
            }
        }
        schainsInternal.deleteGroup(schainId);
        schainsInternal.removeSchain(schainId, from);
        schainsInternal.removeHolesForSchain(schainId);
        nodeRotation.removeRotation(schainId);
        emit SchainDeleted(from, name, schainId);
    }

    function deleteSchainByRoot(string calldata name) external allow("SkaleManager") {
        NodeRotation nodeRotation = NodeRotation(contractManager.getContract("NodeRotation"));
        bytes32 schainId = keccak256(abi.encodePacked(name));
        SchainsInternal schainsInternal = SchainsInternal(
            contractManager.getContract("SchainsInternal"));
        require(schainsInternal.isSchainExist(schainId), "Schain does not exist");

        // removes Schain from Nodes
        uint[] memory nodesInGroup = schainsInternal.getNodesInGroup(schainId);
        uint8 partOfNode = schainsInternal.getSchainsPartOfNode(schainId);
        for (uint i = 0; i < nodesInGroup.length; i++) {
            uint schainIndex = schainsInternal.findSchainAtSchainsForNode(
                nodesInGroup[i],
                schainId
            );
            if (schainsInternal.checkHoleForSchain(schainId, i)) {
                continue;
            }
            require(
                schainIndex < schainsInternal.getLengthOfSchainsForNode(nodesInGroup[i]),
                "Some Node does not contain given Schain");
            schainsInternal.removeNodeFromSchain(nodesInGroup[i], schainId);
            schainsInternal.removeNodeFromExceptions(schainId, nodesInGroup[i]);
            this.addSpace(nodesInGroup[i], partOfNode);
        }
        schainsInternal.deleteGroup(schainId);
        address from = schainsInternal.getSchainOwner(schainId);
        schainsInternal.removeSchain(schainId, from);
        schainsInternal.removeHolesForSchain(schainId);
        nodeRotation.removeRotation(schainId);
        emit SchainDeleted(from, name, schainId);
    }

    function restartSchainCreation(string calldata name) external allow("SkaleManager") {
        NodeRotation nodeRotation = NodeRotation(contractManager.getContract("NodeRotation"));
        bytes32 schainId = keccak256(abi.encodePacked(name));
        ISkaleDKG skaleDKG = ISkaleDKG(contractManager.getContract("SkaleDKG"));
        require(!skaleDKG.isLastDKGSuccesful(schainId), "DKG success");
        SchainsInternal schainsInternal = SchainsInternal(
            contractManager.getContract("SchainsInternal"));
        require(schainsInternal.isAnyFreeNode(schainId), "No any free Nodes for rotation");
        uint newNodeIndex = nodeRotation.selectNodeToGroup(schainId);
        skaleDKG.openChannel(schainId);
        emit NodeAdded(schainId, newNodeIndex);
    }

    /**
     * @dev addSpace - return occupied space to Node
     * @param nodeIndex - index of Node at common array of Nodes
     * @param partOfNode - divisor of given type of Schain
     */
    function addSpace(uint nodeIndex, uint8 partOfNode) external allowTwo("Schains", "NodeRotation") {
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));
        nodes.addSpaceToNode(nodeIndex, partOfNode);
    }

    /**
     * @dev verifySignature - verify signature which create Group by Groups BLS master public key     
     * @param signatureA - first part of BLS signature
     * @param signatureB - second part of BLS signature
     * @param hash - hashed message
     * @param counter - smallest sub from square
     * @param hashA - first part of hashed message
     * @param hashB - second part of hashed message
     * @param schainName - name of the Schain
     * @return true - if correct, false - if not
     */
    function verifySchainSignature(
        uint signatureA,
        uint signatureB,
        bytes32 hash,
        uint counter,
        uint hashA,
        uint hashB,
        string calldata schainName
    )
        external
        view
        returns (bool)
    {
        SkaleVerifier skaleVerifier = SkaleVerifier(contractManager.getContract("SkaleVerifier"));

        G2Operations.G2Point memory publicKey = KeyStorage(
            contractManager.getContract("KeyStorage")
        ).getCommonPublicKey(
            keccak256(abi.encodePacked(schainName))
        );
        return skaleVerifier.verify(
            Fp2Operations.Fp2Point({
                a: signatureA,
                b: signatureB
            }),
            hash, counter,
            hashA, hashB,
            publicKey
        );
    }

    function initialize(address newContractsAddress) public override initializer {
        Permissions.initialize(newContractsAddress);
    }

    /**
     * @dev getSchainPrice - returns current price for given Schain
     * @param typeOfSchain - type of Schain
     * @param lifetime - lifetime of Schain
     * @return current price for given Schain
     */
    function getSchainPrice(uint typeOfSchain, uint lifetime) public view returns (uint) {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        uint nodeDeposit = constantsHolder.NODE_DEPOSIT();
        uint numberOfNodes;
        uint8 divisor;
        (numberOfNodes, divisor) = getNodesDataFromTypeOfSchain(typeOfSchain);
        if (divisor == 0) {
            return 1e18;
        } else {
            uint up = nodeDeposit.mul(numberOfNodes.mul(lifetime.mul(2)));
            uint down = uint(
                uint(constantsHolder.SMALL_DIVISOR())
                    .mul(uint(constantsHolder.SECONDS_TO_YEAR()))
                    .div(divisor)
            );
            return up.div(down);
        }
    }

    /**
     * @dev getNodesDataFromTypeOfSchain - returns number if Nodes
     * and part of Node which needed to this Schain
     * @param typeOfSchain - type of Schain
     * @return numberOfNodes - number of Nodes needed to this Schain
     * @return partOfNode - divisor of given type of Schain
     */
    function getNodesDataFromTypeOfSchain(uint typeOfSchain)
        public
        view
        returns (uint numberOfNodes, uint8 partOfNode)
    {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        numberOfNodes = constantsHolder.NUMBER_OF_NODES_FOR_SCHAIN();
        if (typeOfSchain == 1) {
            partOfNode = constantsHolder.SMALL_DIVISOR() / constantsHolder.SMALL_DIVISOR();
        } else if (typeOfSchain == 2) {
            partOfNode = constantsHolder.SMALL_DIVISOR() / constantsHolder.MEDIUM_DIVISOR();
        } else if (typeOfSchain == 3) {
            partOfNode = constantsHolder.SMALL_DIVISOR() / constantsHolder.LARGE_DIVISOR();
        } else if (typeOfSchain == 4) {
            partOfNode = 0;
            numberOfNodes = constantsHolder.NUMBER_OF_NODES_FOR_TEST_SCHAIN();
        } else if (typeOfSchain == 5) {
            partOfNode = constantsHolder.SMALL_DIVISOR() / constantsHolder.MEDIUM_TEST_DIVISOR();
            numberOfNodes = constantsHolder.NUMBER_OF_NODES_FOR_MEDIUM_TEST_SCHAIN();
        } else {
            revert("Bad schain type");
        }
    }

    function _initializeSchainInSchainsInternal(
        string memory name,
        address from,
        uint deposit,
        uint lifetime) private
    {
        address dataAddress = contractManager.getContract("SchainsInternal");
        require(SchainsInternal(dataAddress).isSchainNameAvailable(name), "Schain name is not available");

        // initialize Schain
        SchainsInternal(dataAddress).initializeSchain(
            name,
            from,
            lifetime,
            deposit);
        SchainsInternal(dataAddress).setSchainIndex(keccak256(abi.encodePacked(name)), from);
    }

    /**
     * @dev fallbackSchainParameterDataConverter - converts data from bytes to normal parameters
     * @param data - concatenated parameters
     * @return schainParameters Parsed lifetime, typeOfSchain, nonce and name
     */
    function _fallbackSchainParametersDataConverter(bytes memory data)
        private
        pure
        returns (SchainParameters memory schainParameters)
    {
        (schainParameters.lifetime,
        schainParameters.typeOfSchain,
        schainParameters.nonce,
        schainParameters.name) = abi.decode(data, (uint, uint8, uint16, string));
    }

    /**
     * @dev _createGroupForSchain - creates Group for Schain
     * @param schainName - name of Schain
     * @param schainId - hash by name of Schain
     * @param numberOfNodes - number of Nodes needed for this Schain
     * @param partOfNode - divisor of given type of Schain
     */
    function _createGroupForSchain(
        string memory schainName,
        bytes32 schainId,
        uint numberOfNodes,
        uint8 partOfNode
    )
        private
    {
        SchainsInternal schainsInternal = SchainsInternal(contractManager.getContract("SchainsInternal"));
        uint[] memory nodesInGroup = schainsInternal.createGroupForSchain(schainId, numberOfNodes, partOfNode);
        ISkaleDKG(contractManager.getContract("SkaleDKG")).openChannel(schainId);

        emit SchainNodes(
            schainName,
            schainId,
            nodesInGroup,
            block.timestamp,
            gasleft());
    }

    /**
     * @dev _addSchain - create Schain in the system
     * function could be run only by executor
     * @param from - owner of Schain
     * @param deposit - received amoung of SKL
     * @param schainParameters - Schain's data
     */
    function _addSchain(address from, uint deposit, SchainParameters memory schainParameters) private {
        uint numberOfNodes;
        uint8 partOfNode;

        require(schainParameters.typeOfSchain <= 5, "Invalid type of Schain");

        //initialize Schain
        _initializeSchainInSchainsInternal(
            schainParameters.name,
            from,
            deposit,
            schainParameters.lifetime);

        // create a group for Schain
        (numberOfNodes, partOfNode) = getNodesDataFromTypeOfSchain(schainParameters.typeOfSchain);

        _createGroupForSchain(
            schainParameters.name,
            keccak256(abi.encodePacked(schainParameters.name)),
            numberOfNodes,
            partOfNode
        );

        emit SchainCreated(
            schainParameters.name,
            from,
            partOfNode,
            schainParameters.lifetime,
            numberOfNodes,
            deposit,
            schainParameters.nonce,
            keccak256(abi.encodePacked(schainParameters.name)),
            block.timestamp,
            gasleft());
    }
}

File 38 of 45: SchainsInternal.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    SchainsInternal.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;
pragma experimental ABIEncoderV2;

import "./ConstantsHolder.sol";
import "./Nodes.sol";
import "./ISkaleDKG.sol";

/**
 * @title SchainsInternal - contract contains all functionality logic to manage Schains
 */
contract SchainsInternal is Permissions {

    struct Schain {
        string name;
        address owner;
        uint indexInOwnerList;
        uint8 partOfNode;
        uint lifetime;
        uint startDate;
        uint startBlock;
        uint deposit;
        uint64 index;
    }


    // mapping which contain all schains
    mapping (bytes32 => Schain) public schains;

    mapping (bytes32 => bool) public isSchainActive;

    mapping (bytes32 => uint[]) public schainsGroups;

    mapping (bytes32 => mapping (uint => bool)) private _exceptionsForGroups;
    // mapping shows schains by owner's address
    mapping (address => bytes32[]) public schainIndexes;
    // mapping shows schains which Node composed in
    mapping (uint => bytes32[]) public schainsForNodes;

    mapping (uint => uint[]) public holesForNodes;

    mapping (bytes32 => uint[]) public holesForSchains;


    // array which contain all schains
    bytes32[] public schainsAtSystem;

    uint64 public numberOfSchains;
    // total resources that schains occupied
    uint public sumOfSchainsResources;

    mapping (bytes32 => bool) public usedSchainNames;

    /**
     * @dev initializeSchain - initializes Schain
     * function could be run only by executor
     * @param name - SChain name
     * @param from - Schain owner
     * @param lifetime - initial lifetime of Schain
     * @param deposit - given amount of SKL
     */
    function initializeSchain(
        string calldata name,
        address from,
        uint lifetime,
        uint deposit) external allow("Schains")
    {
        bytes32 schainId = keccak256(abi.encodePacked(name));
        schains[schainId].name = name;
        schains[schainId].owner = from;
        schains[schainId].startDate = block.timestamp;
        schains[schainId].startBlock = block.number;
        schains[schainId].lifetime = lifetime;
        schains[schainId].deposit = deposit;
        schains[schainId].index = numberOfSchains;
        isSchainActive[schainId] = true;
        numberOfSchains++;
        schainsAtSystem.push(schainId);
        usedSchainNames[schainId] = true;
    }

    function createGroupForSchain(
        bytes32 schainId,
        uint numberOfNodes,
        uint8 partOfNode
    )
        external
        allow("Schains")
        returns (uint[] memory)
    {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        schains[schainId].partOfNode = partOfNode;
        if (partOfNode > 0) {
            sumOfSchainsResources = sumOfSchainsResources.add(
                numberOfNodes.mul(constantsHolder.TOTAL_SPACE_ON_NODE()).div(partOfNode)
            );
        }
        return _generateGroup(schainId, numberOfNodes);
    }

    /**
     * @dev setSchainIndex - adds Schain's hash to owner
     * function could be run only by executor
     * @param schainId - hash by Schain name
     * @param from - Schain owner
     */
    function setSchainIndex(bytes32 schainId, address from) external allow("Schains") {
        schains[schainId].indexInOwnerList = schainIndexes[from].length;
        schainIndexes[from].push(schainId);
    }

    /**
     * @dev changeLifetime - changes Lifetime for Schain
     * function could be run only by executor
     * @param schainId - hash by Schain name
     * @param lifetime - time which would be added to lifetime of Schain
     * @param deposit - amount of SKL which payed for this time
     */
    function changeLifetime(bytes32 schainId, uint lifetime, uint deposit) external allow("Schains") {
        schains[schainId].deposit = schains[schainId].deposit.add(deposit);
        schains[schainId].lifetime = schains[schainId].lifetime.add(lifetime);
    }

    /**
     * @dev removeSchain - removes Schain from the system
     * function could be run only by executor
     * @param schainId - hash by Schain name
     * @param from - owner of Schain
     */
    function removeSchain(bytes32 schainId, address from) external allow("Schains") {
        isSchainActive[schainId] = false;
        uint length = schainIndexes[from].length;
        uint index = schains[schainId].indexInOwnerList;
        if (index != length.sub(1)) {
            bytes32 lastSchainId = schainIndexes[from][length.sub(1)];
            schains[lastSchainId].indexInOwnerList = index;
            schainIndexes[from][index] = lastSchainId;
        }
        schainIndexes[from].pop();

        // TODO:
        // optimize
        for (uint i = 0; i + 1 < schainsAtSystem.length; i++) {
            if (schainsAtSystem[i] == schainId) {
                schainsAtSystem[i] = schainsAtSystem[schainsAtSystem.length.sub(1)];
                break;
            }
        }
        schainsAtSystem.pop();

        delete schains[schainId];
        numberOfSchains--;
    }

    function removeNodeFromSchain(
        uint nodeIndex,
        bytes32 schainHash
    )
        external
        allowThree("NodeRotation", "SkaleDKG", "Schains")
    {
        uint indexOfNode = _findNode(schainHash, nodeIndex);
        uint indexOfLastNode = schainsGroups[schainHash].length.sub(1);

        if (indexOfNode == indexOfLastNode) {
            schainsGroups[schainHash].pop();
        } else {
            delete schainsGroups[schainHash][indexOfNode];
            if (holesForSchains[schainHash].length > 0 && holesForSchains[schainHash][0] > indexOfNode) {
                uint hole = holesForSchains[schainHash][0];
                holesForSchains[schainHash][0] = indexOfNode;
                holesForSchains[schainHash].push(hole);
            } else {
                holesForSchains[schainHash].push(indexOfNode);
            }
        }

        uint schainId = findSchainAtSchainsForNode(nodeIndex, schainHash);
        removeSchainForNode(nodeIndex, schainId);
    }

    function removeNodeFromExceptions(bytes32 schainHash, uint nodeIndex) external allow("Schains") {
        _exceptionsForGroups[schainHash][nodeIndex] = false;
    }

    /**
     * @dev deleteGroup - delete Group from Data contract
     * function could be run only by executor
     * @param schainId - Groups identifier
     */
    function deleteGroup(bytes32 schainId) external allow("Schains") {
        // delete channel
        ISkaleDKG skaleDKG = ISkaleDKG(contractManager.getContract("SkaleDKG"));

        delete schainsGroups[schainId];
        if (skaleDKG.isChannelOpened(schainId)) {
            skaleDKG.deleteChannel(schainId);
        }
    }

    /**
     * @dev setException - sets a Node like exception
     * function could be run only by executor
     * @param schainId - Groups identifier
     * @param nodeIndex - index of Node which would be notes like exception
     */
    function setException(bytes32 schainId, uint nodeIndex) external allowTwo("Schains", "NodeRotation") {
        _exceptionsForGroups[schainId][nodeIndex] = true;
    }

    /**
     * @dev setNodeInGroup - adds Node to Group
     * function could be run only by executor
     * @param schainId - Groups
     * @param nodeIndex - index of Node which would be added to the Group
     */
    function setNodeInGroup(bytes32 schainId, uint nodeIndex) external allowTwo("Schains", "NodeRotation") {
        if (holesForSchains[schainId].length == 0) {
            schainsGroups[schainId].push(nodeIndex);
        } else {
            schainsGroups[schainId][holesForSchains[schainId][0]] = nodeIndex;
            uint min = uint(-1);
            uint index = 0;
            for (uint i = 1; i < holesForSchains[schainId].length; i++) {
                if (min > holesForSchains[schainId][i]) {
                    min = holesForSchains[schainId][i];
                    index = i;
                }
            }
            if (min == uint(-1)) {
                delete holesForSchains[schainId];
            } else {
                holesForSchains[schainId][0] = min;
                holesForSchains[schainId][index] =
                    holesForSchains[schainId][holesForSchains[schainId].length - 1];
                holesForSchains[schainId].pop();
            }
        }
    }

    function removeHolesForSchain(bytes32 schainHash) external allow("Schains") {
        delete holesForSchains[schainHash];
    }

    /**
     * @dev getSchains - gets all Schains at the system
     * @return array of hashes by Schain names
     */
    function getSchains() external view returns (bytes32[] memory) {
        return schainsAtSystem;
    }

    /**
     * @dev getSchainsPartOfNode - gets occupied space for given Schain
     * @param schainId - hash by Schain name
     * @return occupied space
     */
    function getSchainsPartOfNode(bytes32 schainId) external view returns (uint8) {
        return schains[schainId].partOfNode;
    }

    /**
     * @dev getSchainListSize - gets number of created Schains at the system by owner
     * @param from - owner of Schain
     * return number of Schains
     */
    function getSchainListSize(address from) external view returns (uint) {
        return schainIndexes[from].length;
    }

    /**
     * @dev getSchainIdsByAddress - gets array of hashes by Schain names which owned by `from`
     * @param from - owner of some Schains
     * @return array of hashes by Schain names
     */
    function getSchainIdsByAddress(address from) external view returns (bytes32[] memory) {
        return schainIndexes[from];
    }

    /**
     * @dev getSchainIdsForNode - returns array of hashes by Schain names,
     * which given Node composed
     * @param nodeIndex - index of Node
     * @return array of hashes by Schain names
     */
    function getSchainIdsForNode(uint nodeIndex) external view returns (bytes32[] memory) {
        return schainsForNodes[nodeIndex];
    }

    function getSchainOwner(bytes32 schainId) external view returns (address) {
        return schains[schainId].owner;
    }

    /**
     * @dev isSchainNameAvailable - checks is given name available
     * Need to delete - copy of web3.utils.soliditySha3
     * @param name - possible new name of Schain
     * @return if available - true, else - false
     */
    function isSchainNameAvailable(string calldata name) external view returns (bool) {
        bytes32 schainId = keccak256(abi.encodePacked(name));
        return schains[schainId].owner == address(0) && !usedSchainNames[schainId];
    }

    /**
     * @dev isTimeExpired - checks is Schain lifetime expired
     * @param schainId - hash by Schain name
     * @return if expired - true, else - false
     */
    function isTimeExpired(bytes32 schainId) external view returns (bool) {
        return uint(schains[schainId].startDate).add(schains[schainId].lifetime) < block.timestamp;
    }

    /**
     * @dev isOwnerAddress - checks is `from` - owner of `schainId` Schain
     * @param from - owner of Schain
     * @param schainId - hash by Schain name
     * @return if owner - true, else - false
     */
    function isOwnerAddress(address from, bytes32 schainId) external view returns (bool) {
        return schains[schainId].owner == from;
    }

    function isSchainExist(bytes32 schainId) external view returns (bool) {
        return keccak256(abi.encodePacked(schains[schainId].name)) != keccak256(abi.encodePacked(""));
    }

    function getSchainName(bytes32 schainId) external view returns (string memory) {
        return schains[schainId].name;
    }

    function getActiveSchain(uint nodeIndex) external view returns (bytes32) {
        for (uint i = schainsForNodes[nodeIndex].length; i > 0; i--) {
            if (schainsForNodes[nodeIndex][i - 1] != bytes32(0)) {
                return schainsForNodes[nodeIndex][i - 1];
            }
        }
        return bytes32(0);
    }

    function getActiveSchains(uint nodeIndex) external view returns (bytes32[] memory activeSchains) {
        uint activeAmount = 0;
        for (uint i = 0; i < schainsForNodes[nodeIndex].length; i++) {
            if (schainsForNodes[nodeIndex][i] != bytes32(0)) {
                activeAmount++;
            }
        }

        uint cursor = 0;
        activeSchains = new bytes32[](activeAmount);
        for (uint i = schainsForNodes[nodeIndex].length; i > 0; i--) {
            if (schainsForNodes[nodeIndex][i - 1] != bytes32(0)) {
                activeSchains[cursor++] = schainsForNodes[nodeIndex][i - 1];
            }
        }
    }

    /**
     * @dev getNumberOfNodesInGroup - shows number of Nodes in Group
     * @param schainId - Groups identifier
     * @return number of Nodes in Group
     */
    function getNumberOfNodesInGroup(bytes32 schainId) external view returns (uint) {
        return schainsGroups[schainId].length;
    }

    /**
     * @dev getNodesInGroup - shows Nodes in Group
     * @param schainId - Groups identifier
     * @return array of indexes of Nodes in Group
     */
    function getNodesInGroup(bytes32 schainId) external view returns (uint[] memory) {
        return schainsGroups[schainId];
    }

    /**
     * @dev getNodeIndexInGroup - looks for Node in Group
     * @param schainId - Groups identifier
     * @param nodeId - Nodes identifier
     * @return index of Node in Group
     */
    function getNodeIndexInGroup(bytes32 schainId, uint nodeId) external view returns (uint) {
        for (uint index = 0; index < schainsGroups[schainId].length; index++) {
            if (schainsGroups[schainId][index] == nodeId) {
                return index;
            }
        }
        return schainsGroups[schainId].length;
    }

    function isAnyFreeNode(bytes32 schainId) external view returns (bool) {
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));
        uint8 space = schains[schainId].partOfNode;
        uint[] memory nodesWithFreeSpace = nodes.getNodesWithFreeSpace(space);
        for (uint i = 0; i < nodesWithFreeSpace.length; i++) {
            if (_isCorrespond(schainId, nodesWithFreeSpace[i])) {
                return true;
            }
        }
        return false;
    }

    function checkException(bytes32 schainId, uint nodeIndex) external view returns (bool) {
        return _exceptionsForGroups[schainId][nodeIndex];
    }

    function checkHoleForSchain(bytes32 schainHash, uint indexOfNode) external view returns (bool) {
        for (uint i = 0; i < holesForSchains[schainHash].length; i++) {
            if (holesForSchains[schainHash][i] == indexOfNode) {
                return true;
            }
        }
        return false;
    }

    function initialize(address newContractsAddress) public override initializer {
        Permissions.initialize(newContractsAddress);

        numberOfSchains = 0;
        sumOfSchainsResources = 0;
    }

    /**
     * @dev addSchainForNode - adds Schain hash to Node
     * function could be run only by executor
     * @param nodeIndex - index of Node
     * @param schainId - hash by Schain name
     */
    function addSchainForNode(uint nodeIndex, bytes32 schainId) public allowTwo("Schains", "NodeRotation") {
        if (holesForNodes[nodeIndex].length == 0) {
            schainsForNodes[nodeIndex].push(schainId);
        } else {
            schainsForNodes[nodeIndex][holesForNodes[nodeIndex][0]] = schainId;
            uint min = uint(-1);
            uint index = 0;
            for (uint i = 1; i < holesForNodes[nodeIndex].length; i++) {
                if (min > holesForNodes[nodeIndex][i]) {
                    min = holesForNodes[nodeIndex][i];
                    index = i;
                }
            }
            if (min == uint(-1)) {
                delete holesForNodes[nodeIndex];
            } else {
                holesForNodes[nodeIndex][0] = min;
                holesForNodes[nodeIndex][index] = holesForNodes[nodeIndex][holesForNodes[nodeIndex].length - 1];
                holesForNodes[nodeIndex].pop();
            }
        }
    }

    /**
     * @dev removesSchainForNode - clean given Node of Schain
     * function could be run only by executor
     * @param nodeIndex - index of Node
     * @param schainIndex - index of Schain in schainsForNodes array by this Node
     */
    function removeSchainForNode(uint nodeIndex, uint schainIndex)
        public
        allowThree("NodeRotation", "SkaleDKG", "Schains")
    {
        uint length = schainsForNodes[nodeIndex].length;
        if (schainIndex == length.sub(1)) {
            schainsForNodes[nodeIndex].pop();
        } else {
            schainsForNodes[nodeIndex][schainIndex] = bytes32(0);
            if (holesForNodes[nodeIndex].length > 0 && holesForNodes[nodeIndex][0] > schainIndex) {
                uint hole = holesForNodes[nodeIndex][0];
                holesForNodes[nodeIndex][0] = schainIndex;
                holesForNodes[nodeIndex].push(hole);
            } else {
                holesForNodes[nodeIndex].push(schainIndex);
            }
        }
    }

    /**
     * @dev getLengthOfSchainsForNode - returns number of Schains which contain given Node
     * @param nodeIndex - index of Node
     * @return number of Schains
     */
    function getLengthOfSchainsForNode(uint nodeIndex) public view returns (uint) {
        return schainsForNodes[nodeIndex].length;
    }

    /**
     * @dev findSchainAtSchainsForNode - finds index of Schain at schainsForNode array
     * @param nodeIndex - index of Node at common array of Nodes
     * @param schainId - hash of name of Schain
     * @return index of Schain at schainsForNode array
     */
    function findSchainAtSchainsForNode(uint nodeIndex, bytes32 schainId) public view returns (uint) {
        uint length = getLengthOfSchainsForNode(nodeIndex);
        for (uint i = 0; i < length; i++) {
            if (schainsForNodes[nodeIndex][i] == schainId) {
                return i;
            }
        }
        return length;
    }

    function isEnoughNodes(bytes32 schainId) public view returns (uint[] memory result) {
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));
        uint8 space = schains[schainId].partOfNode;
        uint[] memory nodesWithFreeSpace = nodes.getNodesWithFreeSpace(space);
        uint counter = 0;
        for (uint i = 0; i < nodesWithFreeSpace.length; i++) {
            if (!_isCorrespond(schainId, nodesWithFreeSpace[i])) {
                counter++;
            }
        }
        if (counter < nodesWithFreeSpace.length) {
            result = new uint[](nodesWithFreeSpace.length.sub(counter));
            counter = 0;
            for (uint i = 0; i < nodesWithFreeSpace.length; i++) {
                if (_isCorrespond(schainId, nodesWithFreeSpace[i])) {
                    result[counter] = nodesWithFreeSpace[i];
                    counter++;
                }
            }
        }
    }

    /**
     * @dev _generateGroup - generates Group for Schain
     * @param schainId - index of Group
     */
    function _generateGroup(bytes32 schainId, uint numberOfNodes) private returns (uint[] memory nodesInGroup) {
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));
        uint8 space = schains[schainId].partOfNode;
        nodesInGroup = new uint[](numberOfNodes);

        uint[] memory possibleNodes = isEnoughNodes(schainId);
        require(possibleNodes.length >= nodesInGroup.length, "Not enough nodes to create Schain");
        uint ignoringTail = 0;
        uint random = uint(keccak256(abi.encodePacked(uint(blockhash(block.number.sub(1))), schainId)));
        for (uint i = 0; i < nodesInGroup.length; ++i) {
            uint index = random % (possibleNodes.length.sub(ignoringTail));
            uint node = possibleNodes[index];
            nodesInGroup[i] = node;
            _swap(possibleNodes, index, possibleNodes.length.sub(ignoringTail).sub(1));
            ++ignoringTail;

            _exceptionsForGroups[schainId][node] = true;
            addSchainForNode(node, schainId);
            require(nodes.removeSpaceFromNode(node, space), "Could not remove space from Node");
        }

        // set generated group
        schainsGroups[schainId] = nodesInGroup;
    }

    function _isCorrespond(bytes32 schainId, uint nodeIndex) private view returns (bool) {
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));
        return !_exceptionsForGroups[schainId][nodeIndex] && nodes.isNodeActive(nodeIndex);
    }

    function _swap(uint[] memory array, uint index1, uint index2) private pure {
        uint buffer = array[index1];
        array[index1] = array[index2];
        array[index2] = buffer;
    }

    /**
     * @dev findNode - find local index of Node in Schain
     * @param schainId - Groups identifier
     * @param nodeIndex - global index of Node
     * @return local index of Node in Schain
     */
    function _findNode(bytes32 schainId, uint nodeIndex) private view returns (uint) {
        uint[] memory nodesInGroup = schainsGroups[schainId];
        uint index;
        for (index = 0; index < nodesInGroup.length; index++) {
            if (nodesInGroup[index] == nodeIndex) {
                return index;
            }
        }
        return index;
    }

}

File 39 of 45: SkaleManager.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    SkaleManager.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;
pragma experimental ABIEncoderV2;

import "./IERC1820Registry.sol";
import "./IERC777.sol";
import "./IERC777Recipient.sol";

import "./Distributor.sol";
import "./ValidatorService.sol";
import "./IMintableToken.sol";
import "./Bounty.sol";
import "./ConstantsHolder.sol";
import "./Monitors.sol";
import "./NodeRotation.sol";
import "./Permissions.sol";
import "./Schains.sol";


contract SkaleManager is IERC777Recipient, Permissions {
    IERC1820Registry private _erc1820;

    bytes32 constant private _TOKENS_RECIPIENT_INTERFACE_HASH =
        0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;

    bytes32 constant public ADMIN_ROLE = keccak256("ADMIN_ROLE");

    event BountyGot(
        uint indexed nodeIndex,
        address owner,
        uint averageDowntime,
        uint averageLatency,
        uint bounty,
        uint previousBlockEvent,
        uint time,
        uint gasSpend
    );

    function tokensReceived(
        address, // operator
        address from,
        address to,
        uint256 value,
        bytes calldata userData,
        bytes calldata // operator data
    )
        external override
        allow("SkaleToken")
    {
        require(to == address(this), "Receiver is incorrect");
        if (userData.length > 0) {
            Schains schains = Schains(
                contractManager.getContract("Schains"));
            schains.addSchain(from, value, userData);
        }
    }

    function createNode(
        uint16 port,
        uint16 nonce,
        bytes4 ip,
        bytes4 publicIp,
        bytes32[2] calldata publicKey,
        string calldata name)
        external
    {
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));
        // validators checks inside checkPossibilityCreatingNode
        nodes.checkPossibilityCreatingNode(msg.sender);

        Nodes.NodeCreationParams memory params = Nodes.NodeCreationParams({
            name: name,
            ip: ip,
            publicIp: publicIp,
            port: port,
            publicKey: publicKey,
            nonce: nonce});
        nodes.createNode(msg.sender, params);
        // uint nodeIndex = nodes.createNode(msg.sender, params);
        // Monitors monitors = Monitors(contractManager.getContract("Monitors"));
        // monitors.addMonitor(nodeIndex);
    }

    function nodeExit(uint nodeIndex) external {
        NodeRotation nodeRotation = NodeRotation(contractManager.getContract("NodeRotation"));
        ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));
        uint validatorId = nodes.getValidatorId(nodeIndex);
        bool permitted = (_isOwner() || nodes.isNodeExist(msg.sender, nodeIndex));
        if (!permitted) {
            permitted = validatorService.getValidatorId(msg.sender) == validatorId;
        }
        require(permitted, "Sender is not permitted to call this function");
        SchainsInternal schainsInternal = SchainsInternal(contractManager.getContract("SchainsInternal"));
        ConstantsHolder constants = ConstantsHolder(contractManager.getContract("ConstantsHolder"));
        nodeRotation.freezeSchains(nodeIndex);
        if (nodes.isNodeActive(nodeIndex)) {
            require(nodes.initExit(nodeIndex), "Initialization of node exit is failed");
        }
        bool completed;
        bool isSchains = false;
        if (schainsInternal.getActiveSchain(nodeIndex) != bytes32(0)) {
            completed = nodeRotation.exitFromSchain(nodeIndex);
            isSchains = true;
        } else {
            completed = true;
        }
        if (completed) {
            require(nodes.completeExit(nodeIndex), "Finishing of node exit is failed");
            nodes.changeNodeFinishTime(nodeIndex, now.add(isSchains ? constants.rotationDelay() : 0));
            // Monitors monitors = Monitors(contractManager.getContract("Monitors"));
            // monitors.removeCheckedNodes(nodeIndex);
            // monitors.deleteMonitor(nodeIndex);
            nodes.deleteNodeForValidator(validatorId, nodeIndex);
        }
    }

    function deleteSchain(string calldata name) external {
        Schains schains = Schains(contractManager.getContract("Schains"));
        // schain owner checks inside deleteSchain
        schains.deleteSchain(msg.sender, name);
    }

    function deleteSchainByRoot(string calldata name) external onlyAdmin {
        Schains schains = Schains(contractManager.getContract("Schains"));
        schains.deleteSchainByRoot(name);
    }

    // function sendVerdict(uint fromMonitorIndex, Monitors.Verdict calldata verdict) external {
    //     Nodes nodes = Nodes(contractManager.getContract("Nodes"));
    //     require(nodes.isNodeExist(msg.sender, fromMonitorIndex), "Node does not exist for Message sender");

    //     Monitors monitors = Monitors(contractManager.getContract("Monitors"));
    //     // additional checks for monitoring inside sendVerdict
    //     monitors.sendVerdict(fromMonitorIndex, verdict);
    // }

    // function sendVerdicts(uint fromMonitorIndex, Monitors.Verdict[] calldata verdicts) external {
    //     Nodes nodes = Nodes(contractManager.getContract("Nodes"));
    //     require(nodes.isNodeExist(msg.sender, fromMonitorIndex), "Node does not exist for Message sender");

    //     Monitors monitors = Monitors(contractManager.getContract("Monitors"));
    //     for (uint i = 0; i < verdicts.length; i++) {
    //         // additional checks for monitoring inside sendVerdict
    //         monitors.sendVerdict(fromMonitorIndex, verdicts[i]);
    //     }
    // }

    function getBounty(uint nodeIndex) external {
        Nodes nodes = Nodes(contractManager.getContract("Nodes"));
        require(nodes.isNodeExist(msg.sender, nodeIndex), "Node does not exist for Message sender");
        require(nodes.isTimeForReward(nodeIndex), "Not time for bounty");
        require(
            nodes.isNodeActive(nodeIndex) || nodes.isNodeLeaving(nodeIndex), "Node is not Active and is not Leaving"
        );
        Bounty bountyContract = Bounty(contractManager.getContract("Bounty"));
        uint averageDowntime;
        uint averageLatency;
        Monitors monitors = Monitors(contractManager.getContract("Monitors"));
        (averageDowntime, averageLatency) = monitors.calculateMetrics(nodeIndex);

        uint bounty = bountyContract.getBounty(
            nodeIndex,
            averageDowntime,
            averageLatency);

        nodes.changeNodeLastRewardDate(nodeIndex);
        // monitors.deleteMonitor(nodeIndex);
        // monitors.addMonitor(nodeIndex);

        if (bounty > 0) {
            _payBounty(bounty, nodes.getValidatorId(nodeIndex));
        }

        _emitBountyEvent(nodeIndex, msg.sender, averageDowntime, averageLatency, bounty);
    }

    function initialize(address newContractsAddress) public override initializer {
        Permissions.initialize(newContractsAddress);
        _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
        _erc1820.setInterfaceImplementer(address(this), _TOKENS_RECIPIENT_INTERFACE_HASH, address(this));
    }

    function _payBounty(uint bounty, uint validatorId) private returns (bool) {        
        IERC777 skaleToken = IERC777(contractManager.getContract("SkaleToken"));
        Distributor distributor = Distributor(contractManager.getContract("Distributor"));
        
        require(
            IMintableToken(address(skaleToken)).mint(address(distributor), bounty, abi.encode(validatorId), ""),
            "Token was not minted"
        );
    }

    function _emitBountyEvent(
        uint nodeIndex,
        address from,
        uint averageDowntime,
        uint averageLatency,
        uint bounty
    )
        private
    {
        Monitors monitors = Monitors(contractManager.getContract("Monitors"));
        uint previousBlockEvent = monitors.getLastBountyBlock(nodeIndex);
        monitors.setLastBountyBlock(nodeIndex);

        emit BountyGot(
            nodeIndex,
            from,
            averageDowntime,
            averageLatency,
            bounty,
            previousBlockEvent,
            block.timestamp,
            gasleft());
    }
}

File 40 of 45: SkaleVerifier.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    SkaleVerifier.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;
pragma experimental ABIEncoderV2;

import "./Permissions.sol";
import "./SchainsInternal.sol";
import "./Precompiled.sol";
import "./FieldOperations.sol";


contract SkaleVerifier is Permissions {  
    using Fp2Operations for Fp2Operations.Fp2Point;

    function verify(
        Fp2Operations.Fp2Point calldata signature,
        bytes32 hash,
        uint counter,
        uint hashA,
        uint hashB,
        G2Operations.G2Point calldata publicKey
    )
        external
        view
        returns (bool)
    {
        if (!_checkHashToGroupWithHelper(
            hash,
            counter,
            hashA,
            hashB
            )
        )
        {
            return false;
        }

        uint newSignB;
        if (!(signature.a == 0 && signature.b == 0)) {
            newSignB = Fp2Operations.P.sub((signature.b % Fp2Operations.P));
        } else {
            newSignB = signature.b;
        }

        require(G2Operations.isG1Point(signature.a, newSignB), "Sign not in G1");
        require(G2Operations.isG1Point(hashA, hashB), "Hash not in G1");

        G2Operations.G2Point memory g2 = G2Operations.getG2();
        require(
            G2Operations.isG2(publicKey),
            "Public Key not in G2"
        );

        return Precompiled.bn256Pairing(
            signature.a, newSignB,
            g2.x.b, g2.x.a, g2.y.b, g2.y.a,
            hashA, hashB,
            publicKey.x.b, publicKey.x.a, publicKey.y.b, publicKey.y.a
        );
    }

    function initialize(address newContractsAddress) public override initializer {
        Permissions.initialize(newContractsAddress);
    }

    function _checkHashToGroupWithHelper(
        bytes32 hash,
        uint counter,
        uint hashA,
        uint hashB
    )
        private
        pure
        returns (bool)
    {
        uint xCoord = uint(hash) % Fp2Operations.P;
        xCoord = (xCoord.add(counter)) % Fp2Operations.P;

        uint ySquared = addmod(
            mulmod(mulmod(xCoord, xCoord, Fp2Operations.P), xCoord, Fp2Operations.P),
            3,
            Fp2Operations.P
        );
        if (hashB < Fp2Operations.P.div(2) || mulmod(hashB, hashB, Fp2Operations.P) != ySquared || xCoord != hashA) {
            return false;
        }

        return true;
    }
}

File 41 of 45: StringUtils.sol
// 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.6.10;

import "./SafeMath.sol";


library StringUtils {
    using SafeMath for uint;

    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.add(_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);
    }

    function uint2str(uint i) internal pure returns (string memory) {
        if (i == 0) {
            return "0";
        }
        uint j = i;
        uint _i = i;
        uint len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len.sub(1);
        while (_i != 0) {
            bstr[k--] = byte(uint8(48 + _i % 10));
            _i /= 10;
        }
        return string(bstr);
    }
}

File 42 of 45: TimeHelpers.sol
// SPDX-License-Identifier: AGPL-3.0-only

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

import "./SafeMath.sol";

import "./BokkyPooBahsDateTimeLibrary.sol";

/**
 * @title TimeHelpers
 * @dev The contract performs time operations.
 *
 * These functions are used to calculate monthly and Proof of Use epochs.
 */
contract TimeHelpers {
    using SafeMath for uint;

    uint constant private _ZERO_YEAR = 2020;

    function calculateProofOfUseLockEndTime(uint month, uint lockUpPeriodDays) external view returns (uint timestamp) {
        timestamp = BokkyPooBahsDateTimeLibrary.addDays(monthToTimestamp(month), lockUpPeriodDays);
    }

    function addDays(uint fromTimestamp, uint n) external pure returns (uint) {
        return BokkyPooBahsDateTimeLibrary.addDays(fromTimestamp, n);
    }

    function addMonths(uint fromTimestamp, uint n) external pure returns (uint) {
        return BokkyPooBahsDateTimeLibrary.addMonths(fromTimestamp, n);
    }

    function addYears(uint fromTimestamp, uint n) external pure returns (uint) {
        return BokkyPooBahsDateTimeLibrary.addYears(fromTimestamp, n);
    }

    function getCurrentMonth() external view virtual returns (uint) {
        return timestampToMonth(now);
    }

    function timestampToDay(uint timestamp) external view returns (uint) {
        uint wholeDays = timestamp / BokkyPooBahsDateTimeLibrary.SECONDS_PER_DAY;
        uint zeroDay = BokkyPooBahsDateTimeLibrary.timestampFromDate(_ZERO_YEAR, 1, 1) /
            BokkyPooBahsDateTimeLibrary.SECONDS_PER_DAY;
        require(wholeDays >= zeroDay, "Timestamp is too far in the past");
        return wholeDays - zeroDay;
    }

    function timestampToYear(uint timestamp) external view virtual returns (uint) {
        uint year;
        (year, , ) = BokkyPooBahsDateTimeLibrary.timestampToDate(timestamp);
        require(year >= _ZERO_YEAR, "Timestamp is too far in the past");
        return year - _ZERO_YEAR;
    }

    function timestampToMonth(uint timestamp) public view virtual returns (uint) {
        uint year;
        uint month;
        (year, month, ) = BokkyPooBahsDateTimeLibrary.timestampToDate(timestamp);
        require(year >= _ZERO_YEAR, "Timestamp is too far in the past");
        month = month.sub(1).add(year.sub(_ZERO_YEAR).mul(12));
        require(month > 0, "Timestamp is too far in the past");
        return month;
    }

    function monthToTimestamp(uint month) public view virtual returns (uint timestamp) {
        uint year = _ZERO_YEAR;
        uint _month = month;
        year = year.add(_month.div(12));
        _month = _month.mod(12);
        _month = _month.add(1);
        return BokkyPooBahsDateTimeLibrary.timestampFromDate(year, _month, 1);
    }
}

File 43 of 45: TokenLaunchLocker.sol
// SPDX-License-Identifier: AGPL-3.0-only

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

import "./Permissions.sol";
import "./ILocker.sol";
import "./ConstantsHolder.sol";
import "./MathUtils.sol";

import "./DelegationController.sol";
import "./TimeHelpers.sol";
import "./PartialDifferences.sol";


contract TokenLaunchLocker is Permissions, ILocker {
    using MathUtils for uint;
    using PartialDifferences for PartialDifferences.Value;

    /**
     * @dev Emitted when an `amount` is unlocked.
     */
    event Unlocked(
        address holder,
        uint amount
    );

    /**
     * @dev Emitted when an `amount` is locked.
     */
    event Locked(
        address holder,
        uint amount
    );

    struct DelegatedAmountAndMonth {
        uint delegated;
        uint month;
    }

    //        holder => tokens
    mapping (address => uint) private _locked;

    //        holder => tokens
    mapping (address => PartialDifferences.Value) private _delegatedAmount;

    mapping (address => DelegatedAmountAndMonth) private _totalDelegatedAmount;

    // delegationId => tokens
    mapping (uint => uint) private _delegationAmount;

    function lock(address holder, uint amount) external allow("TokenLaunchManager") {
        _locked[holder] = _locked[holder].add(amount);

        emit Locked(holder, amount);
    }

    function handleDelegationAdd(
        address holder, uint delegationId, uint amount, uint month)
        external allow("DelegationController")
    {
        if (_locked[holder] > 0) {
            TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract("TimeHelpers"));

            uint currentMonth = timeHelpers.getCurrentMonth();
            uint fromLocked = amount;
            uint locked = _locked[holder].boundedSub(_getAndUpdateDelegatedAmount(holder, currentMonth));
            if (fromLocked > locked) {
                fromLocked = locked;
            }
            if (fromLocked > 0) {
                require(_delegationAmount[delegationId] == 0, "Delegation was already added");
                _addToDelegatedAmount(holder, fromLocked, month);
                _addToTotalDelegatedAmount(holder, fromLocked, month);
                _delegationAmount[delegationId] = fromLocked;
            }
        }
    }

    function handleDelegationRemoving(
        address holder,
        uint delegationId,
        uint month)
        external allow("DelegationController")
    {
        if (_delegationAmount[delegationId] > 0) {
            if (_locked[holder] > 0) {
                _removeFromDelegatedAmount(holder, _delegationAmount[delegationId], month);
            }
            delete _delegationAmount[delegationId];
        }
    }

    function getAndUpdateLockedAmount(address wallet) external override returns (uint) {
        if (_locked[wallet] > 0) {
            DelegationController delegationController = DelegationController(
                contractManager.getContract("DelegationController"));
            TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract("TimeHelpers"));
            ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));

            uint currentMonth = timeHelpers.getCurrentMonth();
            if (_totalDelegatedSatisfiesProofOfUserCondition(wallet) &&
                timeHelpers.calculateProofOfUseLockEndTime(
                    _totalDelegatedAmount[wallet].month,
                    constantsHolder.proofOfUseLockUpPeriodDays()
                ) <= now) {
                _unlock(wallet);
                return 0;
            } else {
                uint lockedByDelegationController = _getAndUpdateDelegatedAmount(wallet, currentMonth)
                    .add(delegationController.getLockedInPendingDelegations(wallet));
                if (_locked[wallet] > lockedByDelegationController) {
                    return _locked[wallet].boundedSub(lockedByDelegationController);
                } else {
                    return 0;
                }
            }
        } else {
            return 0;
        }
    }

    function getAndUpdateForbiddenForDelegationAmount(address) external override returns (uint) {
        return 0;
    }

    function initialize(address contractManagerAddress) public override initializer {
        Permissions.initialize(contractManagerAddress);
    }

    // private

    function _getAndUpdateDelegatedAmount(address holder, uint currentMonth) private returns (uint) {
        return _delegatedAmount[holder].getAndUpdateValue(currentMonth);
    }

    function _addToDelegatedAmount(address holder, uint amount, uint month) private {
        _delegatedAmount[holder].addToValue(amount, month);
    }

    function _removeFromDelegatedAmount(address holder, uint amount, uint month) private {
        _delegatedAmount[holder].subtractFromValue(amount, month);
    }

    function _addToTotalDelegatedAmount(address holder, uint amount, uint month) private {
        require(
            _totalDelegatedAmount[holder].month == 0 || _totalDelegatedAmount[holder].month <= month,
            "Can't add to total delegated in the past");

        // do not update counter if it is big enough
        // because it will override month value
        if (!_totalDelegatedSatisfiesProofOfUserCondition(holder)) {
            _totalDelegatedAmount[holder].delegated = _totalDelegatedAmount[holder].delegated.add(amount);
            _totalDelegatedAmount[holder].month = month;
        }
    }

    function _unlock(address holder) private {
        emit Unlocked(holder, _locked[holder]);
        delete _locked[holder];
        _deleteDelegatedAmount(holder);
        _deleteTotalDelegatedAmount(holder);
    }

    function _deleteDelegatedAmount(address holder) private {
        _delegatedAmount[holder].clear();
    }

    function _deleteTotalDelegatedAmount(address holder) private {
        delete _totalDelegatedAmount[holder].delegated;
        delete _totalDelegatedAmount[holder].month;
    }

    function _totalDelegatedSatisfiesProofOfUserCondition(address holder) private view returns (bool) {
        ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder"));

        return _totalDelegatedAmount[holder].delegated.mul(100) >=
            _locked[holder].mul(constantsHolder.proofOfUseDelegationPercentage());
    }
}

File 44 of 45: TokenState.sol
// 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.6.10;
pragma experimental ABIEncoderV2;

import "./Permissions.sol";
import "./DelegationController.sol";
import "./TimeHelpers.sol";
import "./ILocker.sol";


/**
 * @title Token State
 * @dev This contract manages lockers to control token transferability.
 *
 * See ILocker.
 */
contract TokenState is Permissions, ILocker {

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

    string[] private _lockers;

    /**
     *  @dev Return and update the total locked amount of a given `holder`.
     *
     *  @param holder address of the token holder
     *  @return total locked amount
    */
    function getAndUpdateLockedAmount(address holder) external override returns (uint) {
        uint locked = 0;
        for (uint i = 0; i < _lockers.length; ++i) {
            ILocker locker = ILocker(contractManager.getContract(_lockers[i]));
            locked = locked.add(locker.getAndUpdateLockedAmount(holder));
        }
        return locked;
    }

    /**
     * @dev Return and update the total locked and un-delegatable amount of a given `holder`.
     *
     * @param holder address of the token holder
     * @return amount total slashed amount (non-transferable and non-delegatable)
    */
    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.add(locker.getAndUpdateForbiddenForDelegationAmount(holder));
        }
        return forbidden;
    }

    /**
     * @dev Allows the Owner to remove a contract from the locker.
     *
     * Emits a LockerWasRemoved event.
     *
     * @param locker string name of contract to remove from locker
     */
    function removeLocker(string calldata locker) external onlyOwner {
        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.sub(1)) {
                _lockers[index] = _lockers[_lockers.length.sub(1)];
            }
            delete _lockers[_lockers.length.sub(1)];
            _lockers.pop();
            emit LockerWasRemoved(locker);
        }
    }

    function initialize(address contractManagerAddress) public override initializer {
        Permissions.initialize(contractManagerAddress);
        addLocker("DelegationController");
        addLocker("Punisher");
        addLocker("TokenLaunchLocker");
    }

    /**
     * @dev Allows the Owner to add a contract to the Locker.
     *
     * Emits a LockerWasAdded event.
     *
     * @param locker string name of contract to add to locker
     */
    function addLocker(string memory locker) public onlyOwner {
        _lockers.push(locker);
        emit LockerWasAdded(locker);
    }
}

File 45 of 45: ValidatorService.sol
// SPDX-License-Identifier: AGPL-3.0-only

/*
    ValidatorService.sol - SKALE Manager
    Copyright (C) 2019-Present SKALE Labs
    @author Dmytro Stebaiev
    @author Artem Payvin
    @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.6.10;
pragma experimental ABIEncoderV2;

import "./ECDSA.sol";

import "./Permissions.sol";
import "./ConstantsHolder.sol";

import "./DelegationController.sol";

/**
 * @title ValidatorService
 * @dev This contract handles all validator operations including registration,
 * node management, validator-specific delegation parameters, and more.
 *
 * Validators register an address, and use this address to accept delegations and
 * register nodes.
 *
 */
contract ValidatorService is Permissions {

    using ECDSA for bytes32;

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

    event ValidatorWasEnabled(
        uint validatorId
    );

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

    mapping (uint => Validator) public validators;
    mapping (uint => bool) private _trustedValidators;
    uint[] public trustedValidatorsList;
    //       address => validatorId
    mapping (address => uint) private _validatorAddressToId;
    //       address => validatorId
    mapping (address => uint) private _nodeAddressToValidatorId;
    // validatorId => nodeAddress[]
    mapping (uint => address[]) private _nodeAddresses;
    uint public numberOfValidators;
    bool public useWhitelist;

    modifier checkValidatorExists(uint validatorId) {
        require(validatorExists(validatorId), "Validator with such ID does not exist");
        _;
    }

    /**
     * @dev Creates a new validator Id.
     *
     * Requirements:
     *
     * - sender must not already have registered a validator Id.
     * - fee rate must be between 0 - 1000‰. Note: per mille!
     *
     * Emits ValidatorRegistered event.
     *
     * @param name string
     * @param description string
     * @param feeRate uint Fee charged on delegations by the validator per mille
     * @param minimumDelegationAmount uint Minimum delegation amount accepted by the validator
     */
    function registerValidator(
        string calldata name,
        string calldata description,
        uint feeRate,
        uint minimumDelegationAmount
    )
        external
        returns (uint validatorId)
    {
        require(!validatorAddressExists(msg.sender), "Validator with such address already exists");
        require(feeRate < 1000, "Fee rate of validator should be lower than 100%");
        validatorId = ++numberOfValidators;
        validators[validatorId] = Validator(
            name,
            msg.sender,
            address(0),
            description,
            feeRate,
            now,
            minimumDelegationAmount,
            true
        );
        _setValidatorAddress(validatorId, msg.sender);

        emit ValidatorRegistered(validatorId);
    }

    function enableValidator(uint validatorId) external checkValidatorExists(validatorId) onlyAdmin {
        require(!_trustedValidators[validatorId], "Validator is already enabled");
        _trustedValidators[validatorId] = true;
        trustedValidatorsList.push(validatorId);
        emit ValidatorWasEnabled(validatorId);
    }

    function disableValidator(uint validatorId) external checkValidatorExists(validatorId) onlyAdmin {
        require(_trustedValidators[validatorId], "Validator is already disabled");
        _trustedValidators[validatorId] = false;
        uint position = _find(trustedValidatorsList, validatorId);
        if (position < trustedValidatorsList.length) {
            trustedValidatorsList[position] =
                trustedValidatorsList[trustedValidatorsList.length.sub(1)];
        }
        trustedValidatorsList.pop();
        emit ValidatorWasDisabled(validatorId);
    }

    /**
     * @dev Owner can disable the validator whitelist. Once turned off the
     * whitelist cannot be re-enabled.
     */
    function disableWhitelist() external onlyOwner {
        useWhitelist = false;
    }

    /**
     * @dev Allows a validator to request a new address.
     *
     * Requirements:
     *
     * - new address must not be null
     * - new address must not be already registered as a validator
     *
     * @param newValidatorAddress address
     */
    function requestForNewAddress(address newValidatorAddress) external {
        require(newValidatorAddress != address(0), "New address cannot be null");
        require(_validatorAddressToId[newValidatorAddress] == 0, "Address already registered");
        // check Validator Exist inside getValidatorId
        uint validatorId = getValidatorId(msg.sender);

        validators[validatorId].requestedAddress = newValidatorAddress;
    }

    function confirmNewAddress(uint validatorId)
        external
        checkValidatorExists(validatorId)
    {
        require(
            getValidator(validatorId).requestedAddress == msg.sender,
            "The validator address cannot be changed because it is not the actual owner"
        );
        delete validators[validatorId].requestedAddress;
        _setValidatorAddress(validatorId, msg.sender);

        emit ValidatorAddressChanged(validatorId, validators[validatorId].validatorAddress);
    }

    /**
     * @dev Links a given node address.
     *
     * Requirements:
     *
     * - the given signature must be valid.
     * - the address must not be assigned to a validator.
     *
     * Emits NodeAddressWasAdded event.
     *
     * @param nodeAddress address
     * @param sig bytes signature of validator Id by node operator.
     */
    function linkNodeAddress(address nodeAddress, bytes calldata sig) external {
        // check Validator Exist inside getValidatorId
        uint validatorId = getValidatorId(msg.sender);
        require(
            keccak256(abi.encodePacked(validatorId)).toEthSignedMessageHash().recover(sig) == nodeAddress,
            "Signature is not pass"
        );
        require(_validatorAddressToId[nodeAddress] == 0, "Node address is a validator");

        _addNodeAddress(validatorId, nodeAddress);
        emit NodeAddressWasAdded(validatorId, nodeAddress);
    }

    /**
     * @dev Unlinks a given node address from a validator.
     *
     * Emits NodeAddressWasRemoved event.
     *
     * @param nodeAddress address
     */
    function unlinkNodeAddress(address nodeAddress) external {
        // check Validator Exist inside getValidatorId
        uint validatorId = getValidatorId(msg.sender);

        _removeNodeAddress(validatorId, nodeAddress);
        emit NodeAddressWasRemoved(validatorId, nodeAddress);
    }

    function setValidatorMDA(uint minimumDelegationAmount) external {
        // check Validator Exist inside getValidatorId
        uint validatorId = getValidatorId(msg.sender);

        validators[validatorId].minimumDelegationAmount = minimumDelegationAmount;
    }

    /**
     * @dev Allows a validator to set a new validator name.
     *
     * @param newName string
     */
    function setValidatorName(string calldata newName) external {
        // check Validator Exist inside getValidatorId
        uint validatorId = getValidatorId(msg.sender);

        validators[validatorId].name = newName;
    }

    /**
     * @dev Allows a validator to set a new validator description.
     *
     * @param newDescription string
     */
    function setValidatorDescription(string calldata newDescription) external {
        // check Validator Exist inside getValidatorId
        uint validatorId = getValidatorId(msg.sender);

        validators[validatorId].description = newDescription;
    }

    /**
     * @dev Allows a validator to start accepting new delegation requests.
     *
     * Requirements:
     *
     * - validator must not have already enabled accepting new requests
     */
    function startAcceptingNewRequests() external {
        // check Validator Exist inside getValidatorId
        uint validatorId = getValidatorId(msg.sender);
        require(!isAcceptingNewRequests(validatorId), "Accepting request is already enabled");

        validators[validatorId].acceptNewRequests = true;
    }

    /**
     * @dev Allows a validator to stop accepting new delegation requests.
     *
     * Requirements:
     *
     * - validator must not have already stopped accepting new requests
     */
    function stopAcceptingNewRequests() external {
        // check Validator Exist inside getValidatorId
        uint validatorId = getValidatorId(msg.sender);
        require(isAcceptingNewRequests(validatorId), "Accepting request is already disabled");

        validators[validatorId].acceptNewRequests = false;
    }

    /**
     * @dev Returns the amount of validator bond.
     *
     * @param validatorId uint ID of validator to return the amount of locked funds
     * @return bondAmount uint the amount of self-delegated funds by the validator
    */
    function getAndUpdateBondAmount(uint validatorId)
        external
        returns (uint)
    {
        DelegationController delegationController = DelegationController(
            contractManager.getContract("DelegationController")
        );
        return delegationController.getAndUpdateDelegatedByHolderToValidatorNow(
            getValidator(validatorId).validatorAddress,
            validatorId
        );
    }

    function getMyNodesAddresses() external view returns (address[] memory) {
        return getNodeAddresses(getValidatorId(msg.sender));
    }

    /**
     * @dev Returns a list of trusted validators.
     *
     * @return uint[] trusted validators
     */
    function getTrustedValidators() external view returns (uint[] memory) {
        return trustedValidatorsList;
    }

    function checkMinimumDelegation(uint validatorId, uint amount)
        external
        view
        checkValidatorExists(validatorId)
        allow("DelegationController")
        returns (bool)
    {
        return validators[validatorId].minimumDelegationAmount <= amount ? true : false;
    }

    function checkValidatorAddressToId(address validatorAddress, uint validatorId)
        external
        view
        returns (bool)
    {
        return getValidatorId(validatorAddress) == validatorId ? true : false;
    }

    function getValidatorIdByNodeAddress(address nodeAddress) external view returns (uint validatorId) {
        validatorId = _nodeAddressToValidatorId[nodeAddress];
        require(validatorId != 0, "Node address is not assigned to a validator");
    }


    function isAuthorizedValidator(uint validatorId) external view checkValidatorExists(validatorId) returns (bool) {
        return _trustedValidators[validatorId] || !useWhitelist;
    }

    function initialize(address contractManagerAddress) public override initializer {
        Permissions.initialize(contractManagerAddress);
        useWhitelist = true;
    }

    function getNodeAddresses(uint validatorId) public view returns (address[] memory) {
        return _nodeAddresses[validatorId];
    }

    function validatorExists(uint validatorId) public view returns (bool) {
        return validatorId <= numberOfValidators && validatorId != 0;
    }

    function validatorAddressExists(address validatorAddress) public view returns (bool) {
        return _validatorAddressToId[validatorAddress] != 0;
    }

    function checkIfValidatorAddressExists(address validatorAddress) public view {
        require(validatorAddressExists(validatorAddress), "Validator with given address does not exist");
    }

    function getValidator(uint validatorId) public view checkValidatorExists(validatorId) returns (Validator memory) {
        return validators[validatorId];
    }

    function getValidatorId(address validatorAddress) public view returns (uint) {
        checkIfValidatorAddressExists(validatorAddress);
        return _validatorAddressToId[validatorAddress];
    }

    function isAcceptingNewRequests(uint validatorId) public view checkValidatorExists(validatorId) returns (bool) {
        return validators[validatorId].acceptNewRequests;
    }

    // private
    function _setValidatorAddress(uint validatorId, address validatorAddress) private {
        if (_validatorAddressToId[validatorAddress] == validatorId) {
            return;
        }
        require(_validatorAddressToId[validatorAddress] == 0, "Address is in use by another validator");
        address oldAddress = validators[validatorId].validatorAddress;
        delete _validatorAddressToId[oldAddress];
        _nodeAddressToValidatorId[validatorAddress] = validatorId;
        validators[validatorId].validatorAddress = validatorAddress;
        _validatorAddressToId[validatorAddress] = validatorId;
    }

    function _addNodeAddress(uint validatorId, address nodeAddress) private {
        if (_nodeAddressToValidatorId[nodeAddress] == validatorId) {
            return;
        }
        require(_nodeAddressToValidatorId[nodeAddress] == 0, "Validator cannot override node address");
        _nodeAddressToValidatorId[nodeAddress] = validatorId;
        _nodeAddresses[validatorId].push(nodeAddress);
    }

    function _removeNodeAddress(uint validatorId, address nodeAddress) private {
        require(_nodeAddressToValidatorId[nodeAddress] == validatorId,
            "Validator does not have permissions to unlink node");
        delete _nodeAddressToValidatorId[nodeAddress];
        for (uint i = 0; i < _nodeAddresses[validatorId].length; ++i) {
            if (_nodeAddresses[validatorId][i] == nodeAddress) {
                if (i + 1 < _nodeAddresses[validatorId].length) {
                    _nodeAddresses[validatorId][i] =
                        _nodeAddresses[validatorId][_nodeAddresses[validatorId].length.sub(1)];
                }
                delete _nodeAddresses[validatorId][_nodeAddresses[validatorId].length.sub(1)];
                _nodeAddresses[validatorId].pop();
                break;
            }
        }
    }

    function _find(uint[] memory array, uint index) private pure returns (uint) {
        uint i;
        for (i = 0; i < array.length; i++) {
            if (array[i] == index) {
                return i;
            }
        }
        return array.length;
    }
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"delegationId","type":"uint256"}],"name":"DelegationAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"delegationId","type":"uint256"}],"name":"DelegationProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"delegationId","type":"uint256"}],"name":"DelegationRequestCanceledByUser","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"delegationId","type":"uint256"}],"name":"UndelegationRequested","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"delegationId","type":"uint256"}],"name":"acceptPendingDelegation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"delegationId","type":"uint256"}],"name":"cancelPendingDelegation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"validatorId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"confiscate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractManager","outputs":[{"internalType":"contract ContractManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"validatorId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"delegationPeriod","type":"uint256"},{"internalType":"string","name":"info","type":"string"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"delegations","outputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"validatorId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"delegationPeriod","type":"uint256"},{"internalType":"uint256","name":"created","type":"uint256"},{"internalType":"uint256","name":"started","type":"uint256"},{"internalType":"uint256","name":"finished","type":"uint256"},{"internalType":"string","name":"info","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"delegationsByHolder","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"delegationsByValidator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"getAndUpdateDelegatedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"validatorId","type":"uint256"}],"name":"getAndUpdateDelegatedByHolderToValidatorNow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"validatorId","type":"uint256"},{"internalType":"uint256","name":"month","type":"uint256"}],"name":"getAndUpdateDelegatedToValidator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"validatorId","type":"uint256"}],"name":"getAndUpdateDelegatedToValidatorNow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"validatorId","type":"uint256"},{"internalType":"uint256","name":"month","type":"uint256"}],"name":"getAndUpdateEffectiveDelegatedByHolderToValidator","outputs":[{"internalType":"uint256","name":"effectiveDelegated","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"validatorId","type":"uint256"},{"internalType":"uint256","name":"month","type":"uint256"}],"name":"getAndUpdateEffectiveDelegatedToValidator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"getAndUpdateForbiddenForDelegationAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"getAndUpdateLockedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"delegationId","type":"uint256"}],"name":"getDelegation","outputs":[{"components":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"validatorId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"delegationPeriod","type":"uint256"},{"internalType":"uint256","name":"created","type":"uint256"},{"internalType":"uint256","name":"started","type":"uint256"},{"internalType":"uint256","name":"finished","type":"uint256"},{"internalType":"string","name":"info","type":"string"}],"internalType":"struct DelegationController.Delegation","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"getDelegationsByHolderLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"validatorId","type":"uint256"}],"name":"getDelegationsByValidatorLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"validatorId","type":"uint256"}],"name":"getFirstDelegationMonth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"getLockedInPendingDelegations","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"delegationId","type":"uint256"}],"name":"getState","outputs":[{"internalType":"enum DelegationController.State","name":"state","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"hasUnprocessedSlashes","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contractsAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"processAllSlashes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"processSlashes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"delegationId","type":"uint256"}],"name":"requestUndelegation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50615f1980620000216000396000f3fe608060405234801561001057600080fd5b50600436106102115760003560e01c806356574b8c11610125578063b39e12cf116100ad578063ca15c8731161007c578063ca15c8731461049a578063d547741f146104ad578063dda641ae146104c0578063fa8dacba14610216578063ff1f7799146104d357610211565b8063b39e12cf14610445578063b86315851461044d578063c4336c1c14610460578063c4d66de81461048757610211565b806391d14854116100f457806391d14854146103f15780639654ff16146104045780639ac1c4ad146104175780639c3e452f1461042a578063a217fddf1461043d57610211565b806356574b8c146103985780635fd55293146103ab5780637ce845d0146103be5780639010d07c146103d157610211565b8063248a9ca3116101a85780632f7263cd116101775780632f7263cd1461031f57806336568abe1461033f5780633d42b1ce14610352578063416880b01461036557806344c9af281461037857610211565b8063248a9ca3146102d357806327040f68146102e657806327e5455a146102f95780632f2ff15d1461030c57610211565b80631d703812116101e45780631d703812146102875780631d9c7f0a1461029a5780631da42e5e146102ad57806321eb5859146102c057610211565b80630b975991146102165780630dd357011461023f5780630e01bff81461025f5780631c8a253e14610272575b600080fd5b610229610224366004615046565b6104e6565b6040516102369190615324565b60405180910390f35b61025261024d3660046150fd565b6104f9565b6040516102369190615e27565b61022961026d3660046150a9565b61063c565b61028561028036600461507e565b610794565b005b6102296102953660046150fd565b6107aa565b6102296102a8366004615144565b6107b8565b6102856102bb366004615144565b6107e6565b6102856102ce36600461517d565b610a25565b6102296102e13660046150fd565b611065565b6102296102f4366004615046565b61107a565b6102856103073660046150fd565b611085565b61028561031a366004615115565b6117d2565b61033261032d366004615046565b611816565b6040516102369190615319565b61028561034d366004615115565b61184b565b6102296103603660046150fd565b61188d565b610229610373366004615144565b61189f565b61038b6103863660046150fd565b6119ca565b604051610236919061532d565b6102296103a636600461507e565b611c29565b6102856103b9366004615046565b611c42565b6102296103cc366004615046565b611c50565b6103e46103df366004615144565b611cab565b6040516102369190615250565b6103326103ff366004615115565b611cd2565b61022961041236600461507e565b611cf0565b6102856104253660046150fd565b611d1c565b610229610438366004615144565b611e6f565b610229611f8a565b6103e4611f8f565b61028561045b3660046150fd565b611f9e565b61047361046e3660046150fd565b612434565b6040516102369897969594939291906152c4565b610285610495366004615046565b61251d565b6102296104a83660046150fd565b6125a9565b6102856104bb366004615115565b6125c0565b6102296104ce366004615046565b6125fa565b6102296104e136600461507e565b612615565b60006104f182612629565b90505b919050565b610501614f2f565b6098548290811061052d5760405162461bcd60e51b815260040161052490615cc0565b60405180910390fd5b6098838154811061053a57fe5b600091825260209182902060408051610100808201835260089490940290920180546001600160a01b0316835260018082015484870152600280830154858501526003830154606086015260048301546080860152600583015460a0860152600683015460c08601526007830180548551938116159097026000190190961604601f81018790048702820187019093528281529294909360e086019392909183018282801561062a5780601f106105ff5761010080835404028352916020019161062a565b820191906000526020600020905b81548152906001019060200180831161060d57829003601f168201915b50505050508152505091505b50919050565b604080518082018252600b81526a2234b9ba3934b13aba37b960a91b602080830191909152609754925160009333926001600160a01b039091169163ec56a3739161068991869101615234565b604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004016106bb9190615324565b60206040518083038186803b1580156106d357600080fd5b505afa1580156106e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070b9190615062565b6001600160a01b03161480610723575061072361264c565b61073f5760405162461bcd60e51b815260040161052490615d40565b606061074a8661265e565b6001600160a01b038716600090815260a1602090815260408083208984529091529020909150610780908563ffffffff61266b16565b925061078b816127b2565b50509392505050565b6107a66107a183836129b4565b6127b2565b5050565b60006104f182610438612c74565b609960205281600052604060002081815481106107d157fe5b90600052602060002001600091509150505481565b6040805180820182526008815267283ab734b9b432b960c11b6020808301919091526097549251919233926001600160a01b039091169163ec56a3739161082f91869101615234565b604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004016108619190615324565b60206040518083038186803b15801561087957600080fd5b505afa15801561088d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b19190615062565b6001600160a01b031614806108c957506108c961264c565b6108e55760405162461bcd60e51b815260040161052490615d40565b60006108ef612c74565b90506108f9614f7d565b6000858152609c6020526040902061091890858463ffffffff612d7016565b6000868152609d6020526040902090915061093a90828463ffffffff612e4216565b6000858152609e60205260409020610953908284612f92565b60408051606081018252918252602080830196875290820192835260a28054600181018255600091909152915180517faaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf42d60049094029384015501517faaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf42e82015593517faaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf42f850155517faaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf430909301929092555050565b609754604051633581777360e01b81526000916001600160a01b031690633581777390610a5490600401615dae565b60206040518083038186803b158015610a6c57600080fd5b505afa158015610a80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa49190615062565b609754604051633581777360e01b81529192506000916001600160a01b0390911690633581777390610ad8906004016159f9565b60206040518083038186803b158015610af057600080fd5b505afa158015610b04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b289190615062565b609754604051633581777360e01b81529192506000916001600160a01b0390911690633581777390610b5c90600401615871565b60206040518083038186803b158015610b7457600080fd5b505afa158015610b88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bac9190615062565b609754604051633581777360e01b81529192506000916001600160a01b0390911690633581777390610be090600401615ac3565b60206040518083038186803b158015610bf857600080fd5b505afa158015610c0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c309190615062565b6040516370cb318960e01b81529091506001600160a01b038516906370cb318990610c61908c908c90600401615e94565b60206040518083038186803b158015610c7957600080fd5b505afa158015610c8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb191906150dd565b610ccd5760405162461bcd60e51b815260040161052490615c12565b60405163f93c86f160e01b81526001600160a01b0385169063f93c86f190610cf9908c90600401615324565b60206040518083038186803b158015610d1157600080fd5b505afa158015610d25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4991906150dd565b610d655760405162461bcd60e51b81526004016105249061551d565b60405163a795d29360e01b81526001600160a01b0384169063a795d29390610d91908a90600401615324565b60206040518083038186803b158015610da957600080fd5b505afa158015610dbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de191906150dd565b610dfd5760405162461bcd60e51b815260040161052490615bcd565b60405163461a6f3f60e11b81526001600160a01b03851690638c34de7e90610e29908c90600401615324565b60206040518083038186803b158015610e4157600080fd5b505afa158015610e55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7991906150dd565b610e955760405162461bcd60e51b8152600401610524906155a3565b610e9f338a61308e565b506060610eab3361265e565b90506000610ef2338c8c8c8c8c8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061321a92505050565b90506000846001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401610f229190615250565b60206040518083038186803b158015610f3a57600080fd5b505afa158015610f4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f729190615165565b90506000846001600160a01b0316630b975991336040518263ffffffff1660e01b8152600401610fa29190615250565b602060405180830381600087803b158015610fbc57600080fd5b505af1158015610fd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff49190615165565b9050808210156110165760405162461bcd60e51b8152600401610524906158cc565b7f839237f8da6208af7e49773f22501b3082aaae94d5b6ce8ee96f117835fe2f67836040516110459190615324565b60405180910390a1611056846127b2565b50505050505050505050505050565b60009081526065602052604090206002015490565b60006104f182613479565b609854819081106110a85760405162461bcd60e51b815260040161052490615cc0565b60046110b3836119ca565b60068111156110be57fe5b146110db5760405162461bcd60e51b8152600401610524906157ba565b609754604051633581777360e01b81526000916001600160a01b03169063358177739061110a90600401615dae565b60206040518083038186803b15801561112257600080fd5b505afa158015611136573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115a9190615062565b9050336001600160a01b03166098848154811061117357fe5b60009182526020909120600890910201546001600160a01b031614806112b357506040516224441f60e71b81526001600160a01b038216906312220f80906111bf903390600401615250565b60206040518083038186803b1580156111d757600080fd5b505afa1580156111eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061120f91906150dd565b80156112b35750604051630ba7341960e11b81526001600160a01b0382169063174e683290611242903390600401615250565b60206040518083038186803b15801561125a57600080fd5b505afa15801561126e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112929190615165565b6098848154811061129f57fe5b906000526020600020906008020160010154145b6112cf5760405162461bcd60e51b815260040161052490615cf7565b609754604051633581777360e01b81526000916001600160a01b0316906335817773906112fe90600401615684565b60206040518083038186803b15801561131657600080fd5b505afa15801561132a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134e9190615062565b609754604051633581777360e01b81529192506000916001600160a01b0390911690633581777390611382906004016159f9565b60206040518083038186803b15801561139a57600080fd5b505afa1580156113ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113d29190615062565b9050611426609886815481106113e457fe5b6000918252602090912060089091020154609880546001600160a01b03909216918890811061140f57fe5b9060005260206000209060080201600101546134b7565b61142f33611c42565b6114388561358e565b6098868154811061144557fe5b906000526020600020906008020160060181905550600061146586613695565b90506114ae6098878154811061147757fe5b906000526020600020906008020160010154826098898154811061149757fe5b9060005260206000209060080201600601546137ce565b611508609887815481106114be57fe5b906000526020600020906008020160000160009054906101000a90046001600160a01b031682609889815481106114f157fe5b9060005260206000209060080201600601546137ed565b61157a6098878154811061151857fe5b6000918252602090912060089091020154609880546001600160a01b03909216918990811061154357fe5b9060005260206000209060080201600101548360988a8154811061156357fe5b906000526020600020906008020160060154613816565b6000611626836001600160a01b031663f5b98f4160988a8154811061159b57fe5b9060005260206000209060080201600301546040518263ffffffff1660e01b81526004016115c99190615324565b60206040518083038186803b1580156115e157600080fd5b505afa1580156115f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116199190615165565b839063ffffffff61384a16565b905061166f6098888154811061163857fe5b9060005260206000209060080201600101548260988a8154811061165857fe5b906000526020600020906008020160060154613884565b6116e16098888154811061167f57fe5b6000918252602090912060089091020154609880546001600160a01b03909216918a9081106116aa57fe5b9060005260206000209060080201600101548360988b815481106116ca57fe5b9060005260206000209060080201600601546138a3565b836001600160a01b0316630d4e8fd1609889815481106116fd57fe5b906000526020600020906008020160000160009054906101000a90046001600160a01b03168960988b8154811061173057fe5b9060005260206000209060080201600601546040518463ffffffff1660e01b81526004016117609392919061527d565b600060405180830381600087803b15801561177a57600080fd5b505af115801561178e573d6000803e3d6000fd5b505050507fb0142de902382ce87e0ae1e5ec0699b26d25bec2eeb06bca82e1253099b3119c876040516117c19190615324565b60405180910390a150505050505050565b6000828152606560205260409020600201546117f0906103ff6138d7565b61180c5760405162461bcd60e51b8152600401610524906153e6565b6107a682826138db565b60006118218261394a565b80156104f157505060a2546001600160a01b0391909116600090815260a360205260409020541090565b6118536138d7565b6001600160a01b0316816001600160a01b0316146118835760405162461bcd60e51b815260040161052490615dd8565b6107a68282613967565b60009081526099602052604090205490565b604080518082018252600b81526a2234b9ba3934b13aba37b960a91b602080830191909152609754925160009333926001600160a01b039091169163ec56a373916118ec91869101615234565b604051602081830303815290604052805190602001206040518263ffffffff1660e01b815260040161191e9190615324565b60206040518083038186803b15801561193657600080fd5b505afa15801561194a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061196e9190615062565b6001600160a01b03161480611986575061198661264c565b6119a25760405162461bcd60e51b815260040161052490615d40565b6000848152609d602052604090206119c0908463ffffffff61266b16565b91505b5092915050565b609854600090829081106119f05760405162461bcd60e51b815260040161052490615cc0565b609883815481106119fd57fe5b90600052602060002090600802016005015460001415611b845760988381548110611a2457fe5b90600052602060002090600802016006015460001415611b7b57609754604051633581777360e01b81526000916001600160a01b031690633581777390611a6d9060040161565f565b60206040518083038186803b158015611a8557600080fd5b505afa158015611a99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611abd9190615062565b9050806001600160a01b031663bf64d84960988681548110611adb57fe5b9060005260206000209060080201600401546040518263ffffffff1660e01b8152600401611b099190615324565b60206040518083038186803b158015611b2157600080fd5b505afa158015611b35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b599190615165565b611b61612c74565b1415611b71576000925050610636565b6003925050610636565b60029150610636565b60988381548110611b9157fe5b906000526020600020906008020160050154611bab612c74565b1015611bba5760019150610636565b60988381548110611bc757fe5b90600052602060002090600802016006015460001415611bea5760049150610636565b60988381548110611bf757fe5b906000526020600020906008020160060154611c11612c74565b1015611c205760059150610636565b60069150610636565b609a60205281600052604060002081815481106107d157fe5b611c4d816000610794565b50565b600080611c5b612c74565b6001600160a01b038416600090815260a56020526040902060010154909150811115611c8b5760009150506104f4565b50506001600160a01b038116600090815260a560205260409020546104f4565b6000828152606560205260408120611cc9908363ffffffff6139d616565b90505b92915050565b6000828152606560205260408120611cc9908363ffffffff6139e216565b6001600160a01b0391909116600090815260a46020908152604080832093835260019093019052205490565b60985481908110611d3f5760405162461bcd60e51b815260040161052490615cc0565b60988281548110611d4c57fe5b60009182526020909120600890910201546001600160a01b03163314611d845760405162461bcd60e51b815260040161052490615354565b6000611d8f836119ca565b6006811115611d9a57fe5b14611db75760405162461bcd60e51b815260040161052490615b29565b611dbf612c74565b60988381548110611dcc57fe5b906000526020600020906008020160060181905550611e3360988381548110611df157fe5b6000918252602090912060089091020154609880546001600160a01b039092169185908110611e1c57fe5b9060005260206000209060080201600201546139f7565b507fc42cff898171c085fa87ecad4869a5fb22753dddf61048199b8c740c2109fb1182604051611e639190615324565b60405180910390a15050565b60408051808201825260058152644e6f64657360d81b602080830191909152609754925160009333926001600160a01b039091169163ec56a37391611eb691869101615234565b604051602081830303815290604052805190602001206040518263ffffffff1660e01b8152600401611ee89190615324565b60206040518083038186803b158015611f0057600080fd5b505afa158015611f14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f389190615062565b6001600160a01b03161480611f505750611f5061264c565b611f6c5760405162461bcd60e51b815260040161052490615d40565b6000848152609c602052604090206119c0908463ffffffff613ac316565b600081565b6097546001600160a01b031681565b60985481908110611fc15760405162461bcd60e51b815260040161052490615cc0565b609754604051633581777360e01b81526000916001600160a01b031690633581777390611ff090600401615dae565b60206040518083038186803b15801561200857600080fd5b505afa15801561201c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120409190615062565b9050806001600160a01b031663bed5012e336098868154811061205f57fe5b9060005260206000209060080201600101546040518363ffffffff1660e01b815260040161208e929190615264565b60206040518083038186803b1580156120a657600080fd5b505afa1580156120ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120de91906150dd565b6120fa5760405162461bcd60e51b81526004016105249061583c565b61214c6098848154811061210a57fe5b6000918252602090912060089091020154609880546001600160a01b03909216918690811061213557fe5b90600052602060002090600802016001015461308e565b506000612158846119ca565b9050600081600681111561216857fe5b1461223357600181600681111561217b57fe5b14806121925750600481600681111561219057fe5b145b806121a8575060058160068111156121a657fe5b145b806121be575060068160068111156121bc57fe5b145b156121db5760405162461bcd60e51b8152600401610524906154d5565b60028160068111156121e957fe5b14156122075760405162461bcd60e51b815260040161052490615c6f565b600381600681111561221557fe5b14156122335760405162461bcd60e51b8152600401610524906156f1565b600081600681111561224157fe5b1461225e5760405162461bcd60e51b815260040161052490615b86565b609754604051633581777360e01b81526000916001600160a01b03169063358177739061228d90600401615684565b60206040518083038186803b1580156122a557600080fd5b505afa1580156122b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122dd9190615062565b90506060612310609887815481106122f157fe5b60009182526020909120600890910201546001600160a01b031661265e565b905061231b86613be3565b816001600160a01b03166394df393f6098888154811061233757fe5b906000526020600020906008020160000160009054906101000a90046001600160a01b03168860988a8154811061236a57fe5b90600052602060002090600802016002015460988b8154811061238957fe5b9060005260206000209060080201600501546040518563ffffffff1660e01b81526004016123ba949392919061529e565b600060405180830381600087803b1580156123d457600080fd5b505af11580156123e8573d6000803e3d6000fd5b505050506123f5816127b2565b7fdb0c41de0e1a6e61f3ea29d9618edd8bfe8cb4e041a267c54eec70418341272d866040516124249190615324565b60405180910390a1505050505050565b6098818154811061244157fe5b60009182526020918290206008909102018054600180830154600280850154600386015460048701546005880154600689015460078a01805460408051601f6000199c841615610100029c909c0190921698909804998a018d90048d0281018d019097528887526001600160a01b039099169b50959993989297919690959492938301828280156125135780601f106124e857610100808354040283529160200191612513565b820191906000526020600020905b8154815290600101906020018083116124f657829003601f168201915b5050505050905088565b600054610100900460ff16806125365750612536614095565b80612544575060005460ff16155b6125605760405162461bcd60e51b815260040161052490615a75565b600054610100900460ff1615801561258b576000805460ff1961ff0019909116610100171660011790555b6125948261409b565b80156107a6576000805461ff00191690555050565b60008181526065602052604081206104f190614125565b6000828152606560205260409020600201546125de906103ff6138d7565b6118835760405162461bcd60e51b81526004016105249061576a565b6001600160a01b03166000908152609a602052604090205490565b6000611cc98383612624612c74565b614130565b60006104f161263783611c50565b61264084613479565b9063ffffffff61416b16565b60006126588133611cd2565b90505b90565b60606104f18260006129b4565b600082600301546000141561268257506000611ccc565b8183600301541161279b5760038301545b8281116127835760008181526001808601602090815260408084205491889052832054612700926126f4919060028a019086906126d790899063ffffffff61419016565b81526020019081526020016000205461416b90919063ffffffff16565b9063ffffffff6141d216565b6000838152600287016020526040902054909150811461272e57600082815260028601602052604090208190555b60008281526020869052604090205415612752576000828152602086905260408120555b60008281526001860160205260409020541561277a5760008281526001860160205260408120555b50600101612693565b5061279582600163ffffffff61416b16565b60038401555b506000908152600291909101602052604090205490565b609754604051633581777360e01b81526000916001600160a01b0316906335817773906127e19060040161546c565b60206040518083038186803b1580156127f957600080fd5b505afa15801561280d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128319190615062565b9050600080805b845181101561294657826001600160a01b031685828151811061285757fe5b6020026020010151600001516001600160a01b0316146129115781156128d857604051634458328b60e01b81526001600160a01b03851690634458328b906128a59086908690600401615264565b600060405180830381600087803b1580156128bf57600080fd5b505af11580156128d3573d6000803e3d6000fd5b505050505b8481815181106128e457fe5b60200260200101516000015192508481815181106128fe57fe5b602002602001015160200151915061293e565b61293b85828151811061292057fe5b6020026020010151602001518361416b90919063ffffffff16565b91505b600101612838565b5080156129ae57604051634458328b60e01b81526001600160a01b03841690634458328b9061297b9085908590600401615264565b600060405180830381600087803b15801561299557600080fd5b505af11580156129a9573d6000803e3d6000fd5b505050505b50505050565b60606129bf83611816565b15611ccc576001600160a01b038316600090815260a3602052604090205460a25483158015906129fd5750806129fb838663ffffffff61416b16565b105b15612a1557612a12828563ffffffff61416b16565b90505b612a25818363ffffffff61419016565b67ffffffffffffffff81118015612a3b57600080fd5b50604051908082528060200260200182016040528015612a7557816020015b612a62614f97565b815260200190600190039081612a5a5790505b509250815b81831015612c5357600060a28481548110612a9157fe5b9060005260206000209060040201600201549050600060a28581548110612ab457fe5b90600052602060002090600402016003015490506000612ad5898484614130565b9050612ae881600063ffffffff61422516565b15612c45576001600160a01b0389166000908152609f6020526040902060a28054612b6a92919089908110612b1957fe5b6000918252602080832060408051808201825260049094029091018054845260010154838301526001600160a01b038f16845260a0825280842089855290915290912091908563ffffffff61423e16565b612bca60a28781548110612b7a57fe5b6000918252602080832060408051808201825260049094029091018054845260010154838301526001600160a01b038e16845260a18252808420888552909152909120908463ffffffff612e4216565b8887612bdc888763ffffffff61419016565b81518110612be657fe5b60209081029190910101516001600160a01b039091169052612c19612c0c8a8585614130565b829063ffffffff6141d216565b87612c2a888763ffffffff61419016565b81518110612c3457fe5b602002602001015160200181815250505b505050826001019250612a7a565b506001600160a01b038516600090815260a360205260409020555092915050565b609754604051633581777360e01b815260009182916001600160a01b0390911690633581777390612ca79060040161565f565b60206040518083038186803b158015612cbf57600080fd5b505afa158015612cd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cf79190615062565b9050806001600160a01b031663ddd1b67e6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d3257600080fd5b505afa158015612d46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d6a9190615165565b91505090565b612d78614f7d565b6003840154612d8e83600163ffffffff61416b16565b1015612dac5760405162461bcd60e51b815260040161052490615435565b6003840154612dc657612dbf600061424c565b9050612e3b565b6000612dd28584613ac3565b9050612de581600063ffffffff61425f16565b15612dfc57612df4600061424c565b915050612e3b565b8380821015612e085750805b612e10614f7d565b612e29612e23848463ffffffff6141d216565b84614285565b9050612e368782876142d5565b925050505b9392505050565b6003830154612e5882600163ffffffff61416b16565b1015612e765760405162461bcd60e51b815260040161052490615733565b602082015182511115612e9b5760405162461bcd60e51b81526004016105249061548e565b6003830154612ea957612f8d565b6000612eb5848361266b565b9050612ec881600063ffffffff61425f16565b15612ed35750612f8d565b6020808401518451600085815260028801909352604090922054612f0d92612f01919063ffffffff61384a16565b9063ffffffff6142e316565b6000838152600286016020526040812091909155612f3283600163ffffffff61416b16565b90505b84600401548111612f8a576020808501518551600084815260018901909352604090922054612f6e92612f01919063ffffffff61384a16565b6000828152600180880160205260409091209190915501612f35565b50505b505050565b6001830154612fd057600180840182905560028085018390556000838152602086815260408220865181559086015193810193909355910155612f8d565b8083600201541115612ff45760405162461bcd60e51b815260040161052490615a30565b808360020154141561305557600081815260208481526040918290208251808401909352805483526001015490820152613034908363ffffffff61432516565b60008281526020858152604090912082518155910151600190910155612f8d565b60008181526020848152604080832085518155918501516001830155600291820183905581860180548452922001829055819055505050565b609754604051633581777360e01b815260009182916001600160a01b03909116906335817773906130c19060040161557a565b60206040518083038186803b1580156130d957600080fd5b505afa1580156130ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131119190615062565b6001600160a01b038516600090815260a6602090815260408083208784526001019091529020549091501515806131fe57506001600160a01b038416600090815260a6602090815260408083208684526001019091529020541580156131fe5750806001600160a01b031663049e41776040518163ffffffff1660e01b815260040160206040518083038186803b1580156131ab57600080fd5b505afa1580156131bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131e39190615165565b6001600160a01b038516600090815260a66020526040902054105b6119c35760405162461bcd60e51b815260040161052490615d77565b6098805460408051610100810182526001600160a01b03898116825260208083018a8152938301898152606084018981524260808601908152600060a0870181815260c0880182815260e089018d815260018c018d559b90925287517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d81460088c0290810180546001600160a01b0319169290991691909117885598517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d8158a015593517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d81689015591517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d817880155517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d81887015590517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d819860155517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d81a850155945180519495929491936133d1937f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d81b90910192910190614fae565b5050506000858152609960209081526040808320805460018181018355918552838520018590556001600160a01b038a168452609a83529083208054918201815583529120018190556098805461346f91908390811061342d57fe5b6000918252602090912060089091020154609880546001600160a01b03909216918490811061345857fe5b906000526020600020906008020160020154614363565b5095945050505050565b600080613484612c74565b905061348f83611c42565b6001600160a01b0383166000908152609f60205260409020612e3b908263ffffffff613ac316565b6001600160a01b038216600090815260a660209081526040808320848452600190810190925290912054141561352b576001600160a01b038216600090815260a6602052604090205461351190600163ffffffff61419016565b6001600160a01b038316600090815260a660205260409020555b6001600160a01b038216600090815260a6602090815260408083208484526001908101909252909120546135649163ffffffff61419016565b6001600160a01b03909216600090815260a660209081526040808320938352600190930190522055565b600080613599612c74565b90506000609884815481106135aa57fe5b906000526020600020906008020160050154905080821015613600576135f7609885815481106135d657fe5b9060005260206000209060080201600301548261416b90919063ffffffff16565b925050506104f4565b60006136376098868154811061361257fe5b906000526020600020906008020160030154612f01848661419090919063ffffffff16565b905061368b61367e6098878154811061364c57fe5b90600052602060002090600802016003015461367260018561416b90919063ffffffff16565b9063ffffffff61384a16565b839063ffffffff61416b16565b93505050506104f4565b6000818152609b602052604081205460988054839190859081106136b557fe5b90600052602060002090600802016001015490506000609885815481106136d857fe5b90600052602060002090600802016002015490508260001415613718576000828152609e60205260409020600101549250826137185792506104f4915050565b825b60008111801561374757506098868154811061373257fe5b90600052602060002090600802016006015481105b156137c5576098868154811061375957fe5b90600052602060002090600802016005015481106137a4576000838152609e602090815260408083208484529091529020600181015490546137a19190612f0190859061384a565b91505b6000838152609e60209081526040808320938352929052206002015461371a565b50949350505050565b6000838152609c60205260409020612f8d90838363ffffffff61440716565b6001600160a01b0383166000908152609f60205260409020612f8d90838363ffffffff61440716565b6001600160a01b038416600090815260a06020908152604080832086845290915290206129ae90838363ffffffff61440716565b60008261385957506000611ccc565b8282028284828161386657fe5b0414611cc95760405162461bcd60e51b815260040161052490615920565b6000838152609d60205260409020612f8d90838363ffffffff6144c816565b6001600160a01b038416600090815260a16020908152604080832086845290915290206129ae90838363ffffffff6144c816565b3390565b60008281526065602052604090206138f9908263ffffffff61454616565b156107a6576139066138d7565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6001600160a01b0316600090815260a46020526040902054151590565b6000828152606560205260409020613985908263ffffffff61455b16565b156107a6576139926138d7565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b6000611cc98383614570565b6000611cc9836001600160a01b0384166145b5565b600080613a02612c74565b6001600160a01b038516600090815260a560205260409020600101549091508114613a3f5760405162461bcd60e51b8152600401610524906157f1565b6001600160a01b038416600090815260a56020526040902054831115613a775760405162461bcd60e51b815260040161052490615895565b6001600160a01b038416600090815260a56020526040902054613aa0908463ffffffff61419016565b6001600160a01b03909416600090815260a5602052604090209390935592915050565b6003820154600090613adc83600163ffffffff61416b16565b1015613afa5760405162461bcd60e51b8152600401610524906156af565b6003830154613b0b57506000611ccc565b81836003015411613bda5760038301545b828111613bc2576000818152600185016020908152604080832054918790528220546002870154613b5892916126f4919063ffffffff61416b16565b905080856002015414613b6d57600285018190555b60008281526020869052604090205415613b91576000828152602086905260408120555b600082815260018601602052604090205415613bb95760008281526001860160205260408120555b50600101613b1c565b50613bd482600163ffffffff61416b16565b60038401555b50506002015490565b609754604051633581777360e01b81526000916001600160a01b031690633581777390613c12906004016159f9565b60206040518083038186803b158015613c2a57600080fd5b505afa158015613c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c629190615062565b90506000613c6e612c74565b9050613c8181600163ffffffff61416b16565b60988481548110613c8e57fe5b9060005260206000209060080201600501819055506000609e600060988681548110613cb657fe5b9060005260206000209060080201600101548152602001908152602001600020600201541115613d2757609e600060988581548110613cf157fe5b60009182526020808320600160089093020191909101548352828101939093526040918201812060020154868252609b90935220555b613d8160988481548110613d3757fe5b90600052602060002090600802016001015460988581548110613d5657fe5b906000526020600020906008020160020154613d7c60018561416b90919063ffffffff16565b6145cd565b613de760988481548110613d9157fe5b6000918252602090912060089091020154609880546001600160a01b039092169186908110613dbc57fe5b906000526020600020906008020160020154613de260018561416b90919063ffffffff16565b6145ec565b613e6c60988481548110613df757fe5b6000918252602090912060089091020154609880546001600160a01b039092169186908110613e2257fe5b90600052602060002090600802016001015460988681548110613e4157fe5b906000526020600020906008020160020154613e6760018661416b90919063ffffffff16565b614615565b613ed260988481548110613e7c57fe5b6000918252602090912060089091020154609880546001600160a01b039092169186908110613ea757fe5b906000526020600020906008020160010154613ecd60018561416b90919063ffffffff16565b614649565b6000613f9e836001600160a01b031663f5b98f4160988781548110613ef357fe5b9060005260206000209060080201600301546040518263ffffffff1660e01b8152600401613f219190615324565b60206040518083038186803b158015613f3957600080fd5b505afa158015613f4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f719190615165565b60988681548110613f7e57fe5b90600052602060002090600802016002015461384a90919063ffffffff16565b9050613fdc60988581548110613fb057fe5b90600052602060002090600802016001015482613fd760018661416b90919063ffffffff16565b6146e7565b61404360988581548110613fec57fe5b6000918252602090912060089091020154609880546001600160a01b03909216918790811061401757fe5b9060005260206000209060080201600101548361403e60018761416b90919063ffffffff16565b614706565b6129ae6098858154811061405357fe5b6000918252602090912060089091020154609880546001600160a01b03909216918790811061407e57fe5b90600052602060002090600802016001015461473a565b303b1590565b600054610100900460ff16806140b457506140b4614095565b806140c2575060005460ff16155b6140de5760405162461bcd60e51b815260040161052490615a75565b600054610100900460ff16158015614109576000805460ff1961ff0019909116610100171660011790555b6141116147e2565b61411c60003361180c565b61259482614874565b60006104f1826148ea565b6001600160a01b038316600090815260a0602090815260408083208584529091528120614163908363ffffffff613ac316565b949350505050565b600082820183811015611cc95760405162461bcd60e51b8152600401610524906155f8565b6000611cc983836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506148ee565b60008183106141e45750808203611ccc565b7f5b70a077a991facb623c7b2ee44cc539dc6ba345b6636552b8ea97fbbd4d54198383604051614215929190615e94565b60405180910390a1506000611ccc565b6000620f424019821061423457fe5b50620f4240011090565b6129ae84848484600161491a565b614254614f7d565b6104f1826001614285565b6000818311156142775750620f424081830310611ccc565b50620f424082820310611ccc565b61428d614f7d565b600082116142ad5760405162461bcd60e51b815260040161052490615998565b6142b5614f7d565b6040518060400160405280858152602001848152509050611cc981614b01565b612f8d83848484600061491a565b6000611cc983836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614b4d565b61432d614f7d565b81518351611cc991614345919063ffffffff61384a16565b6020808501519086015161435e9163ffffffff61384a16565b614285565b60008061436e612c74565b6001600160a01b038516600090815260a560205260409020600101549091508111156143ba576001600160a01b038416600090815260a5602052604090208381556001018190556119c3565b6001600160a01b038416600090815260a5602052604090206001015481146143de57fe5b6001600160a01b038416600090815260a56020526040902054613aa0908463ffffffff61416b16565b61441881600163ffffffff61416b16565b8360030154111561443b5760405162461bcd60e51b8152600401610524906159c2565b60038301546144535760038301819055600483018190555b826004015481111561446757600483018190555b826003015481106144a9576000818152600184016020526040902054614493908363ffffffff61416b16565b6000828152600185016020526040902055612f8d565b60028301546144be908363ffffffff6141d216565b6002840155505050565b80836003015411156144ec5760405162461bcd60e51b8152600401610524906159c2565b60038301546144fd57600383018190555b600081815260018401602052604090205461451e908363ffffffff61416b16565b600082815260018501602052604090205560048301548114612f8d5760048301819055505050565b6000611cc9836001600160a01b038416614b84565b6000611cc9836001600160a01b038416614bce565b815460009082106145935760405162461bcd60e51b8152600401610524906153a4565b8260000182815481106145a257fe5b9060005260206000200154905092915050565b60009081526001919091016020526040902054151590565b6000838152609c60205260409020612f8d90838363ffffffff614c9416565b6001600160a01b0383166000908152609f60205260409020612f8d90838363ffffffff614c9416565b6001600160a01b038416600090815260a06020908152604080832086845290915290206129ae90838363ffffffff614c9416565b6001600160a01b038316600090815260a46020526040902054614691576001600160a01b038316600090815260a46020908152604080832084905560a25460a3909252909120555b6001600160a01b038316600090815260a460209081526040808320858452600101909152902054612f8d576001600160a01b0392909216600090815260a460209081526040808320938352600190930190522055565b6000838152609d60205260409020612f8d90838363ffffffff614d3716565b6001600160a01b038416600090815260a16020908152604080832086845290915290206129ae90838363ffffffff614d3716565b6001600160a01b038216600090815260a6602090815260408083208484526001019091529020546147a9576001600160a01b038216600090815260a6602052604090205461478f90600163ffffffff61416b16565b6001600160a01b038316600090815260a660205260409020555b6001600160a01b038216600090815260a6602090815260408083208484526001908101909252909120546135649163ffffffff61416b16565b600054610100900460ff16806147fb57506147fb614095565b80614809575060005460ff16155b6148255760405162461bcd60e51b815260040161052490615a75565b600054610100900460ff16158015614850576000805460ff1961ff0019909116610100171660011790555b614858614db1565b614860614db1565b8015611c4d576000805461ff001916905550565b6001600160a01b03811661489a5760405162461bcd60e51b815260040161052490615ae7565b6148ac816001600160a01b0316614e32565b6148c85760405162461bcd60e51b815260040161052490615961565b609780546001600160a01b0319166001600160a01b0392909216919091179055565b5490565b600081848411156149125760405162461bcd60e51b81526004016105249190615341565b505050900390565b600385015461493083600163ffffffff61416b16565b101561494e5760405162461bcd60e51b815260040161052490615435565b801561498857600384015461496a83600163ffffffff61416b16565b10156149885760405162461bcd60e51b815260040161052490615435565b6020830151835111156149ad5760405162461bcd60e51b81526004016105249061548e565b60038501546149bb57612f8a565b60006149c78684613ac3565b90506149da81600063ffffffff61425f16565b156149e55750612f8a565b6000614a0a8560200151612f0187600001518a6002015461384a90919063ffffffff16565b90508215614a3357614a3386614a2d838a600201546141d290919063ffffffff16565b86614407565b600287018190556000614a4d85600163ffffffff61416b16565b90505b876004015481116129a9576020808701518751600084815260018c019093526040832054614a899291612f01919063ffffffff61384a16565b90508415614ae557600082815260018a016020526040902054614ad390614ab6908363ffffffff6141d216565b600084815260018b0160205260409020549063ffffffff6141d216565b600083815260018a0160205260409020555b60008281526001808b0160205260409091209190915501614a50565b6000614b1582600001518360200151614e6b565b8251909150614b2a908263ffffffff6142e316565b82526020820151614b41908263ffffffff6142e316565b60209092019190915250565b60008183614b6e5760405162461bcd60e51b81526004016105249190615341565b506000838581614b7a57fe5b0495945050505050565b6000614b9083836145b5565b614bc657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611ccc565b506000611ccc565b60008181526001830160205260408120548015614c8a5783546000198083019190810190600090879083908110614c0157fe5b9060005260206000200154905080876000018481548110614c1e57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080614c4e57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050611ccc565b6000915050611ccc565b8083600301541115614cb85760405162461bcd60e51b81526004016105249061562f565b6003830154614cd05760038301819055600483018190555b8260040154811115614ce457600483018190555b82600301548110614d2257600081815260208490526040902054614d0e908363ffffffff61416b16565b600082815260208590526040902055612f8d565b60028301546144be908363ffffffff61416b16565b8083600301541115614d5b5760405162461bcd60e51b81526004016105249061562f565b6003830154614d6c57600383018190555b600081815260208490526040902054614d8b908363ffffffff61416b16565b60008281526020859052604090205560048301548114612f8d5760048301819055505050565b600054610100900460ff1680614dca5750614dca614095565b80614dd8575060005460ff16155b614df45760405162461bcd60e51b815260040161052490615a75565b600054610100900460ff16158015614860576000805460ff1961ff0019909116610100171660011790558015611c4d576000805461ff001916905550565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590614163575050151592915050565b6000828281811115614e8757614e818282614ebb565b90925090505b8015614eb357614e9d828263ffffffff614ebe16565b9150614ea98282614ebb565b9092509050614e87565b509392505050565b91565b6000611cc983836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f000000000000000081525060008183614f1c5760405162461bcd60e51b81526004016105249190615341565b50828481614f2657fe5b06949350505050565b60405180610100016040528060006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001606081525090565b604051806040016040528060008152602001600081525090565b604080518082019091526000808252602082015290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10614fef57805160ff191683800117855561501c565b8280016001018555821561501c579182015b8281111561501c578251825591602001919060010190615001565b5061502892915061502c565b5090565b61265b91905b808211156150285760008155600101615032565b600060208284031215615057578081fd5b8135611cc981615ece565b600060208284031215615073578081fd5b8151611cc981615ece565b60008060408385031215615090578081fd5b823561509b81615ece565b946020939093013593505050565b6000806000606084860312156150bd578081fd5b83356150c881615ece565b95602085013595506040909401359392505050565b6000602082840312156150ee578081fd5b81518015158114611cc9578182fd5b60006020828403121561510e578081fd5b5035919050565b60008060408385031215615127578182fd5b82359150602083013561513981615ece565b809150509250929050565b60008060408385031215615156578182fd5b50508035926020909101359150565b600060208284031215615176578081fd5b5051919050565b600080600080600060808688031215615194578081fd5b853594506020860135935060408601359250606086013567ffffffffffffffff808211156151c0578283fd5b81880189601f8201126151d1578384fd5b80359250818311156151e1578384fd5b8960208483010111156151f2578384fd5b6020810194505050809150509295509295909350565b60008151808452615220816020860160208601615ea2565b601f01601f19169290920160200192915050565b60008251615246818460208701615ea2565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b0394909416845260208401929092526040830152606082015260800190565b600061010060018060a01b038b1683528960208401528860408401528760608401528660808401528560a08401528460c08401528060e084015261530a81840185615208565b9b9a5050505050505050505050565b901515815260200190565b90815260200190565b602081016007831061533b57fe5b91905290565b600060208252611cc96020830184615208565b60208082526030908201527f4f6e6c7920746f6b656e20686f6c646572732063616e2063616e63656c20646560408201526f1b1959d85d1a5bdb881c995c5d595cdd60821b606082015260800190565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b6020808252602f908201527f416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e60408201526e0818591b5a5b881d1bc819dc985b9d608a1b606082015260800190565b6020808252601f908201527f43616e6e6f74207265647563652076616c756520696e20746865207061737400604082015260600190565b602080825260089082015267283ab734b9b432b960c11b604082015260600190565b60208082526027908201527f496e6372656173696e67206f662076616c756573206973206e6f7420696d706c604082015266195b595b9d195960ca1b606082015260800190565b60208082526028908201527f5468652064656c65676174696f6e20686173206265656e20616c7265616479206040820152671858d8d95c1d195960c21b606082015260800190565b60208082526038908201527f56616c696461746f72206973206e6f7420617574686f72697a656420746f206160408201527f63636570742064656c65676174696f6e20726571756573740000000000000000606082015260800190565b6020808252600f908201526e21b7b739ba30b73a39a437b63232b960891b604082015260600190565b60208082526035908201527f5468652076616c696461746f72206973206e6f742063757272656e746c7920616040820152746363657074696e67206e657720726571756573747360581b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b60208082526016908201527510d85b9b9bdd08185919081d1bc81d1a19481c185cdd60521b604082015260600190565b6020808252600b908201526a54696d6548656c7065727360a81b604082015260600190565b6020808252601190820152702a37b5b2b72630bab731b42637b1b5b2b960791b604082015260600190565b60208082526022908201527f43616e6e6f742063616c63756c6174652076616c756520696e207468652070616040820152611cdd60f21b606082015260800190565b60208082526022908201527f5468652064656c65676174696f6e2072657175657374206973206f7574646174604082015261195960f21b606082015260800190565b6020808252601e908201527f43616e2774207265647563652076616c756520696e2074686520706173740000604082015260600190565b60208082526030908201527f416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e60408201526f2061646d696e20746f207265766f6b6560801b606082015260800190565b6020808252601b908201527f43616e6e6f74207265717565737420756e64656c65676174696f6e0000000000604082015260600190565b6020808252602b908201527f546865726520617265206e6f2064656c65676174696f6e20726571756573747360408201526a040e8d0d2e640dadedce8d60ab1b606082015260800190565b6020808252818101527f4e6f207065726d697373696f6e7320746f206163636570742072657175657374604082015260600190565b6020808252600a908201526929b5b0b632aa37b5b2b760b11b604082015260600190565b6020808252601b908201527f556e6c6f636b696e6720616d6f756e7420697320746f6f206269670000000000604082015260600190565b60208082526034908201527f546f6b656e20686f6c64657220646f6573206e6f74206861766520656e6f75676040820152736820746f6b656e7320746f2064656c656761746560601b606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b60208082526017908201527f41646472657373206973206e6f7420636f6e7472616374000000000000000000604082015260600190565b60208082526010908201526f4469766973696f6e206279207a65726f60801b604082015260600190565b6020808252601d908201527f43616e6e6f742073756274726163742066726f6d207468652070617374000000604082015260600190565b60208082526017908201527f44656c65676174696f6e506572696f644d616e61676572000000000000000000604082015260600190565b60208082526025908201527f43616e6e6f742070757420736c617368696e67206576656e7420696e20746865604082015264081c185cdd60da1b606082015260800190565b6020808252602e908201527f436f6e747261637420696e7374616e63652068617320616c726561647920626560408201526d195b881a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252600a9082015269546f6b656e537461746560b01b604082015260600190565b60208082526022908201527f436f6e74726163744d616e616765722061646472657373206973206e6f742073604082015261195d60f21b606082015260800190565b6020808252603a908201527f546f6b656e20686f6c6465727320617265206f6e6c792061626c6520746f206360408201527f616e63656c2050524f504f5345442064656c65676174696f6e73000000000000606082015260800190565b60208082526027908201527f43616e6e6f74207365742064656c65676174696f6e20737461746520746f206160408201526618d8d95c1d195960ca1b606082015260800190565b60208082526025908201527f546869732064656c65676174696f6e20706572696f64206973206e6f7420616c6040820152641b1bddd95960da1b606082015260800190565b6020808252603e908201527f416d6f756e7420646f6573206e6f74206d656574207468652076616c6964617460408201527f6f722773206d696e696d756d2064656c65676174696f6e20616d6f756e740000606082015260800190565b60208082526031908201527f5468652064656c65676174696f6e20686173206265656e2063616e63656c6c656040820152703210313c903a37b5b2b7103437b63232b960791b606082015260800190565b60208082526019908201527f44656c65676174696f6e20646f6573206e6f7420657869737400000000000000604082015260600190565b60208082526029908201527f5065726d697373696f6e2064656e69656420746f207265717565737420756e6460408201526832b632b3b0ba34b7b760b91b606082015260800190565b60208082526019908201527f4d6573736167652073656e64657220697320696e76616c696400000000000000604082015260600190565b6020808252601e908201527f4c696d6974206f662076616c696461746f727320697320726561636865640000604082015260600190565b60208082526010908201526f56616c696461746f725365727669636560801b604082015260600190565b6020808252602f908201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560408201526e103937b632b9903337b91039b2b63360891b606082015260800190565b60006020825260018060a01b038351166020830152602083015160408301526040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015160e083015260e08301516101008081850152506119c0610120840182615208565b918252602082015260400190565b60005b83811015615ebd578181015183820152602001615ea5565b838111156129ae5750506000910152565b6001600160a01b0381168114611c4d57600080fdfea264697066735822122088c330383e3eca2d1dd22b553b5b109eafd04aa92a00d266c363bddb03bdaf0064736f6c634300060a0033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102115760003560e01c806356574b8c11610125578063b39e12cf116100ad578063ca15c8731161007c578063ca15c8731461049a578063d547741f146104ad578063dda641ae146104c0578063fa8dacba14610216578063ff1f7799146104d357610211565b8063b39e12cf14610445578063b86315851461044d578063c4336c1c14610460578063c4d66de81461048757610211565b806391d14854116100f457806391d14854146103f15780639654ff16146104045780639ac1c4ad146104175780639c3e452f1461042a578063a217fddf1461043d57610211565b806356574b8c146103985780635fd55293146103ab5780637ce845d0146103be5780639010d07c146103d157610211565b8063248a9ca3116101a85780632f7263cd116101775780632f7263cd1461031f57806336568abe1461033f5780633d42b1ce14610352578063416880b01461036557806344c9af281461037857610211565b8063248a9ca3146102d357806327040f68146102e657806327e5455a146102f95780632f2ff15d1461030c57610211565b80631d703812116101e45780631d703812146102875780631d9c7f0a1461029a5780631da42e5e146102ad57806321eb5859146102c057610211565b80630b975991146102165780630dd357011461023f5780630e01bff81461025f5780631c8a253e14610272575b600080fd5b610229610224366004615046565b6104e6565b6040516102369190615324565b60405180910390f35b61025261024d3660046150fd565b6104f9565b6040516102369190615e27565b61022961026d3660046150a9565b61063c565b61028561028036600461507e565b610794565b005b6102296102953660046150fd565b6107aa565b6102296102a8366004615144565b6107b8565b6102856102bb366004615144565b6107e6565b6102856102ce36600461517d565b610a25565b6102296102e13660046150fd565b611065565b6102296102f4366004615046565b61107a565b6102856103073660046150fd565b611085565b61028561031a366004615115565b6117d2565b61033261032d366004615046565b611816565b6040516102369190615319565b61028561034d366004615115565b61184b565b6102296103603660046150fd565b61188d565b610229610373366004615144565b61189f565b61038b6103863660046150fd565b6119ca565b604051610236919061532d565b6102296103a636600461507e565b611c29565b6102856103b9366004615046565b611c42565b6102296103cc366004615046565b611c50565b6103e46103df366004615144565b611cab565b6040516102369190615250565b6103326103ff366004615115565b611cd2565b61022961041236600461507e565b611cf0565b6102856104253660046150fd565b611d1c565b610229610438366004615144565b611e6f565b610229611f8a565b6103e4611f8f565b61028561045b3660046150fd565b611f9e565b61047361046e3660046150fd565b612434565b6040516102369897969594939291906152c4565b610285610495366004615046565b61251d565b6102296104a83660046150fd565b6125a9565b6102856104bb366004615115565b6125c0565b6102296104ce366004615046565b6125fa565b6102296104e136600461507e565b612615565b60006104f182612629565b90505b919050565b610501614f2f565b6098548290811061052d5760405162461bcd60e51b815260040161052490615cc0565b60405180910390fd5b6098838154811061053a57fe5b600091825260209182902060408051610100808201835260089490940290920180546001600160a01b0316835260018082015484870152600280830154858501526003830154606086015260048301546080860152600583015460a0860152600683015460c08601526007830180548551938116159097026000190190961604601f81018790048702820187019093528281529294909360e086019392909183018282801561062a5780601f106105ff5761010080835404028352916020019161062a565b820191906000526020600020905b81548152906001019060200180831161060d57829003601f168201915b50505050508152505091505b50919050565b604080518082018252600b81526a2234b9ba3934b13aba37b960a91b602080830191909152609754925160009333926001600160a01b039091169163ec56a3739161068991869101615234565b604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004016106bb9190615324565b60206040518083038186803b1580156106d357600080fd5b505afa1580156106e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070b9190615062565b6001600160a01b03161480610723575061072361264c565b61073f5760405162461bcd60e51b815260040161052490615d40565b606061074a8661265e565b6001600160a01b038716600090815260a1602090815260408083208984529091529020909150610780908563ffffffff61266b16565b925061078b816127b2565b50509392505050565b6107a66107a183836129b4565b6127b2565b5050565b60006104f182610438612c74565b609960205281600052604060002081815481106107d157fe5b90600052602060002001600091509150505481565b6040805180820182526008815267283ab734b9b432b960c11b6020808301919091526097549251919233926001600160a01b039091169163ec56a3739161082f91869101615234565b604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004016108619190615324565b60206040518083038186803b15801561087957600080fd5b505afa15801561088d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b19190615062565b6001600160a01b031614806108c957506108c961264c565b6108e55760405162461bcd60e51b815260040161052490615d40565b60006108ef612c74565b90506108f9614f7d565b6000858152609c6020526040902061091890858463ffffffff612d7016565b6000868152609d6020526040902090915061093a90828463ffffffff612e4216565b6000858152609e60205260409020610953908284612f92565b60408051606081018252918252602080830196875290820192835260a28054600181018255600091909152915180517faaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf42d60049094029384015501517faaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf42e82015593517faaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf42f850155517faaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf430909301929092555050565b609754604051633581777360e01b81526000916001600160a01b031690633581777390610a5490600401615dae565b60206040518083038186803b158015610a6c57600080fd5b505afa158015610a80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa49190615062565b609754604051633581777360e01b81529192506000916001600160a01b0390911690633581777390610ad8906004016159f9565b60206040518083038186803b158015610af057600080fd5b505afa158015610b04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b289190615062565b609754604051633581777360e01b81529192506000916001600160a01b0390911690633581777390610b5c90600401615871565b60206040518083038186803b158015610b7457600080fd5b505afa158015610b88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bac9190615062565b609754604051633581777360e01b81529192506000916001600160a01b0390911690633581777390610be090600401615ac3565b60206040518083038186803b158015610bf857600080fd5b505afa158015610c0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c309190615062565b6040516370cb318960e01b81529091506001600160a01b038516906370cb318990610c61908c908c90600401615e94565b60206040518083038186803b158015610c7957600080fd5b505afa158015610c8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb191906150dd565b610ccd5760405162461bcd60e51b815260040161052490615c12565b60405163f93c86f160e01b81526001600160a01b0385169063f93c86f190610cf9908c90600401615324565b60206040518083038186803b158015610d1157600080fd5b505afa158015610d25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4991906150dd565b610d655760405162461bcd60e51b81526004016105249061551d565b60405163a795d29360e01b81526001600160a01b0384169063a795d29390610d91908a90600401615324565b60206040518083038186803b158015610da957600080fd5b505afa158015610dbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de191906150dd565b610dfd5760405162461bcd60e51b815260040161052490615bcd565b60405163461a6f3f60e11b81526001600160a01b03851690638c34de7e90610e29908c90600401615324565b60206040518083038186803b158015610e4157600080fd5b505afa158015610e55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7991906150dd565b610e955760405162461bcd60e51b8152600401610524906155a3565b610e9f338a61308e565b506060610eab3361265e565b90506000610ef2338c8c8c8c8c8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061321a92505050565b90506000846001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401610f229190615250565b60206040518083038186803b158015610f3a57600080fd5b505afa158015610f4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f729190615165565b90506000846001600160a01b0316630b975991336040518263ffffffff1660e01b8152600401610fa29190615250565b602060405180830381600087803b158015610fbc57600080fd5b505af1158015610fd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff49190615165565b9050808210156110165760405162461bcd60e51b8152600401610524906158cc565b7f839237f8da6208af7e49773f22501b3082aaae94d5b6ce8ee96f117835fe2f67836040516110459190615324565b60405180910390a1611056846127b2565b50505050505050505050505050565b60009081526065602052604090206002015490565b60006104f182613479565b609854819081106110a85760405162461bcd60e51b815260040161052490615cc0565b60046110b3836119ca565b60068111156110be57fe5b146110db5760405162461bcd60e51b8152600401610524906157ba565b609754604051633581777360e01b81526000916001600160a01b03169063358177739061110a90600401615dae565b60206040518083038186803b15801561112257600080fd5b505afa158015611136573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115a9190615062565b9050336001600160a01b03166098848154811061117357fe5b60009182526020909120600890910201546001600160a01b031614806112b357506040516224441f60e71b81526001600160a01b038216906312220f80906111bf903390600401615250565b60206040518083038186803b1580156111d757600080fd5b505afa1580156111eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061120f91906150dd565b80156112b35750604051630ba7341960e11b81526001600160a01b0382169063174e683290611242903390600401615250565b60206040518083038186803b15801561125a57600080fd5b505afa15801561126e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112929190615165565b6098848154811061129f57fe5b906000526020600020906008020160010154145b6112cf5760405162461bcd60e51b815260040161052490615cf7565b609754604051633581777360e01b81526000916001600160a01b0316906335817773906112fe90600401615684565b60206040518083038186803b15801561131657600080fd5b505afa15801561132a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134e9190615062565b609754604051633581777360e01b81529192506000916001600160a01b0390911690633581777390611382906004016159f9565b60206040518083038186803b15801561139a57600080fd5b505afa1580156113ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113d29190615062565b9050611426609886815481106113e457fe5b6000918252602090912060089091020154609880546001600160a01b03909216918890811061140f57fe5b9060005260206000209060080201600101546134b7565b61142f33611c42565b6114388561358e565b6098868154811061144557fe5b906000526020600020906008020160060181905550600061146586613695565b90506114ae6098878154811061147757fe5b906000526020600020906008020160010154826098898154811061149757fe5b9060005260206000209060080201600601546137ce565b611508609887815481106114be57fe5b906000526020600020906008020160000160009054906101000a90046001600160a01b031682609889815481106114f157fe5b9060005260206000209060080201600601546137ed565b61157a6098878154811061151857fe5b6000918252602090912060089091020154609880546001600160a01b03909216918990811061154357fe5b9060005260206000209060080201600101548360988a8154811061156357fe5b906000526020600020906008020160060154613816565b6000611626836001600160a01b031663f5b98f4160988a8154811061159b57fe5b9060005260206000209060080201600301546040518263ffffffff1660e01b81526004016115c99190615324565b60206040518083038186803b1580156115e157600080fd5b505afa1580156115f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116199190615165565b839063ffffffff61384a16565b905061166f6098888154811061163857fe5b9060005260206000209060080201600101548260988a8154811061165857fe5b906000526020600020906008020160060154613884565b6116e16098888154811061167f57fe5b6000918252602090912060089091020154609880546001600160a01b03909216918a9081106116aa57fe5b9060005260206000209060080201600101548360988b815481106116ca57fe5b9060005260206000209060080201600601546138a3565b836001600160a01b0316630d4e8fd1609889815481106116fd57fe5b906000526020600020906008020160000160009054906101000a90046001600160a01b03168960988b8154811061173057fe5b9060005260206000209060080201600601546040518463ffffffff1660e01b81526004016117609392919061527d565b600060405180830381600087803b15801561177a57600080fd5b505af115801561178e573d6000803e3d6000fd5b505050507fb0142de902382ce87e0ae1e5ec0699b26d25bec2eeb06bca82e1253099b3119c876040516117c19190615324565b60405180910390a150505050505050565b6000828152606560205260409020600201546117f0906103ff6138d7565b61180c5760405162461bcd60e51b8152600401610524906153e6565b6107a682826138db565b60006118218261394a565b80156104f157505060a2546001600160a01b0391909116600090815260a360205260409020541090565b6118536138d7565b6001600160a01b0316816001600160a01b0316146118835760405162461bcd60e51b815260040161052490615dd8565b6107a68282613967565b60009081526099602052604090205490565b604080518082018252600b81526a2234b9ba3934b13aba37b960a91b602080830191909152609754925160009333926001600160a01b039091169163ec56a373916118ec91869101615234565b604051602081830303815290604052805190602001206040518263ffffffff1660e01b815260040161191e9190615324565b60206040518083038186803b15801561193657600080fd5b505afa15801561194a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061196e9190615062565b6001600160a01b03161480611986575061198661264c565b6119a25760405162461bcd60e51b815260040161052490615d40565b6000848152609d602052604090206119c0908463ffffffff61266b16565b91505b5092915050565b609854600090829081106119f05760405162461bcd60e51b815260040161052490615cc0565b609883815481106119fd57fe5b90600052602060002090600802016005015460001415611b845760988381548110611a2457fe5b90600052602060002090600802016006015460001415611b7b57609754604051633581777360e01b81526000916001600160a01b031690633581777390611a6d9060040161565f565b60206040518083038186803b158015611a8557600080fd5b505afa158015611a99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611abd9190615062565b9050806001600160a01b031663bf64d84960988681548110611adb57fe5b9060005260206000209060080201600401546040518263ffffffff1660e01b8152600401611b099190615324565b60206040518083038186803b158015611b2157600080fd5b505afa158015611b35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b599190615165565b611b61612c74565b1415611b71576000925050610636565b6003925050610636565b60029150610636565b60988381548110611b9157fe5b906000526020600020906008020160050154611bab612c74565b1015611bba5760019150610636565b60988381548110611bc757fe5b90600052602060002090600802016006015460001415611bea5760049150610636565b60988381548110611bf757fe5b906000526020600020906008020160060154611c11612c74565b1015611c205760059150610636565b60069150610636565b609a60205281600052604060002081815481106107d157fe5b611c4d816000610794565b50565b600080611c5b612c74565b6001600160a01b038416600090815260a56020526040902060010154909150811115611c8b5760009150506104f4565b50506001600160a01b038116600090815260a560205260409020546104f4565b6000828152606560205260408120611cc9908363ffffffff6139d616565b90505b92915050565b6000828152606560205260408120611cc9908363ffffffff6139e216565b6001600160a01b0391909116600090815260a46020908152604080832093835260019093019052205490565b60985481908110611d3f5760405162461bcd60e51b815260040161052490615cc0565b60988281548110611d4c57fe5b60009182526020909120600890910201546001600160a01b03163314611d845760405162461bcd60e51b815260040161052490615354565b6000611d8f836119ca565b6006811115611d9a57fe5b14611db75760405162461bcd60e51b815260040161052490615b29565b611dbf612c74565b60988381548110611dcc57fe5b906000526020600020906008020160060181905550611e3360988381548110611df157fe5b6000918252602090912060089091020154609880546001600160a01b039092169185908110611e1c57fe5b9060005260206000209060080201600201546139f7565b507fc42cff898171c085fa87ecad4869a5fb22753dddf61048199b8c740c2109fb1182604051611e639190615324565b60405180910390a15050565b60408051808201825260058152644e6f64657360d81b602080830191909152609754925160009333926001600160a01b039091169163ec56a37391611eb691869101615234565b604051602081830303815290604052805190602001206040518263ffffffff1660e01b8152600401611ee89190615324565b60206040518083038186803b158015611f0057600080fd5b505afa158015611f14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f389190615062565b6001600160a01b03161480611f505750611f5061264c565b611f6c5760405162461bcd60e51b815260040161052490615d40565b6000848152609c602052604090206119c0908463ffffffff613ac316565b600081565b6097546001600160a01b031681565b60985481908110611fc15760405162461bcd60e51b815260040161052490615cc0565b609754604051633581777360e01b81526000916001600160a01b031690633581777390611ff090600401615dae565b60206040518083038186803b15801561200857600080fd5b505afa15801561201c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120409190615062565b9050806001600160a01b031663bed5012e336098868154811061205f57fe5b9060005260206000209060080201600101546040518363ffffffff1660e01b815260040161208e929190615264565b60206040518083038186803b1580156120a657600080fd5b505afa1580156120ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120de91906150dd565b6120fa5760405162461bcd60e51b81526004016105249061583c565b61214c6098848154811061210a57fe5b6000918252602090912060089091020154609880546001600160a01b03909216918690811061213557fe5b90600052602060002090600802016001015461308e565b506000612158846119ca565b9050600081600681111561216857fe5b1461223357600181600681111561217b57fe5b14806121925750600481600681111561219057fe5b145b806121a8575060058160068111156121a657fe5b145b806121be575060068160068111156121bc57fe5b145b156121db5760405162461bcd60e51b8152600401610524906154d5565b60028160068111156121e957fe5b14156122075760405162461bcd60e51b815260040161052490615c6f565b600381600681111561221557fe5b14156122335760405162461bcd60e51b8152600401610524906156f1565b600081600681111561224157fe5b1461225e5760405162461bcd60e51b815260040161052490615b86565b609754604051633581777360e01b81526000916001600160a01b03169063358177739061228d90600401615684565b60206040518083038186803b1580156122a557600080fd5b505afa1580156122b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122dd9190615062565b90506060612310609887815481106122f157fe5b60009182526020909120600890910201546001600160a01b031661265e565b905061231b86613be3565b816001600160a01b03166394df393f6098888154811061233757fe5b906000526020600020906008020160000160009054906101000a90046001600160a01b03168860988a8154811061236a57fe5b90600052602060002090600802016002015460988b8154811061238957fe5b9060005260206000209060080201600501546040518563ffffffff1660e01b81526004016123ba949392919061529e565b600060405180830381600087803b1580156123d457600080fd5b505af11580156123e8573d6000803e3d6000fd5b505050506123f5816127b2565b7fdb0c41de0e1a6e61f3ea29d9618edd8bfe8cb4e041a267c54eec70418341272d866040516124249190615324565b60405180910390a1505050505050565b6098818154811061244157fe5b60009182526020918290206008909102018054600180830154600280850154600386015460048701546005880154600689015460078a01805460408051601f6000199c841615610100029c909c0190921698909804998a018d90048d0281018d019097528887526001600160a01b039099169b50959993989297919690959492938301828280156125135780601f106124e857610100808354040283529160200191612513565b820191906000526020600020905b8154815290600101906020018083116124f657829003601f168201915b5050505050905088565b600054610100900460ff16806125365750612536614095565b80612544575060005460ff16155b6125605760405162461bcd60e51b815260040161052490615a75565b600054610100900460ff1615801561258b576000805460ff1961ff0019909116610100171660011790555b6125948261409b565b80156107a6576000805461ff00191690555050565b60008181526065602052604081206104f190614125565b6000828152606560205260409020600201546125de906103ff6138d7565b6118835760405162461bcd60e51b81526004016105249061576a565b6001600160a01b03166000908152609a602052604090205490565b6000611cc98383612624612c74565b614130565b60006104f161263783611c50565b61264084613479565b9063ffffffff61416b16565b60006126588133611cd2565b90505b90565b60606104f18260006129b4565b600082600301546000141561268257506000611ccc565b8183600301541161279b5760038301545b8281116127835760008181526001808601602090815260408084205491889052832054612700926126f4919060028a019086906126d790899063ffffffff61419016565b81526020019081526020016000205461416b90919063ffffffff16565b9063ffffffff6141d216565b6000838152600287016020526040902054909150811461272e57600082815260028601602052604090208190555b60008281526020869052604090205415612752576000828152602086905260408120555b60008281526001860160205260409020541561277a5760008281526001860160205260408120555b50600101612693565b5061279582600163ffffffff61416b16565b60038401555b506000908152600291909101602052604090205490565b609754604051633581777360e01b81526000916001600160a01b0316906335817773906127e19060040161546c565b60206040518083038186803b1580156127f957600080fd5b505afa15801561280d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128319190615062565b9050600080805b845181101561294657826001600160a01b031685828151811061285757fe5b6020026020010151600001516001600160a01b0316146129115781156128d857604051634458328b60e01b81526001600160a01b03851690634458328b906128a59086908690600401615264565b600060405180830381600087803b1580156128bf57600080fd5b505af11580156128d3573d6000803e3d6000fd5b505050505b8481815181106128e457fe5b60200260200101516000015192508481815181106128fe57fe5b602002602001015160200151915061293e565b61293b85828151811061292057fe5b6020026020010151602001518361416b90919063ffffffff16565b91505b600101612838565b5080156129ae57604051634458328b60e01b81526001600160a01b03841690634458328b9061297b9085908590600401615264565b600060405180830381600087803b15801561299557600080fd5b505af11580156129a9573d6000803e3d6000fd5b505050505b50505050565b60606129bf83611816565b15611ccc576001600160a01b038316600090815260a3602052604090205460a25483158015906129fd5750806129fb838663ffffffff61416b16565b105b15612a1557612a12828563ffffffff61416b16565b90505b612a25818363ffffffff61419016565b67ffffffffffffffff81118015612a3b57600080fd5b50604051908082528060200260200182016040528015612a7557816020015b612a62614f97565b815260200190600190039081612a5a5790505b509250815b81831015612c5357600060a28481548110612a9157fe5b9060005260206000209060040201600201549050600060a28581548110612ab457fe5b90600052602060002090600402016003015490506000612ad5898484614130565b9050612ae881600063ffffffff61422516565b15612c45576001600160a01b0389166000908152609f6020526040902060a28054612b6a92919089908110612b1957fe5b6000918252602080832060408051808201825260049094029091018054845260010154838301526001600160a01b038f16845260a0825280842089855290915290912091908563ffffffff61423e16565b612bca60a28781548110612b7a57fe5b6000918252602080832060408051808201825260049094029091018054845260010154838301526001600160a01b038e16845260a18252808420888552909152909120908463ffffffff612e4216565b8887612bdc888763ffffffff61419016565b81518110612be657fe5b60209081029190910101516001600160a01b039091169052612c19612c0c8a8585614130565b829063ffffffff6141d216565b87612c2a888763ffffffff61419016565b81518110612c3457fe5b602002602001015160200181815250505b505050826001019250612a7a565b506001600160a01b038516600090815260a360205260409020555092915050565b609754604051633581777360e01b815260009182916001600160a01b0390911690633581777390612ca79060040161565f565b60206040518083038186803b158015612cbf57600080fd5b505afa158015612cd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cf79190615062565b9050806001600160a01b031663ddd1b67e6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d3257600080fd5b505afa158015612d46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d6a9190615165565b91505090565b612d78614f7d565b6003840154612d8e83600163ffffffff61416b16565b1015612dac5760405162461bcd60e51b815260040161052490615435565b6003840154612dc657612dbf600061424c565b9050612e3b565b6000612dd28584613ac3565b9050612de581600063ffffffff61425f16565b15612dfc57612df4600061424c565b915050612e3b565b8380821015612e085750805b612e10614f7d565b612e29612e23848463ffffffff6141d216565b84614285565b9050612e368782876142d5565b925050505b9392505050565b6003830154612e5882600163ffffffff61416b16565b1015612e765760405162461bcd60e51b815260040161052490615733565b602082015182511115612e9b5760405162461bcd60e51b81526004016105249061548e565b6003830154612ea957612f8d565b6000612eb5848361266b565b9050612ec881600063ffffffff61425f16565b15612ed35750612f8d565b6020808401518451600085815260028801909352604090922054612f0d92612f01919063ffffffff61384a16565b9063ffffffff6142e316565b6000838152600286016020526040812091909155612f3283600163ffffffff61416b16565b90505b84600401548111612f8a576020808501518551600084815260018901909352604090922054612f6e92612f01919063ffffffff61384a16565b6000828152600180880160205260409091209190915501612f35565b50505b505050565b6001830154612fd057600180840182905560028085018390556000838152602086815260408220865181559086015193810193909355910155612f8d565b8083600201541115612ff45760405162461bcd60e51b815260040161052490615a30565b808360020154141561305557600081815260208481526040918290208251808401909352805483526001015490820152613034908363ffffffff61432516565b60008281526020858152604090912082518155910151600190910155612f8d565b60008181526020848152604080832085518155918501516001830155600291820183905581860180548452922001829055819055505050565b609754604051633581777360e01b815260009182916001600160a01b03909116906335817773906130c19060040161557a565b60206040518083038186803b1580156130d957600080fd5b505afa1580156130ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131119190615062565b6001600160a01b038516600090815260a6602090815260408083208784526001019091529020549091501515806131fe57506001600160a01b038416600090815260a6602090815260408083208684526001019091529020541580156131fe5750806001600160a01b031663049e41776040518163ffffffff1660e01b815260040160206040518083038186803b1580156131ab57600080fd5b505afa1580156131bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131e39190615165565b6001600160a01b038516600090815260a66020526040902054105b6119c35760405162461bcd60e51b815260040161052490615d77565b6098805460408051610100810182526001600160a01b03898116825260208083018a8152938301898152606084018981524260808601908152600060a0870181815260c0880182815260e089018d815260018c018d559b90925287517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d81460088c0290810180546001600160a01b0319169290991691909117885598517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d8158a015593517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d81689015591517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d817880155517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d81887015590517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d819860155517f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d81a850155945180519495929491936133d1937f2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d81b90910192910190614fae565b5050506000858152609960209081526040808320805460018181018355918552838520018590556001600160a01b038a168452609a83529083208054918201815583529120018190556098805461346f91908390811061342d57fe5b6000918252602090912060089091020154609880546001600160a01b03909216918490811061345857fe5b906000526020600020906008020160020154614363565b5095945050505050565b600080613484612c74565b905061348f83611c42565b6001600160a01b0383166000908152609f60205260409020612e3b908263ffffffff613ac316565b6001600160a01b038216600090815260a660209081526040808320848452600190810190925290912054141561352b576001600160a01b038216600090815260a6602052604090205461351190600163ffffffff61419016565b6001600160a01b038316600090815260a660205260409020555b6001600160a01b038216600090815260a6602090815260408083208484526001908101909252909120546135649163ffffffff61419016565b6001600160a01b03909216600090815260a660209081526040808320938352600190930190522055565b600080613599612c74565b90506000609884815481106135aa57fe5b906000526020600020906008020160050154905080821015613600576135f7609885815481106135d657fe5b9060005260206000209060080201600301548261416b90919063ffffffff16565b925050506104f4565b60006136376098868154811061361257fe5b906000526020600020906008020160030154612f01848661419090919063ffffffff16565b905061368b61367e6098878154811061364c57fe5b90600052602060002090600802016003015461367260018561416b90919063ffffffff16565b9063ffffffff61384a16565b839063ffffffff61416b16565b93505050506104f4565b6000818152609b602052604081205460988054839190859081106136b557fe5b90600052602060002090600802016001015490506000609885815481106136d857fe5b90600052602060002090600802016002015490508260001415613718576000828152609e60205260409020600101549250826137185792506104f4915050565b825b60008111801561374757506098868154811061373257fe5b90600052602060002090600802016006015481105b156137c5576098868154811061375957fe5b90600052602060002090600802016005015481106137a4576000838152609e602090815260408083208484529091529020600181015490546137a19190612f0190859061384a565b91505b6000838152609e60209081526040808320938352929052206002015461371a565b50949350505050565b6000838152609c60205260409020612f8d90838363ffffffff61440716565b6001600160a01b0383166000908152609f60205260409020612f8d90838363ffffffff61440716565b6001600160a01b038416600090815260a06020908152604080832086845290915290206129ae90838363ffffffff61440716565b60008261385957506000611ccc565b8282028284828161386657fe5b0414611cc95760405162461bcd60e51b815260040161052490615920565b6000838152609d60205260409020612f8d90838363ffffffff6144c816565b6001600160a01b038416600090815260a16020908152604080832086845290915290206129ae90838363ffffffff6144c816565b3390565b60008281526065602052604090206138f9908263ffffffff61454616565b156107a6576139066138d7565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6001600160a01b0316600090815260a46020526040902054151590565b6000828152606560205260409020613985908263ffffffff61455b16565b156107a6576139926138d7565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b6000611cc98383614570565b6000611cc9836001600160a01b0384166145b5565b600080613a02612c74565b6001600160a01b038516600090815260a560205260409020600101549091508114613a3f5760405162461bcd60e51b8152600401610524906157f1565b6001600160a01b038416600090815260a56020526040902054831115613a775760405162461bcd60e51b815260040161052490615895565b6001600160a01b038416600090815260a56020526040902054613aa0908463ffffffff61419016565b6001600160a01b03909416600090815260a5602052604090209390935592915050565b6003820154600090613adc83600163ffffffff61416b16565b1015613afa5760405162461bcd60e51b8152600401610524906156af565b6003830154613b0b57506000611ccc565b81836003015411613bda5760038301545b828111613bc2576000818152600185016020908152604080832054918790528220546002870154613b5892916126f4919063ffffffff61416b16565b905080856002015414613b6d57600285018190555b60008281526020869052604090205415613b91576000828152602086905260408120555b600082815260018601602052604090205415613bb95760008281526001860160205260408120555b50600101613b1c565b50613bd482600163ffffffff61416b16565b60038401555b50506002015490565b609754604051633581777360e01b81526000916001600160a01b031690633581777390613c12906004016159f9565b60206040518083038186803b158015613c2a57600080fd5b505afa158015613c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c629190615062565b90506000613c6e612c74565b9050613c8181600163ffffffff61416b16565b60988481548110613c8e57fe5b9060005260206000209060080201600501819055506000609e600060988681548110613cb657fe5b9060005260206000209060080201600101548152602001908152602001600020600201541115613d2757609e600060988581548110613cf157fe5b60009182526020808320600160089093020191909101548352828101939093526040918201812060020154868252609b90935220555b613d8160988481548110613d3757fe5b90600052602060002090600802016001015460988581548110613d5657fe5b906000526020600020906008020160020154613d7c60018561416b90919063ffffffff16565b6145cd565b613de760988481548110613d9157fe5b6000918252602090912060089091020154609880546001600160a01b039092169186908110613dbc57fe5b906000526020600020906008020160020154613de260018561416b90919063ffffffff16565b6145ec565b613e6c60988481548110613df757fe5b6000918252602090912060089091020154609880546001600160a01b039092169186908110613e2257fe5b90600052602060002090600802016001015460988681548110613e4157fe5b906000526020600020906008020160020154613e6760018661416b90919063ffffffff16565b614615565b613ed260988481548110613e7c57fe5b6000918252602090912060089091020154609880546001600160a01b039092169186908110613ea757fe5b906000526020600020906008020160010154613ecd60018561416b90919063ffffffff16565b614649565b6000613f9e836001600160a01b031663f5b98f4160988781548110613ef357fe5b9060005260206000209060080201600301546040518263ffffffff1660e01b8152600401613f219190615324565b60206040518083038186803b158015613f3957600080fd5b505afa158015613f4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f719190615165565b60988681548110613f7e57fe5b90600052602060002090600802016002015461384a90919063ffffffff16565b9050613fdc60988581548110613fb057fe5b90600052602060002090600802016001015482613fd760018661416b90919063ffffffff16565b6146e7565b61404360988581548110613fec57fe5b6000918252602090912060089091020154609880546001600160a01b03909216918790811061401757fe5b9060005260206000209060080201600101548361403e60018761416b90919063ffffffff16565b614706565b6129ae6098858154811061405357fe5b6000918252602090912060089091020154609880546001600160a01b03909216918790811061407e57fe5b90600052602060002090600802016001015461473a565b303b1590565b600054610100900460ff16806140b457506140b4614095565b806140c2575060005460ff16155b6140de5760405162461bcd60e51b815260040161052490615a75565b600054610100900460ff16158015614109576000805460ff1961ff0019909116610100171660011790555b6141116147e2565b61411c60003361180c565b61259482614874565b60006104f1826148ea565b6001600160a01b038316600090815260a0602090815260408083208584529091528120614163908363ffffffff613ac316565b949350505050565b600082820183811015611cc95760405162461bcd60e51b8152600401610524906155f8565b6000611cc983836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506148ee565b60008183106141e45750808203611ccc565b7f5b70a077a991facb623c7b2ee44cc539dc6ba345b6636552b8ea97fbbd4d54198383604051614215929190615e94565b60405180910390a1506000611ccc565b6000620f424019821061423457fe5b50620f4240011090565b6129ae84848484600161491a565b614254614f7d565b6104f1826001614285565b6000818311156142775750620f424081830310611ccc565b50620f424082820310611ccc565b61428d614f7d565b600082116142ad5760405162461bcd60e51b815260040161052490615998565b6142b5614f7d565b6040518060400160405280858152602001848152509050611cc981614b01565b612f8d83848484600061491a565b6000611cc983836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614b4d565b61432d614f7d565b81518351611cc991614345919063ffffffff61384a16565b6020808501519086015161435e9163ffffffff61384a16565b614285565b60008061436e612c74565b6001600160a01b038516600090815260a560205260409020600101549091508111156143ba576001600160a01b038416600090815260a5602052604090208381556001018190556119c3565b6001600160a01b038416600090815260a5602052604090206001015481146143de57fe5b6001600160a01b038416600090815260a56020526040902054613aa0908463ffffffff61416b16565b61441881600163ffffffff61416b16565b8360030154111561443b5760405162461bcd60e51b8152600401610524906159c2565b60038301546144535760038301819055600483018190555b826004015481111561446757600483018190555b826003015481106144a9576000818152600184016020526040902054614493908363ffffffff61416b16565b6000828152600185016020526040902055612f8d565b60028301546144be908363ffffffff6141d216565b6002840155505050565b80836003015411156144ec5760405162461bcd60e51b8152600401610524906159c2565b60038301546144fd57600383018190555b600081815260018401602052604090205461451e908363ffffffff61416b16565b600082815260018501602052604090205560048301548114612f8d5760048301819055505050565b6000611cc9836001600160a01b038416614b84565b6000611cc9836001600160a01b038416614bce565b815460009082106145935760405162461bcd60e51b8152600401610524906153a4565b8260000182815481106145a257fe5b9060005260206000200154905092915050565b60009081526001919091016020526040902054151590565b6000838152609c60205260409020612f8d90838363ffffffff614c9416565b6001600160a01b0383166000908152609f60205260409020612f8d90838363ffffffff614c9416565b6001600160a01b038416600090815260a06020908152604080832086845290915290206129ae90838363ffffffff614c9416565b6001600160a01b038316600090815260a46020526040902054614691576001600160a01b038316600090815260a46020908152604080832084905560a25460a3909252909120555b6001600160a01b038316600090815260a460209081526040808320858452600101909152902054612f8d576001600160a01b0392909216600090815260a460209081526040808320938352600190930190522055565b6000838152609d60205260409020612f8d90838363ffffffff614d3716565b6001600160a01b038416600090815260a16020908152604080832086845290915290206129ae90838363ffffffff614d3716565b6001600160a01b038216600090815260a6602090815260408083208484526001019091529020546147a9576001600160a01b038216600090815260a6602052604090205461478f90600163ffffffff61416b16565b6001600160a01b038316600090815260a660205260409020555b6001600160a01b038216600090815260a6602090815260408083208484526001908101909252909120546135649163ffffffff61416b16565b600054610100900460ff16806147fb57506147fb614095565b80614809575060005460ff16155b6148255760405162461bcd60e51b815260040161052490615a75565b600054610100900460ff16158015614850576000805460ff1961ff0019909116610100171660011790555b614858614db1565b614860614db1565b8015611c4d576000805461ff001916905550565b6001600160a01b03811661489a5760405162461bcd60e51b815260040161052490615ae7565b6148ac816001600160a01b0316614e32565b6148c85760405162461bcd60e51b815260040161052490615961565b609780546001600160a01b0319166001600160a01b0392909216919091179055565b5490565b600081848411156149125760405162461bcd60e51b81526004016105249190615341565b505050900390565b600385015461493083600163ffffffff61416b16565b101561494e5760405162461bcd60e51b815260040161052490615435565b801561498857600384015461496a83600163ffffffff61416b16565b10156149885760405162461bcd60e51b815260040161052490615435565b6020830151835111156149ad5760405162461bcd60e51b81526004016105249061548e565b60038501546149bb57612f8a565b60006149c78684613ac3565b90506149da81600063ffffffff61425f16565b156149e55750612f8a565b6000614a0a8560200151612f0187600001518a6002015461384a90919063ffffffff16565b90508215614a3357614a3386614a2d838a600201546141d290919063ffffffff16565b86614407565b600287018190556000614a4d85600163ffffffff61416b16565b90505b876004015481116129a9576020808701518751600084815260018c019093526040832054614a899291612f01919063ffffffff61384a16565b90508415614ae557600082815260018a016020526040902054614ad390614ab6908363ffffffff6141d216565b600084815260018b0160205260409020549063ffffffff6141d216565b600083815260018a0160205260409020555b60008281526001808b0160205260409091209190915501614a50565b6000614b1582600001518360200151614e6b565b8251909150614b2a908263ffffffff6142e316565b82526020820151614b41908263ffffffff6142e316565b60209092019190915250565b60008183614b6e5760405162461bcd60e51b81526004016105249190615341565b506000838581614b7a57fe5b0495945050505050565b6000614b9083836145b5565b614bc657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611ccc565b506000611ccc565b60008181526001830160205260408120548015614c8a5783546000198083019190810190600090879083908110614c0157fe5b9060005260206000200154905080876000018481548110614c1e57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080614c4e57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050611ccc565b6000915050611ccc565b8083600301541115614cb85760405162461bcd60e51b81526004016105249061562f565b6003830154614cd05760038301819055600483018190555b8260040154811115614ce457600483018190555b82600301548110614d2257600081815260208490526040902054614d0e908363ffffffff61416b16565b600082815260208590526040902055612f8d565b60028301546144be908363ffffffff61416b16565b8083600301541115614d5b5760405162461bcd60e51b81526004016105249061562f565b6003830154614d6c57600383018190555b600081815260208490526040902054614d8b908363ffffffff61416b16565b60008281526020859052604090205560048301548114612f8d5760048301819055505050565b600054610100900460ff1680614dca5750614dca614095565b80614dd8575060005460ff16155b614df45760405162461bcd60e51b815260040161052490615a75565b600054610100900460ff16158015614860576000805460ff1961ff0019909116610100171660011790558015611c4d576000805461ff001916905550565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590614163575050151592915050565b6000828281811115614e8757614e818282614ebb565b90925090505b8015614eb357614e9d828263ffffffff614ebe16565b9150614ea98282614ebb565b9092509050614e87565b509392505050565b91565b6000611cc983836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f000000000000000081525060008183614f1c5760405162461bcd60e51b81526004016105249190615341565b50828481614f2657fe5b06949350505050565b60405180610100016040528060006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001606081525090565b604051806040016040528060008152602001600081525090565b604080518082019091526000808252602082015290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10614fef57805160ff191683800117855561501c565b8280016001018555821561501c579182015b8281111561501c578251825591602001919060010190615001565b5061502892915061502c565b5090565b61265b91905b808211156150285760008155600101615032565b600060208284031215615057578081fd5b8135611cc981615ece565b600060208284031215615073578081fd5b8151611cc981615ece565b60008060408385031215615090578081fd5b823561509b81615ece565b946020939093013593505050565b6000806000606084860312156150bd578081fd5b83356150c881615ece565b95602085013595506040909401359392505050565b6000602082840312156150ee578081fd5b81518015158114611cc9578182fd5b60006020828403121561510e578081fd5b5035919050565b60008060408385031215615127578182fd5b82359150602083013561513981615ece565b809150509250929050565b60008060408385031215615156578182fd5b50508035926020909101359150565b600060208284031215615176578081fd5b5051919050565b600080600080600060808688031215615194578081fd5b853594506020860135935060408601359250606086013567ffffffffffffffff808211156151c0578283fd5b81880189601f8201126151d1578384fd5b80359250818311156151e1578384fd5b8960208483010111156151f2578384fd5b6020810194505050809150509295509295909350565b60008151808452615220816020860160208601615ea2565b601f01601f19169290920160200192915050565b60008251615246818460208701615ea2565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b0394909416845260208401929092526040830152606082015260800190565b600061010060018060a01b038b1683528960208401528860408401528760608401528660808401528560a08401528460c08401528060e084015261530a81840185615208565b9b9a5050505050505050505050565b901515815260200190565b90815260200190565b602081016007831061533b57fe5b91905290565b600060208252611cc96020830184615208565b60208082526030908201527f4f6e6c7920746f6b656e20686f6c646572732063616e2063616e63656c20646560408201526f1b1959d85d1a5bdb881c995c5d595cdd60821b606082015260800190565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b6020808252602f908201527f416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e60408201526e0818591b5a5b881d1bc819dc985b9d608a1b606082015260800190565b6020808252601f908201527f43616e6e6f74207265647563652076616c756520696e20746865207061737400604082015260600190565b602080825260089082015267283ab734b9b432b960c11b604082015260600190565b60208082526027908201527f496e6372656173696e67206f662076616c756573206973206e6f7420696d706c604082015266195b595b9d195960ca1b606082015260800190565b60208082526028908201527f5468652064656c65676174696f6e20686173206265656e20616c7265616479206040820152671858d8d95c1d195960c21b606082015260800190565b60208082526038908201527f56616c696461746f72206973206e6f7420617574686f72697a656420746f206160408201527f63636570742064656c65676174696f6e20726571756573740000000000000000606082015260800190565b6020808252600f908201526e21b7b739ba30b73a39a437b63232b960891b604082015260600190565b60208082526035908201527f5468652076616c696461746f72206973206e6f742063757272656e746c7920616040820152746363657074696e67206e657720726571756573747360581b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b60208082526016908201527510d85b9b9bdd08185919081d1bc81d1a19481c185cdd60521b604082015260600190565b6020808252600b908201526a54696d6548656c7065727360a81b604082015260600190565b6020808252601190820152702a37b5b2b72630bab731b42637b1b5b2b960791b604082015260600190565b60208082526022908201527f43616e6e6f742063616c63756c6174652076616c756520696e207468652070616040820152611cdd60f21b606082015260800190565b60208082526022908201527f5468652064656c65676174696f6e2072657175657374206973206f7574646174604082015261195960f21b606082015260800190565b6020808252601e908201527f43616e2774207265647563652076616c756520696e2074686520706173740000604082015260600190565b60208082526030908201527f416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e60408201526f2061646d696e20746f207265766f6b6560801b606082015260800190565b6020808252601b908201527f43616e6e6f74207265717565737420756e64656c65676174696f6e0000000000604082015260600190565b6020808252602b908201527f546865726520617265206e6f2064656c65676174696f6e20726571756573747360408201526a040e8d0d2e640dadedce8d60ab1b606082015260800190565b6020808252818101527f4e6f207065726d697373696f6e7320746f206163636570742072657175657374604082015260600190565b6020808252600a908201526929b5b0b632aa37b5b2b760b11b604082015260600190565b6020808252601b908201527f556e6c6f636b696e6720616d6f756e7420697320746f6f206269670000000000604082015260600190565b60208082526034908201527f546f6b656e20686f6c64657220646f6573206e6f74206861766520656e6f75676040820152736820746f6b656e7320746f2064656c656761746560601b606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b60208082526017908201527f41646472657373206973206e6f7420636f6e7472616374000000000000000000604082015260600190565b60208082526010908201526f4469766973696f6e206279207a65726f60801b604082015260600190565b6020808252601d908201527f43616e6e6f742073756274726163742066726f6d207468652070617374000000604082015260600190565b60208082526017908201527f44656c65676174696f6e506572696f644d616e61676572000000000000000000604082015260600190565b60208082526025908201527f43616e6e6f742070757420736c617368696e67206576656e7420696e20746865604082015264081c185cdd60da1b606082015260800190565b6020808252602e908201527f436f6e747261637420696e7374616e63652068617320616c726561647920626560408201526d195b881a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252600a9082015269546f6b656e537461746560b01b604082015260600190565b60208082526022908201527f436f6e74726163744d616e616765722061646472657373206973206e6f742073604082015261195d60f21b606082015260800190565b6020808252603a908201527f546f6b656e20686f6c6465727320617265206f6e6c792061626c6520746f206360408201527f616e63656c2050524f504f5345442064656c65676174696f6e73000000000000606082015260800190565b60208082526027908201527f43616e6e6f74207365742064656c65676174696f6e20737461746520746f206160408201526618d8d95c1d195960ca1b606082015260800190565b60208082526025908201527f546869732064656c65676174696f6e20706572696f64206973206e6f7420616c6040820152641b1bddd95960da1b606082015260800190565b6020808252603e908201527f416d6f756e7420646f6573206e6f74206d656574207468652076616c6964617460408201527f6f722773206d696e696d756d2064656c65676174696f6e20616d6f756e740000606082015260800190565b60208082526031908201527f5468652064656c65676174696f6e20686173206265656e2063616e63656c6c656040820152703210313c903a37b5b2b7103437b63232b960791b606082015260800190565b60208082526019908201527f44656c65676174696f6e20646f6573206e6f7420657869737400000000000000604082015260600190565b60208082526029908201527f5065726d697373696f6e2064656e69656420746f207265717565737420756e6460408201526832b632b3b0ba34b7b760b91b606082015260800190565b60208082526019908201527f4d6573736167652073656e64657220697320696e76616c696400000000000000604082015260600190565b6020808252601e908201527f4c696d6974206f662076616c696461746f727320697320726561636865640000604082015260600190565b60208082526010908201526f56616c696461746f725365727669636560801b604082015260600190565b6020808252602f908201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560408201526e103937b632b9903337b91039b2b63360891b606082015260800190565b60006020825260018060a01b038351166020830152602083015160408301526040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015160e083015260e08301516101008081850152506119c0610120840182615208565b918252602082015260400190565b60005b83811015615ebd578181015183820152602001615ea5565b838111156129ae5750506000910152565b6001600160a01b0381168114611c4d57600080fdfea264697066735822122088c330383e3eca2d1dd22b553b5b109eafd04aa92a00d266c363bddb03bdaf0064736f6c634300060a0033

Deployed Bytecode Sourcemap

2253:33346:8:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10230:156;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;18180:181;;;;;;;;;:::i;:::-;;;;;;;;6907:465;;;;;;;;;:::i;19200:142::-;;;;;;;;;:::i;:::-;;6580:176;;;;;;;;;:::i;4822:54::-;;;;;;;;;:::i;17134:571::-;;;;;;;;;:::i;8031:1965::-;;;;;;;;;:::i;3900:112:0:-;;;;;;;;;:::i;6762:139:8:-;;;;;;;;;:::i;13944:2609::-;;;;;;;;;:::i;4262:223:0:-;;;;;;;;;:::i;21120:180:8:-;;;;;;;;;:::i;:::-;;;;;;;;5436:205:0;;;;;;;;;:::i;18546:154:8:-;;;;;;;;;:::i;17711:240::-;;;;;;;;;:::i;19581:1206::-;;;;;;;;;:::i;:::-;;;;;;;;4922:54;;;;;;;;;:::i;19348:92::-;;;;;;;;;:::i;20793:321::-;;;;;;;;;:::i;3583:136:0:-;;;;;;;;;:::i;:::-;;;;;;;;2568:137;;;;;;;;;:::i;18367:173:8:-;;;;;;;;;:::i;10768:586::-;;;;;;;;;:::i;18990:204::-;;;;;;;;;:::i;1758:49:0:-;;;:::i;1187:38:31:-;;;:::i;11734:1868:8:-;;;;;;;;;:::i;4747:31::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;18853:131;;;;;;;;;:::i;2873:125:0:-;;;;;;;;;:::i;4719:226::-;;;;;;;;;:::i;18706:141:8:-;;;;;;;;;:::i;17957:217::-;;;;;;;;;:::i;10230:156::-;10323:4;10346:33;10372:6;10346:25;:33::i;:::-;10339:40;;10230:156;;;;:::o;18180:181::-;18289:17;;:::i;:::-;6508:11;:18;18266:12;;6493:33;;6485:71;;;;-1:-1:-1;;;6485:71:8;;;;;;;;;;;;;;;;;18329:11:::1;18341:12;18329:25;;;;;;;;;::::0;;;::::1;::::0;;;;18322:32:::1;::::0;;::::1;::::0;;::::1;::::0;;18329:25:::1;::::0;;;::::1;::::0;;::::1;18322:32:::0;;-1:-1:-1;;;;;18322:32:8::1;::::0;;;;;::::1;::::0;;;::::1;::::0;::::1;::::0;;::::1;::::0;;;;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;::::1;::::0;;;;;;::::1;;::::0;;::::1;-1:-1:-1::0;;18322:32:8;;;::::1;;;::::0;::::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;;;;;18329:25;;18322:32;;;;;;;;::::1;::::0;;;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;6566:1;18180:181:::0;;;;:::o;6907:465::-;1722:230:31;;;;;;;;;;;-1:-1:-1;;;1722:230:31;;;;;;;;1796:15;;1832:30;;-1:-1:-1;;1868:10:31;;-1:-1:-1;;;;;1796:15:31;;;;:25;;1832:30;;1722:230;;1832:30;;;;;;;;;;;;;;;;1822:41;;;;;;1796:68;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1796:82:31;;:96;;;;1882:10;:8;:10::i;:::-;1775:159;;;;-1:-1:-1;;;1775:159:31;;;;;;;;;7098:39:8::1;7140:40;7173:6;7140:32;:40::i;:::-;-1:-1:-1::0;;;;;7211:46:8;::::1;;::::0;;;:38:::1;:46;::::0;;;;;;;:59;;;;;;;;7098:82;;-1:-1:-1;7211:107:8::1;::::0;7312:5;7211:107:::1;:100;:107;:::i;:::-;7190:128;;7328:37;7349:15;7328:20;:37::i;:::-;1944:1:31;6907:465:8::0;;;;;;:::o;19200:142::-;19269:66;19290:44;19320:6;19328:5;19290:29;:44::i;:::-;19269:20;:66::i;:::-;19200:142;;:::o;6580:176::-;6661:4;6684:65;6717:11;6730:18;:16;:18::i;4822:54::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;17134:571::-;1722:230:31;;;;;;;;;;;-1:-1:-1;;;1722:230:31;;;;;;;;1796:15;;1832:30;;1722:230;;1868:10;;-1:-1:-1;;;;;1796:15:31;;;;:25;;1832:30;;1722:230;;1832:30;;;;;;;;;;;;;;;;1822:41;;;;;;1796:68;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1796:82:31;;:96;;;;1882:10;:8;:10::i;:::-;1775:159;;;;-1:-1:-1;;;1775:159:31;;;;;;;;;17222:17:8::1;17242:18;:16;:18::i;:::-;17222:38;;17270:41;;:::i;:::-;17326:34;::::0;;;:21:::1;:34;::::0;;;;:68:::1;::::0;17373:6;17381:12;17326:68:::1;:46;:68;:::i;:::-;17404:43;::::0;;;:30:::1;:43;::::0;;;;17270:124;;-1:-1:-1;17404:85:8::1;::::0;17270:124;17476:12;17404:85:::1;:58;:85;:::i;:::-;17517:32;::::0;;;:19:::1;:32;::::0;;;;17499:78:::1;::::0;17551:11;17564:12;17499:17:::1;:78::i;:::-;17601:96;::::0;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;;::::1;::::0;;;;;;;;;17587:8:::1;:111:::0;;::::1;::::0;::::1;::::0;;-1:-1:-1;17587:111:8;;;;;;;;;::::1;::::0;;::::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;17134:571:8:o;8031:1965::-;8247:15;;:47;;-1:-1:-1;;;8247:47:8;;8194:33;;-1:-1:-1;;;;;8247:15:8;;:27;;:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8392:15;;:54;;-1:-1:-1;;;8392:54:8;;8194:101;;-1:-1:-1;8305:47:8;;-1:-1:-1;;;;;8392:15:8;;;;:27;;:54;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8486:15;;:41;;-1:-1:-1;;;8486:41:8;;8305:142;;-1:-1:-1;8457:18:8;;-1:-1:-1;;;;;8486:15:8;;;;:27;;:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8573:15;;:41;;-1:-1:-1;;;8573:41:8;;8457:71;;-1:-1:-1;8538:21:8;;-1:-1:-1;;;;;8573:15:8;;;;:27;;:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8647:60;;-1:-1:-1;;;8647:60:8;;8538:77;;-1:-1:-1;;;;;;8647:39:8;;;;;:60;;8687:11;;8700:6;;8647:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8626:160;;;;-1:-1:-1;;;8626:160:8;;;;;;;;;8817:51;;-1:-1:-1;;;8817:51:8;;-1:-1:-1;;;;;8817:38:8;;;;;:51;;8856:11;;8817:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8796:145;;;;-1:-1:-1;;;8796:145:8;;;;;;;;;8972:67;;-1:-1:-1;;;8972:67:8;;-1:-1:-1;;;;;8972:49:8;;;;;:67;;9022:16;;8972:67;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8951:142;;;;-1:-1:-1;;;8951:142:8;;;;;;;;;9124:52;;-1:-1:-1;;;9124:52:8;;-1:-1:-1;;;;;9124:39:8;;;;;:52;;9164:11;;9124:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9103:143;;;;-1:-1:-1;;;9103:143:8;;;;;;;;;9256:52;9284:10;9296:11;9256:27;:52::i;:::-;;9319:39;9361:44;9394:10;9361:32;:44::i;:::-;9319:86;;9416:17;9436:132;9464:10;9488:11;9513:6;9533:16;9563:4;;9436:132;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;9436:14:8;;-1:-1:-1;;;9436:132:8:i;:::-;9416:152;;9623:18;9644:10;-1:-1:-1;;;;;9644:20:8;;9665:10;9644:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9623:53;;9686:27;9716:10;-1:-1:-1;;;;;9716:51:8;;9768:10;9716:63;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9686:93;;9814:22;9797:13;:39;;9789:104;;;;-1:-1:-1;;;9789:104:8;;;;;;;;;9909:32;9928:12;9909:32;;;;;;;;;;;;;;;9952:37;9973:15;9952:20;:37::i;:::-;8031:1965;;;;;;;;;;;;;:::o;3900:112:0:-;3957:7;3983:12;;;:6;:12;;;;;:22;;;;3900:112::o;6762:139:8:-;6833:4;6856:38;6887:6;6856:30;:38::i;13944:2609::-;6508:11;:18;14023:12;;6493:33;;6485:71;;;;-1:-1:-1;;;6485:71:8;;;;;;;;;14081:15:::1;14055:22;14064:12;14055:8;:22::i;:::-;:41;;;;;;;;;14047:81;;;;-1:-1:-1::0;;;14047:81:8::1;;;;;;;;;14191:15;::::0;:47:::1;::::0;-1:-1:-1;;;14191:47:8;;14138:33:::1;::::0;-1:-1:-1;;;;;14191:15:8::1;::::0;:27:::1;::::0;:47:::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14138:101;;14306:10;-1:-1:-1::0;;;;;14270:46:8::1;:11;14282:12;14270:25;;;;;;;;;::::0;;;::::1;::::0;;;::::1;::::0;;::::1;;:32:::0;-1:-1:-1;;;;;14270:32:8::1;:46;::::0;:215:::1;;-1:-1:-1::0;14333:51:8::1;::::0;-1:-1:-1;;;14333:51:8;;-1:-1:-1;;;;;14333:39:8;::::1;::::0;::::1;::::0;:51:::1;::::0;14373:10:::1;::::0;14333:51:::1;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:151;;;;-1:-1:-1::0;14441:43:8::1;::::0;-1:-1:-1;;;14441:43:8;;-1:-1:-1;;;;;14441:31:8;::::1;::::0;::::1;::::0;:43:::1;::::0;14473:10:::1;::::0;14441:43:::1;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14400:11;14412:12;14400:25;;;;;;;;;;;;;;;;;;:37;;;:84;14333:151;14249:294;;;;-1:-1:-1::0;;;14249:294:8::1;;;;;;;;;14609:15;::::0;:48:::1;::::0;-1:-1:-1;;;14609:48:8;;14553:35:::1;::::0;-1:-1:-1;;;;;14609:15:8::1;::::0;:27:::1;::::0;:48:::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14755:15;::::0;:54:::1;::::0;-1:-1:-1;;;14755:54:8;;14553:105;;-1:-1:-1;14668:47:8::1;::::0;-1:-1:-1;;;;;14755:15:8;;::::1;::::0;:27:::1;::::0;:54:::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14668:142;;14820:150;14877:11;14889:12;14877:25;;;;;;;;;::::0;;;::::1;::::0;;;::::1;::::0;;::::1;;:32:::0;14923:11:::1;:25:::0;;-1:-1:-1;;;;;14877:32:8;;::::1;::::0;14935:12;;14923:25;::::1;;;;;;;;;;;;;;;:37;;;14820:43;:150::i;:::-;14980:29;14998:10;14980:17;:29::i;:::-;15056:42;15085:12;15056:28;:42::i;:::-;15019:11;15031:12;15019:25;;;;;;;;;;;;;;;;;;:34;;:79;;;;15108:24;15135:53;15175:12;15135:39;:53::i;:::-;15108:80;;15198:164;15243:11;15255:12;15243:25;;;;;;;;;;;;;;;;;;:37;;;15294:19;15327:11;15339:12;15327:25;;;;;;;;;;;;;;;;;;:34;;;15198:31;:164::i;:::-;15372:156;15414:11;15426:12;15414:25;;;;;;;;;;;;;;;;;;:32;;;;;;;;;;-1:-1:-1::0;;;;;15414:32:8::1;15460:19;15493:11;15505:12;15493:25;;;;;;;;;;;;;;;;;;:34;;;15372:28;:156::i;:::-;15538:218;15591:11;15603:12;15591:25;;;;;;;;;::::0;;;::::1;::::0;;;::::1;::::0;;::::1;;:32:::0;15637:11:::1;:25:::0;;-1:-1:-1;;;;;15591:32:8;;::::1;::::0;15649:12;;15637:25;::::1;;;;;;;;;;;;;;;:37;;;15688:19;15721:11;15733:12;15721:25;;;;;;;;;;;;;;;;;;:34;;;15538:39;:218::i;:::-;15766:20;15789:122;15813:23;-1:-1:-1::0;;;;;15813:40:8::1;;15867:11;15879:12;15867:25;;;;;;;;;;;;;;;;;;:42;;;15813:97;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15789:19:::0;;:122:::1;:23;:122;:::i;:::-;15766:145;;15921:169;15975:11;15987:12;15975:25;;;;;;;;;;;;;;;;;;:37;;;16026:15;16055:11;16067:12;16055:25;;;;;;;;;;;;;;;;;;:34;;;15921:40;:169::i;:::-;16100:223;16162:11;16174:12;16162:25;;;;;;;;;::::0;;;::::1;::::0;;;::::1;::::0;;::::1;;:32:::0;16208:11:::1;:25:::0;;-1:-1:-1;;;;;16162:32:8;;::::1;::::0;16220:12;;16208:25;::::1;;;;;;;;;;;;;;;:37;;;16259:15;16288:11;16300:12;16288:25;;;;;;;;;;;;;;;;;;:34;;;16100:48;:223::i;:::-;16333:17;-1:-1:-1::0;;;;;16333:42:8::1;;16389:11;16401:12;16389:25;;;;;;;;;;;;;;;;;;:32;;;;;;;;;;-1:-1:-1::0;;;;;16389:32:8::1;16435:12;16461:11;16473:12;16461:25;;;;;;;;;;;;;;;;;;:34;;;16333:163;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;16511:35;16533:12;16511:35;;;;;;;;;;;;;;;6566:1;;;;;13944:2609:::0;;:::o;4262:223:0:-;4353:12;;;;:6;:12;;;;;:22;;;4345:45;;4377:12;:10;:12::i;4345:45::-;4337:105;;;;-1:-1:-1;;;4337:105:0;;;;;;;;;4453:25;4464:4;4470:7;4453:10;:25::i;21120:180:8:-;21188:4;21211:22;21226:6;21211:14;:22::i;:::-;:82;;;;-1:-1:-1;;21278:8:8;:15;-1:-1:-1;;;;;21237:38:8;;;;;;;;:30;:38;;;;;;:56;;21120:180::o;5436:205:0:-;5533:12;:10;:12::i;:::-;-1:-1:-1;;;;;5522:23:0;:7;-1:-1:-1;;;;;5522:23:0;;5514:83;;;;-1:-1:-1;;;5514:83:0;;;;;;;;;5608:26;5620:4;5626:7;5608:11;:26::i;18546:154:8:-;18628:4;18651:35;;;:22;:35;;;;;:42;;18546:154::o;17711:240::-;1722:230:31;;;;;;;;;;;-1:-1:-1;;;1722:230:31;;;;;;;;1796:15;;1832:30;;-1:-1:-1;;1868:10:31;;-1:-1:-1;;;;;1796:15:31;;;;:25;;1832:30;;1722:230;;1832:30;;;;;;;;;;;;;;;;1822:41;;;;;;1796:68;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1796:82:31;;:96;;;;1882:10;:8;:10::i;:::-;1775:159;;;;-1:-1:-1;;;1775:159:31;;;;;;;;;17866:43:8::1;::::0;;;:30:::1;:43;::::0;;;;:78:::1;::::0;17938:5;17866:78:::1;:71;:78;:::i;:::-;17859:85;;1944:1:31;17711:240:8::0;;;;;:::o;19581:1206::-;6508:11;:18;19675:11;;19652:12;;6493:33;;6485:71;;;;-1:-1:-1;;;6485:71:8;;;;;;;;;19702:11:::1;19714:12;19702:25;;;;;;;;;;;;;;;;;;:33;;;19739:1;19702:38;19698:1083;;;19760:11;19772:12;19760:25;;;;;;;;;;;;;;;;;;:34;;;19798:1;19760:39;19756:457;;;19857:15;::::0;:42:::1;::::0;-1:-1:-1;;;19857:42:8;;19819:23:::1;::::0;-1:-1:-1;;;;;19857:15:8::1;::::0;:27:::1;::::0;:42:::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19819:81;;19944:11;-1:-1:-1::0;;;;;19944:28:8::1;;19973:11;19985:12;19973:25;;;;;;;;;;;;;;;;;;:33;;;19944:63;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19922:18;:16;:18::i;:::-;:85;19918:221;;;20038:14;20031:21;;;;;19918:221;20106:14;20099:21;;;;;19756:457;20184:14;20177:21;;;;19698:1083;20268:11;20280:12;20268:25;;;;;;;;;;;;;;;;;;:33;;;20247:18;:16;:18::i;:::-;:54;20243:528;;;20328:14;20321:21;;;;20243:528;20385:11;20397:12;20385:25;;;;;;;;;;;;;;;;;;:34;;;20423:1;20385:39;20381:376;;;20455:15;20448:22;;;;20381:376;20542:11;20554:12;20542:25;;;;;;;;;;;;;;;;;;:34;;;20521:18;:16;:18::i;:::-;:55;20517:222;;;20611:28;20604:35;;;;20517:222;20701:15;20694:22;;;;4922:54:::0;;;;;;;;;;;;;;;;;;19348:92;19408:25;19423:6;19431:1;19408:14;:25::i;:::-;19348:92;:::o;20793:321::-;20869:4;20885:17;20905:18;:16;:18::i;:::-;-1:-1:-1;;;;;20937:35:8;;;;;;:27;:35;;;;;:41;;;20885:38;;-1:-1:-1;20937:56:8;-1:-1:-1;20933:175:8;;;21016:1;21009:8;;;;;20933:175;-1:-1:-1;;;;;;;21055:35:8;;;;;;:27;:35;;;;;:42;21048:49;;3583:136:0;3656:7;3682:12;;;:6;:12;;;;;:30;;3706:5;3682:30;:23;:30;:::i;:::-;3675:37;;3583:136;;;;;:::o;2568:137::-;2637:4;2660:12;;;:6;:12;;;;;:38;;2690:7;2660:38;:29;:38;:::i;18367:173:8:-;-1:-1:-1;;;;;18479:29:8;;;;18456:4;18479:29;;;:21;:29;;;;;;;;:54;;;:41;;;;:54;;;;;18367:173::o;10768:586::-;6508:11;:18;10851:12;;6493:33;;6485:71;;;;-1:-1:-1;;;6485:71:8;;;;;;;;;10897:11:::1;10909:12;10897:25;;;;;;;;;::::0;;;::::1;::::0;;;::::1;::::0;;::::1;;:32:::0;-1:-1:-1;;;;;10897:32:8::1;10883:10;:46;10875:107;;;;-1:-1:-1::0;;;10875:107:8::1;;;;;;;;;11026:14;11000:22;11009:12;11000:8;:22::i;:::-;:40;;;;;;;;;10992:111;;;;-1:-1:-1::0;;;10992:111:8::1;;;;;;;;;11151:18;:16;:18::i;:::-;11114:11;11126:12;11114:25;;;;;;;;;;;;;;;;;;:34;;:55;;;;11179:107;11219:11;11231:12;11219:25;;;;;;;;;::::0;;;::::1;::::0;;;::::1;::::0;;::::1;;:32:::0;11253:11:::1;:25:::0;;-1:-1:-1;;;;;11219:32:8;;::::1;::::0;11265:12;;11253:25;::::1;;;;;;;;;;;;;;;:32;;;11179:39;:107::i;:::-;;11302:45;11334:12;11302:45;;;;;;;;;;;;;;;10768:586:::0;;:::o;18990:204::-;1722:230:31;;;;;;;;;;;-1:-1:-1;;;1722:230:31;;;;;;;;1796:15;;1832:30;;-1:-1:-1;;1868:10:31;;-1:-1:-1;;;;;1796:15:31;;;;:25;;1832:30;;1722:230;;1832:30;;;;;;;;;;;;;;;;1822:41;;;;;;1796:68;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1796:82:31;;:96;;;;1882:10;:8;:10::i;:::-;1775:159;;;;-1:-1:-1;;;1775:159:31;;;;;;;;;19128:34:8::1;::::0;;;:21:::1;:34;::::0;;;;:59:::1;::::0;19181:5;19128:59:::1;:52;:59;:::i;1758:49:0:-:0;1803:4;1758:49;:::o;1187:38:31:-;;;-1:-1:-1;;;;;1187:38:31;;:::o;11734:1868:8:-;6508:11;:18;11817:12;;6493:33;;6485:71;;;;-1:-1:-1;;;6485:71:8;;;;;;;;;11894:15:::1;::::0;:47:::1;::::0;-1:-1:-1;;;11894:47:8;;11841:33:::1;::::0;-1:-1:-1;;;;;11894:15:8::1;::::0;:27:::1;::::0;:47:::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11841:101;;11973:16;-1:-1:-1::0;;;;;11973:42:8::1;;12016:10;12028:11;12040:12;12028:25;;;;;;;;;;;;;;;;;;:37;;;11973:93;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11952:163;;;;-1:-1:-1::0;;;11952:163:8::1;;;;;;;;;12125:100;12153:11;12165:12;12153:25;;;;;;;;;::::0;;;::::1;::::0;;;::::1;::::0;;::::1;;:32:::0;12187:11:::1;:25:::0;;-1:-1:-1;;;;;12153:32:8;;::::1;::::0;12199:12;;12187:25;::::1;;;;;;;;;;;;;;;:37;;;12125:27;:100::i;:::-;;12244:18;12265:22;12274:12;12265:8;:22::i;:::-;12244:43:::0;-1:-1:-1;12317:14:8::1;12301:12;:30;;;;;;;;;12297:610;;12367:14;12351:12;:30;;;;;;;;;:81;;;-1:-1:-1::0;12417:15:8::1;12401:12;:31;;;;;;;;;12351:81;:145;;;-1:-1:-1::0;12468:28:8::1;12452:12;:44;;;;;;;;;12351:145;:196;;;-1:-1:-1::0;12532:15:8::1;12516:12;:31;;;;;;;;;12351:196;12347:550;;;12579:50;;-1:-1:-1::0;;;12579:50:8::1;;;;;;;;12347:550;12670:14;12654:12;:30;;;;;;;;;12650:247;;;12704:59;;-1:-1:-1::0;;;12704:59:8::1;;;;;;;;12650:247;12804:14;12788:12;:30;;;;;;;;;12784:113;;;12838:44;;-1:-1:-1::0;;;12838:44:8::1;;;;;;;;12784:113;12940:14;12924:12;:30;;;;;;;;;12916:82;;;;-1:-1:-1::0;;;12916:82:8::1;;;;;;;;;13073:15;::::0;:48:::1;::::0;-1:-1:-1;;;13073:48:8;;13017:35:::1;::::0;-1:-1:-1;;;;;13073:15:8::1;::::0;:27:::1;::::0;:48:::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13017:105;;13133:39;13175:66;13208:11;13220:12;13208:25;;;;;;;;;::::0;;;::::1;::::0;;;::::1;::::0;;::::1;;:32:::0;-1:-1:-1;;;;;13208:32:8::1;13175;:66::i;:::-;13133:108;;13252:33;13272:12;13252:19;:33::i;:::-;13296:17;-1:-1:-1::0;;;;;13296:37:8::1;;13347:11;13359:12;13347:25;;;;;;;;;;;;;;;;;;:32;;;;;;;;;;-1:-1:-1::0;;;;;13347:32:8::1;13393:12;13419:11;13431:12;13419:25;;;;;;;;;;;;;;;;;;:32;;;13465:11;13477:12;13465:25;;;;;;;;;;;;;;;;;;:33;;;13296:203;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;13510:37;13531:15;13510:20;:37::i;:::-;13563:32;13582:12;13563:32;;;;;;;;;;;;;;;6566:1;;;;11734:1868:::0;;:::o;4747:31::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4747:31:8;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;4747:31:8;;;;-1:-1:-1;4747:31:8;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;18853:131::-;1024:12:23;;;;;;;;:31;;;1040:15;:13;:15::i;:::-;1024:47;;;-1:-1:-1;1060:11:23;;;;1059:12;1024:47;1016:106;;;;-1:-1:-1;;;1016:106:23;;;;;;;;;1129:19;1152:12;;;;;;1151:13;1170:80;;;;1198:12;:19;;-1:-1:-1;;;;1198:19:23;;;;;1225:18;1213:4;1225:18;;;1170:80;18937:40:8::1;18960:16;18937:22;:40::i;:::-;1268:14:23::0;1264:55;;;1307:5;1292:20;;-1:-1:-1;;1292:20:23;;;18853:131:8;;:::o;2873:125:0:-;2936:7;2962:12;;;:6;:12;;;;;:29;;:27;:29::i;4719:226::-;4811:12;;;;:6;:12;;;;;:22;;;4803:45;;4835:12;:10;:12::i;4803:45::-;4795:106;;;;-1:-1:-1;;;4795:106:0;;;;;;;;18706:141:8;-1:-1:-1;;;;;18806:27:8;18783:4;18806:27;;;:19;:27;;;;;:34;;18706:141::o;17957:217::-;18062:4;18085:82;18127:6;18135:11;18148:18;:16;:18::i;:::-;18085:41;:82::i;27124:179::-;27192:4;27215:81;27258:37;27288:6;27258:29;:37::i;:::-;27215:38;27246:6;27215:30;:38::i;:::-;:42;:81;:42;:81;:::i;3104:112:31:-;3147:4;3170:39;3147:4;3198:10;3170:7;:39::i;:::-;3163:46;;3104:112;;:::o;31883:192:8:-;31966:39;32028:40;32058:6;32066:1;32028:29;:40::i;3265:925:30:-;3359:4;3379:8;:30;;;3413:1;3379:35;3375:74;;;-1:-1:-1;3437:1:30;3430:8;;3375:74;3497:5;3463:8;:30;;;:39;3459:686;;3532:30;;;;3518:558;3569:5;3564:1;:10;3518:558;;3599:14;3677:24;;;:21;;;;:24;;;;;;;;;3645:19;;;;;;;3616:86;;:49;;3645:19;3616:14;;;;3599;;3631:8;;3699:1;;3631:8;:5;:8;:::i;:::-;3616:24;;;;;;;;;;;;:28;;:49;;;;:::i;:::-;:60;:86;:60;:86;:::i;:::-;3724:17;;;;:14;;;:17;;;;;;3599:103;;-1:-1:-1;3724:30:30;;3720:106;;3778:17;;;;:14;;;:17;;;;;:29;;;3720:106;3869:1;3847:19;;;;;;;;;;;:23;3843:96;;3901:16;:19;;;;;;;;;;3894:26;3843:96;3987:1;3960:24;;;:21;;;:24;;;;;;:28;3956:106;;4019:24;;;;:21;;;:24;;;;;4012:31;3956:106;-1:-1:-1;3576:3:30;;3518:558;;;-1:-1:-1;4122:12:30;:5;4132:1;4122:12;:9;:12;:::i;:::-;4089:30;;;:45;3459:686;-1:-1:-1;4162:21:30;;;;:14;;;;;:21;;;;;;;3265:925::o;32081:889:8:-;32199:15;;:39;;-1:-1:-1;;;32199:39:8;;32170:17;;-1:-1:-1;;;;;32199:15:8;;:27;;:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;32170:69;-1:-1:-1;32249:22:8;;;32331:515;32352:15;:22;32348:1;:26;32331:515;;;32428:14;-1:-1:-1;;;;;32399:43:8;:15;32415:1;32399:18;;;;;;;;;;;;;;:25;;;-1:-1:-1;;;;;32399:43:8;;32395:441;;32466:22;;32462:125;;32512:56;;-1:-1:-1;;;32512:56:8;;-1:-1:-1;;;;;32512:20:8;;;;;:56;;32533:14;;32549:18;;32512:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;32462:125;32621:15;32637:1;32621:18;;;;;;;;;;;;;;:25;;;32604:42;;32685:15;32701:1;32685:18;;;;;;;;;;;;;;:26;;;32664:47;;32395:441;;;32771:50;32794:15;32810:1;32794:18;;;;;;;;;;;;;;:26;;;32771:18;:22;;:50;;;;:::i;:::-;32750:71;;32395:441;32376:3;;32331:515;;;-1:-1:-1;32859:22:8;;32855:109;;32897:56;;-1:-1:-1;;;32897:56:8;;-1:-1:-1;;;;;32897:20:8;;;;;:56;;32918:14;;32934:18;;32897:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;32855:109;32081:889;;;;:::o;30271:1606::-;30363:39;30422:29;30444:6;30422:21;:29::i;:::-;30418:1453;;;-1:-1:-1;;;;;30480:38:8;;30467:10;30480:38;;;:30;:38;;;;;;30543:8;:15;30576:9;;;;;:35;;-1:-1:-1;30608:3:8;30589:16;:5;30599;30589:16;:9;:16;:::i;:::-;:22;30576:35;30572:96;;;30637:16;:5;30647;30637:16;:9;:16;:::i;:::-;30631:22;;30572:96;30720:14;:3;30728:5;30720:14;:7;:14;:::i;:::-;30699:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;30681:54:8;-1:-1:-1;30762:5:8;30781:1022;30796:3;30788:5;:11;30781:1022;;;30828:16;30847:8;30856:5;30847:15;;;;;;;;;;;;;;;;;;:27;;;30828:46;;30892:10;30905:8;30914:5;30905:15;;;;;;;;;;;;;;;;;;:21;;;30892:34;;30944:13;30960:69;31002:6;31010:11;31023:5;30960:41;:69::i;:::-;30944:85;-1:-1:-1;31051:23:8;30944:85;31072:1;31051:23;:20;:23;:::i;:::-;31047:742;;;-1:-1:-1;;;;;31211:26:8;;;;;;:18;:26;;;;;31263:8;:15;;31098:232;;31211:26;31263:8;31272:5;;31263:15;;;;;;;;;;;;;;31098:232;;;;;;;;31263:15;;;;;;;31098:232;;;;;;;;;;;-1:-1:-1;;;;;31098:37:8;;;;:29;:37;;;;;:50;;;;;;;;;;:232;31324:5;31098:232;:87;:232;:::i;:::-;31352:167;31452:8;31461:5;31452:15;;;;;;;;;;;;;;;;31352:167;;;;;;;;31452:15;;;;;;;31352:167;;;;;;;;;;;-1:-1:-1;;;;;31352:46:8;;;;:38;:46;;;;;:59;;;;;;;;;;31513:5;31352:167;:74;:167;:::i;:::-;31584:6;31541:15;31557:16;:5;31567;31557:16;:9;:16;:::i;:::-;31541:33;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;31541:49:8;;;;;31680:90;31700:69;31742:6;31750:11;31763:5;31700:41;:69::i;:::-;31680:8;;:90;:19;:90;:::i;:::-;31612:15;31628:16;:5;31638;31628:16;:9;:16;:::i;:::-;31612:33;;;;;;;;;;;;;;:41;;:158;;;;;31047:742;30781:1022;;;30801:7;;;;;30781:1022;;;-1:-1:-1;;;;;;31816:38:8;;;;;;:30;:38;;;;;:44;-1:-1:-1;30271:1606:8;;;;:::o;26918:200::-;27022:15;;:42;;-1:-1:-1;;;27022:42:8;;26968:4;;;;-1:-1:-1;;;;;27022:15:8;;;;:27;;:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26984:81;;27082:11;-1:-1:-1;;;;;27082:27:8;;:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27075:36;;;26918:200;:::o;7629:884:30:-;7750:29;;:::i;:::-;7819:30;;;;7803:12;:5;7813:1;7803:12;:9;:12;:::i;:::-;:46;;7795:90;;;;-1:-1:-1;;;7795:90:30;;;;;;;;;7899:30;;;;7895:104;;7957:31;7986:1;7957:28;:31::i;:::-;7950:38;;;;7895:104;8008:10;8021:34;8039:8;8049:5;8021:17;:34::i;:::-;8008:47;-1:-1:-1;8069:27:30;8008:47;8094:1;8069:27;:24;:27;:::i;:::-;8065:96;;;8119:31;8148:1;8119:28;:31::i;:::-;8112:38;;;;;8065:96;8186:6;8206:14;;;8202:60;;;-1:-1:-1;8246:5:30;8202:60;8272:49;;:::i;:::-;8336:62;8365:25;:5;8382:7;8365:25;:16;:25;:::i;:::-;8392:5;8336:28;:62::i;:::-;8272:126;;8408:62;8433:8;8443:19;8464:5;8408:24;:62::i;:::-;8487:19;-1:-1:-1;;;7629:884:30;;;;;;:::o;4196:1041::-;4382:30;;;;4366:12;:5;4376:1;4366:12;:9;:12;:::i;:::-;:46;;4358:89;;;;-1:-1:-1;;;4358:89:30;;;;;;;;;4511:31;;;;4478:29;;:64;;4457:141;;;;-1:-1:-1;;;4457:141:30;;;;;;;;;4612:30;;;;4608:72;;4663:7;;4608:72;4689:10;4702:44;4730:8;4740:5;4702:27;:44::i;:::-;4689:57;-1:-1:-1;4760:27:30;4689:57;4785:1;4760:27;:24;:27;:::i;:::-;4756:64;;;4803:7;;;4756:64;4941:31;;;;;4893:29;;;4854:21;;;:14;;;:21;;;;;;;;:119;;:69;;:21;:69;:38;:69;:::i;:::-;:86;:119;:86;:119;:::i;:::-;4830:21;;;;:14;;;:21;;;;;:143;;;;4998:12;4845:5;5008:1;4998:12;:9;:12;:::i;:::-;4989:21;;4984:247;5017:8;:25;;;5012:1;:30;4984:247;;5188:31;;;;;5136:29;;;5090:24;;;:21;;;:24;;;;;;;;:130;;:76;;:24;:76;:45;:76;:::i;:130::-;5063:24;;;;:21;;;;:24;;;;;;:157;;;;5044:3;4984:247;;;;4196:1041;;;;;:::o;29330:935:8:-;29496:14;;;;29492:767;;29531:14;;;;:22;;;29567:13;;;;:21;;;-1:-1:-1;29602:18:8;;;;;;;;;;:52;;;;;;;;;;;;;;;29668:28;;:32;29492:767;;;29756:5;29739:3;:13;;;:22;;29731:72;;;;-1:-1:-1;;;29731:72:8;;;;;;;;;29838:5;29821:3;:13;;;:22;29817:432;;;29924:11;:18;;;;;;;;;;;;:55;;;;;;;;;;;;;;;;;;;:68;;29980:11;29924:68;:55;:68;:::i;:::-;29863:11;:18;;;;;;;;;;;:129;;;;;;;;;;;;29817:432;;;30031:11;:18;;;;;;;;;;;:52;;;;;;;;;;;;30101:28;;;;:32;;;30163:13;;;;;30151:26;;;;:36;:44;;;30213:21;;;29330:935;;;:::o;34986:611::-;35145:15;;:46;;-1:-1:-1;;;35145:46:8;;35079:4;;;;-1:-1:-1;;;;;35145:15:8;;;;:27;;:46;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;35223:39:8;;35288:1;35223:39;;;:31;:39;;;;;;;;:62;;;:49;;:62;;;;;;35095:97;;-1:-1:-1;35223:66:8;;;:311;;-1:-1:-1;;;;;;35331:39:8;;;;;;:31;:39;;;;;;;;:62;;;:49;;:62;;;;;;:67;:185;;;;;35471:15;-1:-1:-1;;;;;35471:43:8;;:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;35422:39:8;;;;;;:31;:39;;;;;:46;:94;35331:185;35202:388;;;;-1:-1:-1;;;35202:388:8;;;;;;;;21326:704;21567:11;:18;;21612:180;;;;;;;;-1:-1:-1;;;;;21612:180:8;;;;;;;;;;;;;;;;;;;;;;;;21731:3;21612:180;;;;;;-1:-1:-1;21612:180:8;;;;;;;;;;;;;;;;;;21595:198;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;21595:198:8;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21567:18;;21612:180;;21595:198;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;21803:35:8;;;;:22;:35;;;;;;;;:54;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;21867:27:8;;;;:19;:27;;;;;:46;;;;;;;;;;;;;;;21956:11;:25;;21923:100;;21956:11;21844:12;;21956:25;;;;;;;;;;;;;;;;;;;:32;21990:11;:25;;-1:-1:-1;;;;;21956:32:8;;;;22002:12;;21990:25;;;;;;;;;;;;;;;;:32;;;21923;:100::i;:::-;;21326:704;;;;;;;:::o;25304:244::-;25377:4;25393:17;25413:18;:16;:18::i;:::-;25393:38;;25441:25;25459:6;25441:17;:25::i;:::-;-1:-1:-1;;;;;25483:26:8;;;;;;:18;:26;;;;;:58;;25528:12;25483:58;:44;:58;:::i;24230:468::-;-1:-1:-1;;;;;24339:39:8;;;;;;:31;:39;;;;;;;;:62;;;:49;;;;:62;;;;;;;:67;24335:200;;;-1:-1:-1;;;;;24471:39:8;;;;;;:31;:39;;;;;:46;:53;;24522:1;24471:53;:50;:53;:::i;:::-;-1:-1:-1;;;;;24422:39:8;;;;;;:31;:39;;;;;:102;24335:200;-1:-1:-1;;;;;24622:39:8;;;;;;:31;:39;;;;;;;;:62;;;24689:1;24622:49;;;:62;;;;;;;:69;;;:66;:69;:::i;:::-;-1:-1:-1;;;;;24544:39:8;;;;;;;:31;:39;;;;;;;;:75;;;:62;;;;:75;;;:147;24230:468::o;22036:556::-;22115:4;22131:17;22151:18;:16;:18::i;:::-;22131:38;;22179:12;22194:11;22206:12;22194:25;;;;;;;;;;;;;;;;;;:33;;;22179:48;;22257:7;22242:12;:22;22238:348;;;22287:55;22299:11;22311:12;22299:25;;;;;;;;;;;;;;;;;;:42;;;22287:7;:11;;:55;;;;:::i;:::-;22280:62;;;;;;22238:348;22373:21;22397:73;22427:11;22439:12;22427:25;;;;;;;;;;;;;;;;;;:42;;;22397:25;22414:7;22397:12;:16;;:25;;;;:::i;:73::-;22373:97;;22491:84;22503:71;22531:11;22543:12;22531:25;;;;;;;;;;;;;;;;;;:42;;;22503:23;22524:1;22503:16;:20;;:23;;;;:::i;:::-;:27;:71;:27;:71;:::i;:::-;22491:7;;:84;:11;:84;:::i;:::-;22484:91;;;;;;;28307:1017;28397:4;28431:31;;;:17;:31;;;;;:65;28525:11;:25;;28397:4;;28525:11;28449:12;;28525:25;;;;;;;;;;;;;;;;:37;;;28506:56;;28572:11;28586;28598:12;28586:25;;;;;;;;;;;;;;;;;;:32;;;28572:46;;28632:10;28646:1;28632:15;28628:182;;;28676:32;;;;:19;:32;;;;;:43;;;;-1:-1:-1;28737:15:8;28733:67;;28779:6;-1:-1:-1;28772:13:8;;-1:-1:-1;;28772:13:8;28733:67;28833:10;28819:476;28861:1;28857;:5;:47;;;;;28870:11;28882:12;28870:25;;;;;;;;;;;;;;;;;;:34;;;28866:1;:38;28857:47;28819:476;;;29000:11;29012:12;29000:25;;;;;;;;;;;;;;;;;;:33;;;28995:1;:38;28991:294;;29194:32;;;;:19;:32;;;;;;;;:43;;;;;;;;:75;;;;29094:73;;29062:208;;29194:75;29062:106;;:6;;:31;:106::i;:208::-;29053:217;;28991:294;28922:32;;;;:19;:32;;;;;;;;:43;;;;;;;:53;;;28819:476;;;-1:-1:-1;29311:6:8;28307:1017;-1:-1:-1;;;;28307:1017:8:o;27904:176::-;28006:34;;;;:21;:34;;;;;:67;;28059:6;28067:5;28006:67;:52;:67;:::i;23826:163::-;-1:-1:-1;;;;;23923:26:8;;;;;;:18;:26;;;;;:59;;23968:6;23976:5;23923:59;:44;:59;:::i;23995:229::-;-1:-1:-1;;;;;24134:37:8;;;;;;:29;:37;;;;;;;;:50;;;;;;;;:83;;24203:6;24211:5;24134:83;:68;:83;:::i;2119:459:35:-;2177:7;2418:6;2414:45;;-1:-1:-1;2447:1:35;2440:8;;2414:45;2481:5;;;2485:1;2481;:5;:1;2504:5;;;;;:10;2496:56;;;;-1:-1:-1;;;2496:56:35;;;;;;;;28086:215:8;28206:43;;;;:30;:43;;;;;:88;;28271:15;28288:5;28206:88;:64;:88;:::i;24998:300::-;-1:-1:-1;;;;;25187:46:8;;;;;;:38;:46;;;;;;;;:59;;;;;;;;:104;;25268:15;25285:5;25187:104;:80;:104;:::i;930::5:-;1017:10;930:104;:::o;6523:184:0:-;6596:12;;;;:6;:12;;;;;:33;;6621:7;6596:33;:24;:33;:::i;:::-;6592:109;;;6677:12;:10;:12::i;:::-;-1:-1:-1;;;;;6650:40:0;6668:7;-1:-1:-1;;;;;6650:40:0;6662:4;6650:40;;;;;;;;;;6523:184;;:::o;27767:131:8:-;-1:-1:-1;;;;;27852:29:8;27829:4;27852:29;;;:21;:29;;;;;:35;:39;;;27767:131::o;6713:188:0:-;6787:12;;;;:6;:12;;;;;:36;;6815:7;6787:36;:27;:36;:::i;:::-;6783:112;;;6871:12;:10;:12::i;:::-;-1:-1:-1;;;;;6844:40:0;6862:7;-1:-1:-1;;;;;6844:40:0;6856:4;6844:40;;;;;;;;;;6713:188;;:::o;6052:147:13:-;6126:7;6168:22;6172:3;6184:5;6168:3;:22::i;5368:156::-;5448:4;5471:46;5481:3;-1:-1:-1;;;;;5501:14:13;;5471:9;:46::i;26397:515:8:-;26492:4;26508:17;26528:18;:16;:18::i;:::-;-1:-1:-1;;;;;26577:35:8;;;;;;:27;:35;;;;;:41;;;26508:38;;-1:-1:-1;26577:57:8;;26556:138;;;;-1:-1:-1;;;26556:138:8;;;;;;;;;-1:-1:-1;;;;;26712:35:8;;;;;;:27;:35;;;;;:42;:52;-1:-1:-1;26712:52:8;26704:92;;;;-1:-1:-1;;;26704:92:8;;;;;;;;;-1:-1:-1;;;;;26851:35:8;;;;;;:27;:35;;;;;:42;:54;;26898:6;26851:54;:46;:54;:::i;:::-;-1:-1:-1;;;;;26806:35:8;;;;;;;:27;:35;;;;;:99;;;;26397:515;;-1:-1:-1;;26397:515:8:o;6609:1014:30:-;6743:30;;;;6690:4;;6727:12;:5;6737:1;6727:12;:9;:12;:::i;:::-;:46;;6706:118;;;;-1:-1:-1;;;6706:118:30;;;;;;;;;6838:30;;;;6834:74;;-1:-1:-1;6896:1:30;6889:8;;6834:74;6956:5;6922:8;:30;;;:39;6918:667;;6991:30;;;;6977:539;7028:5;7023:1;:10;6977:539;;7058:13;7125:24;;;:21;;;:24;;;;;;;;;7093:19;;;;;;;7074:14;;;;:76;;7125:24;7074:39;;:14;:39;:18;:39;:::i;:76::-;7058:92;;7190:8;7172;:14;;;:26;7168:98;;7222:14;;;:25;;;7168:98;7309:1;7287:19;;;;;;;;;;;:23;7283:96;;7341:16;:19;;;;;;;;;;7334:26;7283:96;7427:1;7400:24;;;:21;;;:24;;;;;;:28;7396:106;;7459:24;;;;:21;;;:24;;;;;7452:31;7396:106;-1:-1:-1;7035:3:30;;6977:539;;;-1:-1:-1;7562:12:30;:5;7572:1;7562:12;:9;:12;:::i;:::-;7529:30;;;:45;6918:667;-1:-1:-1;;7602:14:30;;;;6609:1014::o;32976:2004:8:-;33129:15;;:54;;-1:-1:-1;;;33129:54:8;;33042:47;;-1:-1:-1;;;;;33129:15:8;;:27;;:54;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;33042:142;;33195:17;33215:18;:16;:18::i;:::-;33195:38;-1:-1:-1;33279:19:8;33195:38;33296:1;33279:19;:16;:19;:::i;:::-;33243:11;33255:12;33243:25;;;;;;;;;;;;;;;;;;:33;;:55;;;;33383:1;33312:19;:58;33332:11;33344:12;33332:25;;;;;;;;;;;;;;;;;;:37;;;33312:58;;;;;;;;;;;:68;;;:72;33308:255;;;33484:19;:58;33504:11;33516:12;33504:25;;;;;;;;;;;;;;;;:37;:25;;;;;:37;;;;;33484:58;;;;;;;;;;;;;;;:68;;;33400:31;;;:17;:31;;;;:152;33308:255;33573:157;33613:11;33625:12;33613:25;;;;;;;;;;;;;;;;;;:37;;;33664:11;33676:12;33664:25;;;;;;;;;;;;;;;;;;:32;;;33710:19;33727:1;33710:12;:16;;:19;;;;:::i;:::-;33573:26;:157::i;:::-;33740:149;33777:11;33789:12;33777:25;;;;;;;;;;;;;;;;;;;;;:32;33823:11;:25;;-1:-1:-1;;;;;33777:32:8;;;;33835:12;;33823:25;;;;;;;;;;;;;;;;:32;;;33869:19;33886:1;33869:12;:16;;:19;;;;:::i;:::-;33740:23;:149::i;:::-;33899:211;33947:11;33959:12;33947:25;;;;;;;;;;;;;;;;;;;;;:32;33993:11;:25;;-1:-1:-1;;;;;33947:32:8;;;;34005:12;;33993:25;;;;;;;;;;;;;;;;:37;;;34044:11;34056:12;34044:25;;;;;;;;;;;;;;;;;;:32;;;34090:19;34107:1;34090:12;:16;;:19;;;;:::i;:::-;33899:34;:211::i;:::-;34120:158;34161:11;34173:12;34161:25;;;;;;;;;;;;;;;;;;;;;:32;34207:11;:25;;-1:-1:-1;;;;;34161:32:8;;;;34219:12;;34207:25;;;;;;;;;;;;;;;;:37;;;34258:19;34275:1;34258:12;:16;;:19;;;;:::i;:::-;34120:27;:158::i;:::-;34288:20;34311:135;34348:23;-1:-1:-1;;;;;34348:40:8;;34402:11;34414:12;34402:25;;;;;;;;;;;;;;;;;;:42;;;34348:97;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;34311:11;34323:12;34311:25;;;;;;;;;;;;;;;;;;:32;;;:36;;:135;;;;:::i;:::-;34288:158;;34456:149;34505:11;34517:12;34505:25;;;;;;;;;;;;;;;;;;:37;;;34556:15;34585:19;34602:1;34585:12;:16;;:19;;;;:::i;:::-;34456:35;:149::i;:::-;34615:203;34672:11;34684:12;34672:25;;;;;;;;;;;;;;;;;;;;;:32;34718:11;:25;;-1:-1:-1;;;;;34672:32:8;;;;34730:12;;34718:25;;;;;;;;;;;;;;;;:37;;;34769:15;34798:19;34815:1;34798:12;:16;;:19;;;;:::i;:::-;34615:43;:203::i;:::-;34828:145;34880:11;34892:12;34880:25;;;;;;;;;;;;;;;;;;;;;:32;34926:11;:25;;-1:-1:-1;;;;;34880:32:8;;;;34938:12;;34926:25;;;;;;;;;;;;;;;;:37;;;34828:38;:145::i;1409:498:23:-;1820:4;1864:17;1895:7;1409:498;:::o;2850:248:31:-;1024:12:23;;;;;;;;:31;;;1040:15;:13;:15::i;:::-;1024:47;;;-1:-1:-1;1060:11:23;;;;1059:12;1024:47;1016:106;;;;-1:-1:-1;;;1016:106:23;;;;;;;;;1129:19;1152:12;;;;;;1151:13;1170:80;;;;1198:12;:19;;-1:-1:-1;;;;1198:19:23;;;;;1225:18;1213:4;1225:18;;;1170:80;2939:47:31::1;:45;:47::i;:::-;2996:42;1803:4:0;3027:10:31;2996;:42::i;:::-;3048:43;3068:22;3048:19;:43::i;5605:115:13:-:0;5668:7;5694:19;5702:3;5694:7;:19::i;25554:256:8:-;-1:-1:-1;;;;;25728:37:8;;25701:4;25728:37;;;:29;:37;;;;;;;;:50;;;;;;;;:75;;25797:5;25728:75;:68;:75;:::i;:::-;25721:82;25554:256;-1:-1:-1;;;;25554:256:8:o;834:176:35:-;892:7;923:5;;;946:6;;;;938:46;;;;-1:-1:-1;;;938:46:35;;;;;;;;1274:134;1332:7;1358:43;1362:1;1365;1358:43;;;;;;;;;;;;;;;;;:3;:43::i;988:212:25:-;1048:7;1076:1;1071;:6;1067:127;;-1:-1:-1;1100:5:25;;;1093:12;;1067:127;1141:20;1156:1;1159;1141:20;;;;;;;;;;;;;;;;-1:-1:-1;1182:1:25;1175:8;;1402:145;1468:4;-1:-1:-1;;1491:19:25;-1:-1:-1;1484:27:25;;;;-1:-1:-1;978:3:25;1532:8;-1:-1:-1;1528:12:25;1402:145::o;8869:385:30:-;9085:162;9143:8;9165:11;9190:19;9223:5;9242:4;9085:44;:162::i;1344:124:15:-;1403:15;;:::i;:::-;1437:24;1452:5;1459:1;1437:14;:24::i;1553:200:25:-;1626:4;1650:1;1646;:5;1642:105;;;-1:-1:-1;978:3:25;1674:5;;;:12;1667:19;;1642:105;-1:-1:-1;978:3:25;1724:5;;;:12;1717:19;;1025:313:15;1106:15;;:::i;:::-;1155:1;1141:11;:15;1133:44;;;;-1:-1:-1;;;1133:44:15;;;;;;;;;1187:24;;:::i;:::-;1214:58;;;;;;;;1235:9;1214:58;;;;1259:11;1214:58;;;1187:85;;1282:24;1297:8;1282:14;:24::i;8519:344:30:-;8696:160;8754:8;8776;8798:19;8831:5;8850;8696:44;:160::i;3033:130:35:-;3091:7;3117:39;3121:1;3124;3117:39;;;;;;;;;;;;;;;;;:3;:39::i;1740:206:15:-;1827:15;;:::i;:::-;1892:11;;1876;;1861:78;;1876:28;;:11;:28;:15;:28;:::i;:::-;1924:13;;;;;1906;;;;:32;;;:17;:32;:::i;:::-;1861:14;:78::i;25816:575:8:-;25904:4;25920:17;25940:18;:16;:18::i;:::-;-1:-1:-1;;;;;25972:35:8;;;;;;:27;:35;;;;;:41;;;25920:38;;-1:-1:-1;25972:56:8;-1:-1:-1;25968:417:8;;;-1:-1:-1;;;;;26044:35:8;;;;;;:27;:35;;;;;:51;;;26109:41;;:56;;;25968:417;;;-1:-1:-1;;;;;26203:35:8;;;;;;:27;:35;;;;;:41;;;:57;;26196:65;;;;-1:-1:-1;;;;;26320:35:8;;;;;;:27;:35;;;;;:42;:54;;26367:6;26320:54;:46;:54;:::i;5921:682:30:-;6056:12;:5;6066:1;6056:12;:9;:12;:::i;:::-;6022:8;:30;;;:46;;6014:88;;;;-1:-1:-1;;;6014:88:30;;;;;;;;;6116:30;;;;6112:151;;6167:30;;;:38;;;6219:25;;;:33;;;6112:151;6284:8;:25;;;6276:5;:33;6272:97;;;6325:25;;;:33;;;6272:97;6392:8;:30;;;6383:5;:39;6379:218;;6469:28;;;;:21;;;:28;;;;;;:38;;6502:4;6469:38;:32;:38;:::i;:::-;6438:28;;;;:21;;;:28;;;;;:69;6379:218;;;6555:14;;;;:31;;6581:4;6555:31;:25;:31;:::i;:::-;6538:14;;;:48;5921:682;;;:::o;2773:486::-;2914:5;2880:8;:30;;;:39;;2872:81;;;;-1:-1:-1;;;2872:81:30;;;;;;;;;2967:30;;;;2963:104;;3018:30;;;:38;;;2963:104;3107:28;;;;:21;;;:28;;;;;;:38;;3140:4;3107:38;:32;:38;:::i;:::-;3076:28;;;;:21;;;:28;;;;;:69;3159:25;;;;:34;;3155:98;;3209:25;;;:33;;;2773:486;;;:::o;4831:141:13:-;4901:4;4924:41;4929:3;-1:-1:-1;;;;;4949:14:13;;4924:4;:41::i;5140:147::-;5213:4;5236:44;5244:3;-1:-1:-1;;;;;5264:14:13;;5236:7;:44::i;4390:201::-;4484:18;;4457:7;;4484:26;-1:-1:-1;4476:73:13;;;;-1:-1:-1;;;4476:73:13;;;;;;;;;4566:3;:11;;4578:5;4566:18;;;;;;;;;;;;;;;;4559:25;;4390:201;;;;:::o;3743:127::-;3816:4;3839:19;;;:12;;;;;:19;;;;;;:24;;;3743:127::o;22598:164:8:-;22695:34;;;;:21;:34;;;;;:60;;22741:6;22749:5;22695:60;:45;:60;:::i;22977:151::-;-1:-1:-1;;;;;23069:26:8;;;;;;:18;:26;;;;;:52;;23107:6;23115:5;23069:52;:37;:52;:::i;23134:217::-;-1:-1:-1;;;;;23268:37:8;;;;;;:29;:37;;;;;;;;:50;;;;;;;;:76;;23330:6;23338:5;23268:76;:61;:76;:::i;27309:452::-;-1:-1:-1;;;;;27414:29:8;;;;;;:21;:29;;;;;:35;27410:184;;-1:-1:-1;;;;;27470:29:8;;;;;;:21;:29;;;;;;;;:43;;;27568:8;:15;27527:30;:38;;;;;;:56;27410:184;-1:-1:-1;;;;;27607:29:8;;;;;;:21;:29;;;;;;;;:54;;;:41;;:54;;;;;;27603:152;;-1:-1:-1;;;;;27682:29:8;;;;;;;;:21;:29;;;;;;;;:54;;;:41;;;;:54;;;:62;27309:452::o;22768:203::-;22883:43;;;;:30;:43;;;;;:81;;22941:15;22958:5;22883:81;:57;:81;:::i;24704:288::-;-1:-1:-1;;;;;24888:46:8;;;;;;:38;:46;;;;;;;;:59;;;;;;;;:97;;24962:15;24979:5;24888:97;:73;:97;:::i;23357:463::-;-1:-1:-1;;;;;23461:39:8;;;;;;:31;:39;;;;;;;;:62;;;:49;;:62;;;;;;23457:200;;-1:-1:-1;;;;;23593:39:8;;;;;;:31;:39;;;;;:46;:53;;23644:1;23593:53;:50;:53;:::i;:::-;-1:-1:-1;;;;;23544:39:8;;;;;;:31;:39;;;;;:102;23457:200;-1:-1:-1;;;;;23744:39:8;;;;;;:31;:39;;;;;;;;:62;;;23811:1;23744:49;;;:62;;;;;;;:69;;;:66;:69;:::i;1301:138:0:-;1024:12:23;;;;;;;;:31;;;1040:15;:13;:15::i;:::-;1024:47;;;-1:-1:-1;1060:11:23;;;;1059:12;1024:47;1016:106;;;;-1:-1:-1;;;1016:106:23;;;;;;;;;1129:19;1152:12;;;;;;1151:13;1170:80;;;;1198:12;:19;;-1:-1:-1;;;;1198:19:23;;;;;1225:18;1213:4;1225:18;;;1170:80;1364:26:0::1;:24;:26::i;:::-;1400:32;:30;:32::i;:::-;1268:14:23::0;1264:55;;;1307:5;1292:20;;-1:-1:-1;;1292:20:23;;;1301:138:0;:::o;3704:317:31:-;-1:-1:-1;;;;;3791:36:31;;3783:83;;;;-1:-1:-1;;;3783:83:31;;;;;;;;;3884:35;:22;-1:-1:-1;;;;;3884:33:31;;:35::i;:::-;3876:71;;;;-1:-1:-1;;;3876:71:31;;;;;;;;;3957:15;:57;;-1:-1:-1;;;;;;3957:57:31;-1:-1:-1;;;;;3957:57:31;;;;;;;;;;3704:317::o;3951:107:13:-;4033:18;;3951:107::o;1692:187:35:-;1778:7;1813:12;1805:6;;;;1797:29;;;;-1:-1:-1;;;1797:29:35;;;;;;;;;;-1:-1:-1;;;1848:5:35;;;1692:187::o;9260:1629:30:-;9537:30;;;;9521:12;:5;9531:1;9521:12;:9;:12;:::i;:::-;:46;;9513:90;;;;-1:-1:-1;;;9513:90:30;;;;;;;;;9617:14;9613:138;;;9671:33;;;;9655:12;:5;9665:1;9655:12;:9;:12;:::i;:::-;:49;;9647:93;;;;-1:-1:-1;;;9647:93:30;;;;;;;;;9814:31;;;;9781:29;;:64;;9760:141;;;;-1:-1:-1;;;9760:141:30;;;;;;;;;9915:30;;;;9911:72;;9966:7;;9911:72;9992:10;10005:34;10023:8;10033:5;10005:17;:34::i;:::-;9992:47;-1:-1:-1;10053:27:30;9992:47;10078:1;10053:27;:24;:27;:::i;:::-;10049:64;;;10096:7;;;10049:64;10123:13;10139:86;10193:19;:31;;;10139:49;10158:19;:29;;;10139:8;:14;;;:18;;:49;;;;:::i;:86::-;10123:102;;10239:14;10235:119;;;10269:74;10287:11;10300:35;10326:8;10300;:14;;;:25;;:35;;;;:::i;:::-;10337:5;10269:17;:74::i;:::-;10363:14;;;:25;;;10404:6;10413:12;:5;10423:1;10413:12;:9;:12;:::i;:::-;10404:21;;10399:484;10432:8;:25;;;10427:1;:30;10399:484;;10591:31;;;;;10539:29;;10478:12;10493:24;;;:21;;;:24;;;;;;;:130;;10591:31;10493:76;;:24;:76;:45;:76;:::i;:130::-;10478:145;;10641:14;10637:188;;;10765:24;;;;:21;;;:24;;;;;;10705:105;;10765:44;;10801:7;10765:44;:35;:44;:::i;:::-;10705:27;;;;:24;;;:27;;;;;;;:105;:59;:105;:::i;:::-;10675:27;;;;:24;;;:27;;;;;:135;10637:188;10838:24;;;;:21;;;;:24;;;;;;:34;;;;10459:3;10399:484;;1474:260:15;1548:9;1560:45;1564:8;:18;;;1584:8;:20;;;1560:3;:45::i;:::-;1636:18;;1548:57;;-1:-1:-1;1636:28:15;;1548:57;1636:28;:22;:28;:::i;:::-;1615:49;;1697:20;;;;:30;;1722:4;1697:30;:24;:30;:::i;:::-;1674:20;;;;:53;;;;-1:-1:-1;1474:260:15:o;3638:338:35:-;3724:7;3824:12;3817:5;3809:28;;;;-1:-1:-1;;;3809:28:35;;;;;;;;;;;3847:9;3863:1;3859;:5;;;;;;;3638:338;-1:-1:-1;;;;;3638:338:35:o;1578:404:13:-;1641:4;1662:21;1672:3;1677:5;1662:9;:21::i;:::-;1657:319;;-1:-1:-1;1699:23:13;;;;;;;;:11;:23;;;;;;;;;;;;;1879:18;;1857:19;;;:12;;;:19;;;;;;:40;;;;1911:11;;1657:319;-1:-1:-1;1960:5:13;1953:12;;2150:1512;2216:4;2353:19;;;:12;;;:19;;;;;;2387:15;;2383:1273;;2816:18;;-1:-1:-1;;2768:14:13;;;;2816:22;;;;2744:21;;2816:3;;:22;;3098;;;;;;;;;;;;;;3078:42;;3241:9;3212:3;:11;;3224:13;3212:26;;;;;;;;;;;;;;;;;;;:38;;;;3316:23;;;3358:1;3316:12;;;:23;;;;;;3342:17;;;3316:43;;3465:17;;3316:3;;3465:17;;;;;;;;;;;;;;;;;;;;;;3557:3;:12;;:19;3570:5;3557:19;;;;;;;;;;;3550:26;;;3598:4;3591:11;;;;;;;;2383:1273;3640:5;3633:12;;;;;5271:644:30;5399:5;5365:8;:30;;;:39;;5357:74;;;;-1:-1:-1;;;5357:74:30;;;;;;;;;5445:30;;;;5441:151;;5496:30;;;:38;;;5548:25;;;:33;;;5441:151;5613:8;:25;;;5605:5;:33;5601:97;;;5654:25;;;:33;;;5601:97;5721:8;:30;;;5712:5;:39;5708:201;;5793:16;:23;;;;;;;;;;;:33;;5821:4;5793:33;:27;:33;:::i;:::-;5767:16;:23;;;;;;;;;;:59;5708:201;;;5874:14;;;;:24;;5893:4;5874:24;:18;:24;:::i;2305:462::-;2439:5;2405:8;:30;;;:39;;2397:74;;;;-1:-1:-1;;;2397:74:30;;;;;;;;;2485:30;;;;2481:104;;2536:30;;;:38;;;2481:104;2620:16;:23;;;;;;;;;;;:33;;2648:4;2620:33;:27;:33;:::i;:::-;2594:16;:23;;;;;;;;;;:59;2667:25;;;;:34;;2663:98;;2717:25;;;:33;;;2305:462;;;:::o;857:66:5:-;1024:12:23;;;;;;;;:31;;;1040:15;:13;:15::i;:::-;1024:47;;;-1:-1:-1;1060:11:23;;;;1059:12;1024:47;1016:106;;;;-1:-1:-1;;;1016:106:23;;;;;;;;;1129:19;1152:12;;;;;;1151:13;1170:80;;;;1198:12;:19;;-1:-1:-1;;;;1198:19:23;;;;;1225:18;1213:4;1225:18;;;1268:14;1264:55;;;1307:5;1292:20;;-1:-1:-1;;1292:20:23;;;857:66:5;:::o;685:610:1:-;745:4;1206:20;;1051:66;1245:23;;;;;;:42;;-1:-1:-1;;1272:15:1;;;1237:51;-1:-1:-1;;685:610:1:o;1952:298:15:-;2004:4;2030:1;2051;2066:7;;;2062:61;;;2100:12;2105:2;2109;2100:4;:12::i;:::-;2089:23;;-1:-1:-1;2089:23:15;-1:-1:-1;2062:61:15;2139:6;;2132:93;;2166:10;:2;2173;2166:10;:6;:10;:::i;:::-;2161:15;;2201:13;2207:2;2211;2201:4;:13::i;:::-;2190:24;;-1:-1:-1;2190:24:15;-1:-1:-1;2132:93:15;;;-1:-1:-1;2241:2:15;1952:298;-1:-1:-1;;;1952:298:15:o;2256:95::-;2339:1;2256:95::o;4420:128:35:-;4478:7;4504:37;4508:1;4511;4504:37;;;;;;;;;;;;;;;;;5098:7;5133:12;5125:6;5117:29;;;;-1:-1:-1;;;5117:29:35;;;;;;;;;;;5167:1;5163;:5;;;;;;;5012:163;-1:-1:-1;;;;5012:163:35:o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;1193:241;;1297:2;1285:9;1276:7;1272:23;1268:32;1265:2;;;-1:-1;;1303:12;1265:2;85:6;72:20;97:33;124:5;97:33;;1441:263;;1556:2;1544:9;1535:7;1531:23;1527:32;1524:2;;;-1:-1;;1562:12;1524:2;226:6;220:13;238:33;265:5;238:33;;1711:366;;;1832:2;1820:9;1811:7;1807:23;1803:32;1800:2;;;-1:-1;;1838:12;1800:2;85:6;72:20;97:33;124:5;97:33;;;1890:63;1990:2;2029:22;;;;982:20;;-1:-1;;;1794:283;2084:491;;;;2222:2;2210:9;2201:7;2197:23;2193:32;2190:2;;;-1:-1;;2228:12;2190:2;85:6;72:20;97:33;124:5;97:33;;;2280:63;2380:2;2419:22;;982:20;;-1:-1;2488:2;2527:22;;;982:20;;2184:391;-1:-1;;;2184:391;2582:257;;2694:2;2682:9;2673:7;2669:23;2665:32;2662:2;;;-1:-1;;2700:12;2662:2;364:6;358:13;52415:5;50448:13;50441:21;52393:5;52390:32;52380:2;;-1:-1;;52426:12;2846:241;;2950:2;2938:9;2929:7;2925:23;2921:32;2918:2;;;-1:-1;;2956:12;2918:2;-1:-1;485:20;;2912:175;-1:-1;2912:175;3094:366;;;3215:2;3203:9;3194:7;3190:23;3186:32;3183:2;;;-1:-1;;3221:12;3183:2;498:6;485:20;3273:63;;3373:2;3416:9;3412:22;72:20;97:33;124:5;97:33;;;3381:63;;;;3177:283;;;;;;3467:366;;;3588:2;3576:9;3567:7;3563:23;3559:32;3556:2;;;-1:-1;;3594:12;3556:2;-1:-1;;485:20;;;3746:2;3785:22;;;982:20;;-1:-1;3550:283;4088:263;;4203:2;4191:9;4182:7;4178:23;4174:32;4171:2;;;-1:-1;;4209:12;4171:2;-1:-1;1130:13;;4165:186;-1:-1;4165:186;4731:743;;;;;;4906:3;4894:9;4885:7;4881:23;4877:33;4874:2;;;-1:-1;;4913:12;4874:2;995:6;982:20;4965:63;;5065:2;5108:9;5104:22;982:20;5073:63;;5173:2;5216:9;5212:22;982:20;5181:63;;5309:2;5298:9;5294:18;5281:32;5333:18;;5325:6;5322:30;5319:2;;;-1:-1;;5355:12;5319:2;5441:6;5430:9;5426:22;685:3;678:4;670:6;666:17;662:27;652:2;;-1:-1;;693:12;652:2;736:6;723:20;713:30;;5333:18;755:6;752:30;749:2;;;-1:-1;;785:12;749:2;880:3;5065:2;860:17;821:6;846:32;;843:41;840:2;;;-1:-1;;887:12;840:2;5065;821:6;817:17;5383:75;;;;;;;;4868:606;;;;;;;;;6421:327;;6556:5;49755:12;49902:6;49897:3;49890:19;6640:52;6685:6;49939:4;49934:3;49930:14;49939:4;6666:5;6662:16;6640:52;;;52081:7;52065:14;-1:-1;;52061:28;6704:39;;;;49939:4;6704:39;;6503:245;-1:-1;;6503:245;25247:275;;7272:5;49755:12;7384:52;7429:6;7424:3;7417:4;7410:5;7406:16;7384:52;;;7448:16;;;;;25383:139;-1:-1;;25383:139;25529:222;-1:-1;;;;;50748:54;;;;5691:37;;25656:2;25641:18;;25627:124;26003:349;-1:-1;;;;;50748:54;;;;5560:58;;26338:2;26323:18;;6042:37;26166:2;26151:18;;26137:215;26699:444;-1:-1;;;;;50748:54;;;;5691:37;;27046:2;27031:18;;6042:37;;;;27129:2;27114:18;;6042:37;26882:2;26867:18;;26853:290;27150:556;-1:-1;;;;;50748:54;;;;5691:37;;27526:2;27511:18;;6042:37;;;;27609:2;27594:18;;6042:37;27692:2;27677:18;;6042:37;27361:3;27346:19;;27332:374;27713:1092;;28056:3;5333:18;;50759:42;;;50364:5;50748:54;5698:3;5691:37;6072:5;28221:2;28210:9;28206:18;6042:37;6072:5;28304:2;28293:9;28289:18;6042:37;6072:5;28387:2;28376:9;28372:18;6042:37;6072:5;28470:3;28459:9;28455:19;6042:37;6072:5;28554:3;28543:9;28539:19;6042:37;6072:5;28638:3;28627:9;28623:19;6042:37;28056:3;28676;28665:9;28661:19;28654:49;28717:78;28056:3;28045:9;28041:19;28781:6;28717:78;;;28709:86;28027:778;-1:-1;;;;;;;;;;;28027:778;28812:210;50448:13;;50441:21;5925:34;;28933:2;28918:18;;28904:118;29029:222;6042:37;;;29156:2;29141:18;;29127:124;29535:238;29670:2;29655:18;;52181:1;52171:12;;52161:2;;52187:9;52161:2;6351:58;;;29641:132;;29780:310;;29927:2;29948:17;29941:47;30002:78;29927:2;29916:9;29912:18;30066:6;30002:78;;30097:416;30297:2;30311:47;;;7701:2;30282:18;;;49890:19;7737:34;49930:14;;;7717:55;-1:-1;;;7792:12;;;7785:40;7844:12;;;30268:245;30520:416;30720:2;30734:47;;;8095:2;30705:18;;;49890:19;8131:34;49930:14;;;8111:55;-1:-1;;;8186:12;;;8179:26;8224:12;;;30691:245;30943:416;31143:2;31157:47;;;8475:2;31128:18;;;49890:19;8511:34;49930:14;;;8491:55;-1:-1;;;8566:12;;;8559:39;8617:12;;;31114:245;31366:416;31566:2;31580:47;;;8868:2;31551:18;;;49890:19;8904:33;49930:14;;;8884:54;8957:12;;;31537:245;31789:416;31989:2;32003:47;;;9208:1;31974:18;;;49890:19;-1:-1;;;49930:14;;;9223:31;9273:12;;;31960:245;32212:416;32412:2;32426:47;;;9524:2;32397:18;;;49890:19;9560:34;49930:14;;;9540:55;-1:-1;;;9615:12;;;9608:31;9658:12;;;32383:245;32635:416;32835:2;32849:47;;;9909:2;32820:18;;;49890:19;9945:34;49930:14;;;9925:55;-1:-1;;;10000:12;;;9993:32;10044:12;;;32806:245;33058:416;33258:2;33272:47;;;10295:2;33243:18;;;49890:19;10331:34;49930:14;;;10311:55;10400:26;10386:12;;;10379:48;10446:12;;;33229:245;33481:416;33681:2;33695:47;;;10697:2;33666:18;;;49890:19;-1:-1;;;49930:14;;;10713:38;10770:12;;;33652:245;33904:416;34104:2;34118:47;;;11021:2;34089:18;;;49890:19;11057:34;49930:14;;;11037:55;-1:-1;;;11112:12;;;11105:45;11169:12;;;34075:245;34327:416;34527:2;34541:47;;;11420:2;34512:18;;;49890:19;11456:29;49930:14;;;11436:50;11505:12;;;34498:245;34750:416;34950:2;34964:47;;;11756:2;34935:18;;;49890:19;-1:-1;;;49930:14;;;11772:45;11836:12;;;34921:245;35173:416;35373:2;35387:47;;;12087:2;35358:18;;;49890:19;-1:-1;;;49930:14;;;12103:34;12156:12;;;35344:245;35596:416;35796:2;35810:47;;;12407:2;35781:18;;;49890:19;-1:-1;;;49930:14;;;12423:40;12482:12;;;35767:245;36019:416;36219:2;36233:47;;;12733:2;36204:18;;;49890:19;12769:34;49930:14;;;12749:55;-1:-1;;;12824:12;;;12817:26;12862:12;;;36190:245;36442:416;36642:2;36656:47;;;13113:2;36627:18;;;49890:19;13149:34;49930:14;;;13129:55;-1:-1;;;13204:12;;;13197:26;13242:12;;;36613:245;36865:416;37065:2;37079:47;;;13493:2;37050:18;;;49890:19;13529:32;49930:14;;;13509:53;13581:12;;;37036:245;37288:416;37488:2;37502:47;;;13832:2;37473:18;;;49890:19;13868:34;49930:14;;;13848:55;-1:-1;;;13923:12;;;13916:40;13975:12;;;37459:245;37711:416;37911:2;37925:47;;;14226:2;37896:18;;;49890:19;14262:29;49930:14;;;14242:50;14311:12;;;37882:245;38134:416;38334:2;38348:47;;;14562:2;38319:18;;;49890:19;14598:34;49930:14;;;14578:55;-1:-1;;;14653:12;;;14646:35;14700:12;;;38305:245;38557:416;38757:2;38771:47;;;38742:18;;;49890:19;14987:34;49930:14;;;14967:55;15041:12;;;38728:245;38980:416;39180:2;39194:47;;;15292:2;39165:18;;;49890:19;-1:-1;;;49930:14;;;15308:33;15360:12;;;39151:245;39403:416;39603:2;39617:47;;;15611:2;39588:18;;;49890:19;15647:29;49930:14;;;15627:50;15696:12;;;39574:245;39826:416;40026:2;40040:47;;;15947:2;40011:18;;;49890:19;15983:34;49930:14;;;15963:55;-1:-1;;;16038:12;;;16031:44;16094:12;;;39997:245;40249:416;40449:2;40463:47;;;16345:2;40434:18;;;49890:19;16381:34;49930:14;;;16361:55;-1:-1;;;16436:12;;;16429:25;16473:12;;;40420:245;40672:416;40872:2;40886:47;;;16724:2;40857:18;;;49890:19;16760:25;49930:14;;;16740:46;16805:12;;;40843:245;41095:416;41295:2;41309:47;;;17056:2;41280:18;;;49890:19;-1:-1;;;49930:14;;;17072:39;17130:12;;;41266:245;41518:416;41718:2;41732:47;;;17381:2;41703:18;;;49890:19;17417:31;49930:14;;;17397:52;17468:12;;;41689:245;41941:416;42141:2;42155:47;;;17719:2;42126:18;;;49890:19;17755:25;49930:14;;;17735:46;17800:12;;;42112:245;42364:416;42564:2;42578:47;;;18051:2;42549:18;;;49890:19;18087:34;49930:14;;;18067:55;-1:-1;;;18142:12;;;18135:29;18183:12;;;42535:245;42787:416;42987:2;43001:47;;;18434:2;42972:18;;;49890:19;18470:34;49930:14;;;18450:55;-1:-1;;;18525:12;;;18518:38;18575:12;;;42958:245;43210:416;43410:2;43424:47;;;18826:2;43395:18;;;49890:19;-1:-1;;;49930:14;;;18842:33;18894:12;;;43381:245;43633:416;43833:2;43847:47;;;19145:2;43818:18;;;49890:19;19181:34;49930:14;;;19161:55;-1:-1;;;19236:12;;;19229:26;19274:12;;;43804:245;44056:416;44256:2;44270:47;;;19525:2;44241:18;;;49890:19;19561:34;49930:14;;;19541:55;19630:28;19616:12;;;19609:50;19678:12;;;44227:245;44479:416;44679:2;44693:47;;;19929:2;44664:18;;;49890:19;19965:34;49930:14;;;19945:55;-1:-1;;;20020:12;;;20013:31;20063:12;;;44650:245;44902:416;45102:2;45116:47;;;20314:2;45087:18;;;49890:19;20350:34;49930:14;;;20330:55;-1:-1;;;20405:12;;;20398:29;20446:12;;;45073:245;45325:416;45525:2;45539:47;;;20697:2;45510:18;;;49890:19;20733:34;49930:14;;;20713:55;20802:32;20788:12;;;20781:54;20854:12;;;45496:245;45748:416;45948:2;45962:47;;;21105:2;45933:18;;;49890:19;21141:34;49930:14;;;21121:55;-1:-1;;;21196:12;;;21189:41;21249:12;;;45919:245;46171:416;46371:2;46385:47;;;21500:2;46356:18;;;49890:19;21536:27;49930:14;;;21516:48;21583:12;;;46342:245;46594:416;46794:2;46808:47;;;21834:2;46779:18;;;49890:19;21870:34;49930:14;;;21850:55;-1:-1;;;21925:12;;;21918:33;21970:12;;;46765:245;47017:416;47217:2;47231:47;;;22221:2;47202:18;;;49890:19;22257:27;49930:14;;;22237:48;22304:12;;;47188:245;47440:416;47640:2;47654:47;;;22555:2;47625:18;;;49890:19;22591:32;49930:14;;;22571:53;22643:12;;;47611:245;47863:416;48063:2;48077:47;;;22894:2;48048:18;;;49890:19;-1:-1;;;49930:14;;;22910:39;22968:12;;;48034:245;48286:416;48486:2;48500:47;;;23219:2;48471:18;;;49890:19;23255:34;49930:14;;;23235:55;-1:-1;;;23310:12;;;23303:39;23361:12;;;48457:245;48709:382;;48892:2;48913:17;48906:47;5333:18;;50759:42;;;23707:16;23701:23;50748:54;48892:2;48881:9;48877:18;5691:37;48892:2;23872:5;23868:16;23862:23;23939:14;48881:9;23939:14;6042:37;23939:14;24028:5;24024:16;24018:23;24095:14;48881:9;24095:14;6042:37;24095:14;24194:5;24190:16;24184:23;24261:14;48881:9;24261:14;6042:37;24261:14;24351:5;24347:16;24341:23;24418:14;48881:9;24418:14;6042:37;24418:14;24508:5;24504:16;24498:23;24575:14;48881:9;24575:14;6042:37;24575:14;24666:5;24662:16;24656:23;24733:14;48881:9;24733:14;6042:37;24733:14;24820:5;24816:16;24810:23;23631:6;;;48881:9;24853:14;24846:38;;24899:73;23622:16;48881:9;23622:16;24953:12;24899:73;;49327:333;6042:37;;;49646:2;49631:18;;6042:37;49482:2;49467:18;;49453:207;51721:268;51786:1;51793:101;51807:6;51804:1;51801:13;51793:101;;;51874:11;;;51868:18;51855:11;;;51848:39;51829:2;51822:10;51793:101;;;51909:6;51906:1;51903:13;51900:2;;;-1:-1;;51786:1;51956:16;;51949:27;51770:219;52210:117;-1:-1;;;;;50748:54;;52269:35;;52259:2;;52318:1;;52308:12

Swarm Source

ipfs://88c330383e3eca2d1dd22b553b5b109eafd04aa92a00d266c363bddb03bdaf00

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.