FTM Price: $0.699836 (+1.62%)
 

Overview

FTM Balance

Fantom LogoFantom LogoFantom Logo0 FTM

FTM Value

$0.00

Sponsored

Transaction Hash
Method
Block
From
To
0x60806040506165132022-11-08 12:08:42700 days ago1667909322IN
 Create: SFCLib
0 FTM0.65992979150.20833333

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
872125112024-07-30 10:25:3970 days ago1722335139
0xAdB3D6B5...2Fa2f470A
713.14221152 FTM
872124802024-07-30 10:25:1670 days ago1722335116
0xAdB3D6B5...2Fa2f470A
1,387.43487012 FTM
872124482024-07-30 10:24:5370 days ago1722335093
0xAdB3D6B5...2Fa2f470A
461.31501031 FTM
872107832024-07-30 10:03:2370 days ago1722333803
0xAdB3D6B5...2Fa2f470A
4,559.97579953 FTM
872073952024-07-30 9:19:3270 days ago1722331172
0xAdB3D6B5...2Fa2f470A
23.35200458 FTM
872073012024-07-30 9:18:1870 days ago1722331098
0xAdB3D6B5...2Fa2f470A
0.16524345 FTM
872049302024-07-30 8:48:0770 days ago1722329287
0xAdB3D6B5...2Fa2f470A
10,814 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
12.13350855 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
10.9201577 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
16.92662589 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
12.14100432 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
7.3286987 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
23.89243009 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
12.12379223 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
12.14100432 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
12.21449784 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
12.17472794 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
12.15338706 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
4.88579913 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
12.16777738 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
12.20527504 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
24.28200865 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
18.23008059 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
12.16777738 FTM
872012172024-07-30 8:00:5070 days ago1722326450
0xAdB3D6B5...2Fa2f470A
12.20527504 FTM
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SFCLib

Compiler Version
v0.5.17+commit.d19bba13

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion
File 1 of 20 : SFCLib.sol
pragma solidity ^0.5.0;

import "../common/Decimal.sol";
import "./GasPriceConstants.sol";
import "./SFCBase.sol";
import "./StakeTokenizer.sol";
import "./NodeDriver.sol";

contract SFCLib is SFCBase {
    event CreatedValidator(uint256 indexed validatorID, address indexed auth, uint256 createdEpoch, uint256 createdTime);
    event Delegated(address indexed delegator, uint256 indexed toValidatorID, uint256 amount);
    event Undelegated(address indexed delegator, uint256 indexed toValidatorID, uint256 indexed wrID, uint256 amount);
    event Withdrawn(address indexed delegator, uint256 indexed toValidatorID, uint256 indexed wrID, uint256 amount);
    event ClaimedRewards(address indexed delegator, uint256 indexed toValidatorID, uint256 lockupExtraReward, uint256 lockupBaseReward, uint256 unlockedReward);
    event RestakedRewards(address indexed delegator, uint256 indexed toValidatorID, uint256 lockupExtraReward, uint256 lockupBaseReward, uint256 unlockedReward);
    event BurntFTM(uint256 amount);
    event LockedUpStake(address indexed delegator, uint256 indexed validatorID, uint256 duration, uint256 amount);
    event UnlockedStake(address indexed delegator, uint256 indexed validatorID, uint256 amount, uint256 penalty);
    event UpdatedSlashingRefundRatio(uint256 indexed validatorID, uint256 refundRatio);
    event RefundedSlashedLegacyDelegation(address indexed delegator, uint256 indexed validatorID, uint256 amount);

    /*
    Constructor
    */

    function setGenesisValidator(address auth, uint256 validatorID, bytes calldata pubkey, uint256 status, uint256 createdEpoch, uint256 createdTime, uint256 deactivatedEpoch, uint256 deactivatedTime) external onlyDriver {
        _rawCreateValidator(auth, validatorID, pubkey, status, createdEpoch, createdTime, deactivatedEpoch, deactivatedTime);
        if (validatorID > lastValidatorID) {
            lastValidatorID = validatorID;
        }
    }

    function setGenesisDelegation(address delegator, uint256 toValidatorID, uint256 stake, uint256 lockedStake, uint256 lockupFromEpoch, uint256 lockupEndTime, uint256 lockupDuration, uint256 earlyUnlockPenalty, uint256 rewards) external onlyDriver {
        _rawDelegate(delegator, toValidatorID, stake, false);
        _rewardsStash[delegator][toValidatorID].unlockedReward = rewards;
        _mintNativeToken(stake);
        if (lockedStake != 0) {
            require(lockedStake <= stake, "locked stake is greater than the whole stake");
            LockedDelegation storage ld = getLockupInfo[delegator][toValidatorID];
            ld.lockedStake = lockedStake;
            ld.fromEpoch = lockupFromEpoch;
            ld.endTime = lockupEndTime;
            ld.duration = lockupDuration;
            getStashedLockupRewards[delegator][toValidatorID].lockupExtraReward = earlyUnlockPenalty;
            emit LockedUpStake(delegator, toValidatorID, lockupDuration, lockedStake);
        }
    }

    /*
    Methods
    */

    function createValidator(bytes calldata pubkey) external payable {
        require(msg.value >= c.minSelfStake(), "insufficient self-stake");
        require(pubkey.length == 66 && pubkey[0] == 0xc0, "malformed pubkey");
        require(pubkeyHashToValidatorID[keccak256(pubkey)] == 0, "already used");
        _createValidator(msg.sender, pubkey);
        _delegate(msg.sender, lastValidatorID, msg.value);
    }

    function _createValidator(address auth, bytes memory pubkey) internal {
        uint256 validatorID = ++lastValidatorID;
        _rawCreateValidator(auth, validatorID, pubkey, OK_STATUS, currentEpoch(), _now(), 0, 0);
    }

    function _rawCreateValidator(address auth, uint256 validatorID, bytes memory pubkey, uint256 status, uint256 createdEpoch, uint256 createdTime, uint256 deactivatedEpoch, uint256 deactivatedTime) internal {
        require(getValidatorID[auth] == 0, "validator already exists");
        getValidatorID[auth] = validatorID;
        getValidator[validatorID].status = status;
        getValidator[validatorID].createdEpoch = createdEpoch;
        getValidator[validatorID].createdTime = createdTime;
        getValidator[validatorID].deactivatedTime = deactivatedTime;
        getValidator[validatorID].deactivatedEpoch = deactivatedEpoch;
        getValidator[validatorID].auth = auth;
        getValidatorPubkey[validatorID] = pubkey;
        pubkeyHashToValidatorID[keccak256(pubkey)] = validatorID;

        emit CreatedValidator(validatorID, auth, createdEpoch, createdTime);
        if (deactivatedEpoch != 0) {
            emit DeactivatedValidator(validatorID, deactivatedEpoch, deactivatedTime);
        }
        if (status != 0) {
            emit ChangedValidatorStatus(validatorID, status);
        }
    }

    function getSelfStake(uint256 validatorID) public view returns (uint256) {
        return getStake[getValidator[validatorID].auth][validatorID];
    }

    function _checkDelegatedStakeLimit(uint256 validatorID) internal view returns (bool) {
        return getValidator[validatorID].receivedStake <= getSelfStake(validatorID).mul(c.maxDelegatedRatio()).div(Decimal.unit());
    }

    function delegate(uint256 toValidatorID) external payable {
        _delegate(msg.sender, toValidatorID, msg.value);
    }

    function _delegate(address delegator, uint256 toValidatorID, uint256 amount) internal {
        require(_validatorExists(toValidatorID), "validator doesn't exist");
        require(getValidator[toValidatorID].status == OK_STATUS, "validator isn't active");
        _rawDelegate(delegator, toValidatorID, amount, true);
        require(_checkDelegatedStakeLimit(toValidatorID), "validator's delegations limit is exceeded");
    }

    function _rawDelegate(address delegator, uint256 toValidatorID, uint256 amount, bool strict) internal {
        require(amount > 0, "zero amount");

        _stashRewards(delegator, toValidatorID);

        getStake[delegator][toValidatorID] = getStake[delegator][toValidatorID].add(amount);
        uint256 origStake = getValidator[toValidatorID].receivedStake;
        getValidator[toValidatorID].receivedStake = origStake.add(amount);
        totalStake = totalStake.add(amount);
        if (getValidator[toValidatorID].status == OK_STATUS) {
            totalActiveStake = totalActiveStake.add(amount);
        }

        _syncValidator(toValidatorID, origStake == 0);

        emit Delegated(delegator, toValidatorID, amount);

        _recountVotes(delegator, getValidator[toValidatorID].auth, strict);
    }

    function recountVotes(address delegator, address validatorAuth, bool strict, uint256 gas) external {
        (bool success,) = voteBookAddress.call.gas(gas)(abi.encodeWithSignature("recountVotes(address,address)", delegator, validatorAuth));
        require(success || !strict, "gov votes recounting failed");
    }

    function _rawUndelegate(address delegator, uint256 toValidatorID, uint256 amount, bool strict, bool forceful, bool checkDelegatedStake) internal {
        getStake[delegator][toValidatorID] -= amount;
        getValidator[toValidatorID].receivedStake = getValidator[toValidatorID].receivedStake.sub(amount);
        totalStake = totalStake.sub(amount);
        if (getValidator[toValidatorID].status == OK_STATUS) {
            totalActiveStake = totalActiveStake.sub(amount);
        }

        uint256 selfStakeAfterwards = getSelfStake(toValidatorID);
        if (selfStakeAfterwards != 0 && getValidator[toValidatorID].status == OK_STATUS) {
            if (!(selfStakeAfterwards >= c.minSelfStake())) {
                if (forceful) {
                    revert("insufficient self-stake");
                } else {
                    _setValidatorDeactivated(toValidatorID, WITHDRAWN_BIT);
                }
            }
            require(!checkDelegatedStake || _checkDelegatedStakeLimit(toValidatorID), "validator's delegations limit is exceeded");
        } else {
            _setValidatorDeactivated(toValidatorID, WITHDRAWN_BIT);
        }

        _recountVotes(delegator, getValidator[toValidatorID].auth, strict);
    }

    function undelegate(uint256 toValidatorID, uint256 wrID, uint256 amount) public {
        address delegator = msg.sender;

        _stashRewards(delegator, toValidatorID);

        require(amount > 0, "zero amount");
        require(amount <= getUnlockedStake(delegator, toValidatorID), "not enough unlocked stake");
        require(_checkAllowedToWithdraw(delegator, toValidatorID), "outstanding sFTM balance");

        require(getWithdrawalRequest[delegator][toValidatorID][wrID].amount == 0, "wrID already exists");

        _rawUndelegate(delegator, toValidatorID, amount, true, false, true);

        getWithdrawalRequest[delegator][toValidatorID][wrID].amount = amount;
        getWithdrawalRequest[delegator][toValidatorID][wrID].epoch = currentEpoch();
        getWithdrawalRequest[delegator][toValidatorID][wrID].time = _now();

        _syncValidator(toValidatorID, false);

        emit Undelegated(delegator, toValidatorID, wrID, amount);
    }

    // liquidateSFTM is used for finalization of last fMint positions with outstanding sFTM balances
    // it allows to undelegate without the unboding period, and also to unlock stake without a penalty.
    // Such a simplification, which might be dangerous generally, is okay here because there's only a small amount
    // of leftover sFTM
    function liquidateSFTM(address delegator, uint256 toValidatorID, uint256 amount) external {
        require(msg.sender == sftmFinalizer, "not sFTM finalizer");
        _stashRewards(delegator, toValidatorID);

        require(amount > 0, "zero amount");
        StakeTokenizer(stakeTokenizerAddress).redeemSFTMFor(msg.sender, delegator, toValidatorID, amount);
        require(amount <= getStake[delegator][toValidatorID], "not enough stake");
        uint256 unlockedStake = getUnlockedStake(delegator, toValidatorID);
        if (amount > unlockedStake) {
            LockedDelegation storage ld = getLockupInfo[delegator][toValidatorID];
            ld.lockedStake = ld.lockedStake.sub(amount - unlockedStake);
            emit UnlockedStake(delegator, toValidatorID, amount - unlockedStake, 0);
        }

        _rawUndelegate(delegator, toValidatorID, amount, false, true, false);

        _syncValidator(toValidatorID, false);

        emit Undelegated(delegator, toValidatorID, 0xffffffffff, amount);

        // It's important that we transfer after erasing (protection against Re-Entrancy)
        (bool sent,) = msg.sender.call.value(amount)("");
        require(sent, "Failed to send FTM");

        emit Withdrawn(delegator, toValidatorID, 0xffffffffff, amount);
    }

    function isSlashed(uint256 validatorID) view public returns (bool) {
        return getValidator[validatorID].status & CHEATER_MASK != 0;
    }

    function getSlashingPenalty(uint256 amount, bool isCheater, uint256 refundRatio) internal pure returns (uint256 penalty) {
        if (!isCheater || refundRatio >= Decimal.unit()) {
            return 0;
        }
        // round penalty upwards (ceiling) to prevent dust amount attacks
        penalty = amount.mul(Decimal.unit() - refundRatio).div(Decimal.unit()).add(1);
        if (penalty > amount) {
            return amount;
        }
        return penalty;
    }

    function _withdraw(address payable delegator, uint256 toValidatorID, uint256 wrID, address payable receiver) private {
        WithdrawalRequest memory request = getWithdrawalRequest[delegator][toValidatorID][wrID];
        require(request.epoch != 0, "request doesn't exist");
        require(_checkAllowedToWithdraw(delegator, toValidatorID), "outstanding sFTM balance");

        uint256 requestTime = request.time;
        uint256 requestEpoch = request.epoch;
        if (getValidator[toValidatorID].deactivatedTime != 0 && getValidator[toValidatorID].deactivatedTime < requestTime) {
            requestTime = getValidator[toValidatorID].deactivatedTime;
            requestEpoch = getValidator[toValidatorID].deactivatedEpoch;
        }

        require(_now() >= requestTime + c.withdrawalPeriodTime(), "not enough time passed");
        require(currentEpoch() >= requestEpoch + c.withdrawalPeriodEpochs(), "not enough epochs passed");

        uint256 amount = getWithdrawalRequest[delegator][toValidatorID][wrID].amount;
        bool isCheater = isSlashed(toValidatorID);
        uint256 penalty = getSlashingPenalty(amount, isCheater, slashingRefundRatio[toValidatorID]);
        delete getWithdrawalRequest[delegator][toValidatorID][wrID];

        totalSlashedStake += penalty;
        require(amount > penalty, "stake is fully slashed");
        // It's important that we transfer after erasing (protection against Re-Entrancy)
        (bool sent,) = receiver.call.value(amount.sub(penalty))("");
        require(sent, "Failed to send FTM");
        _burnFTM(penalty);

        emit Withdrawn(delegator, toValidatorID, wrID, amount);
    }

    function withdraw(uint256 toValidatorID, uint256 wrID) public {
        _withdraw(msg.sender, toValidatorID, wrID, _receiverOf(msg.sender));
    }

    function deactivateValidator(uint256 validatorID, uint256 status) external onlyDriver {
        require(status != OK_STATUS, "wrong status");

        _setValidatorDeactivated(validatorID, status);
        _syncValidator(validatorID, false);
        address validatorAddr = getValidator[validatorID].auth;
        _recountVotes(validatorAddr, validatorAddr, false);
    }

    function _highestPayableEpoch(uint256 validatorID) internal view returns (uint256) {
        if (getValidator[validatorID].deactivatedEpoch != 0) {
            if (currentSealedEpoch < getValidator[validatorID].deactivatedEpoch) {
                return currentSealedEpoch;
            }
            return getValidator[validatorID].deactivatedEpoch;
        }
        return currentSealedEpoch;
    }

    // find highest epoch such that _isLockedUpAtEpoch returns true (using binary search)
    function _highestLockupEpoch(address delegator, uint256 validatorID) internal view returns (uint256) {
        uint256 l = getLockupInfo[delegator][validatorID].fromEpoch;
        uint256 r = currentSealedEpoch;
        if (_isLockedUpAtEpoch(delegator, validatorID, r)) {
            return r;
        }
        if (!_isLockedUpAtEpoch(delegator, validatorID, l)) {
            return 0;
        }
        if (l > r) {
            return 0;
        }
        while (l < r) {
            uint256 m = (l + r) / 2;
            if (_isLockedUpAtEpoch(delegator, validatorID, m)) {
                l = m + 1;
            } else {
                r = m;
            }
        }
        if (r == 0) {
            return 0;
        }
        return r - 1;
    }

    function _newRewards(address delegator, uint256 toValidatorID) internal view returns (Rewards memory) {
        uint256 stashedUntil = stashedRewardsUntilEpoch[delegator][toValidatorID];
        uint256 payableUntil = _highestPayableEpoch(toValidatorID);
        uint256 lockedUntil = _highestLockupEpoch(delegator, toValidatorID);
        if (lockedUntil > payableUntil) {
            lockedUntil = payableUntil;
        }
        if (lockedUntil < stashedUntil) {
            lockedUntil = stashedUntil;
        }

        LockedDelegation storage ld = getLockupInfo[delegator][toValidatorID];
        uint256 wholeStake = getStake[delegator][toValidatorID];
        uint256 unlockedStake = wholeStake.sub(ld.lockedStake);
        uint256 fullReward;

        // count reward for locked stake during lockup epochs
        fullReward = _newRewardsOf(ld.lockedStake, toValidatorID, stashedUntil, lockedUntil);
        Rewards memory plReward = _scaleLockupReward(fullReward, ld.duration);
        // count reward for unlocked stake during lockup epochs
        fullReward = _newRewardsOf(unlockedStake, toValidatorID, stashedUntil, lockedUntil);
        Rewards memory puReward = _scaleLockupReward(fullReward, 0);
        // count lockup reward for unlocked stake during unlocked epochs
        fullReward = _newRewardsOf(wholeStake, toValidatorID, lockedUntil, payableUntil);
        Rewards memory wuReward = _scaleLockupReward(fullReward, 0);

        return sumRewards(plReward, puReward, wuReward);
    }

    function _newRewardsOf(uint256 stakeAmount, uint256 toValidatorID, uint256 fromEpoch, uint256 toEpoch) internal view returns (uint256) {
        if (fromEpoch >= toEpoch) {
            return 0;
        }
        uint256 stashedRate = getEpochSnapshot[fromEpoch].accumulatedRewardPerToken[toValidatorID];
        uint256 currentRate = getEpochSnapshot[toEpoch].accumulatedRewardPerToken[toValidatorID];
        return currentRate.sub(stashedRate).mul(stakeAmount).div(Decimal.unit());
    }

    function _pendingRewards(address delegator, uint256 toValidatorID) internal view returns (Rewards memory) {
        Rewards memory reward = _newRewards(delegator, toValidatorID);
        return sumRewards(_rewardsStash[delegator][toValidatorID], reward);
    }

    function pendingRewards(address delegator, uint256 toValidatorID) public view returns (uint256) {
        Rewards memory reward = _pendingRewards(delegator, toValidatorID);
        return reward.unlockedReward.add(reward.lockupBaseReward).add(reward.lockupExtraReward);
    }

    function stashRewards(address delegator, uint256 toValidatorID) external {
        require(_stashRewards(delegator, toValidatorID), "nothing to stash");
    }

    function _stashRewards(address delegator, uint256 toValidatorID) internal returns (bool updated) {
        Rewards memory nonStashedReward = _newRewards(delegator, toValidatorID);
        stashedRewardsUntilEpoch[delegator][toValidatorID] = _highestPayableEpoch(toValidatorID);
        _rewardsStash[delegator][toValidatorID] = sumRewards(_rewardsStash[delegator][toValidatorID], nonStashedReward);
        getStashedLockupRewards[delegator][toValidatorID] = sumRewards(getStashedLockupRewards[delegator][toValidatorID], nonStashedReward);
        if (!isLockedUp(delegator, toValidatorID)) {
            delete getLockupInfo[delegator][toValidatorID];
            delete getStashedLockupRewards[delegator][toValidatorID];
        }
        _truncateLegacyPenalty(delegator, toValidatorID);
        return nonStashedReward.lockupBaseReward != 0 || nonStashedReward.lockupExtraReward != 0 || nonStashedReward.unlockedReward != 0;
    }

    function _claimRewards(address delegator, uint256 toValidatorID) internal returns (Rewards memory rewards) {
        require(_checkAllowedToWithdraw(delegator, toValidatorID), "outstanding sFTM balance");
        _stashRewards(delegator, toValidatorID);
        rewards = _rewardsStash[delegator][toValidatorID];
        uint256 totalReward = rewards.unlockedReward.add(rewards.lockupBaseReward).add(rewards.lockupExtraReward);
        require(totalReward != 0, "zero rewards");
        delete _rewardsStash[delegator][toValidatorID];
        // It's important that we mint after erasing (protection against Re-Entrancy)
        _mintNativeToken(totalReward);
        return rewards;
    }

    function claimRewards(uint256 toValidatorID) public {
        address payable delegator = msg.sender;
        Rewards memory rewards = _claimRewards(delegator, toValidatorID);
        // It's important that we transfer after erasing (protection against Re-Entrancy)
        (bool sent,) = _receiverOf(delegator).call.value(rewards.lockupExtraReward.add(rewards.lockupBaseReward).add(rewards.unlockedReward))("");
        require(sent, "Failed to send FTM");

        emit ClaimedRewards(delegator, toValidatorID, rewards.lockupExtraReward, rewards.lockupBaseReward, rewards.unlockedReward);
    }

    function restakeRewards(uint256 toValidatorID) public {
        address delegator = msg.sender;
        Rewards memory rewards = _claimRewards(delegator, toValidatorID);

        uint256 lockupReward = rewards.lockupExtraReward.add(rewards.lockupBaseReward);
        _delegate(delegator, toValidatorID, lockupReward.add(rewards.unlockedReward));
        getLockupInfo[delegator][toValidatorID].lockedStake += lockupReward;
        emit RestakedRewards(delegator, toValidatorID, rewards.lockupExtraReward, rewards.lockupBaseReward, rewards.unlockedReward);
    }

    // burnFTM allows SFC to burn an arbitrary amount of FTM tokens
    function burnFTM(uint256 amount) onlyOwner external {
        _burnFTM(amount);
    }

    function _burnFTM(uint256 amount) internal {
        if (amount != 0) {
            address(0).transfer(amount);
            emit BurntFTM(amount);
        }
    }

    function epochEndTime(uint256 epoch) view internal returns (uint256) {
        return getEpochSnapshot[epoch].endTime;
    }

    function _isLockedUpAtEpoch(address delegator, uint256 toValidatorID, uint256 epoch) internal view returns (bool) {
        return getLockupInfo[delegator][toValidatorID].fromEpoch <= epoch && epochEndTime(epoch) <= getLockupInfo[delegator][toValidatorID].endTime;
    }

    function _checkAllowedToWithdraw(address delegator, uint256 toValidatorID) internal view returns (bool) {
        if (stakeTokenizerAddress == address(0)) {
            return true;
        }
        return StakeTokenizer(stakeTokenizerAddress).allowedToWithdrawStake(delegator, toValidatorID);
    }

    function getUnlockedStake(address delegator, uint256 toValidatorID) public view returns (uint256) {
        if (!isLockedUp(delegator, toValidatorID)) {
            return getStake[delegator][toValidatorID];
        }
        return getStake[delegator][toValidatorID].sub(getLockupInfo[delegator][toValidatorID].lockedStake);
    }

    function _lockStake(address delegator, uint256 toValidatorID, uint256 lockupDuration, uint256 amount, bool relock) internal {
        require(!_redirected(delegator), "redirected");
        require(amount <= getUnlockedStake(delegator, toValidatorID), "not enough stake");
        require(getValidator[toValidatorID].status == OK_STATUS, "validator isn't active");

        require(lockupDuration >= c.minLockupDuration() && lockupDuration <= c.maxLockupDuration(), "incorrect duration");
        uint256 endTime = _now().add(lockupDuration);
        address validatorAddr = getValidator[toValidatorID].auth;
        if (delegator != validatorAddr) {
            require(getLockupInfo[validatorAddr][toValidatorID].endTime + 30 * 24 * 60 * 60 >= endTime, "validator's lockup will end too early");
        }

        _stashRewards(delegator, toValidatorID);
        _delStalePenalties(delegator, toValidatorID);

        // stash the previous penalty and clean getStashedLockupRewards
        LockedDelegation storage ld = getLockupInfo[delegator][toValidatorID];
        if (relock) {
            Penalty[] storage penalties = getStashedPenalties[delegator][toValidatorID];

            uint256 penalty = _popNonStashedUnlockPenalty(delegator, toValidatorID, ld.lockedStake, ld.lockedStake);
            if (penalty != 0) {
                penalties.push(Penalty(penalty, ld.endTime));
                require(penalties.length <= 30, "too many ongoing relocks");
                require(amount > ld.lockedStake / 100 || penalties.length <= 3 || endTime >= ld.endTime + 14 * 24 * 60 * 60, "too frequent relocks (github.com/Fantom-foundation/opera-sfc/wiki/Lockup-calls-reference#re-lock-stake)");
            }
        }

        // check lockup duration after _stashRewards, which has erased previous lockup if it has unlocked already
        require(lockupDuration >= ld.duration, "lockup duration cannot decrease");

        ld.lockedStake = ld.lockedStake.add(amount);
        ld.fromEpoch = currentEpoch();
        ld.endTime = endTime;
        ld.duration = lockupDuration;

        emit LockedUpStake(delegator, toValidatorID, lockupDuration, amount);
    }

    function lockStake(uint256 toValidatorID, uint256 lockupDuration, uint256 amount) public {
        address delegator = msg.sender;
        require(amount > 0, "zero amount");
        require(!isLockedUp(delegator, toValidatorID), "already locked up");
        _lockStake(delegator, toValidatorID, lockupDuration, amount, false);
    }

    function relockStake(uint256 toValidatorID, uint256 lockupDuration, uint256 amount) public {
        address delegator = msg.sender;
        require(isLockedUp(delegator, toValidatorID), "not locked up");
        _lockStake(delegator, toValidatorID, lockupDuration, amount, true);
    }

    function _popNonStashedUnlockPenalty(address delegator, uint256 toValidatorID, uint256 unlockAmount, uint256 totalAmount) internal returns (uint256) {
        Rewards storage r = getStashedLockupRewards[delegator][toValidatorID];
        uint256 lockupExtraRewardShare = r.lockupExtraReward.mul(unlockAmount).div(totalAmount);
        uint256 lockupBaseRewardShare = r.lockupBaseReward.mul(unlockAmount).div(totalAmount);
        uint256 penalty = lockupExtraRewardShare + lockupBaseRewardShare / 2;
        r.lockupExtraReward = r.lockupExtraReward.sub(lockupExtraRewardShare);
        r.lockupBaseReward = r.lockupBaseReward.sub(lockupBaseRewardShare);
        return penalty;
    }

    function _popStashedUnlockPenalty(address delegator, uint256 toValidatorID, uint256 unlockAmount, uint256 totalAmount) internal returns (uint256) {
        _delStalePenalties(delegator, toValidatorID);
        Penalty[] storage penalties = getStashedPenalties[delegator][toValidatorID];
        uint256 total = 0;
        for (uint256 i = 0; i < penalties.length; i++) {
            uint256 penalty = penalties[i].amount.mul(unlockAmount).div(totalAmount);
            penalties[i].amount = penalties[i].amount.sub(penalty);
            total = total.add(penalty);
        }
        return total;
    }

    function _popWholeUnlockPenalty(address delegator, uint256 toValidatorID, uint256 unlockAmount, uint256 totalAmount) internal returns (uint256) {
        uint256 nonStashed = _popNonStashedUnlockPenalty(delegator, toValidatorID, unlockAmount, totalAmount);
        uint256 stashed = _popStashedUnlockPenalty(delegator, toValidatorID, unlockAmount, totalAmount);
        return nonStashed + stashed;
    }

    function unlockStake(uint256 toValidatorID, uint256 amount) external returns (uint256) {
        address delegator = msg.sender;
        LockedDelegation storage ld = getLockupInfo[delegator][toValidatorID];

        require(amount > 0, "zero amount");
        require(isLockedUp(delegator, toValidatorID), "not locked up");
        require(amount <= ld.lockedStake, "not enough locked stake");
        require(_checkAllowedToWithdraw(delegator, toValidatorID), "outstanding sFTM balance");
        require(!_redirected(delegator), "redirected");

        _stashRewards(delegator, toValidatorID);

        uint256 penalty = _popWholeUnlockPenalty(delegator, toValidatorID, amount, ld.lockedStake);
        if (penalty > amount) {
            penalty = amount;
        }
        ld.lockedStake -= amount;
        if (penalty != 0) {
            _rawUndelegate(delegator, toValidatorID, penalty, true, false, false);
            treasuryAddress.call.value(penalty)("");
        }

        emit UnlockedStake(delegator, toValidatorID, amount, penalty);
        return penalty;
    }

    function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio) onlyOwner external {
        require(isSlashed(validatorID), "validator isn't slashed");
        require(refundRatio <= Decimal.unit(), "must be less than or equal to 1.0");
        slashingRefundRatio[validatorID] = refundRatio;
        emit UpdatedSlashingRefundRatio(validatorID, refundRatio);
    }

    function _delStalePenalties(address delegator, uint256 toValidatorID) public {
        Penalty[] storage penalties = getStashedPenalties[delegator][toValidatorID];
        for (uint256 i = 0; i < penalties.length;) {
            if (penalties[i].end < _now() || penalties[i].amount == 0) {
                penalties[i] = penalties[penalties.length - 1];
                penalties.pop();
            } else {
                i++;
            }
        }
    }

    function redirectedAccs() private pure returns(address[] memory, address[] memory) {
        // the addresses below were reported as stolen by their owners via the signatures below:
//        I redirect SFC withdrawals to account 0x80f93310709624636852d0111fd6c4A6e02ED0aA due to a potential attacker gaining access to my account.
//        {
//        "address": "0x93419fcb5d9dc7989439f0512d4f737421ed48d9",
//        "msg": "0x4920726564697265637420534643207769746864726177616c7320746f206163636f756e74203078383066393333313037303936323436333638353264303131316664366334413665303245443061412064756520746f206120706f74656e7469616c2061747461636b6572206761696e696e672061636365737320746f206d79206163636f756e742e",
//        "sig": "1c4f3168e01d499a657f0d1cd453b26e5f69aaf14372983ff62e54a1d53959e55edb0746f4aea0959899b06bf31dc6a0160f6ac428cd75d4657184ab2337e46e1c",
//        "version": "3",
//        "signer": "MEW"
//        }
//        --
//        I redirect SFC withdrawals to account 0x91B20102Dfd2ff1b00D0915266584009d0b1Ae39 due to a potential attacker gaining access to my account.
//        {
//        "address": "0xfbcae1b28ca5039dafec4f10a89e022bc8118394",
//        "msg": "0x4920726564697265637420534643207769746864726177616c7320746f206163636f756e74203078393142323031303244666432666631623030443039313532363635383430303964306231416533392064756520746f206120706f74656e7469616c2061747461636b6572206761696e696e672061636365737320746f206d79206163636f756e742e",
//        "sig": "c98431cc1b6f26b8248ca83f860721f31ec79097831e69c28d352512182bbfa93911564ed46ba11547b544c4d65380781a4f3cc6afe9f075d43a24e0947853151c",
//        "version": "3",
//        "signer": "MEW"
//        }
//        --
//        I redirect SFC withdrawals to account 0xCA3C54c11172A7263300a801E9937780b5143c08 due to a potential attacker gaining access to my account.
//        {
//        "address": "0x15c2ec517905fb3282f26f3ac3e12889755a2ed7",
//        "msg": "0x4920726564697265637420534643207769746864726177616c7320746f206163636f756e74203078434133433534633131313732413732363333303061383031453939333737383062353134336330382064756520746f206120706f74656e7469616c2061747461636b6572206761696e696e672061636365737320746f206d79206163636f756e742e",
//        "sig": "8d933ea6b1dfaa70c92d7dd8f68e9c821934eabd9c454dc792a90c9c58d0c4ec5c60d7737e7b8ed38cfdfe3bd7fce9a2c38133b9a98d6699088d79edb09ec3c21b",
//        "version": "3",
//        "signer": "MEW"
//        }
// --
// I redirect SFC withdrawals to account 0x5A1CAd027EACE4C052f5DEE0f42Da6c62E39b779 due to a potential attacker gaining access to my account.
// {
//  "address": "0xbdAaEC5f9317cC63D26FD7d79aD17372Ccd7d763",
//  "msg": "0x4920726564697265637420534643207769746864726177616c7320746f206163636f756e74203078354131434164303237454143453443303532663544454530663432446136633632453339623737392064756520746f206120706f74656e7469616c2061747461636b6572206761696e696e672061636365737320746f206d79206163636f756e742e",
//  "sig": "0e9b3ce37f665ab03bdfd3095671249e1b2842b1dd314fd4281bbed527ea69014ca510227e57f973b35ef175c1214fb1a842be70ff5a9290cb260799c544eed900",
//  "version": "3",
//  "signer": "MEW"
// }
// --
// I redirect SFC withdrawals to account 0x4A15B527475977D9B0CB3fcfE825d6Aa7428fAFC due to a potential attacker gaining access to my account.
// {
//  "address": "0xf72148504819A1D1B038694B02d299F65BfA312d",
//  "msg": "0x4920726564697265637420534643207769746864726177616c7320746f206163636f756e74203078344131354235323734373539373744394230434233666366453832356436416137343238664146432064756520746f206120706f74656e7469616c2061747461636b6572206761696e696e672061636365737320746f206d79206163636f756e742e",
//  "sig": "cf6386edbbee504c07ae95cb7c5ef06e7e0f57b34d51ab4e4047b5cb326af9bc236f544a3ced994cd20601047966e683aaaf329772fbb6bf37f0bd12200d1e6100",
//  "version": "3",
//  "signer": "MEW"
// }
        // The contract does not lock these positions; instead, it restricts withdrawals exclusively to the account designated in the signature.
        // This measure prevents an attacker from transferring FTM following a withdrawal.

        address[] memory froms = new address[](5);
        address[] memory tos = new address[](5);
        assert(froms.length == tos.length);
        froms[0] = 0x93419FcB5d9DC7989439f0512d4F737421ed48D9;
        tos[0] = 0x80f93310709624636852d0111fd6c4A6e02ED0aA;
        froms[1] = 0xFbCAe1B28ca5039DAFec4f10A89e022Bc8118394;
        tos[1] = 0x91B20102Dfd2ff1b00D0915266584009d0b1Ae39;
        froms[2] = 0x15C2EC517905fB3282f26F3aC3e12889755a2ed7;
        tos[2] = 0xCA3C54c11172A7263300a801E9937780b5143c08;
        froms[3] = 0xbdAaEC5f9317cC63D26FD7d79aD17372Ccd7d763;
        tos[3] = 0x5A1CAd027EACE4C052f5DEE0f42Da6c62E39b779;
        froms[4] = 0xf72148504819A1D1B038694B02d299F65BfA312d;
        tos[4] = 0x4A15B527475977D9B0CB3fcfE825d6Aa7428fAFC;
        return (froms, tos);
    }

    function _redirected(address addr) internal view returns(bool) {
        (address[] memory froms,) = redirectedAccs();
        for (uint256 i = 0; i < froms.length; i++) {
            if (addr == froms[i]) {
                return true;
            }
        }
        return getRedirection[addr] != address(0);
    }

    function _redirectedTo(address addr) internal view returns(address) {
        (address[] memory froms, address[] memory tos) = redirectedAccs();
        for (uint256 i = 0; i < froms.length; i++) {
            if (addr == froms[i]) {
                return tos[i];
            }
        }
        return getRedirection[addr];
    }

    function _receiverOf(address addr) internal view returns(address payable) {
        address to = _redirectedTo(addr);
        if (to == address(0)) {
            return address(uint160(addr));
        }
        return address(uint160(to));
    }

    // code below can be erased after 1 year since deployment of multipenalties

    function _getAvgEpochStep(uint256 duration) internal view returns(uint256) {
        // estimate number of epochs such that we would make approximately 15 iterations
        uint256 tryEpochs = currentSealedEpoch / 5;
        if (tryEpochs > 10000) {
            tryEpochs = 10000;
        }
        uint256 tryEndTime = getEpochSnapshot[currentSealedEpoch - tryEpochs].endTime;
        if (tryEndTime == 0 || tryEpochs == 0) {
            return 0;
        }
        uint256 secondsPerEpoch = _now().sub(tryEndTime) / tryEpochs;
        return duration / (secondsPerEpoch * 15 + 1);
    }

    function _getAvgReceivedStake(uint256 validatorID, uint256 duration, uint256 step) internal view returns(uint256) {
        uint256 receivedStakeSum = getValidator[validatorID].receivedStake;
        uint256 samples = 1;

        uint256 until = _now().sub(duration);
        for (uint256 i = 1; i <= 30; i++) {
            uint256 e = currentSealedEpoch - i * step;
            EpochSnapshot storage s = getEpochSnapshot[e];
            if (s.endTime < until) {
                break;
            }
            uint256 sample = s.receivedStake[validatorID];
            if (sample != 0) {
                samples++;
                receivedStakeSum += sample;
            }
        }
        return receivedStakeSum / samples;
    }

    function _getAvgUptime(uint256 validatorID, uint256 duration, uint256 step) internal view returns(uint256) {
        uint256 until = _now().sub(duration);
        uint256 oldUptimeCounter = 0;
        uint256 newUptimeCounter = 0;
        for (uint256 i = 0; i <= 30; i++) {
            uint256 e = currentSealedEpoch - i * step;
            EpochSnapshot storage s = getEpochSnapshot[e];
            uint256 endTime = s.endTime;
            if (endTime < until) {
                if (i <= 2) {
                    return duration;
                }
                break;
            }
            uint256 uptimeCounter = s.accumulatedUptime[validatorID];
            if (uptimeCounter != 0) {
                oldUptimeCounter = uptimeCounter;
                if (newUptimeCounter == 0) {
                    newUptimeCounter = uptimeCounter;
                }
            }
        }
        uint256 uptime = newUptimeCounter - oldUptimeCounter;
        if (uptime > duration*4/5) {
            return duration;
        }
        return uptime;
    }

    function _truncateLegacyPenalty(address delegator, uint256 toValidatorID) internal {
        Rewards storage r = getStashedLockupRewards[delegator][toValidatorID];
        uint256 storedPenalty = r.lockupExtraReward + r.lockupBaseReward / 2;
        if (storedPenalty == 0) {
            return;
        }
        LockedDelegation storage ld = getLockupInfo[delegator][toValidatorID];
        uint256 duration = ld.duration;
        uint256 lockedStake = ld.lockedStake;
        uint256 step = _getAvgEpochStep(duration);
        if (step == 0) {
            return;
        }
        uint256 RPS = _getAvgUptime(toValidatorID, duration, step).mul(2092846271).div(duration); // corresponds to 6.6% APR
        uint256 selfStake = getStake[delegator][toValidatorID];

        uint256 avgFullReward = selfStake.mul(RPS).mul(duration).div(1e18).mul(Decimal.unit().sub(c.validatorCommission())).div(Decimal.unit()); // reward for self-stake
        if (getValidator[toValidatorID].auth == delegator) { // reward for received portion of stake
            uint256 receivedStakeAvg = _getAvgReceivedStake(toValidatorID, duration, step).mul(11).div(10);
            avgFullReward += receivedStakeAvg.mul(RPS).mul(duration).div(1e18).mul(c.validatorCommission()).div(Decimal.unit());
        }
        avgFullReward = avgFullReward.mul(lockedStake).div(selfStake);
        Rewards memory avgReward = _scaleLockupReward(avgFullReward, duration);
        uint256 maxReasonablePenalty = avgReward.lockupBaseReward / 2 + avgReward.lockupExtraReward;
        maxReasonablePenalty = maxReasonablePenalty;
        if (storedPenalty > maxReasonablePenalty) {
            r.lockupExtraReward = r.lockupExtraReward.mul(maxReasonablePenalty).div(storedPenalty);
            r.lockupBaseReward = r.lockupBaseReward.mul(maxReasonablePenalty).div(storedPenalty);
        }
    }
}

File 2 of 20 : Decimal.sol
pragma solidity ^0.5.0;

library Decimal {
    // unit is used for decimals, e.g. 0.123456
    function unit() internal pure returns (uint256) {
        return 1e18;
    }
}

File 3 of 20 : GasPriceConstants.sol
pragma solidity ^0.5.0;

import "../common/Decimal.sol";

library GP {
    function trimGasPriceChangeRatio(uint256 x) internal pure returns (uint256) {
        if (x > Decimal.unit() * 105 / 100) {
            return Decimal.unit() * 105 / 100;
        }
        if (x < Decimal.unit() * 95 / 100) {
            return Decimal.unit() * 95 / 100;
        }
        return x;
    }

    function trimMinGasPrice(uint256 x) internal pure returns (uint256) {
        if (x > 1000000 * 1e9) {
            return 1000000 * 1e9;
        }
        if (x < 1e9) {
            return 1e9;
        }
        return x;
    }

    function initialMinGasPrice() internal pure returns (uint256) {
        return 100 * 1e9;
    }
}

File 4 of 20 : SFCBase.sol
pragma solidity ^0.5.0;

import "./SFCState.sol";

contract SFCBase is SFCState {
    using SafeMath for uint256;

    uint256 internal constant OK_STATUS = 0;
    uint256 internal constant WITHDRAWN_BIT = 1;
    uint256 internal constant OFFLINE_BIT = 1 << 3;
    uint256 internal constant DOUBLESIGN_BIT = 1 << 7;
    uint256 internal constant CHEATER_MASK = DOUBLESIGN_BIT;

    event DeactivatedValidator(uint256 indexed validatorID, uint256 deactivatedEpoch, uint256 deactivatedTime);
    event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);

    function isNode(address addr) internal view returns (bool) {
        return addr == address(node);
    }

    modifier onlyDriver() {
        require(isNode(msg.sender), "caller is not the NodeDriverAuth contract");
        _;
    }

    function currentEpoch() public view returns (uint256) {
        return currentSealedEpoch + 1;
    }

    function _calcRawValidatorEpochTxReward(uint256 epochFee, uint256 txRewardWeight, uint256 totalTxRewardWeight) internal view returns (uint256) {
        if (txRewardWeight == 0) {
            return 0;
        }
        uint256 txReward = epochFee.mul(txRewardWeight).div(totalTxRewardWeight);
        // fee reward except burntFeeShare and treasuryFeeShare
        return txReward.mul(Decimal.unit() - c.burntFeeShare() - c.treasuryFeeShare()).div(Decimal.unit());
    }

    function _calcRawValidatorEpochBaseReward(uint256 epochDuration, uint256 _baseRewardPerSecond, uint256 baseRewardWeight, uint256 totalBaseRewardWeight) internal pure returns (uint256) {
        if (baseRewardWeight == 0) {
            return 0;
        }
        uint256 totalReward = epochDuration.mul(_baseRewardPerSecond);
        return totalReward.mul(baseRewardWeight).div(totalBaseRewardWeight);
    }

    function _mintNativeToken(uint256 amount) internal {
        // balance will be increased after the transaction is processed
        node.incBalance(address(this), amount);
        totalSupply = totalSupply.add(amount);
    }

    function sumRewards(Rewards memory a, Rewards memory b) internal pure returns (Rewards memory) {
        return Rewards(a.lockupExtraReward.add(b.lockupExtraReward), a.lockupBaseReward.add(b.lockupBaseReward), a.unlockedReward.add(b.unlockedReward));
    }

    function sumRewards(Rewards memory a, Rewards memory b, Rewards memory c) internal pure returns (Rewards memory) {
        return sumRewards(sumRewards(a, b), c);
    }

    function _scaleLockupReward(uint256 fullReward, uint256 lockupDuration) internal view returns (Rewards memory reward) {
        reward = Rewards(0, 0, 0);
        uint256 unlockedRewardRatio = c.unlockedRewardRatio();
        if (lockupDuration != 0) {
            uint256 maxLockupExtraRatio = Decimal.unit() - unlockedRewardRatio;
            uint256 lockupExtraRatio = maxLockupExtraRatio.mul(lockupDuration).div(c.maxLockupDuration());
            uint256 totalScaledReward = fullReward.mul(unlockedRewardRatio + lockupExtraRatio).div(Decimal.unit());
            reward.lockupBaseReward = fullReward.mul(unlockedRewardRatio).div(Decimal.unit());
            reward.lockupExtraReward = totalScaledReward - reward.lockupBaseReward;
        } else {
            reward.unlockedReward = fullReward.mul(unlockedRewardRatio).div(Decimal.unit());
        }
        return reward;
    }

    function _recountVotes(address delegator, address validatorAuth, bool strict) internal {
        if (voteBookAddress != address(0)) {
            // Don't allow recountVotes to use up all the gas
            (bool success,) = voteBookAddress.call.gas(8000000)(abi.encodeWithSignature("recountVotes(address,address)", delegator, validatorAuth));
            // Don't revert if recountVotes failed unless strict mode enabled
            require(success || !strict, "gov votes recounting failed");
        }
    }

    function _setValidatorDeactivated(uint256 validatorID, uint256 status) internal {
        if (getValidator[validatorID].status == OK_STATUS && status != OK_STATUS) {
            totalActiveStake = totalActiveStake.sub(getValidator[validatorID].receivedStake);
        }
        // status as a number is proportional to severity
        if (status > getValidator[validatorID].status) {
            getValidator[validatorID].status = status;
            if (getValidator[validatorID].deactivatedEpoch == 0) {
                getValidator[validatorID].deactivatedEpoch = currentEpoch();
                getValidator[validatorID].deactivatedTime = _now();
                emit DeactivatedValidator(validatorID, getValidator[validatorID].deactivatedEpoch, getValidator[validatorID].deactivatedTime);
            }
            emit ChangedValidatorStatus(validatorID, status);
        }
    }

    function _syncValidator(uint256 validatorID, bool syncPubkey) public {
        require(_validatorExists(validatorID), "validator doesn't exist");
        // emit special log for node
        uint256 weight = getValidator[validatorID].receivedStake;
        if (getValidator[validatorID].status != OK_STATUS) {
            weight = 0;
        }
        node.updateValidatorWeight(validatorID, weight);
        if (syncPubkey && weight != 0) {
            node.updateValidatorPubkey(validatorID, getValidatorPubkey[validatorID]);
        }
    }

    function _validatorExists(uint256 validatorID) view internal returns (bool) {
        return getValidator[validatorID].createdTime != 0;
    }

    function _calcValidatorCommission(uint256 rawReward, uint256 commission) internal pure returns (uint256)  {
        return rawReward.mul(commission).div(Decimal.unit());
    }

    function getLockedStake(address delegator, uint256 toValidatorID) public view returns (uint256) {
        if (!isLockedUp(delegator, toValidatorID)) {
            return 0;
        }
        return getLockupInfo[delegator][toValidatorID].lockedStake;
    }

    function isLockedUp(address delegator, uint256 toValidatorID) view public returns (bool) {
        return getLockupInfo[delegator][toValidatorID].endTime != 0 && getLockupInfo[delegator][toValidatorID].lockedStake != 0 && _now() <= getLockupInfo[delegator][toValidatorID].endTime;
    }

    function _now() internal view returns (uint256) {
        return block.timestamp;
    }
}

File 5 of 20 : StakeTokenizer.sol
pragma solidity ^0.5.0;

import "./SFC.sol";
import "../erc20/base/ERC20Burnable.sol";
import "../erc20/base/ERC20Mintable.sol";
import "../common/Initializable.sol";

contract Spacer {
    address private _owner;
}

contract StakeTokenizer is Spacer, Initializable {
    SFC internal sfc;

    mapping(address => mapping(uint256 => uint256)) public outstandingSFTM;

    address public sFTMTokenAddress;

    function initialize(address payable _sfc, address _sFTMTokenAddress) public initializer {
        sfc = SFC(_sfc);
        sFTMTokenAddress = _sFTMTokenAddress;
    }

    function mintSFTM(uint256 toValidatorID) external {
        revert("sFTM minting is disabled");
//        address delegator = msg.sender;
//        uint256 lockedStake = sfc.getLockedStake(delegator, toValidatorID);
//        require(lockedStake > 0, "delegation isn't locked up");
//        require(lockedStake > outstandingSFTM[delegator][toValidatorID], "sFTM is already minted");
//
//        uint256 diff = lockedStake - outstandingSFTM[delegator][toValidatorID];
//        outstandingSFTM[delegator][toValidatorID] = lockedStake;
//
//        // It's important that we mint after updating outstandingSFTM (protection against Re-Entrancy)
//        require(ERC20Mintable(sFTMTokenAddress).mint(delegator, diff), "failed to mint sFTM");
    }

    function redeemSFTM(uint256 validatorID, uint256 amount) external {
        require(outstandingSFTM[msg.sender][validatorID] >= amount, "low outstanding sFTM balance");
        require(IERC20(sFTMTokenAddress).allowance(msg.sender, address(this)) >= amount, "insufficient allowance");
        outstandingSFTM[msg.sender][validatorID] -= amount;

        // It's important that we burn after updating outstandingSFTM (protection against Re-Entrancy)
        ERC20Burnable(sFTMTokenAddress).burnFrom(msg.sender, amount);
    }

    function redeemSFTMFor(address payer, address delegator, uint256 validatorID, uint256 amount) external {
        require(msg.sender == address(sfc), "not SFC");
        require(outstandingSFTM[delegator][validatorID] >= amount, "low outstanding sFTM balance");
        require(IERC20(sFTMTokenAddress).allowance(payer, address(this)) >= amount, "insufficient allowance");
        outstandingSFTM[delegator][validatorID] -= amount;

        // It's important that we burn after updating outstandingSFTM (protection against Re-Entrancy)
        ERC20Burnable(sFTMTokenAddress).burnFrom(payer, amount);
    }

    function allowedToWithdrawStake(address sender, uint256 validatorID) public view returns(bool) {
        return outstandingSFTM[sender][validatorID] == 0;
    }
}

File 6 of 20 : NodeDriver.sol
pragma solidity ^0.5.0;

import "../common/SafeMath.sol";
import "../common/Initializable.sol";
import "../ownership/Ownable.sol";
import "./SFCI.sol";


interface NodeDriverExecutable {
    function execute() external;
}

contract NodeDriverAuth is Initializable, Ownable {
    using SafeMath for uint256;

    SFCI internal sfc;
    NodeDriver internal driver;

    // Initialize NodeDriverAuth, NodeDriver and SFC in one call to allow fewer genesis transactions
    function initialize(address payable _sfc, address _driver, address _owner) external initializer {
        Ownable.initialize(_owner);
        driver = NodeDriver(_driver);
        sfc = SFCI(_sfc);
    }

    modifier onlySFC() {
        require(msg.sender == address(sfc), "caller is not the SFC contract");
        _;
    }

    modifier onlyDriver() {
        require(msg.sender == address(driver), "caller is not the NodeDriver contract");
        _;
    }

    function migrateTo(address newDriverAuth) external onlyOwner {
        driver.setBackend(newDriverAuth);
    }

    function _execute(address executable, address newOwner, bytes32 selfCodeHash, bytes32 driverCodeHash) internal {
        _transferOwnership(executable);
        NodeDriverExecutable(executable).execute();
        _transferOwnership(newOwner);
        //require(driver.backend() == address(this), "ownership of driver is lost");
        require(_getCodeHash(address(this)) == selfCodeHash, "self code hash doesn't match");
        require(_getCodeHash(address(driver)) == driverCodeHash, "driver code hash doesn't match");
    }

    function execute(address executable) external onlyOwner {
        _execute(executable, owner(), _getCodeHash(address(this)), _getCodeHash(address(driver)));
    }

    function mutExecute(address executable, address newOwner, bytes32 selfCodeHash, bytes32 driverCodeHash) external onlyOwner {
        _execute(executable, newOwner, selfCodeHash, driverCodeHash);
    }

    function incBalance(address acc, uint256 diff) external onlySFC {
        require(acc == address(sfc), "recipient is not the SFC contract");
        driver.setBalance(acc, address(acc).balance.add(diff));
    }

    function upgradeCode(address acc, address from) external onlyOwner {
        require(isContract(acc) && isContract(from), "not a contract");
        driver.copyCode(acc, from);
    }

    function copyCode(address acc, address from) external onlyOwner {
        driver.copyCode(acc, from);
    }

    function incNonce(address acc, uint256 diff) external onlyOwner {
        driver.incNonce(acc, diff);
    }

    function updateNetworkRules(bytes calldata diff) external onlyOwner {
        driver.updateNetworkRules(diff);
    }

    function updateMinGasPrice(uint256 minGasPrice) external onlySFC {
        driver.updateNetworkRules(bytes(strConcat("{\"Economy\":{\"MinGasPrice\":", uint256ToStr(minGasPrice), "}}")));
    }

    function updateNetworkVersion(uint256 version) external onlyOwner {
        driver.updateNetworkVersion(version);
    }

    function advanceEpochs(uint256 num) external onlyOwner {
        driver.advanceEpochs(num);
    }

    function updateValidatorWeight(uint256 validatorID, uint256 value) external onlySFC {
        driver.updateValidatorWeight(validatorID, value);
    }

    function updateValidatorPubkey(uint256 validatorID, bytes calldata pubkey) external onlySFC {
        driver.updateValidatorPubkey(validatorID, pubkey);
    }

    function setGenesisValidator(address _auth, uint256 validatorID, bytes calldata pubkey, uint256 status, uint256 createdEpoch, uint256 createdTime, uint256 deactivatedEpoch, uint256 deactivatedTime) external onlyDriver {
        sfc.setGenesisValidator(_auth, validatorID, pubkey, status, createdEpoch, createdTime, deactivatedEpoch, deactivatedTime);
    }

    function setGenesisDelegation(address delegator, uint256 toValidatorID, uint256 stake, uint256 lockedStake, uint256 lockupFromEpoch, uint256 lockupEndTime, uint256 lockupDuration, uint256 earlyUnlockPenalty, uint256 rewards) external onlyDriver {
        sfc.setGenesisDelegation(delegator, toValidatorID, stake, lockedStake, lockupFromEpoch, lockupEndTime, lockupDuration, earlyUnlockPenalty, rewards);
    }

    function deactivateValidator(uint256 validatorID, uint256 status) external onlyDriver {
        sfc.deactivateValidator(validatorID, status);
    }

    function sealEpochValidators(uint256[] calldata nextValidatorIDs) external onlyDriver {
        sfc.sealEpochValidators(nextValidatorIDs);
    }

    function sealEpoch(uint256[] calldata offlineTimes, uint256[] calldata offlineBlocks, uint256[] calldata uptimes, uint256[] calldata originatedTxsFee, uint256 usedGas) external onlyDriver {
        sfc.sealEpoch(offlineTimes, offlineBlocks, uptimes, originatedTxsFee, usedGas);
    }

    function isContract(address account) internal view returns (bool) {
        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly {size := extcodesize(account)}
        return size > 0;
    }

    function decimalsNum(uint256 num) internal pure returns (uint256) {
        uint decimals;
        while (num != 0) {
            decimals++;
            num /= 10;
        }
        return decimals;
    }

    function uint256ToStr(uint256 num) internal pure returns (string memory) {
        if (num == 0) {
            return "0";
        }
        uint decimals = decimalsNum(num);
        bytes memory bstr = new bytes(decimals);
        uint strIdx = decimals - 1;
        while (num != 0) {
            bstr[strIdx] = byte(uint8(48 + num % 10));
            num /= 10;
            strIdx--;
        }
        return string(bstr);
    }

    function strConcat(string memory _a, string memory _b, string memory _c) internal pure returns (string memory) {
        bytes memory _ba = bytes(_a);
        bytes memory _bb = bytes(_b);
        bytes memory _bc = bytes(_c);
        string memory abc = new string(_ba.length + _bb.length + _bc.length);
        bytes memory babc = bytes(abc);
        uint k = 0;
        uint i = 0;
        for (i = 0; i < _ba.length; i++) {
            babc[k++] = _ba[i];
        }
        for (i = 0; i < _bb.length; i++) {
            babc[k++] = _bb[i];
        }
        for (i = 0; i < _bc.length; i++) {
            babc[k++] = _bc[i];
        }
        return string(babc);
    }

    function _getCodeHash(address addr) internal view returns (bytes32) {
        bytes32 codeHash;
        assembly {codeHash := extcodehash(addr)}
        return codeHash;
    }
}

contract NodeDriver is Initializable {
    uint256 private erased0;
    NodeDriverAuth internal backend;
    EVMWriter internal evmWriter;

    event UpdatedBackend(address indexed backend);

    function setBackend(address _backend) external onlyBackend {
        emit UpdatedBackend(_backend);
        backend = NodeDriverAuth(_backend);
    }

    modifier onlyBackend() {
        require(msg.sender == address(backend), "caller is not the backend");
        _;
    }

    event UpdateValidatorWeight(uint256 indexed validatorID, uint256 weight);
    event UpdateValidatorPubkey(uint256 indexed validatorID, bytes pubkey);

    event UpdateNetworkRules(bytes diff);
    event UpdateNetworkVersion(uint256 version);
    event AdvanceEpochs(uint256 num);

    function initialize(address _backend, address _evmWriterAddress) external initializer {
        backend = NodeDriverAuth(_backend);
        emit UpdatedBackend(_backend);
        evmWriter = EVMWriter(_evmWriterAddress);
    }

    function setBalance(address acc, uint256 value) external onlyBackend {
        evmWriter.setBalance(acc, value);
    }

    function copyCode(address acc, address from) external onlyBackend {
        evmWriter.copyCode(acc, from);
    }

    function swapCode(address acc, address with) external onlyBackend {
        evmWriter.swapCode(acc, with);
    }

    function setStorage(address acc, bytes32 key, bytes32 value) external onlyBackend {
        evmWriter.setStorage(acc, key, value);
    }

    function incNonce(address acc, uint256 diff) external onlyBackend {
        evmWriter.incNonce(acc, diff);
    }

    function updateNetworkRules(bytes calldata diff) external onlyBackend {
        emit UpdateNetworkRules(diff);
    }

    function updateNetworkVersion(uint256 version) external onlyBackend {
        emit UpdateNetworkVersion(version);
    }

    function advanceEpochs(uint256 num) external onlyBackend {
        emit AdvanceEpochs(num);
    }

    function updateValidatorWeight(uint256 validatorID, uint256 value) external onlyBackend {
        emit UpdateValidatorWeight(validatorID, value);
    }

    function updateValidatorPubkey(uint256 validatorID, bytes calldata pubkey) external onlyBackend {
        emit UpdateValidatorPubkey(validatorID, pubkey);
    }

    modifier onlyNode() {
        require(msg.sender == address(0), "not callable");
        _;
    }

    // Methods which are called only by the node

    function setGenesisValidator(address _auth, uint256 validatorID, bytes calldata pubkey, uint256 status, uint256 createdEpoch, uint256 createdTime, uint256 deactivatedEpoch, uint256 deactivatedTime) external onlyNode {
        backend.setGenesisValidator(_auth, validatorID, pubkey, status, createdEpoch, createdTime, deactivatedEpoch, deactivatedTime);
    }

    function setGenesisDelegation(address delegator, uint256 toValidatorID, uint256 stake, uint256 lockedStake, uint256 lockupFromEpoch, uint256 lockupEndTime, uint256 lockupDuration, uint256 earlyUnlockPenalty, uint256 rewards) external onlyNode {
        backend.setGenesisDelegation(delegator, toValidatorID, stake, lockedStake, lockupFromEpoch, lockupEndTime, lockupDuration, earlyUnlockPenalty, rewards);
    }

    function deactivateValidator(uint256 validatorID, uint256 status) external onlyNode {
        backend.deactivateValidator(validatorID, status);
    }

    function sealEpochValidators(uint256[] calldata nextValidatorIDs) external onlyNode {
        backend.sealEpochValidators(nextValidatorIDs);
    }

    function sealEpoch(uint256[] calldata offlineTimes, uint256[] calldata offlineBlocks, uint256[] calldata uptimes, uint256[] calldata originatedTxsFee) external onlyNode {
        backend.sealEpoch(offlineTimes, offlineBlocks, uptimes, originatedTxsFee, 841669690);
    }

    function sealEpochV1(uint256[] calldata offlineTimes, uint256[] calldata offlineBlocks, uint256[] calldata uptimes, uint256[] calldata originatedTxsFee, uint256 usedGas) external onlyNode {
        backend.sealEpoch(offlineTimes, offlineBlocks, uptimes, originatedTxsFee, usedGas);
    }
}

interface EVMWriter {
    function setBalance(address acc, uint256 value) external;

    function copyCode(address acc, address from) external;

    function swapCode(address acc, address with) external;

    function setStorage(address acc, bytes32 key, bytes32 value) external;

    function incNonce(address acc, uint256 diff) external;
}

File 7 of 20 : SFCState.sol
pragma solidity ^0.5.0;

import "./NodeDriver.sol";
import "../ownership/Ownable.sol";
import "./ConstantsManager.sol";

contract SFCState is Initializable, Ownable {
    using SafeMath for uint256;
    /**
     * @dev The staking for validation
     */
    struct Validator {
        uint256 status;
        uint256 deactivatedTime;
        uint256 deactivatedEpoch;

        uint256 receivedStake;
        uint256 createdEpoch;
        uint256 createdTime;

        address auth;
    }

    NodeDriverAuth internal node;

    struct Rewards {
        uint256 lockupExtraReward;
        uint256 lockupBaseReward;
        uint256 unlockedReward;
    }

    uint256 public currentSealedEpoch;
    mapping(uint256 => Validator) public getValidator;
    mapping(address => uint256) public getValidatorID;
    mapping(uint256 => bytes) public getValidatorPubkey;

    uint256 public lastValidatorID;
    uint256 public totalStake;
    uint256 public totalActiveStake;
    uint256 public totalSlashedStake;

    mapping(address => mapping(uint256 => Rewards)) internal _rewardsStash; // addr, validatorID -> Rewards

    mapping(address => mapping(uint256 => uint256)) public stashedRewardsUntilEpoch;

    struct WithdrawalRequest {
        uint256 epoch;
        uint256 time;

        uint256 amount;
    }

    mapping(address => mapping(uint256 => mapping(uint256 => WithdrawalRequest))) public getWithdrawalRequest;

    struct LockedDelegation {
        uint256 lockedStake;
        uint256 fromEpoch;
        uint256 endTime;
        uint256 duration;
    }

    mapping(address => mapping(uint256 => uint256)) public getStake;

    mapping(address => mapping(uint256 => LockedDelegation)) public getLockupInfo;

    mapping(address => mapping(uint256 => Rewards)) public getStashedLockupRewards;

    struct EpochSnapshot {
        mapping(uint256 => uint256) receivedStake;
        mapping(uint256 => uint256) accumulatedRewardPerToken;
        mapping(uint256 => uint256) accumulatedUptime;
        mapping(uint256 => uint256) accumulatedOriginatedTxsFee;
        mapping(uint256 => uint256) offlineTime;
        mapping(uint256 => uint256) offlineBlocks;

        uint256[] validatorIDs;

        uint256 endTime;
        uint256 epochFee;
        uint256 totalBaseRewardWeight;
        uint256 totalTxRewardWeight;
        uint256 baseRewardPerSecond;
        uint256 totalStake;
        uint256 totalSupply;
    }

    uint256 private erased0;
    uint256 public totalSupply;
    mapping(uint256 => EpochSnapshot) public getEpochSnapshot;

    uint256 private erased1;
    uint256 private erased2;

    mapping(uint256 => uint256) public slashingRefundRatio; // validator ID -> (slashing refund ratio)

    address public stakeTokenizerAddress;

    uint256 private erased3;
    uint256 private erased4;
    uint256 public minGasPrice;

    address public treasuryAddress;

    address internal libAddress;

    ConstantsManager internal c;

    address public voteBookAddress;

    address internal sftmFinalizer;

    struct Penalty {
        uint256 amount;
        uint256 end;
    }
    // delegator => validatorID => penalties info
    mapping(address => mapping(uint256 => Penalty[])) public getStashedPenalties;

    mapping(uint256 => uint256) internal validatorPubkeyChanges;

    mapping(bytes32 => uint256) internal pubkeyHashToValidatorID;

    address public redirectionAuthorizer;

    mapping(address => address) public getRedirectionRequest;

    mapping(address => address) public getRedirection;
}

File 8 of 20 : SFC.sol
pragma solidity ^0.5.0;

import "./GasPriceConstants.sol";
import "../version/Version.sol";
import "./SFCBase.sol";

/**
 * @dev Stakers contract defines data structure and methods for validators / validators.
 */
contract SFC is SFCBase, Version {
    function _delegate(address implementation) internal {
        assembly {
        // Copy msg.data. We take full control of memory in this inline assembly
        // block because it will not return to Solidity code. We overwrite the
        // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize)

        // Call the implementation.
        // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)

        // Copy the returned data.
            returndatacopy(0, 0, returndatasize)

            switch result
            // delegatecall returns 0 on error.
            case 0 {revert(0, returndatasize)}
            default {return (0, returndatasize)}
        }
    }

    function() payable external {
        require(msg.data.length != 0, "transfers not allowed");
        _delegate(libAddress);
    }

    /*
    Getters
    */

    function getEpochValidatorIDs(uint256 epoch) public view returns (uint256[] memory) {
        return getEpochSnapshot[epoch].validatorIDs;
    }

    function getEpochReceivedStake(uint256 epoch, uint256 validatorID) public view returns (uint256) {
        return getEpochSnapshot[epoch].receivedStake[validatorID];
    }

    function getEpochAccumulatedRewardPerToken(uint256 epoch, uint256 validatorID) public view returns (uint256) {
        return getEpochSnapshot[epoch].accumulatedRewardPerToken[validatorID];
    }

    function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID) public view returns (uint256) {
        return getEpochSnapshot[epoch].accumulatedUptime[validatorID];
    }

    function getEpochAccumulatedOriginatedTxsFee(uint256 epoch, uint256 validatorID) public view returns (uint256) {
        return getEpochSnapshot[epoch].accumulatedOriginatedTxsFee[validatorID];
    }

    function getEpochOfflineTime(uint256 epoch, uint256 validatorID) public view returns (uint256) {
        return getEpochSnapshot[epoch].offlineTime[validatorID];
    }

    function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID) public view returns (uint256) {
        return getEpochSnapshot[epoch].offlineBlocks[validatorID];
    }

    function rewardsStash(address delegator, uint256 validatorID) public view returns (uint256) {
        Rewards memory stash = _rewardsStash[delegator][validatorID];
        return stash.lockupBaseReward.add(stash.lockupExtraReward).add(stash.unlockedReward);
    }

    /*
    Constructor
    */

    function initialize(uint256 sealedEpoch, uint256 _totalSupply, address nodeDriver, address lib, address _c, address owner) external initializer {
        Ownable.initialize(owner);
        currentSealedEpoch = sealedEpoch;
        node = NodeDriverAuth(nodeDriver);
        libAddress = lib;
        c = ConstantsManager(_c);
        totalSupply = _totalSupply;
        minGasPrice = GP.initialMinGasPrice();
        getEpochSnapshot[sealedEpoch].endTime = _now();
    }

    function updateStakeTokenizerAddress(address addr) onlyOwner external {
        stakeTokenizerAddress = addr;
    }

    function updateLibAddress(address v) onlyOwner external {
        libAddress = v;
    }

    function updateTreasuryAddress(address v) onlyOwner external {
        treasuryAddress = v;
    }

    function updateConstsAddress(address v) onlyOwner external {
        c = ConstantsManager(v);
    }

    function constsAddress() external view returns (address) {
        return address(c);
    }

    function updateVoteBookAddress(address v) onlyOwner external {
        voteBookAddress = v;
    }

    function updateSFTMFinalizer(address v) external onlyOwner {
        sftmFinalizer = v;
    }

    function migrateValidatorPubkeyUniquenessFlag(uint256 start, uint256 end) external {
        for (uint256 vid = start; vid < end; vid++) {
            bytes memory pubkey = getValidatorPubkey[vid];
            if (pubkey.length > 0 && pubkeyHashToValidatorID[keccak256(pubkey)] != vid) {
                require(pubkeyHashToValidatorID[keccak256(pubkey)] == 0, "already exists");
                pubkeyHashToValidatorID[keccak256(pubkey)] = vid;
            }
        }
    }

    function updateValidatorPubkey(bytes calldata pubkey) external {
        require(getValidator[1].auth == 0x541E408443A592C38e01Bed0cB31f9De8c1322d0, "not mainnet");
        require(pubkey.length == 66 && pubkey[0] == 0xc0, "malformed pubkey");
        uint256 validatorID = getValidatorID[msg.sender];
        require(validatorID <= 59, "not legacy validator");
        require(_validatorExists(validatorID), "validator doesn't exist");
        require(keccak256(pubkey) != keccak256(getValidatorPubkey[validatorID]), "same pubkey");
        require(pubkeyHashToValidatorID[keccak256(pubkey)] == 0, "already used");
        require(validatorPubkeyChanges[validatorID] == 0, "allowed only once");

        validatorPubkeyChanges[validatorID]++;
        pubkeyHashToValidatorID[keccak256(pubkey)] = validatorID;
        getValidatorPubkey[validatorID] = pubkey;
        _syncValidator(validatorID, true);
    }

    function setRedirectionAuthorizer(address v) onlyOwner external {
        require(redirectionAuthorizer != v, "same");
        redirectionAuthorizer = v;
    }

    event AnnouncedRedirection(address indexed from, address indexed to);

    function announceRedirection(address to) external {
        emit AnnouncedRedirection(msg.sender, to);
    }

    function initiateRedirection(address from, address to) external {
        require(msg.sender == redirectionAuthorizer, "not authorized");
        require(getRedirection[from] != to, "already complete");
        require(from != to, "same address");
        getRedirectionRequest[from] = to;
    }

    function redirect(address to) external {
        address from = msg.sender;
        require(to != address(0), "zero address");
        require(getRedirectionRequest[from] == to, "no request");
        getRedirection[from] = to;
        getRedirectionRequest[from] = address(0);
    }

    /*
    Epoch callbacks
    */

    function _sealEpoch_offline(EpochSnapshot storage snapshot, uint256[] memory validatorIDs, uint256[] memory offlineTime, uint256[] memory offlineBlocks) internal {
        // mark offline nodes
        for (uint256 i = 0; i < validatorIDs.length; i++) {
            if (offlineBlocks[i] > c.offlinePenaltyThresholdBlocksNum() && offlineTime[i] >= c.offlinePenaltyThresholdTime()) {
                _setValidatorDeactivated(validatorIDs[i], OFFLINE_BIT);
                _syncValidator(validatorIDs[i], false);
            }
            // log data
            snapshot.offlineTime[validatorIDs[i]] = offlineTime[i];
            snapshot.offlineBlocks[validatorIDs[i]] = offlineBlocks[i];
        }
    }

    struct _SealEpochRewardsCtx {
        uint256[] baseRewardWeights;
        uint256 totalBaseRewardWeight;
        uint256[] txRewardWeights;
        uint256 totalTxRewardWeight;
        uint256 epochFee;
    }

    function _sealEpoch_rewards(uint256 epochDuration, EpochSnapshot storage snapshot, EpochSnapshot storage prevSnapshot, uint256[] memory validatorIDs, uint256[] memory uptimes, uint256[] memory accumulatedOriginatedTxsFee) internal {
        _SealEpochRewardsCtx memory ctx = _SealEpochRewardsCtx(new uint[](validatorIDs.length), 0, new uint[](validatorIDs.length), 0, 0);

        for (uint256 i = 0; i < validatorIDs.length; i++) {
            uint256 prevAccumulatedTxsFee = prevSnapshot.accumulatedOriginatedTxsFee[validatorIDs[i]];
            uint256 originatedTxsFee = 0;
            if (accumulatedOriginatedTxsFee[i] > prevAccumulatedTxsFee) {
                originatedTxsFee = accumulatedOriginatedTxsFee[i] - prevAccumulatedTxsFee;
            }
            // txRewardWeight = {originatedTxsFee} * {uptime}
            // originatedTxsFee is roughly proportional to {uptime} * {stake}, so the whole formula is roughly
            // {stake} * {uptime} ^ 2
            ctx.txRewardWeights[i] = originatedTxsFee * uptimes[i] / epochDuration;
            ctx.totalTxRewardWeight = ctx.totalTxRewardWeight.add(ctx.txRewardWeights[i]);
            ctx.epochFee = ctx.epochFee.add(originatedTxsFee);
        }

        for (uint256 i = 0; i < validatorIDs.length; i++) {
            // baseRewardWeight = {stake} * {uptime ^ 2}
            ctx.baseRewardWeights[i] = (((snapshot.receivedStake[validatorIDs[i]] * uptimes[i]) / epochDuration) * uptimes[i]) / epochDuration;
            ctx.totalBaseRewardWeight = ctx.totalBaseRewardWeight.add(ctx.baseRewardWeights[i]);
        }

        for (uint256 i = 0; i < validatorIDs.length; i++) {
            uint256 rawReward = _calcRawValidatorEpochBaseReward(epochDuration, c.baseRewardPerSecond(), ctx.baseRewardWeights[i], ctx.totalBaseRewardWeight);
            rawReward = rawReward.add(_calcRawValidatorEpochTxReward(ctx.epochFee, ctx.txRewardWeights[i], ctx.totalTxRewardWeight));

            uint256 validatorID = validatorIDs[i];
            address validatorAddr = getValidator[validatorID].auth;
            // accounting validator's commission
            uint256 commissionRewardFull = _calcValidatorCommission(rawReward, c.validatorCommission());
            uint256 selfStake = getStake[validatorAddr][validatorID];
            if (selfStake != 0) {
                uint256 lCommissionRewardFull = (commissionRewardFull * getLockedStake(validatorAddr, validatorID)) / selfStake;
                uint256 uCommissionRewardFull = commissionRewardFull - lCommissionRewardFull;
                Rewards memory lCommissionReward = _scaleLockupReward(lCommissionRewardFull, getLockupInfo[validatorAddr][validatorID].duration);
                Rewards memory uCommissionReward = _scaleLockupReward(uCommissionRewardFull, 0);
                _rewardsStash[validatorAddr][validatorID] = sumRewards(_rewardsStash[validatorAddr][validatorID], lCommissionReward, uCommissionReward);
                getStashedLockupRewards[validatorAddr][validatorID] = sumRewards(getStashedLockupRewards[validatorAddr][validatorID], lCommissionReward, uCommissionReward);
            }
            // accounting reward per token for delegators
            uint256 delegatorsReward = rawReward - commissionRewardFull;
            // note: use latest stake for the sake of rewards distribution accuracy, not snapshot.receivedStake
            uint256 receivedStake = getValidator[validatorID].receivedStake;
            uint256 rewardPerToken = 0;
            if (receivedStake != 0) {
                rewardPerToken = (delegatorsReward * Decimal.unit()) / receivedStake;
            }
            snapshot.accumulatedRewardPerToken[validatorID] = prevSnapshot.accumulatedRewardPerToken[validatorID] + rewardPerToken;

            snapshot.accumulatedOriginatedTxsFee[validatorID] = accumulatedOriginatedTxsFee[i];
            snapshot.accumulatedUptime[validatorID] = prevSnapshot.accumulatedUptime[validatorID] + uptimes[i];
        }

        snapshot.epochFee = ctx.epochFee;
        snapshot.totalBaseRewardWeight = ctx.totalBaseRewardWeight;
        snapshot.totalTxRewardWeight = ctx.totalTxRewardWeight;
        if (totalSupply > snapshot.epochFee) {
            totalSupply -= snapshot.epochFee;
        } else {
            totalSupply = 0;
        }

        // transfer 10% of fees to treasury
        if (treasuryAddress != address(0)) {
            uint256 feeShare = ctx.epochFee * c.treasuryFeeShare() / Decimal.unit();
            _mintNativeToken(feeShare);
            treasuryAddress.call.value(feeShare).gas(1000000)("");
        }
    }


    function _sealEpoch_minGasPrice(uint256 epochDuration, uint256 epochGas) internal {
        // change minGasPrice proportionally to the difference between target and received epochGas
        uint256 targetEpochGas = epochDuration * c.targetGasPowerPerSecond() + 1;
        uint256 gasPriceDeltaRatio = epochGas * Decimal.unit() / targetEpochGas;
        uint256 counterweight = c.gasPriceBalancingCounterweight();
        // scale down the change speed (estimate gasPriceDeltaRatio ^ (epochDuration / counterweight))
        gasPriceDeltaRatio = (epochDuration * gasPriceDeltaRatio + counterweight * Decimal.unit()) / (epochDuration + counterweight);
        // limit the max/min possible delta in one epoch
        gasPriceDeltaRatio = GP.trimGasPriceChangeRatio(gasPriceDeltaRatio);

        // apply the ratio
        uint256 newMinGasPrice = minGasPrice * gasPriceDeltaRatio / Decimal.unit();
        // limit the max/min possible minGasPrice
        newMinGasPrice = GP.trimMinGasPrice(newMinGasPrice);
        // apply new minGasPrice
        minGasPrice = newMinGasPrice;
    }

    function sealEpoch(uint256[] calldata offlineTime, uint256[] calldata offlineBlocks, uint256[] calldata uptimes, uint256[] calldata originatedTxsFee, uint256 epochGas) external onlyDriver {
        EpochSnapshot storage snapshot = getEpochSnapshot[currentEpoch()];
        uint256[] memory validatorIDs = snapshot.validatorIDs;

        _sealEpoch_offline(snapshot, validatorIDs, offlineTime, offlineBlocks);
        {
            EpochSnapshot storage prevSnapshot = getEpochSnapshot[currentSealedEpoch];
            uint256 epochDuration = 1;
            if (_now() > prevSnapshot.endTime) {
                epochDuration = _now() - prevSnapshot.endTime;
            }
            _sealEpoch_rewards(epochDuration, snapshot, prevSnapshot, validatorIDs, uptimes, originatedTxsFee);
            _sealEpoch_minGasPrice(epochDuration, epochGas);
        }

        currentSealedEpoch = currentEpoch();
        snapshot.endTime = _now();
        snapshot.baseRewardPerSecond = c.baseRewardPerSecond();
        snapshot.totalSupply = totalSupply;
    }

    function sealEpochValidators(uint256[] calldata nextValidatorIDs) external onlyDriver {
        EpochSnapshot storage snapshot = getEpochSnapshot[currentEpoch()];
        // fill data for the next snapshot
        for (uint256 i = 0; i < nextValidatorIDs.length; i++) {
            uint256 validatorID = nextValidatorIDs[i];
            uint256 receivedStake = getValidator[validatorID].receivedStake;
            snapshot.receivedStake[validatorID] = receivedStake;
            snapshot.totalStake = snapshot.totalStake.add(receivedStake);
        }
        snapshot.validatorIDs = nextValidatorIDs;
        node.updateMinGasPrice(minGasPrice);
    }
}

File 9 of 20 : ERC20Burnable.sol
pragma solidity ^0.5.0;

import "./ERC20.sol";


/**
 * @title Burnable Token
 * @dev Token that can be irreversibly burned (destroyed).
 */
contract ERC20Burnable is ERC20 {

    /**
     * @dev Burns a specific amount of tokens.
     * @param value The amount of token to be burned.
     */
    function burn(uint256 value) public {
        _burn(msg.sender, value);
    }

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

    /**
     * @dev Overrides ERC20._burn in order for burn and burnFrom to emit
     * an additional Burn event.
     */
    function _burn(address who, uint256 value) internal {
        super._burn(who, value);
    }
}

File 10 of 20 : ERC20Mintable.sol
pragma solidity ^0.5.0;

import "./ERC20.sol";
import "./MinterRole.sol";


/**
 * @title ERC20Mintable
 * @dev ERC20 minting logic
 */
contract ERC20Mintable is ERC20, MinterRole {
    event MintingFinished();

    bool private _mintingFinished = false;

    modifier onlyBeforeMintingFinished() {
        require(!_mintingFinished);
        _;
    }

    /**
     * @return true if the minting is finished.
     */
    function mintingFinished() public view returns (bool) {
        return _mintingFinished;
    }

    /**
     * @dev Function to mint tokens
     * @param to The address that will receive the minted tokens.
     * @param amount The amount of tokens to mint.
     * @return A boolean that indicates if the operation was successful.
     */
    function mint(
        address to,
        uint256 amount
    )
    public
    onlyMinter
    onlyBeforeMintingFinished
    returns (bool)
    {
        _mint(to, amount);
        return true;
    }

    /**
     * @dev Function to stop minting new tokens.
     * @return True if the operation was successful.
     */
    function finishMinting()
    public
    onlyMinter
    onlyBeforeMintingFinished
    returns (bool)
    {
        _mintingFinished = true;
        emit MintingFinished();
        return true;
    }
}

File 11 of 20 : 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 12 of 20 : SafeMath.sol
pragma solidity ^0.5.0;

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

        return c;
    }

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

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

        return c;
    }

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

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

        return c;
    }

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

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

        return c;
    }

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

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

File 13 of 20 : Ownable.sol
pragma solidity ^0.5.0;

import "../common/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.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be aplied to your functions to restrict their use to
 * the owner.
 */
contract Ownable is Initializable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function initialize(address sender) internal initializer {
        _owner = sender;
        emit OwnershipTransferred(address(0), _owner);
    }

    /**
     * @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(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return msg.sender == _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 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 onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }

    uint256[50] private ______gap;
}

File 14 of 20 : SFCI.sol
pragma solidity ^0.5.0;

interface SFCI {
    event CreatedValidator(uint256 indexed validatorID, address indexed auth, uint256 createdEpoch, uint256 createdTime);
    event Delegated(address indexed delegator, uint256 indexed toValidatorID, uint256 amount);
    event Undelegated(address indexed delegator, uint256 indexed toValidatorID, uint256 indexed wrID, uint256 amount);
    event Withdrawn(address indexed delegator, uint256 indexed toValidatorID, uint256 indexed wrID, uint256 amount);
    event ClaimedRewards(address indexed delegator, uint256 indexed toValidatorID, uint256 lockupExtraReward, uint256 lockupBaseReward, uint256 unlockedReward);
    event RestakedRewards(address indexed delegator, uint256 indexed toValidatorID, uint256 lockupExtraReward, uint256 lockupBaseReward, uint256 unlockedReward);
    event BurntFTM(uint256 amount);
    event LockedUpStake(address indexed delegator, uint256 indexed validatorID, uint256 duration, uint256 amount);
    event UnlockedStake(address indexed delegator, uint256 indexed validatorID, uint256 amount, uint256 penalty);
    event UpdatedSlashingRefundRatio(uint256 indexed validatorID, uint256 refundRatio);
    event RefundedSlashedLegacyDelegation(address indexed delegator, uint256 indexed validatorID, uint256 amount);

    event DeactivatedValidator(uint256 indexed validatorID, uint256 deactivatedEpoch, uint256 deactivatedTime);
    event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);
    event AnnouncedRedirection(address indexed from, address indexed to);

    function currentSealedEpoch() external view returns (uint256);

    function getEpochSnapshot(uint256) external view returns (uint256 endTime, uint256 epochFee, uint256 totalBaseRewardWeight, uint256 totalTxRewardWeight, uint256 _baseRewardPerSecond, uint256 totalStake, uint256 totalSupply);

    function getLockupInfo(address, uint256) external view returns (uint256 lockedStake, uint256 fromEpoch, uint256 endTime, uint256 duration);

    function getStake(address, uint256) external view returns (uint256);

    function getStashedLockupRewards(address, uint256) external view returns (uint256 lockupExtraReward, uint256 lockupBaseReward, uint256 unlockedReward);

    function getValidator(uint256) external view returns (uint256 status, uint256 deactivatedTime, uint256 deactivatedEpoch, uint256 receivedStake, uint256 createdEpoch, uint256 createdTime, address auth);

    function getValidatorID(address) external view returns (uint256);

    function getValidatorPubkey(uint256) external view returns (bytes memory);

    function getWithdrawalRequest(address, uint256, uint256) external view returns (uint256 epoch, uint256 time, uint256 amount);

    function isOwner() external view returns (bool);

    function lastValidatorID() external view returns (uint256);

    function minGasPrice() external view returns (uint256);

    function owner() external view returns (address);

    function renounceOwnership() external;

    function slashingRefundRatio(uint256) external view returns (uint256);

    function stakeTokenizerAddress() external view returns (address);

    function stashedRewardsUntilEpoch(address, uint256) external view returns (uint256);

    function totalActiveStake() external view returns (uint256);

    function totalSlashedStake() external view returns (uint256);

    function totalStake() external view returns (uint256);

    function totalSupply() external view returns (uint256);

    function transferOwnership(address newOwner) external;

    function treasuryAddress() external view returns (address);

    function version() external pure returns (bytes3);

    function currentEpoch() external view returns (uint256);

    function updateConstsAddress(address v) external;

    function constsAddress() external view returns (address);

    function getEpochValidatorIDs(uint256 epoch) external view returns (uint256[] memory);

    function getEpochReceivedStake(uint256 epoch, uint256 validatorID) external view returns (uint256);

    function getEpochAccumulatedRewardPerToken(uint256 epoch, uint256 validatorID) external view returns (uint256);

    function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID) external view returns (uint256);

    function getEpochAccumulatedOriginatedTxsFee(uint256 epoch, uint256 validatorID) external view returns (uint256);

    function getEpochOfflineTime(uint256 epoch, uint256 validatorID) external view returns (uint256);

    function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID) external view returns (uint256);

    function rewardsStash(address delegator, uint256 validatorID) external view returns (uint256);

    function getLockedStake(address delegator, uint256 toValidatorID) external view returns (uint256);

    function createValidator(bytes calldata pubkey) external payable;

    function getSelfStake(uint256 validatorID) external view returns (uint256);

    function delegate(uint256 toValidatorID) external payable;

    function undelegate(uint256 toValidatorID, uint256 wrID, uint256 amount) external;

    function isSlashed(uint256 validatorID) external view returns (bool);

    function withdraw(uint256 toValidatorID, uint256 wrID) external;

    function deactivateValidator(uint256 validatorID, uint256 status) external;

    function pendingRewards(address delegator, uint256 toValidatorID) external view returns (uint256);

    function stashRewards(address delegator, uint256 toValidatorID) external;

    function claimRewards(uint256 toValidatorID) external;

    function restakeRewards(uint256 toValidatorID) external;

    function updateBaseRewardPerSecond(uint256 value) external;

    function updateOfflinePenaltyThreshold(uint256 blocksNum, uint256 time) external;

    function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio) external;

    function updateStakeTokenizerAddress(address addr) external;

    function updateTreasuryAddress(address v) external;

    function burnFTM(uint256 amount) external;

    function sealEpoch(uint256[] calldata offlineTime, uint256[] calldata offlineBlocks, uint256[] calldata uptimes, uint256[] calldata originatedTxsFee, uint256 epochGas) external;

    function sealEpochValidators(uint256[] calldata nextValidatorIDs) external;

    function isLockedUp(address delegator, uint256 toValidatorID) external view returns (bool);

    function getUnlockedStake(address delegator, uint256 toValidatorID) external view returns (uint256);

    function lockStake(uint256 toValidatorID, uint256 lockupDuration, uint256 amount) external;

    function relockStake(uint256 toValidatorID, uint256 lockupDuration, uint256 amount) external;

    function unlockStake(uint256 toValidatorID, uint256 amount) external returns (uint256);

    function initialize(uint256 sealedEpoch, uint256 _totalSupply, address nodeDriver, address lib, address consts, address _owner) external;

    function setGenesisValidator(address auth, uint256 validatorID, bytes calldata pubkey, uint256 status, uint256 createdEpoch, uint256 createdTime, uint256 deactivatedEpoch, uint256 deactivatedTime) external;

    function setGenesisDelegation(address delegator, uint256 toValidatorID, uint256 stake, uint256 lockedStake, uint256 lockupFromEpoch, uint256 lockupEndTime, uint256 lockupDuration, uint256 earlyUnlockPenalty, uint256 rewards) external;

    function updateVoteBookAddress(address v) external;

    function voteBookAddress() external view returns (address);

    function liquidateSFTM(address delegator, uint256 toValidatorID, uint256 amount) external;

    function updateSFTMFinalizer(address v) external;

    function updateValidatorPubkey(bytes calldata pubkey) external;

    function migrateValidatorPubkeyUniquenessFlag(uint256 start, uint256 end) external;

    function setRedirectionAuthorizer(address v) external;

    function announceRedirection(address to) external;

    function initiateRedirection(address from, address to) external;

    function redirect(address to) external;
}

File 15 of 20 : ConstantsManager.sol
pragma solidity ^0.5.0;

import "../common/SafeMath.sol";
import "../ownership/Ownable.sol";
import "../common/Decimal.sol";

contract ConstantsManager is Ownable {
    using SafeMath for uint256;

    // Minimum amount of stake for a validator, i.e., 500000 FTM
    uint256 public minSelfStake;
    // Maximum ratio of delegations a validator can have, say, 15 times of self-stake
    uint256 public maxDelegatedRatio;
    // The commission fee in percentage a validator will get from a delegation, e.g., 15%
    uint256 public validatorCommission;
    // The percentage of fees to burn, e.g., 20%
    uint256 public burntFeeShare;
    // The percentage of fees to transfer to treasury address, e.g., 10%
    uint256 public treasuryFeeShare;
    // The ratio of the reward rate at base rate (no lock), e.g., 30%
    uint256 public unlockedRewardRatio;
    // The minimum duration of a stake/delegation lockup, e.g. 2 weeks
    uint256 public minLockupDuration;
    // The maximum duration of a stake/delegation lockup, e.g. 1 year
    uint256 public maxLockupDuration;
    // the number of epochs that undelegated stake is locked for
    uint256 public withdrawalPeriodEpochs;
    // the number of seconds that undelegated stake is locked for
    uint256 public withdrawalPeriodTime;

    uint256 public baseRewardPerSecond;
    uint256 public offlinePenaltyThresholdBlocksNum;
    uint256 public offlinePenaltyThresholdTime;
    uint256 public targetGasPowerPerSecond;
    uint256 public gasPriceBalancingCounterweight;

    address private secondaryOwner_erased;

//    event SecondaryOwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    function initialize() external initializer {
        Ownable.initialize(msg.sender);
    }

//    function setSecondaryOwner(address v) onlyOwner external {
//        emit SecondaryOwnershipTransferred(secondaryOwner, v);
//        secondaryOwner = v;
//    }

    function updateMinSelfStake(uint256 v) onlyOwner external {
        require(v >= 100000 * 1e18, "too small value");
        require(v <= 10000000 * 1e18, "too large value");
        minSelfStake = v;
    }

    function updateMaxDelegatedRatio(uint256 v) onlyOwner external {
        require(v >= Decimal.unit(), "too small value");
        require(v <= 31 * Decimal.unit(), "too large value");
        maxDelegatedRatio = v;
    }

    function updateValidatorCommission(uint256 v) onlyOwner external {
        require(v <= Decimal.unit() / 2, "too large value");
        validatorCommission = v;
    }

    function updateBurntFeeShare(uint256 v) onlyOwner external {
        require(v <= Decimal.unit() / 2, "too large value");
        burntFeeShare = v;
    }

    function updateTreasuryFeeShare(uint256 v) onlyOwner external {
        require(v <= Decimal.unit() / 2, "too large value");
        treasuryFeeShare = v;
    }

    function updateUnlockedRewardRatio(uint256 v) onlyOwner external {
        require(v >= (5 * Decimal.unit()) / 100, "too small value");
        require(v <= Decimal.unit() / 2, "too large value");
        unlockedRewardRatio = v;
    }

    function updateMinLockupDuration(uint256 v) onlyOwner external {
        require(v >= 86400, "too small value");
        require(v <= 86400 * 30, "too large value");
        minLockupDuration = v;
    }

    function updateMaxLockupDuration(uint256 v) onlyOwner external {
        require(v >= 86400 * 30, "too small value");
        require(v <= 86400 * 1460, "too large value");
        maxLockupDuration = v;
    }

    function updateWithdrawalPeriodEpochs(uint256 v) onlyOwner external {
        require(v >= 2, "too small value");
        require(v <= 100, "too large value");
        withdrawalPeriodEpochs = v;
    }

    function updateWithdrawalPeriodTime(uint256 v) onlyOwner external {
        require(v >= 86400, "too small value");
        require(v <= 30 * 86400, "too large value");
        withdrawalPeriodTime = v;
    }

    function updateBaseRewardPerSecond(uint256 v) onlyOwner external {
        require(v >= 0.5 * 1e18, "too small value");
        require(v <= 32 * 1e18, "too large value");
        baseRewardPerSecond = v;
    }

    function updateOfflinePenaltyThresholdTime(uint256 v) onlyOwner external {
        require(v >= 86400, "too small value");
        require(v <= 10 * 86400, "too large value");
        offlinePenaltyThresholdTime = v;
    }

    function updateOfflinePenaltyThresholdBlocksNum(uint256 v) onlyOwner external {
        require(v >= 100, "too small value");
        require(v <= 1000000, "too large value");
        offlinePenaltyThresholdBlocksNum = v;
    }

    function updateTargetGasPowerPerSecond(uint256 v) onlyOwner external {
        require(v >= 1000000, "too small value");
        require(v <= 500000000, "too large value");
        targetGasPowerPerSecond = v;
    }

    function updateGasPriceBalancingCounterweight(uint256 v) onlyOwner external {
        require(v >= 100, "too small value");
        require(v <= 10 * 86400, "too large value");
        gasPriceBalancingCounterweight = v;
    }
}

File 16 of 20 : Version.sol
pragma solidity ^0.5.0;

/**
 * @dev Version contract gives the versioning information of the implementation contract
 */
contract Version {
    /**
     * @dev Returns the address of the current owner.
     */
    function version() public pure returns (bytes3) {
        // version 3.0.5
        return "305";
    }
}

File 17 of 20 : ERC20.sol
pragma solidity ^0.5.0;

import "../../common/SafeMath.sol";
import "./IERC20.sol";
import "../../common/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20Mintable}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Initializable, IERC20 {
    using SafeMath for uint256;

    mapping(address => uint256) private _balances;

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

    uint256 private _totalSupply;

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public returns (bool) {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20};
     *
     * Requirements:
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for `sender`'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
        _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
        _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: mint to the zero address");

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

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: burn from the zero address");

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
     *
     * This is internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`.`amount` is then deducted
     * from the caller's allowance.
     *
     * See {_burn} and {_approve}.
     */
    function _burnFrom(address account, uint256 amount) internal {
        _burn(account, amount);
        _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount, "ERC20: burn amount exceeds allowance"));
    }

    uint256[50] private ______gap;
}

File 18 of 20 : MinterRole.sol
pragma solidity ^0.5.0;

import "./Roles.sol";


contract MinterRole {
    using Roles for Roles.Role;

    event MinterAdded(address indexed account);
    event MinterRemoved(address indexed account);

    Roles.Role private minters;

    modifier onlyMinter() {
        require(isMinter(msg.sender));
        _;
    }

    function isMinter(address account) public view returns (bool) {
        return minters.has(account);
    }

    function renounceMinter() public {
        minters.remove(msg.sender);
    }

    function _removeMinter(address account) internal {
        minters.remove(account);
        emit MinterRemoved(account);
    }

    function _addMinter(address account) internal {
        minters.add(account);
        emit MinterAdded(account);
    }

    uint256[50] private ______gap;
}

File 19 of 20 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.5.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 20 of 20 : Roles.sol
pragma solidity ^0.5.0;


/**
 * @title Roles
 * @dev Library for managing addresses assigned to a Role.
 */
library Roles {
    struct Role {
        mapping(address => bool) bearer;
    }

    /**
     * @dev give an account access to this role
     */
    function add(Role storage role, address account) internal {
        require(account != address(0));
        role.bearer[account] = true;
    }

    /**
     * @dev remove an account's access to this role
     */
    function remove(Role storage role, address account) internal {
        require(account != address(0));
        role.bearer[account] = false;
    }

    /**
     * @dev check if an account has this role
     * @return bool
     */
    function has(Role storage role, address account)
    internal
    view
    returns (bool)
    {
        require(account != address(0));
        return role.bearer[account];
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "outputSelection": {
    "*": {
      "*": [
        "abi",
        "evm.bytecode",
        "evm.deployedBytecode",
        "evm.methodIdentifiers",
        "metadata"
      ],
      "": [
        "ast"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BurntFTM","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"status","type":"uint256"}],"name":"ChangedValidatorStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockupExtraReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockupBaseReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unlockedReward","type":"uint256"}],"name":"ClaimedRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":true,"internalType":"address","name":"auth","type":"address"},{"indexed":false,"internalType":"uint256","name":"createdEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"createdTime","type":"uint256"}],"name":"CreatedValidator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deactivatedEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deactivatedTime","type":"uint256"}],"name":"DeactivatedValidator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Delegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LockedUpStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RefundedSlashedLegacyDelegation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockupExtraReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockupBaseReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unlockedReward","type":"uint256"}],"name":"RestakedRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"wrID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Undelegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"}],"name":"UnlockedStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"refundRatio","type":"uint256"}],"name":"UpdatedSlashingRefundRatio","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"wrID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"constant":false,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"_delStalePenalties","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"bool","name":"syncPubkey","type":"bool"}],"name":"_syncValidator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnFTM","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"claimRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes","name":"pubkey","type":"bytes"}],"name":"createValidator","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentSealedEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"uint256","name":"status","type":"uint256"}],"name":"deactivateValidator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"delegate","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getEpochSnapshot","outputs":[{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"epochFee","type":"uint256"},{"internalType":"uint256","name":"totalBaseRewardWeight","type":"uint256"},{"internalType":"uint256","name":"totalTxRewardWeight","type":"uint256"},{"internalType":"uint256","name":"baseRewardPerSecond","type":"uint256"},{"internalType":"uint256","name":"totalStake","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"getLockedStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getLockupInfo","outputs":[{"internalType":"uint256","name":"lockedStake","type":"uint256"},{"internalType":"uint256","name":"fromEpoch","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getRedirection","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getRedirectionRequest","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getSelfStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getStashedLockupRewards","outputs":[{"internalType":"uint256","name":"lockupExtraReward","type":"uint256"},{"internalType":"uint256","name":"lockupBaseReward","type":"uint256"},{"internalType":"uint256","name":"unlockedReward","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getStashedPenalties","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"getUnlockedStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getValidator","outputs":[{"internalType":"uint256","name":"status","type":"uint256"},{"internalType":"uint256","name":"deactivatedTime","type":"uint256"},{"internalType":"uint256","name":"deactivatedEpoch","type":"uint256"},{"internalType":"uint256","name":"receivedStake","type":"uint256"},{"internalType":"uint256","name":"createdEpoch","type":"uint256"},{"internalType":"uint256","name":"createdTime","type":"uint256"},{"internalType":"address","name":"auth","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getValidatorID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getValidatorPubkey","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getWithdrawalRequest","outputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"isLockedUp","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"isSlashed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastValidatorID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"liquidateSFTM","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"lockupDuration","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"lockStake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"minGasPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"pendingRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"address","name":"validatorAuth","type":"address"},{"internalType":"bool","name":"strict","type":"bool"},{"internalType":"uint256","name":"gas","type":"uint256"}],"name":"recountVotes","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"redirectionAuthorizer","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"lockupDuration","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"relockStake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"restakeRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"uint256","name":"lockedStake","type":"uint256"},{"internalType":"uint256","name":"lockupFromEpoch","type":"uint256"},{"internalType":"uint256","name":"lockupEndTime","type":"uint256"},{"internalType":"uint256","name":"lockupDuration","type":"uint256"},{"internalType":"uint256","name":"earlyUnlockPenalty","type":"uint256"},{"internalType":"uint256","name":"rewards","type":"uint256"}],"name":"setGenesisDelegation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"auth","type":"address"},{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"uint256","name":"status","type":"uint256"},{"internalType":"uint256","name":"createdEpoch","type":"uint256"},{"internalType":"uint256","name":"createdTime","type":"uint256"},{"internalType":"uint256","name":"deactivatedEpoch","type":"uint256"},{"internalType":"uint256","name":"deactivatedTime","type":"uint256"}],"name":"setGenesisValidator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"slashingRefundRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakeTokenizerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"stashRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stashedRewardsUntilEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalActiveStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSlashedStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"treasuryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"wrID","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"undelegate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unlockStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"uint256","name":"refundRatio","type":"uint256"}],"name":"updateSlashingRefundRatio","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"voteBookAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"wrID","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]

6080604052600436106103555760003560e01c80637cacb1d6116101bb578063b5d89627116100f7578063cc8343aa11610095578063d96ed5051161006f578063d96ed50514610e4a578063de67f21514610e5f578063e9a505a714610e95578063f2fde38b14610eaa57610355565b8063cc8343aa14610da6578063cfd4766314610dd8578063cfdbb7cd14610e1157610355565b8063c3de580e116100d1578063c3de580e14610d28578063c5f956af14610d52578063c65ee0e114610d67578063c7be95de14610d9157610355565b8063b5d8962714610c4e578063b810e41114610cb9578063bd14d90714610cf257610355565b80638f32d59b116101645780639fa6dd351161013e5780639fa6dd3514610b30578063a45e115414610b4d578063a5a470ad14610ba5578063a86a056f14610c1557610355565b80638f32d59b14610a7e57806390a6c47514610aa757806396c7ee4614610ad157610355565b80638b0e9f3f116101955780638b0e9f3f14610a1b5780638cddb01514610a305780638da5cb5b14610a6957610355565b80637cacb1d614610952578063854873e114610967578063893675c614610a0657610355565b80633a488397116102955780635fab23a811610233578063715018a61161020d578063715018a6146108bc578063736de9ae146108d157806375dcf03014610904578063766718081461093d57610355565b80635fab23a8146108355780636099ecb21461084a578063670322f81461088357610355565b80634f7c4efb1161026f5780634f7c4efb146106fa5780634f864df41461072a5780634feb92f3146107605780635601fe011461080b57610355565b80633a48839714610658578063441a3e7014610697578063468f35ee146106c757610355565b806318f628d4116103025780631f270152116102dc5780631f2701521461053957806320c0849d1461059657806328f73148146105e157806339b80c00146105f657610355565b806318f628d4146104745780631d3ac42c146104d95780631e702f831461050957610355565b80630e559d82116103335780630e559d82146103f557806312622d0e1461042657806318160ddd1461045f57610355565b80630135b1db1461035a57806308c368741461039f5780630962ef79146103cb575b600080fd5b34801561036657600080fd5b5061038d6004803603602081101561037d57600080fd5b50356001600160a01b0316610edd565b60408051918252519081900360200190f35b3480156103ab57600080fd5b506103c9600480360360208110156103c257600080fd5b5035610eef565b005b3480156103d757600080fd5b506103c9600480360360208110156103ee57600080fd5b5035610fbb565b34801561040157600080fd5b5061040a61110e565b604080516001600160a01b039092168252519081900360200190f35b34801561043257600080fd5b5061038d6004803603604081101561044957600080fd5b506001600160a01b03813516906020013561111d565b34801561046b57600080fd5b5061038d6111a6565b34801561048057600080fd5b506103c9600480360361012081101561049857600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060a08101359060c08101359060e08101359061010001356111ac565b3480156104e557600080fd5b5061038d600480360360408110156104fc57600080fd5b508035906020013561130e565b34801561051557600080fd5b506103c96004803603604081101561052c57600080fd5b50803590602001356115d4565b34801561054557600080fd5b506105786004803603606081101561055c57600080fd5b506001600160a01b0381351690602081013590604001356116ad565b60408051938452602084019290925282820152519081900360600190f35b3480156105a257600080fd5b506103c9600480360360808110156105b957600080fd5b506001600160a01b0381358116916020810135909116906040810135151590606001356116df565b3480156105ed57600080fd5b5061038d61187c565b34801561060257600080fd5b506106206004803603602081101561061957600080fd5b5035611882565b604080519788526020880196909652868601949094526060860192909252608085015260a084015260c0830152519081900360e00190f35b34801561066457600080fd5b506103c96004803603606081101561067b57600080fd5b506001600160a01b0381351690602081013590604001356118c4565b3480156106a357600080fd5b506103c9600480360360408110156106ba57600080fd5b5080359060200135611c79565b3480156106d357600080fd5b5061040a600480360360208110156106ea57600080fd5b50356001600160a01b0316611c91565b34801561070657600080fd5b506103c96004803603604081101561071d57600080fd5b5080359060200135611cac565b34801561073657600080fd5b506103c96004803603606081101561074d57600080fd5b5080359060208101359060400135611df0565b34801561076c57600080fd5b506103c9600480360361010081101561078457600080fd5b6001600160a01b03823516916020810135918101906060810160408201356401000000008111156107b457600080fd5b8201836020820111156107c657600080fd5b803590602001918460018302840111640100000000831117156107e857600080fd5b91935091508035906020810135906040810135906060810135906080013561208f565b34801561081757600080fd5b5061038d6004803603602081101561082e57600080fd5b5035612135565b34801561084157600080fd5b5061038d61216b565b34801561085657600080fd5b5061038d6004803603604081101561086d57600080fd5b506001600160a01b038135169060200135612171565b34801561088f57600080fd5b5061038d600480360360408110156108a657600080fd5b506001600160a01b0381351690602001356121af565b3480156108c857600080fd5b506103c96121f0565b3480156108dd57600080fd5b5061040a600480360360208110156108f457600080fd5b50356001600160a01b03166122ab565b34801561091057600080fd5b506103c96004803603604081101561092757600080fd5b506001600160a01b0381351690602001356122c6565b34801561094957600080fd5b5061038d61240b565b34801561095e57600080fd5b5061038d612415565b34801561097357600080fd5b506109916004803603602081101561098a57600080fd5b503561241b565b6040805160208082528351818301528351919283929083019185019080838360005b838110156109cb5781810151838201526020016109b3565b50505050905090810190601f1680156109f85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b348015610a1257600080fd5b5061040a6124d4565b348015610a2757600080fd5b5061038d6124e3565b348015610a3c57600080fd5b506103c960048036036040811015610a5357600080fd5b506001600160a01b0381351690602001356124e9565b348015610a7557600080fd5b5061040a612544565b348015610a8a57600080fd5b50610a93612553565b604080519115158252519081900360200190f35b348015610ab357600080fd5b506103c960048036036020811015610aca57600080fd5b5035612564565b348015610add57600080fd5b50610b0a60048036036040811015610af457600080fd5b506001600160a01b0381351690602001356125c9565b604080519485526020850193909352838301919091526060830152519081900360800190f35b6103c960048036036020811015610b4657600080fd5b50356125fb565b348015610b5957600080fd5b50610b8c60048036036060811015610b7057600080fd5b506001600160a01b038135169060208101359060400135612606565b6040805192835260208301919091528051918290030190f35b6103c960048036036020811015610bbb57600080fd5b810190602081018135640100000000811115610bd657600080fd5b820183602082011115610be857600080fd5b80359060200191846001830284011164010000000083111715610c0a57600080fd5b50909250905061264d565b348015610c2157600080fd5b5061038d60048036036040811015610c3857600080fd5b506001600160a01b038135169060200135612886565b348015610c5a57600080fd5b50610c7860048036036020811015610c7157600080fd5b50356128a3565b604080519788526020880196909652868601949094526060860192909252608085015260a08401526001600160a01b031660c0830152519081900360e00190f35b348015610cc557600080fd5b5061057860048036036040811015610cdc57600080fd5b506001600160a01b0381351690602001356128e9565b348015610cfe57600080fd5b506103c960048036036060811015610d1557600080fd5b5080359060208101359060400135612915565b348015610d3457600080fd5b50610a9360048036036020811015610d4b57600080fd5b503561297f565b348015610d5e57600080fd5b5061040a612996565b348015610d7357600080fd5b5061038d60048036036020811015610d8a57600080fd5b50356129a5565b348015610d9d57600080fd5b5061038d6129b7565b348015610db257600080fd5b506103c960048036036040811015610dc957600080fd5b508035906020013515156129bd565b348015610de457600080fd5b5061038d60048036036040811015610dfb57600080fd5b506001600160a01b038135169060200135612bf5565b348015610e1d57600080fd5b50610a9360048036036040811015610e3457600080fd5b506001600160a01b038135169060200135612c12565b348015610e5657600080fd5b5061038d612ca8565b348015610e6b57600080fd5b506103c960048036036060811015610e8257600080fd5b5080359060208101359060400135612cae565b348015610ea157600080fd5b5061040a612d6b565b348015610eb657600080fd5b506103c960048036036020811015610ecd57600080fd5b50356001600160a01b0316612d7a565b60696020526000908152604090205481565b33610ef8615c05565b610f028284612ddc565b60208101518151919250600091610f1e9163ffffffff612f3c16565b9050610f418385610f3c856040015185612f3c90919063ffffffff16565b612f96565b6001600160a01b0383166000818152607360209081526040808320888452825291829020805485019055845185820151868401518451928352928201528083019190915290518692917f4119153d17a36f9597d40e3ab4148d03261a439dddbec4e91799ab7159608e26919081900360600190a350505050565b33610fc4615c05565b610fce8284612ddc565b90506000610fdb836130a2565b6001600160a01b0316611013836040015161100785602001518660000151612f3c90919063ffffffff16565b9063ffffffff612f3c16565b604051600081818185875af1925050503d806000811461104f576040519150601f19603f3d011682016040523d82523d6000602084013e611054565b606091505b50509050806110aa576040805162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642046544d0000000000000000000000000000604482015290519081900360640190fd5b83836001600160a01b03167fc1d8eb6e444b89fb8ff0991c19311c070df704ccb009e210d1462d5b2410bf4584600001518560200151866040015160405180848152602001838152602001828152602001935050505060405180910390a350505050565b607b546001600160a01b031681565b60006111298383612c12565b61115757506001600160a01b03821660009081526072602090815260408083208484529091529020546111a0565b6001600160a01b03831660008181526073602090815260408083208684528252808320549383526072825280832086845290915290205461119d9163ffffffff6130c716565b90505b92915050565b60765481565b6111b533613109565b6111f05760405162461bcd60e51b8152600401808060200182810382526029815260200180615ce56029913960400191505060405180910390fd5b6111fd898989600061311d565b6001600160a01b0389166000908152606f602090815260408083208b8452909152902060020181905561122f876132b5565b851561130357868611156112745760405162461bcd60e51b815260040180806020018281038252602c815260200180615e05602c913960400191505060405180910390fd5b6001600160a01b03891660008181526073602090815260408083208c845282528083208a8155600181018a90556002810189905560038101889055848452607483528184208d855283529281902086905580518781529182018a9052805192938c9390927f138940e95abffcd789b497bf6188bba3afa5fbd22fb5c42c2f6018d1bf0f4e7892908290030190a3505b505050505050505050565b33600081815260736020908152604080832086845290915281209091908361137d576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b6113878286612c12565b6113d8576040805162461bcd60e51b815260206004820152600d60248201527f6e6f74206c6f636b656420757000000000000000000000000000000000000000604482015290519081900360640190fd5b805484111561142e576040805162461bcd60e51b815260206004820152601760248201527f6e6f7420656e6f756768206c6f636b6564207374616b65000000000000000000604482015290519081900360640190fd5b6114388286613353565b611489576040805162461bcd60e51b815260206004820152601860248201527f6f75747374616e64696e67207346544d2062616c616e63650000000000000000604482015290519081900360640190fd5b6114928261340e565b156114e4576040805162461bcd60e51b815260206004820152600a60248201527f7265646972656374656400000000000000000000000000000000000000000000604482015290519081900360640190fd5b6114ee828661348c565b5060006115018387878560000154613661565b90508481111561150e5750835b8154859003825580156115815761152b838783600160008061368e565b607f546040516001600160a01b03909116908290600081818185875af1925050503d8060008114611578576040519150601f19603f3d011682016040523d82523d6000602084013e61157d565b606091505b5050505b85836001600160a01b03167fef6c0c14fe9aa51af36acd791464dec3badbde668b63189b47bfa4e25be9b2b98784604051808381526020018281526020019250505060405180910390a395945050505050565b6115dd33613109565b6116185760405162461bcd60e51b8152600401808060200182810382526029815260200180615ce56029913960400191505060405180910390fd5b8061166a576040805162461bcd60e51b815260206004820152600c60248201527f77726f6e67207374617475730000000000000000000000000000000000000000604482015290519081900360640190fd5b61167482826138b4565b61167f8260006129bd565b6000828152606860205260408120600601546001600160a01b0316906116a890829081906139de565b505050565b607160209081526000938452604080852082529284528284209052825290208054600182015460029092015490919083565b608254604080516001600160a01b03878116602483015286811660448084019190915283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4a7702bb000000000000000000000000000000000000000000000000000000001781529251825160009592909216938693928291908083835b602083106117ae57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611771565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114611811576040519150601f19603f3d011682016040523d82523d6000602084013e611816565b606091505b505090508080611824575082155b611875576040805162461bcd60e51b815260206004820152601b60248201527f676f7620766f746573207265636f756e74696e67206661696c65640000000000604482015290519081900360640190fd5b5050505050565b606d5481565b607760205280600052604060002060009150905080600701549080600801549080600901549080600a01549080600b01549080600c01549080600d0154905087565b6083546001600160a01b03163314611923576040805162461bcd60e51b815260206004820152601260248201527f6e6f74207346544d2066696e616c697a65720000000000000000000000000000604482015290519081900360640190fd5b61192d838361348c565b5060008111611983576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b607b54604080517f63d2bde50000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b0386811660248301526044820186905260648201859052915191909216916363d2bde591608480830192600092919082900301818387803b1580156119fe57600080fd5b505af1158015611a12573d6000803e3d6000fd5b505050506001600160a01b0383166000908152607260209081526040808320858452909152902054811115611a8e576040805162461bcd60e51b815260206004820152601060248201527f6e6f7420656e6f756768207374616b6500000000000000000000000000000000604482015290519081900360640190fd5b6000611a9a848461111d565b905080821115611b29576001600160a01b038416600090815260736020908152604080832086845290915290208054611adb9083850363ffffffff6130c716565b815560408051838503815260006020820152815186926001600160a01b038916927fef6c0c14fe9aa51af36acd791464dec3badbde668b63189b47bfa4e25be9b2b9929081900390910190a3505b611b3a84848460006001600061368e565b611b458360006129bd565b64ffffffffff83856001600160a01b03167fd3bb4e423fbea695d16b982f9f682dc5f35152e5411646a8a5a79a6b02ba8d57856040518082815260200191505060405180910390a4604051600090339084908381818185875af1925050503d8060008114611bcf576040519150601f19603f3d011682016040523d82523d6000602084013e611bd4565b606091505b5050905080611c2a576040805162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642046544d0000000000000000000000000000604482015290519081900360640190fd5b64ffffffffff84866001600160a01b03167f75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21866040518082815260200191505060405180910390a45050505050565b611c8d338383611c88336130a2565b613b88565b5050565b6088602052600090815260409020546001600160a01b031681565b611cb4612553565b611d05576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b611d0e8261297f565b611d5f576040805162461bcd60e51b815260206004820152601760248201527f76616c696461746f722069736e277420736c6173686564000000000000000000604482015290519081900360640190fd5b611d67614098565b811115611da55760405162461bcd60e51b8152600401808060200182810382526021815260200180615dbb6021913960400191505060405180910390fd5b6000828152607a60209081526040918290208390558151838152915184927f047575f43f09a7a093d94ec483064acfc61b7e25c0de28017da442abf99cb91792908290030190a25050565b33611dfb818561348c565b5060008211611e51576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b611e5b818561111d565b821115611eaf576040805162461bcd60e51b815260206004820152601960248201527f6e6f7420656e6f75676820756e6c6f636b6564207374616b6500000000000000604482015290519081900360640190fd5b611eb98185613353565b611f0a576040805162461bcd60e51b815260206004820152601860248201527f6f75747374616e64696e67207346544d2062616c616e63650000000000000000604482015290519081900360640190fd5b6001600160a01b0381166000908152607160209081526040808320878452825280832086845290915290206002015415611f8b576040805162461bcd60e51b815260206004820152601360248201527f7772494420616c72656164792065786973747300000000000000000000000000604482015290519081900360640190fd5b611f9c81858460016000600161368e565b6001600160a01b038116600090815260716020908152604080832087845282528083208684529091529020600201829055611fd561240b565b6001600160a01b038216600090815260716020908152604080832088845282528083208784529091529020556120096140a4565b6001600160a01b038216600090815260716020908152604080832088845282528083208784529091528120600101919091556120469085906129bd565b8284826001600160a01b03167fd3bb4e423fbea695d16b982f9f682dc5f35152e5411646a8a5a79a6b02ba8d57856040518082815260200191505060405180910390a450505050565b61209833613109565b6120d35760405162461bcd60e51b8152600401808060200182810382526029815260200180615ce56029913960400191505060405180910390fd5b61211b898989898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b92508a915089905088886140a8565b606b5488111561130357606b889055505050505050505050565b6000818152606860209081526040808320600601546001600160a01b03168352607282528083208484529091529020545b919050565b606e5481565b600061217b615c05565b6121858484614285565b8051602082015160408301519293506121a7926110079163ffffffff612f3c16565b949350505050565b60006121bb8383612c12565b6121c7575060006111a0565b506001600160a01b03919091166000908152607360209081526040808320938352929052205490565b6121f8612553565b612249576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6089602052600090815260409020546001600160a01b031681565b6001600160a01b03821660009081526084602090815260408083208484529091528120905b8154811015612405576122fc6140a4565b82828154811061230857fe5b9060005260206000209060020201600101541080612343575081818154811061232d57fe5b9060005260206000209060020201600001546000145b156123fc57815482907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810190811061237857fe5b906000526020600020906002020182828154811061239257fe5b60009182526020909120825460029092020190815560019182015491015581548290806123bb57fe5b60008281526020812060027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90930192830201818155600101559055612400565b6001015b6122eb565b50505050565b6067546001015b90565b60675481565b606a6020908152600091825260409182902080548351601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100600186161502019093169290920491820184900484028101840190945280845290918301828280156124cc5780601f106124a1576101008083540402835291602001916124cc565b820191906000526020600020905b8154815290600101906020018083116124af57829003601f168201915b505050505081565b6082546001600160a01b031681565b606c5481565b6124f3828261348c565b611c8d576040805162461bcd60e51b815260206004820152601060248201527f6e6f7468696e6720746f20737461736800000000000000000000000000000000604482015290519081900360640190fd5b6033546001600160a01b031690565b6033546001600160a01b0316331490565b61256c612553565b6125bd576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6125c6816142f3565b50565b607360209081526000928352604080842090915290825290208054600182015460028301546003909301549192909184565b6125c6338234612f96565b6084602052826000526040600020602052816000526040600020818154811061262b57fe5b6000918252602090912060029091020180546001909101549093509150839050565b608160009054906101000a90046001600160a01b03166001600160a01b031663c5f530af6040518163ffffffff1660e01b815260040160206040518083038186803b15801561269b57600080fd5b505afa1580156126af573d6000803e3d6000fd5b505050506040513d60208110156126c557600080fd5b505134101561271b576040805162461bcd60e51b815260206004820152601760248201527f696e73756666696369656e742073656c662d7374616b65000000000000000000604482015290519081900360640190fd5b60428114801561276657508181600081811061273357fe5b9050013560f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191660c060f81b145b6127b7576040805162461bcd60e51b815260206004820152601060248201527f6d616c666f726d6564207075626b657900000000000000000000000000000000604482015290519081900360640190fd5b60866000838360405180838380828437604080519190930181900390208652506020850195909552505050016000205415612839576040805162461bcd60e51b815260206004820152600c60248201527f616c726561647920757365640000000000000000000000000000000000000000604482015290519081900360640190fd5b6128793383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061435d92505050565b611c8d33606b5434612f96565b607060209081526000928352604080842090915290825290205481565b606860205260009081526040902080546001820154600283015460038401546004850154600586015460069096015494959394929391929091906001600160a01b031687565b607460209081526000928352604080842090915290825290208054600182015460029092015490919083565b336129208185612c12565b612971576040805162461bcd60e51b815260206004820152600d60248201527f6e6f74206c6f636b656420757000000000000000000000000000000000000000604482015290519081900360640190fd5b612405818585856001614388565b600090815260686020526040902054608016151590565b607f546001600160a01b031681565b607a6020526000908152604090205481565b606b5481565b6129c6826148e5565b612a17576040805162461bcd60e51b815260206004820152601760248201527f76616c696461746f7220646f65736e2774206578697374000000000000000000604482015290519081900360640190fd5b60008281526068602052604090206003810154905415612a35575060005b606654604080517fa4066fbe000000000000000000000000000000000000000000000000000000008152600481018690526024810184905290516001600160a01b039092169163a4066fbe9160448082019260009290919082900301818387803b158015612aa257600080fd5b505af1158015612ab6573d6000803e3d6000fd5b50505050818015612ac657508015155b156116a8576066546000848152606a60205260409081902081517f242a6e3f0000000000000000000000000000000000000000000000000000000081526004810187815260248201938452825460027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001831615610100020190911604604483018190526001600160a01b039095169463242a6e3f94899493909160649091019084908015612bb75780601f10612b8c57610100808354040283529160200191612bb7565b820191906000526020600020905b815481529060010190602001808311612b9a57829003601f168201915b50509350505050600060405180830381600087803b158015612bd857600080fd5b505af1158015612bec573d6000803e3d6000fd5b50505050505050565b607260209081526000928352604080842090915290825290205481565b6001600160a01b038216600090815260736020908152604080832084845290915281206002015415801590612c6957506001600160a01b038316600090815260736020908152604080832085845290915290205415155b801561119d57506001600160a01b0383166000908152607360209081526040808320858452909152902060020154612c9f6140a4565b11159392505050565b607e5481565b3381612d01576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b612d0b8185612c12565b15612d5d576040805162461bcd60e51b815260206004820152601160248201527f616c7265616479206c6f636b6564207570000000000000000000000000000000604482015290519081900360640190fd5b612405818585856000614388565b6087546001600160a01b031681565b612d82612553565b612dd3576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6125c6816148fc565b612de4615c05565b612dee8383613353565b612e3f576040805162461bcd60e51b815260206004820152601860248201527f6f75747374616e64696e67207346544d2062616c616e63650000000000000000604482015290519081900360640190fd5b612e49838361348c565b50506001600160a01b0382166000908152606f6020908152604080832084845282528083208151606081018352815480825260018301549482018590526002909201549281018390529392612ea7926110079163ffffffff612f3c16565b905080612efb576040805162461bcd60e51b815260206004820152600c60248201527f7a65726f20726577617264730000000000000000000000000000000000000000604482015290519081900360640190fd5b6001600160a01b0384166000908152606f6020908152604080832086845290915281208181556001810182905560020155612f35816132b5565b5092915050565b60008282018381101561119d576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b612f9f826148e5565b612ff0576040805162461bcd60e51b815260206004820152601760248201527f76616c696461746f7220646f65736e2774206578697374000000000000000000604482015290519081900360640190fd5b60008281526068602052604090205415613051576040805162461bcd60e51b815260206004820152601660248201527f76616c696461746f722069736e27742061637469766500000000000000000000604482015290519081900360640190fd5b61305e838383600161311d565b613067826149b5565b6116a85760405162461bcd60e51b8152600401808060200182810382526029815260200180615ddc6029913960400191505060405180910390fd5b6000806130ae83614a89565b90506001600160a01b0381166111a05782915050612166565b600061119d83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250614b1c565b6066546001600160a01b0390811691161490565b60008211613172576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b61317c848461348c565b506001600160a01b03841660009081526072602090815260408083208684529091529020546131b1908363ffffffff612f3c16565b6001600160a01b03851660009081526072602090815260408083208784528252808320939093556068905220600301546131f1818463ffffffff612f3c16565b600085815260686020526040902060030155606c54613216908463ffffffff612f3c16565b606c5560008481526068602052604090205461324357606d5461323f908463ffffffff612f3c16565b606d555b61324e8482156129bd565b60408051848152905185916001600160a01b038816917f9a8f44850296624dadfd9c246d17e47171d35727a181bd090aa14bbbe00238bb9181900360200190a36000848152606860205260409020600601546118759086906001600160a01b0316846139de565b606654604080517f66e7ea0f0000000000000000000000000000000000000000000000000000000081523060048201526024810184905290516001600160a01b03909216916366e7ea0f9160448082019260009290919082900301818387803b15801561332157600080fd5b505af1158015613335573d6000803e3d6000fd5b505060765461334d925090508263ffffffff612f3c16565b60765550565b607b546000906001600160a01b031661336e575060016111a0565b607b54604080517f21d585c30000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015260248201869052915191909216916321d585c3916044808301926020929190829003018186803b1580156133db57600080fd5b505afa1580156133ef573d6000803e3d6000fd5b505050506040513d602081101561340557600080fd5b50519392505050565b6000606061341a614bb8565b50905060005b81518110156134695781818151811061343557fe5b60200260200101516001600160a01b0316846001600160a01b0316141561346157600192505050612166565b600101613420565b5050506001600160a01b0390811660009081526089602052604090205416151590565b6000613496615c05565b6134a08484614ea3565b90506134ab83614fdb565b6001600160a01b0385166000818152607060209081526040808320888452825280832094909455918152606f8252828120868252825282902082516060810184528154815260018201549281019290925260020154918101919091526135119082615036565b6001600160a01b0385166000818152606f602090815260408083208884528252808320855181558583015160018083019190915595820151600291820155938352607482528083208884528252918290208251606081018452815481529481015491850191909152909101549082015261358b9082615036565b6001600160a01b0385166000908152607460209081526040808320878452825291829020835181559083015160018201559101516002909101556135cf8484612c12565b613632576001600160a01b0384166000818152607360209081526040808320878452825280832083815560018082018590556002808301869055600390920185905594845260748352818420888552909252822082815592830182905591909101555b61363c84846150a8565b602081015115158061364e5750805115155b806121a757506040015115159392505050565b600080613670868686866153bb565b905060006136808787878761545a565b919091019695505050505050565b6001600160a01b038616600090815260726020908152604080832088845282528083208054889003905560689091529020600301546136d3908563ffffffff6130c716565b600086815260686020526040902060030155606c546136f8908563ffffffff6130c716565b606c5560008581526068602052604090205461372557606d54613721908563ffffffff6130c716565b606d555b600061373086612135565b9050801580159061374d5750600086815260686020526040902054155b1561388257608160009054906101000a90046001600160a01b03166001600160a01b031663c5f530af6040518163ffffffff1660e01b815260040160206040518083038186803b1580156137a057600080fd5b505afa1580156137b4573d6000803e3d6000fd5b505050506040513d60208110156137ca57600080fd5b5051811015613831578215613826576040805162461bcd60e51b815260206004820152601760248201527f696e73756666696369656e742073656c662d7374616b65000000000000000000604482015290519081900360640190fd5b6138318660016138b4565b8115806138425750613842866149b5565b61387d5760405162461bcd60e51b8152600401808060200182810382526029815260200180615ddc6029913960400191505060405180910390fd5b61388d565b61388d8660016138b4565b600086815260686020526040902060060154612bec9088906001600160a01b0316866139de565b6000828152606860205260409020541580156138cf57508015155b156138fc57600082815260686020526040902060030154606d546138f89163ffffffff6130c716565b606d555b600082815260686020526040902054811115611c8d576000828152606860205260409020818155600201546139a45761393361240b565b60008381526068602052604090206002015561394d6140a4565b6000838152606860209081526040918290206001810184905560020154825190815290810192909252805184927fac4801c32a6067ff757446524ee4e7a373797278ac3c883eac5c693b4ad72e4792908290030190a25b60408051828152905183917fcd35267e7654194727477d6c78b541a553483cff7f92a055d17868d3da6e953e919081900360200190a25050565b6082546001600160a01b0316156116a857608254604080516001600160a01b03868116602483015285811660448084019190915283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4a7702bb00000000000000000000000000000000000000000000000000000000178152925182516000959290921693627a120093928291908083835b60208310613ac157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613a84565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114613b24576040519150601f19603f3d011682016040523d82523d6000602084013e613b29565b606091505b505090508080613b37575081155b612405576040805162461bcd60e51b815260206004820152601b60248201527f676f7620766f746573207265636f756e74696e67206661696c65640000000000604482015290519081900360640190fd5b613b90615c05565b506001600160a01b038416600090815260716020908152604080832086845282528083208584528252918290208251606081018452815480825260018301549382019390935260029091015492810192909252613c34576040805162461bcd60e51b815260206004820152601560248201527f7265717565737420646f65736e27742065786973740000000000000000000000604482015290519081900360640190fd5b613c3e8585613353565b613c8f576040805162461bcd60e51b815260206004820152601860248201527f6f75747374616e64696e67207346544d2062616c616e63650000000000000000604482015290519081900360640190fd5b60208082015182516000878152606890935260409092206001015490919015801590613ccb575060008681526068602052604090206001015482115b15613cec575050600084815260686020526040902060018101546002909101545b608160009054906101000a90046001600160a01b03166001600160a01b031663b82b84276040518163ffffffff1660e01b815260040160206040518083038186803b158015613d3a57600080fd5b505afa158015613d4e573d6000803e3d6000fd5b505050506040513d6020811015613d6457600080fd5b50518201613d706140a4565b1015613dc3576040805162461bcd60e51b815260206004820152601660248201527f6e6f7420656e6f7567682074696d652070617373656400000000000000000000604482015290519081900360640190fd5b608160009054906101000a90046001600160a01b03166001600160a01b031663650acd666040518163ffffffff1660e01b815260040160206040518083038186803b158015613e1157600080fd5b505afa158015613e25573d6000803e3d6000fd5b505050506040513d6020811015613e3b57600080fd5b50518101613e4761240b565b1015613e9a576040805162461bcd60e51b815260206004820152601860248201527f6e6f7420656e6f7567682065706f636873207061737365640000000000000000604482015290519081900360640190fd5b6001600160a01b0387166000908152607160209081526040808320898452825280832088845290915281206002015490613ed38861297f565b90506000613ef58383607a60008d81526020019081526020016000205461553a565b6001600160a01b038b1660009081526071602090815260408083208d845282528083208c845290915281208181556001810182905560020155606e8054820190559050808311613f8c576040805162461bcd60e51b815260206004820152601660248201527f7374616b652069732066756c6c7920736c617368656400000000000000000000604482015290519081900360640190fd5b60006001600160a01b038816613fa8858463ffffffff6130c716565b604051600081818185875af1925050503d8060008114613fe4576040519150601f19603f3d011682016040523d82523d6000602084013e613fe9565b606091505b505090508061403f576040805162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642046544d0000000000000000000000000000604482015290519081900360640190fd5b614048826142f3565b888a8c6001600160a01b03167f75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21876040518082815260200191505060405180910390a45050505050505050505050565b670de0b6b3a764000090565b4290565b6001600160a01b03881660009081526069602052604090205415614113576040805162461bcd60e51b815260206004820152601860248201527f76616c696461746f7220616c7265616479206578697374730000000000000000604482015290519081900360640190fd5b6001600160a01b03881660008181526069602090815260408083208b90558a8352606882528083208981556004810189905560058101889055600181018690556002810187905560060180547fffffffffffffffffffffffff000000000000000000000000000000000000000016909417909355606a8152919020875161419c92890190615c26565b508551602080880191909120600090815260868252604090819020899055805186815291820185905280516001600160a01b038b16928a927f49bca1ed2666922f9f1690c26a569e1299c2a715fe57647d77e81adfabbf25bf92918290030190a3811561423e576040805183815260208101839052815189927fac4801c32a6067ff757446524ee4e7a373797278ac3c883eac5c693b4ad72e47928290030190a25b841561427b5760408051868152905188917fcd35267e7654194727477d6c78b541a553483cff7f92a055d17868d3da6e953e919081900360200190a25b5050505050505050565b61428d615c05565b614295615c05565b61429f8484614ea3565b6001600160a01b0385166000908152606f6020908152604080832087845282529182902082516060810184528154815260018201549281019290925260020154918101919091529091506121a79082615036565b80156125c65760405160009082156108fc0290839083818181858288f19350505050158015614326573d6000803e3d6000fd5b506040805182815290517f8918bd6046d08b314e457977f29562c5d76a7030d79b1edba66e8a5da0b77ae89181900360200190a150565b606b8054600101908190556116a8838284600061437861240b565b6143806140a4565b6000806140a8565b6143918561340e565b156143e3576040805162461bcd60e51b815260206004820152600a60248201527f7265646972656374656400000000000000000000000000000000000000000000604482015290519081900360640190fd5b6143ed858561111d565b821115614441576040805162461bcd60e51b815260206004820152601060248201527f6e6f7420656e6f756768207374616b6500000000000000000000000000000000604482015290519081900360640190fd5b600084815260686020526040902054156144a2576040805162461bcd60e51b815260206004820152601660248201527f76616c696461746f722069736e27742061637469766500000000000000000000604482015290519081900360640190fd5b608160009054906101000a90046001600160a01b03166001600160a01b0316630d7b26096040518163ffffffff1660e01b815260040160206040518083038186803b1580156144f057600080fd5b505afa158015614504573d6000803e3d6000fd5b505050506040513d602081101561451a57600080fd5b505183108015906145a45750608160009054906101000a90046001600160a01b03166001600160a01b0316630d4955e36040518163ffffffff1660e01b815260040160206040518083038186803b15801561457457600080fd5b505afa158015614588573d6000803e3d6000fd5b505050506040513d602081101561459e57600080fd5b50518311155b6145f5576040805162461bcd60e51b815260206004820152601260248201527f696e636f7272656374206475726174696f6e0000000000000000000000000000604482015290519081900360640190fd5b6000614603846110076140a4565b6000868152606860205260409020600601549091506001600160a01b039081169087168114614696576001600160a01b038116600090815260736020908152604080832089845290915290206002015462278d00018211156146965760405162461bcd60e51b8152600401808060200182810382526025815260200180615d0e6025913960400191505060405180910390fd5b6146a0878761348c565b506146ab87876122c6565b6001600160a01b03871660009081526073602090815260408083208984529091529020831561480b576001600160a01b03881660009081526084602090815260408083208a84529091528120825490919061470a908b908b90806153bb565b905080156148085760408051808201909152818152600280850154602080840191825285546001818101885560008881529290922094519302909301918255519101558154601e10156147a4576040805162461bcd60e51b815260206004820152601860248201527f746f6f206d616e79206f6e676f696e672072656c6f636b730000000000000000604482015290519081900360640190fd5b8254606490048711806147b957508154600310155b806147cd5750826002015462127500018510155b6148085760405162461bcd60e51b8152600401808060200182810382526067815260200180615d546067913960800191505060405180910390fd5b50505b8060030154861015614864576040805162461bcd60e51b815260206004820152601f60248201527f6c6f636b7570206475726174696f6e2063616e6e6f7420646563726561736500604482015290519081900360640190fd5b8054614876908663ffffffff612f3c16565b815561488061240b565b600182015560028101839055600381018690556040805187815260208101879052815189926001600160a01b038c16927f138940e95abffcd789b497bf6188bba3afa5fbd22fb5c42c2f6018d1bf0f4e78929081900390910190a35050505050505050565b600090815260686020526040902060050154151590565b6001600160a01b0381166149415760405162461bcd60e51b8152600401808060200182810382526026815260200180615cbf6026913960400191505060405180910390fd5b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6000614a6e6149c2614098565b608154604080517f2265f2840000000000000000000000000000000000000000000000000000000081529051614a62926001600160a01b031691632265f284916004808301926020929190829003018186803b158015614a2157600080fd5b505afa158015614a35573d6000803e3d6000fd5b505050506040513d6020811015614a4b57600080fd5b5051614a5686612135565b9063ffffffff61559916565b9063ffffffff6155f216565b60008381526068602052604090206003015411159050919050565b6000606080614a96614bb8565b909250905060005b8251811015614afa57828181518110614ab357fe5b60200260200101516001600160a01b0316856001600160a01b03161415614af257818181518110614ae057fe5b60200260200101519350505050612166565b600101614a9e565b505050506001600160a01b039081166000908152608960205260409020541690565b60008184841115614bab5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614b70578181015183820152602001614b58565b50505050905090810190601f168015614b9d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50508183035b9392505050565b60408051600580825260c08201909252606091829182916020820160a080388339505060408051600580825260c0820190925292935060609291506020820160a0803883390190505090508051825114614c0e57fe5b7393419fcb5d9dc7989439f0512d4f737421ed48d982600081518110614c3057fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507380f93310709624636852d0111fd6c4a6e02ed0aa81600081518110614c7257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fbcae1b28ca5039dafec4f10a89e022bc811839482600181518110614cb457fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507391b20102dfd2ff1b00d0915266584009d0b1ae3981600181518110614cf657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507315c2ec517905fb3282f26f3ac3e12889755a2ed782600281518110614d3857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073ca3c54c11172a7263300a801e9937780b5143c0881600281518110614d7a57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bdaaec5f9317cc63d26fd7d79ad17372ccd7d76382600381518110614dbc57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735a1cad027eace4c052f5dee0f42da6c62e39b77981600381518110614dfe57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073f72148504819a1d1b038694b02d299f65bfa312d82600481518110614e4057fe5b60200260200101906001600160a01b031690816001600160a01b031681525050734a15b527475977d9b0cb3fcfe825d6aa7428fafc81600481518110614e8257fe5b6001600160a01b039092166020928302919091019091015290925090509091565b614eab615c05565b6001600160a01b038316600090815260706020908152604080832085845290915281205490614ed984614fdb565b90506000614ee78686615634565b905081811115614ef45750805b82811015614eff5750815b6001600160a01b03861660008181526073602090815260408083208984528252808320938352607282528083208984529091528120548254909190614f4b90839063ffffffff6130c716565b90506000614f5f84600001548a8988615711565b9050614f69615c05565b614f7782866003015461577f565b9050614f85838b8a89615711565b9150614f8f615c05565b614f9a83600061577f565b9050614fa8858c898b615711565b9250614fb2615c05565b614fbd84600061577f565b9050614fca838383615940565b9d9c50505050505050505050505050565b6000818152606860205260408120600201541561502e5760008281526068602052604090206002015460675410156150165750606754612166565b50600081815260686020526040902060020154612166565b505060675490565b61503e615c05565b604080516060810190915282518451829161505f919063ffffffff612f3c16565b815260200161507f84602001518660200151612f3c90919063ffffffff16565b815260200161509f84604001518660400151612f3c90919063ffffffff16565b90529392505050565b6001600160a01b0382166000908152607460209081526040808320848452909152902060018101548154600290910401806150e4575050611c8d565b6001600160a01b0384166000908152607360209081526040808320868452909152812060038101548154919290919061511c8361595b565b90508061512e57505050505050611c8d565b600061514784614a62637cbe4cbf614a568c89886159e1565b6001600160a01b038a1660009081526072602090815260408083208c845290915281205491925061522b615179614098565b614a6261520d608160009054906101000a90046001600160a01b03166001600160a01b031663a77865156040518163ffffffff1660e01b815260040160206040518083038186803b1580156151cd57600080fd5b505afa1580156151e1573d6000803e3d6000fd5b505050506040513d60208110156151f757600080fd5b5051615201614098565b9063ffffffff6130c716565b614a56670de0b6b3a7640000614a628c838a8c63ffffffff61559916565b60008b8152606860205260409020600601549091506001600160a01b038c81169116141561532657600061526a600a614a62600b614a568f8c8b615a92565b9050615320615277614098565b608154604080517fa77865150000000000000000000000000000000000000000000000000000000081529051614a62926001600160a01b03169163a7786515916004808301926020929190829003018186803b1580156152d657600080fd5b505afa1580156152ea573d6000803e3d6000fd5b505050506040513d602081101561530057600080fd5b5051614a56670de0b6b3a7640000614a628d83898d63ffffffff61559916565b82019150505b61533a82614a62838863ffffffff61559916565b9050615344615c05565b61534e828861577f565b905060008160000151600283602001518161536557fe5b04019050808a11156153ac578a54615389908b90614a62908463ffffffff61559916565b8b5560018b01546153a6908b90614a62908463ffffffff61559916565b60018c01555b50505050505050505050505050565b6001600160a01b03841660009081526074602090815260408083208684529091528120805482906153f8908590614a62908863ffffffff61559916565b9050600061541785614a6288866001015461559990919063ffffffff16565b83549091506002820483019061542d90846130c7565b84556001840154615444908363ffffffff6130c716565b6001909401939093555090915050949350505050565b600061546685856122c6565b6001600160a01b0385166000908152608460209081526040808320878452909152812090805b825481101561552f5760006154c886614a62898786815481106154ab57fe5b60009182526020909120600290910201549063ffffffff61559916565b90506154f7818584815481106154da57fe5b60009182526020909120600290910201549063ffffffff6130c716565b84838154811061550357fe5b6000918252602090912060029091020155615524838263ffffffff612f3c16565b92505060010161548c565b509695505050505050565b6000821580615550575061554c614098565b8210155b1561555d57506000614bb1565b615588600161100761556d614098565b614a6286615579614098565b8a91900363ffffffff61559916565b905083811115614bb1575082614bb1565b6000826155a8575060006111a0565b828202828482816155b557fe5b041461119d5760405162461bcd60e51b8152600401808060200182810382526021815260200180615d336021913960400191505060405180910390fd5b600061119d83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250615b2a565b6001600160a01b0382166000908152607360209081526040808320848452909152812060010154606754615669858583615b85565b156156775791506111a09050565b615682858584615b85565b615691576000925050506111a0565b808211156156a4576000925050506111a0565b808210156156d7576002818301046156bd868683615b85565b156156cd578060010192506156d1565b8091505b506156a4565b806156e7576000925050506111a0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01949350505050565b6000818310615722575060006121a7565b600083815260776020818152604080842088855260019081018352818520548786529383528185208986520190915290912054615774615760614098565b614a6289614a56858763ffffffff6130c716565b979650505050505050565b615787615c05565b60405180606001604052806000815260200160008152602001600081525090506000608160009054906101000a90046001600160a01b03166001600160a01b0316635e2308d26040518163ffffffff1660e01b815260040160206040518083038186803b1580156157f757600080fd5b505afa15801561580b573d6000803e3d6000fd5b505050506040513d602081101561582157600080fd5b50519050821561591957600081615836614098565b03905060006158c8608160009054906101000a90046001600160a01b03166001600160a01b0316630d4955e36040518163ffffffff1660e01b815260040160206040518083038186803b15801561588c57600080fd5b505afa1580156158a0573d6000803e3d6000fd5b505050506040513d60208110156158b657600080fd5b5051614a62848863ffffffff61559916565b905060006158e96158d7614098565b614a628987860163ffffffff61559916565b90506159066158f6614098565b614a62898763ffffffff61559916565b602086018190529003845250612f359050565b615934615924614098565b614a62868463ffffffff61559916565b60408301525092915050565b615948615c05565b6121a76159558585615036565b83615036565b60008060056067548161596a57fe5b04905061271081111561597c57506127105b60675481900360009081526077602052604090206007015480158061599f575081155b156159af57600092505050612166565b6000826159be836152016140a4565b816159c557fe5b04905080600f0260010185816159d757fe5b0495945050505050565b6000806159f0846152016140a4565b9050600080805b601e8111615a745760675481870290036000818152607760205260409020600781015486811015615a415760028411615a395789975050505050505050614bb1565b505050615a74565b60008b81526002830160205260409020548015615a645795508585615a64578095505b5050600190920191506159f79050565b50818103600560048802048111156157745786945050505050614bb1565b600083815260686020526040812060030154600182615ab3866152016140a4565b905060015b601e8111615b1457606754818702900360008181526077602052604090206007810154841115615ae9575050615b14565b60008a8152602082905260409020548015615b0957958601956001909501945b505050600101615ab8565b50818381615b1e57fe5b04979650505050505050565b60008183615b795760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315614b70578181015183820152602001614b58565b5060008385816159d757fe5b6001600160a01b038316600090815260736020908152604080832085845290915281206001015482108015906121a757506001600160a01b0384166000908152607360209081526040808320868452909152902060020154615be683615bf0565b1115949350505050565b60009081526077602052604090206007015490565b60405180606001604052806000815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10615c6757805160ff1916838001178555615c94565b82800160010185558215615c94579182015b82811115615c94578251825591602001919060010190615c79565b50615ca0929150615ca4565b5090565b61241291905b80821115615ca05760008155600101615caa56fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737363616c6c6572206973206e6f7420746865204e6f64654472697665724175746820636f6e747261637476616c696461746f722773206c6f636b75702077696c6c20656e6420746f6f206561726c79536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77746f6f206672657175656e742072656c6f636b7320286769746875622e636f6d2f46616e746f6d2d666f756e646174696f6e2f6f706572612d7366632f77696b692f4c6f636b75702d63616c6c732d7265666572656e63652372652d6c6f636b2d7374616b65296d757374206265206c657373207468616e206f7220657175616c20746f20312e3076616c696461746f7227732064656c65676174696f6e73206c696d69742069732065786365656465646c6f636b6564207374616b652069732067726561746572207468616e207468652077686f6c65207374616b65a265627a7a723158203c1c9de1f67b505472c83b4a1ca9bff66663f6574ecb9726a0763a868673bf8264736f6c63430005110032

Deployed Bytecode

0x6080604052600436106103555760003560e01c80637cacb1d6116101bb578063b5d89627116100f7578063cc8343aa11610095578063d96ed5051161006f578063d96ed50514610e4a578063de67f21514610e5f578063e9a505a714610e95578063f2fde38b14610eaa57610355565b8063cc8343aa14610da6578063cfd4766314610dd8578063cfdbb7cd14610e1157610355565b8063c3de580e116100d1578063c3de580e14610d28578063c5f956af14610d52578063c65ee0e114610d67578063c7be95de14610d9157610355565b8063b5d8962714610c4e578063b810e41114610cb9578063bd14d90714610cf257610355565b80638f32d59b116101645780639fa6dd351161013e5780639fa6dd3514610b30578063a45e115414610b4d578063a5a470ad14610ba5578063a86a056f14610c1557610355565b80638f32d59b14610a7e57806390a6c47514610aa757806396c7ee4614610ad157610355565b80638b0e9f3f116101955780638b0e9f3f14610a1b5780638cddb01514610a305780638da5cb5b14610a6957610355565b80637cacb1d614610952578063854873e114610967578063893675c614610a0657610355565b80633a488397116102955780635fab23a811610233578063715018a61161020d578063715018a6146108bc578063736de9ae146108d157806375dcf03014610904578063766718081461093d57610355565b80635fab23a8146108355780636099ecb21461084a578063670322f81461088357610355565b80634f7c4efb1161026f5780634f7c4efb146106fa5780634f864df41461072a5780634feb92f3146107605780635601fe011461080b57610355565b80633a48839714610658578063441a3e7014610697578063468f35ee146106c757610355565b806318f628d4116103025780631f270152116102dc5780631f2701521461053957806320c0849d1461059657806328f73148146105e157806339b80c00146105f657610355565b806318f628d4146104745780631d3ac42c146104d95780631e702f831461050957610355565b80630e559d82116103335780630e559d82146103f557806312622d0e1461042657806318160ddd1461045f57610355565b80630135b1db1461035a57806308c368741461039f5780630962ef79146103cb575b600080fd5b34801561036657600080fd5b5061038d6004803603602081101561037d57600080fd5b50356001600160a01b0316610edd565b60408051918252519081900360200190f35b3480156103ab57600080fd5b506103c9600480360360208110156103c257600080fd5b5035610eef565b005b3480156103d757600080fd5b506103c9600480360360208110156103ee57600080fd5b5035610fbb565b34801561040157600080fd5b5061040a61110e565b604080516001600160a01b039092168252519081900360200190f35b34801561043257600080fd5b5061038d6004803603604081101561044957600080fd5b506001600160a01b03813516906020013561111d565b34801561046b57600080fd5b5061038d6111a6565b34801561048057600080fd5b506103c9600480360361012081101561049857600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060a08101359060c08101359060e08101359061010001356111ac565b3480156104e557600080fd5b5061038d600480360360408110156104fc57600080fd5b508035906020013561130e565b34801561051557600080fd5b506103c96004803603604081101561052c57600080fd5b50803590602001356115d4565b34801561054557600080fd5b506105786004803603606081101561055c57600080fd5b506001600160a01b0381351690602081013590604001356116ad565b60408051938452602084019290925282820152519081900360600190f35b3480156105a257600080fd5b506103c9600480360360808110156105b957600080fd5b506001600160a01b0381358116916020810135909116906040810135151590606001356116df565b3480156105ed57600080fd5b5061038d61187c565b34801561060257600080fd5b506106206004803603602081101561061957600080fd5b5035611882565b604080519788526020880196909652868601949094526060860192909252608085015260a084015260c0830152519081900360e00190f35b34801561066457600080fd5b506103c96004803603606081101561067b57600080fd5b506001600160a01b0381351690602081013590604001356118c4565b3480156106a357600080fd5b506103c9600480360360408110156106ba57600080fd5b5080359060200135611c79565b3480156106d357600080fd5b5061040a600480360360208110156106ea57600080fd5b50356001600160a01b0316611c91565b34801561070657600080fd5b506103c96004803603604081101561071d57600080fd5b5080359060200135611cac565b34801561073657600080fd5b506103c96004803603606081101561074d57600080fd5b5080359060208101359060400135611df0565b34801561076c57600080fd5b506103c9600480360361010081101561078457600080fd5b6001600160a01b03823516916020810135918101906060810160408201356401000000008111156107b457600080fd5b8201836020820111156107c657600080fd5b803590602001918460018302840111640100000000831117156107e857600080fd5b91935091508035906020810135906040810135906060810135906080013561208f565b34801561081757600080fd5b5061038d6004803603602081101561082e57600080fd5b5035612135565b34801561084157600080fd5b5061038d61216b565b34801561085657600080fd5b5061038d6004803603604081101561086d57600080fd5b506001600160a01b038135169060200135612171565b34801561088f57600080fd5b5061038d600480360360408110156108a657600080fd5b506001600160a01b0381351690602001356121af565b3480156108c857600080fd5b506103c96121f0565b3480156108dd57600080fd5b5061040a600480360360208110156108f457600080fd5b50356001600160a01b03166122ab565b34801561091057600080fd5b506103c96004803603604081101561092757600080fd5b506001600160a01b0381351690602001356122c6565b34801561094957600080fd5b5061038d61240b565b34801561095e57600080fd5b5061038d612415565b34801561097357600080fd5b506109916004803603602081101561098a57600080fd5b503561241b565b6040805160208082528351818301528351919283929083019185019080838360005b838110156109cb5781810151838201526020016109b3565b50505050905090810190601f1680156109f85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b348015610a1257600080fd5b5061040a6124d4565b348015610a2757600080fd5b5061038d6124e3565b348015610a3c57600080fd5b506103c960048036036040811015610a5357600080fd5b506001600160a01b0381351690602001356124e9565b348015610a7557600080fd5b5061040a612544565b348015610a8a57600080fd5b50610a93612553565b604080519115158252519081900360200190f35b348015610ab357600080fd5b506103c960048036036020811015610aca57600080fd5b5035612564565b348015610add57600080fd5b50610b0a60048036036040811015610af457600080fd5b506001600160a01b0381351690602001356125c9565b604080519485526020850193909352838301919091526060830152519081900360800190f35b6103c960048036036020811015610b4657600080fd5b50356125fb565b348015610b5957600080fd5b50610b8c60048036036060811015610b7057600080fd5b506001600160a01b038135169060208101359060400135612606565b6040805192835260208301919091528051918290030190f35b6103c960048036036020811015610bbb57600080fd5b810190602081018135640100000000811115610bd657600080fd5b820183602082011115610be857600080fd5b80359060200191846001830284011164010000000083111715610c0a57600080fd5b50909250905061264d565b348015610c2157600080fd5b5061038d60048036036040811015610c3857600080fd5b506001600160a01b038135169060200135612886565b348015610c5a57600080fd5b50610c7860048036036020811015610c7157600080fd5b50356128a3565b604080519788526020880196909652868601949094526060860192909252608085015260a08401526001600160a01b031660c0830152519081900360e00190f35b348015610cc557600080fd5b5061057860048036036040811015610cdc57600080fd5b506001600160a01b0381351690602001356128e9565b348015610cfe57600080fd5b506103c960048036036060811015610d1557600080fd5b5080359060208101359060400135612915565b348015610d3457600080fd5b50610a9360048036036020811015610d4b57600080fd5b503561297f565b348015610d5e57600080fd5b5061040a612996565b348015610d7357600080fd5b5061038d60048036036020811015610d8a57600080fd5b50356129a5565b348015610d9d57600080fd5b5061038d6129b7565b348015610db257600080fd5b506103c960048036036040811015610dc957600080fd5b508035906020013515156129bd565b348015610de457600080fd5b5061038d60048036036040811015610dfb57600080fd5b506001600160a01b038135169060200135612bf5565b348015610e1d57600080fd5b50610a9360048036036040811015610e3457600080fd5b506001600160a01b038135169060200135612c12565b348015610e5657600080fd5b5061038d612ca8565b348015610e6b57600080fd5b506103c960048036036060811015610e8257600080fd5b5080359060208101359060400135612cae565b348015610ea157600080fd5b5061040a612d6b565b348015610eb657600080fd5b506103c960048036036020811015610ecd57600080fd5b50356001600160a01b0316612d7a565b60696020526000908152604090205481565b33610ef8615c05565b610f028284612ddc565b60208101518151919250600091610f1e9163ffffffff612f3c16565b9050610f418385610f3c856040015185612f3c90919063ffffffff16565b612f96565b6001600160a01b0383166000818152607360209081526040808320888452825291829020805485019055845185820151868401518451928352928201528083019190915290518692917f4119153d17a36f9597d40e3ab4148d03261a439dddbec4e91799ab7159608e26919081900360600190a350505050565b33610fc4615c05565b610fce8284612ddc565b90506000610fdb836130a2565b6001600160a01b0316611013836040015161100785602001518660000151612f3c90919063ffffffff16565b9063ffffffff612f3c16565b604051600081818185875af1925050503d806000811461104f576040519150601f19603f3d011682016040523d82523d6000602084013e611054565b606091505b50509050806110aa576040805162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642046544d0000000000000000000000000000604482015290519081900360640190fd5b83836001600160a01b03167fc1d8eb6e444b89fb8ff0991c19311c070df704ccb009e210d1462d5b2410bf4584600001518560200151866040015160405180848152602001838152602001828152602001935050505060405180910390a350505050565b607b546001600160a01b031681565b60006111298383612c12565b61115757506001600160a01b03821660009081526072602090815260408083208484529091529020546111a0565b6001600160a01b03831660008181526073602090815260408083208684528252808320549383526072825280832086845290915290205461119d9163ffffffff6130c716565b90505b92915050565b60765481565b6111b533613109565b6111f05760405162461bcd60e51b8152600401808060200182810382526029815260200180615ce56029913960400191505060405180910390fd5b6111fd898989600061311d565b6001600160a01b0389166000908152606f602090815260408083208b8452909152902060020181905561122f876132b5565b851561130357868611156112745760405162461bcd60e51b815260040180806020018281038252602c815260200180615e05602c913960400191505060405180910390fd5b6001600160a01b03891660008181526073602090815260408083208c845282528083208a8155600181018a90556002810189905560038101889055848452607483528184208d855283529281902086905580518781529182018a9052805192938c9390927f138940e95abffcd789b497bf6188bba3afa5fbd22fb5c42c2f6018d1bf0f4e7892908290030190a3505b505050505050505050565b33600081815260736020908152604080832086845290915281209091908361137d576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b6113878286612c12565b6113d8576040805162461bcd60e51b815260206004820152600d60248201527f6e6f74206c6f636b656420757000000000000000000000000000000000000000604482015290519081900360640190fd5b805484111561142e576040805162461bcd60e51b815260206004820152601760248201527f6e6f7420656e6f756768206c6f636b6564207374616b65000000000000000000604482015290519081900360640190fd5b6114388286613353565b611489576040805162461bcd60e51b815260206004820152601860248201527f6f75747374616e64696e67207346544d2062616c616e63650000000000000000604482015290519081900360640190fd5b6114928261340e565b156114e4576040805162461bcd60e51b815260206004820152600a60248201527f7265646972656374656400000000000000000000000000000000000000000000604482015290519081900360640190fd5b6114ee828661348c565b5060006115018387878560000154613661565b90508481111561150e5750835b8154859003825580156115815761152b838783600160008061368e565b607f546040516001600160a01b03909116908290600081818185875af1925050503d8060008114611578576040519150601f19603f3d011682016040523d82523d6000602084013e61157d565b606091505b5050505b85836001600160a01b03167fef6c0c14fe9aa51af36acd791464dec3badbde668b63189b47bfa4e25be9b2b98784604051808381526020018281526020019250505060405180910390a395945050505050565b6115dd33613109565b6116185760405162461bcd60e51b8152600401808060200182810382526029815260200180615ce56029913960400191505060405180910390fd5b8061166a576040805162461bcd60e51b815260206004820152600c60248201527f77726f6e67207374617475730000000000000000000000000000000000000000604482015290519081900360640190fd5b61167482826138b4565b61167f8260006129bd565b6000828152606860205260408120600601546001600160a01b0316906116a890829081906139de565b505050565b607160209081526000938452604080852082529284528284209052825290208054600182015460029092015490919083565b608254604080516001600160a01b03878116602483015286811660448084019190915283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4a7702bb000000000000000000000000000000000000000000000000000000001781529251825160009592909216938693928291908083835b602083106117ae57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611771565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114611811576040519150601f19603f3d011682016040523d82523d6000602084013e611816565b606091505b505090508080611824575082155b611875576040805162461bcd60e51b815260206004820152601b60248201527f676f7620766f746573207265636f756e74696e67206661696c65640000000000604482015290519081900360640190fd5b5050505050565b606d5481565b607760205280600052604060002060009150905080600701549080600801549080600901549080600a01549080600b01549080600c01549080600d0154905087565b6083546001600160a01b03163314611923576040805162461bcd60e51b815260206004820152601260248201527f6e6f74207346544d2066696e616c697a65720000000000000000000000000000604482015290519081900360640190fd5b61192d838361348c565b5060008111611983576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b607b54604080517f63d2bde50000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b0386811660248301526044820186905260648201859052915191909216916363d2bde591608480830192600092919082900301818387803b1580156119fe57600080fd5b505af1158015611a12573d6000803e3d6000fd5b505050506001600160a01b0383166000908152607260209081526040808320858452909152902054811115611a8e576040805162461bcd60e51b815260206004820152601060248201527f6e6f7420656e6f756768207374616b6500000000000000000000000000000000604482015290519081900360640190fd5b6000611a9a848461111d565b905080821115611b29576001600160a01b038416600090815260736020908152604080832086845290915290208054611adb9083850363ffffffff6130c716565b815560408051838503815260006020820152815186926001600160a01b038916927fef6c0c14fe9aa51af36acd791464dec3badbde668b63189b47bfa4e25be9b2b9929081900390910190a3505b611b3a84848460006001600061368e565b611b458360006129bd565b64ffffffffff83856001600160a01b03167fd3bb4e423fbea695d16b982f9f682dc5f35152e5411646a8a5a79a6b02ba8d57856040518082815260200191505060405180910390a4604051600090339084908381818185875af1925050503d8060008114611bcf576040519150601f19603f3d011682016040523d82523d6000602084013e611bd4565b606091505b5050905080611c2a576040805162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642046544d0000000000000000000000000000604482015290519081900360640190fd5b64ffffffffff84866001600160a01b03167f75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21866040518082815260200191505060405180910390a45050505050565b611c8d338383611c88336130a2565b613b88565b5050565b6088602052600090815260409020546001600160a01b031681565b611cb4612553565b611d05576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b611d0e8261297f565b611d5f576040805162461bcd60e51b815260206004820152601760248201527f76616c696461746f722069736e277420736c6173686564000000000000000000604482015290519081900360640190fd5b611d67614098565b811115611da55760405162461bcd60e51b8152600401808060200182810382526021815260200180615dbb6021913960400191505060405180910390fd5b6000828152607a60209081526040918290208390558151838152915184927f047575f43f09a7a093d94ec483064acfc61b7e25c0de28017da442abf99cb91792908290030190a25050565b33611dfb818561348c565b5060008211611e51576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b611e5b818561111d565b821115611eaf576040805162461bcd60e51b815260206004820152601960248201527f6e6f7420656e6f75676820756e6c6f636b6564207374616b6500000000000000604482015290519081900360640190fd5b611eb98185613353565b611f0a576040805162461bcd60e51b815260206004820152601860248201527f6f75747374616e64696e67207346544d2062616c616e63650000000000000000604482015290519081900360640190fd5b6001600160a01b0381166000908152607160209081526040808320878452825280832086845290915290206002015415611f8b576040805162461bcd60e51b815260206004820152601360248201527f7772494420616c72656164792065786973747300000000000000000000000000604482015290519081900360640190fd5b611f9c81858460016000600161368e565b6001600160a01b038116600090815260716020908152604080832087845282528083208684529091529020600201829055611fd561240b565b6001600160a01b038216600090815260716020908152604080832088845282528083208784529091529020556120096140a4565b6001600160a01b038216600090815260716020908152604080832088845282528083208784529091528120600101919091556120469085906129bd565b8284826001600160a01b03167fd3bb4e423fbea695d16b982f9f682dc5f35152e5411646a8a5a79a6b02ba8d57856040518082815260200191505060405180910390a450505050565b61209833613109565b6120d35760405162461bcd60e51b8152600401808060200182810382526029815260200180615ce56029913960400191505060405180910390fd5b61211b898989898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b92508a915089905088886140a8565b606b5488111561130357606b889055505050505050505050565b6000818152606860209081526040808320600601546001600160a01b03168352607282528083208484529091529020545b919050565b606e5481565b600061217b615c05565b6121858484614285565b8051602082015160408301519293506121a7926110079163ffffffff612f3c16565b949350505050565b60006121bb8383612c12565b6121c7575060006111a0565b506001600160a01b03919091166000908152607360209081526040808320938352929052205490565b6121f8612553565b612249576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6089602052600090815260409020546001600160a01b031681565b6001600160a01b03821660009081526084602090815260408083208484529091528120905b8154811015612405576122fc6140a4565b82828154811061230857fe5b9060005260206000209060020201600101541080612343575081818154811061232d57fe5b9060005260206000209060020201600001546000145b156123fc57815482907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810190811061237857fe5b906000526020600020906002020182828154811061239257fe5b60009182526020909120825460029092020190815560019182015491015581548290806123bb57fe5b60008281526020812060027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90930192830201818155600101559055612400565b6001015b6122eb565b50505050565b6067546001015b90565b60675481565b606a6020908152600091825260409182902080548351601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100600186161502019093169290920491820184900484028101840190945280845290918301828280156124cc5780601f106124a1576101008083540402835291602001916124cc565b820191906000526020600020905b8154815290600101906020018083116124af57829003601f168201915b505050505081565b6082546001600160a01b031681565b606c5481565b6124f3828261348c565b611c8d576040805162461bcd60e51b815260206004820152601060248201527f6e6f7468696e6720746f20737461736800000000000000000000000000000000604482015290519081900360640190fd5b6033546001600160a01b031690565b6033546001600160a01b0316331490565b61256c612553565b6125bd576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6125c6816142f3565b50565b607360209081526000928352604080842090915290825290208054600182015460028301546003909301549192909184565b6125c6338234612f96565b6084602052826000526040600020602052816000526040600020818154811061262b57fe5b6000918252602090912060029091020180546001909101549093509150839050565b608160009054906101000a90046001600160a01b03166001600160a01b031663c5f530af6040518163ffffffff1660e01b815260040160206040518083038186803b15801561269b57600080fd5b505afa1580156126af573d6000803e3d6000fd5b505050506040513d60208110156126c557600080fd5b505134101561271b576040805162461bcd60e51b815260206004820152601760248201527f696e73756666696369656e742073656c662d7374616b65000000000000000000604482015290519081900360640190fd5b60428114801561276657508181600081811061273357fe5b9050013560f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191660c060f81b145b6127b7576040805162461bcd60e51b815260206004820152601060248201527f6d616c666f726d6564207075626b657900000000000000000000000000000000604482015290519081900360640190fd5b60866000838360405180838380828437604080519190930181900390208652506020850195909552505050016000205415612839576040805162461bcd60e51b815260206004820152600c60248201527f616c726561647920757365640000000000000000000000000000000000000000604482015290519081900360640190fd5b6128793383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061435d92505050565b611c8d33606b5434612f96565b607060209081526000928352604080842090915290825290205481565b606860205260009081526040902080546001820154600283015460038401546004850154600586015460069096015494959394929391929091906001600160a01b031687565b607460209081526000928352604080842090915290825290208054600182015460029092015490919083565b336129208185612c12565b612971576040805162461bcd60e51b815260206004820152600d60248201527f6e6f74206c6f636b656420757000000000000000000000000000000000000000604482015290519081900360640190fd5b612405818585856001614388565b600090815260686020526040902054608016151590565b607f546001600160a01b031681565b607a6020526000908152604090205481565b606b5481565b6129c6826148e5565b612a17576040805162461bcd60e51b815260206004820152601760248201527f76616c696461746f7220646f65736e2774206578697374000000000000000000604482015290519081900360640190fd5b60008281526068602052604090206003810154905415612a35575060005b606654604080517fa4066fbe000000000000000000000000000000000000000000000000000000008152600481018690526024810184905290516001600160a01b039092169163a4066fbe9160448082019260009290919082900301818387803b158015612aa257600080fd5b505af1158015612ab6573d6000803e3d6000fd5b50505050818015612ac657508015155b156116a8576066546000848152606a60205260409081902081517f242a6e3f0000000000000000000000000000000000000000000000000000000081526004810187815260248201938452825460027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001831615610100020190911604604483018190526001600160a01b039095169463242a6e3f94899493909160649091019084908015612bb75780601f10612b8c57610100808354040283529160200191612bb7565b820191906000526020600020905b815481529060010190602001808311612b9a57829003601f168201915b50509350505050600060405180830381600087803b158015612bd857600080fd5b505af1158015612bec573d6000803e3d6000fd5b50505050505050565b607260209081526000928352604080842090915290825290205481565b6001600160a01b038216600090815260736020908152604080832084845290915281206002015415801590612c6957506001600160a01b038316600090815260736020908152604080832085845290915290205415155b801561119d57506001600160a01b0383166000908152607360209081526040808320858452909152902060020154612c9f6140a4565b11159392505050565b607e5481565b3381612d01576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b612d0b8185612c12565b15612d5d576040805162461bcd60e51b815260206004820152601160248201527f616c7265616479206c6f636b6564207570000000000000000000000000000000604482015290519081900360640190fd5b612405818585856000614388565b6087546001600160a01b031681565b612d82612553565b612dd3576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6125c6816148fc565b612de4615c05565b612dee8383613353565b612e3f576040805162461bcd60e51b815260206004820152601860248201527f6f75747374616e64696e67207346544d2062616c616e63650000000000000000604482015290519081900360640190fd5b612e49838361348c565b50506001600160a01b0382166000908152606f6020908152604080832084845282528083208151606081018352815480825260018301549482018590526002909201549281018390529392612ea7926110079163ffffffff612f3c16565b905080612efb576040805162461bcd60e51b815260206004820152600c60248201527f7a65726f20726577617264730000000000000000000000000000000000000000604482015290519081900360640190fd5b6001600160a01b0384166000908152606f6020908152604080832086845290915281208181556001810182905560020155612f35816132b5565b5092915050565b60008282018381101561119d576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b612f9f826148e5565b612ff0576040805162461bcd60e51b815260206004820152601760248201527f76616c696461746f7220646f65736e2774206578697374000000000000000000604482015290519081900360640190fd5b60008281526068602052604090205415613051576040805162461bcd60e51b815260206004820152601660248201527f76616c696461746f722069736e27742061637469766500000000000000000000604482015290519081900360640190fd5b61305e838383600161311d565b613067826149b5565b6116a85760405162461bcd60e51b8152600401808060200182810382526029815260200180615ddc6029913960400191505060405180910390fd5b6000806130ae83614a89565b90506001600160a01b0381166111a05782915050612166565b600061119d83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250614b1c565b6066546001600160a01b0390811691161490565b60008211613172576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b61317c848461348c565b506001600160a01b03841660009081526072602090815260408083208684529091529020546131b1908363ffffffff612f3c16565b6001600160a01b03851660009081526072602090815260408083208784528252808320939093556068905220600301546131f1818463ffffffff612f3c16565b600085815260686020526040902060030155606c54613216908463ffffffff612f3c16565b606c5560008481526068602052604090205461324357606d5461323f908463ffffffff612f3c16565b606d555b61324e8482156129bd565b60408051848152905185916001600160a01b038816917f9a8f44850296624dadfd9c246d17e47171d35727a181bd090aa14bbbe00238bb9181900360200190a36000848152606860205260409020600601546118759086906001600160a01b0316846139de565b606654604080517f66e7ea0f0000000000000000000000000000000000000000000000000000000081523060048201526024810184905290516001600160a01b03909216916366e7ea0f9160448082019260009290919082900301818387803b15801561332157600080fd5b505af1158015613335573d6000803e3d6000fd5b505060765461334d925090508263ffffffff612f3c16565b60765550565b607b546000906001600160a01b031661336e575060016111a0565b607b54604080517f21d585c30000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015260248201869052915191909216916321d585c3916044808301926020929190829003018186803b1580156133db57600080fd5b505afa1580156133ef573d6000803e3d6000fd5b505050506040513d602081101561340557600080fd5b50519392505050565b6000606061341a614bb8565b50905060005b81518110156134695781818151811061343557fe5b60200260200101516001600160a01b0316846001600160a01b0316141561346157600192505050612166565b600101613420565b5050506001600160a01b0390811660009081526089602052604090205416151590565b6000613496615c05565b6134a08484614ea3565b90506134ab83614fdb565b6001600160a01b0385166000818152607060209081526040808320888452825280832094909455918152606f8252828120868252825282902082516060810184528154815260018201549281019290925260020154918101919091526135119082615036565b6001600160a01b0385166000818152606f602090815260408083208884528252808320855181558583015160018083019190915595820151600291820155938352607482528083208884528252918290208251606081018452815481529481015491850191909152909101549082015261358b9082615036565b6001600160a01b0385166000908152607460209081526040808320878452825291829020835181559083015160018201559101516002909101556135cf8484612c12565b613632576001600160a01b0384166000818152607360209081526040808320878452825280832083815560018082018590556002808301869055600390920185905594845260748352818420888552909252822082815592830182905591909101555b61363c84846150a8565b602081015115158061364e5750805115155b806121a757506040015115159392505050565b600080613670868686866153bb565b905060006136808787878761545a565b919091019695505050505050565b6001600160a01b038616600090815260726020908152604080832088845282528083208054889003905560689091529020600301546136d3908563ffffffff6130c716565b600086815260686020526040902060030155606c546136f8908563ffffffff6130c716565b606c5560008581526068602052604090205461372557606d54613721908563ffffffff6130c716565b606d555b600061373086612135565b9050801580159061374d5750600086815260686020526040902054155b1561388257608160009054906101000a90046001600160a01b03166001600160a01b031663c5f530af6040518163ffffffff1660e01b815260040160206040518083038186803b1580156137a057600080fd5b505afa1580156137b4573d6000803e3d6000fd5b505050506040513d60208110156137ca57600080fd5b5051811015613831578215613826576040805162461bcd60e51b815260206004820152601760248201527f696e73756666696369656e742073656c662d7374616b65000000000000000000604482015290519081900360640190fd5b6138318660016138b4565b8115806138425750613842866149b5565b61387d5760405162461bcd60e51b8152600401808060200182810382526029815260200180615ddc6029913960400191505060405180910390fd5b61388d565b61388d8660016138b4565b600086815260686020526040902060060154612bec9088906001600160a01b0316866139de565b6000828152606860205260409020541580156138cf57508015155b156138fc57600082815260686020526040902060030154606d546138f89163ffffffff6130c716565b606d555b600082815260686020526040902054811115611c8d576000828152606860205260409020818155600201546139a45761393361240b565b60008381526068602052604090206002015561394d6140a4565b6000838152606860209081526040918290206001810184905560020154825190815290810192909252805184927fac4801c32a6067ff757446524ee4e7a373797278ac3c883eac5c693b4ad72e4792908290030190a25b60408051828152905183917fcd35267e7654194727477d6c78b541a553483cff7f92a055d17868d3da6e953e919081900360200190a25050565b6082546001600160a01b0316156116a857608254604080516001600160a01b03868116602483015285811660448084019190915283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4a7702bb00000000000000000000000000000000000000000000000000000000178152925182516000959290921693627a120093928291908083835b60208310613ac157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613a84565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114613b24576040519150601f19603f3d011682016040523d82523d6000602084013e613b29565b606091505b505090508080613b37575081155b612405576040805162461bcd60e51b815260206004820152601b60248201527f676f7620766f746573207265636f756e74696e67206661696c65640000000000604482015290519081900360640190fd5b613b90615c05565b506001600160a01b038416600090815260716020908152604080832086845282528083208584528252918290208251606081018452815480825260018301549382019390935260029091015492810192909252613c34576040805162461bcd60e51b815260206004820152601560248201527f7265717565737420646f65736e27742065786973740000000000000000000000604482015290519081900360640190fd5b613c3e8585613353565b613c8f576040805162461bcd60e51b815260206004820152601860248201527f6f75747374616e64696e67207346544d2062616c616e63650000000000000000604482015290519081900360640190fd5b60208082015182516000878152606890935260409092206001015490919015801590613ccb575060008681526068602052604090206001015482115b15613cec575050600084815260686020526040902060018101546002909101545b608160009054906101000a90046001600160a01b03166001600160a01b031663b82b84276040518163ffffffff1660e01b815260040160206040518083038186803b158015613d3a57600080fd5b505afa158015613d4e573d6000803e3d6000fd5b505050506040513d6020811015613d6457600080fd5b50518201613d706140a4565b1015613dc3576040805162461bcd60e51b815260206004820152601660248201527f6e6f7420656e6f7567682074696d652070617373656400000000000000000000604482015290519081900360640190fd5b608160009054906101000a90046001600160a01b03166001600160a01b031663650acd666040518163ffffffff1660e01b815260040160206040518083038186803b158015613e1157600080fd5b505afa158015613e25573d6000803e3d6000fd5b505050506040513d6020811015613e3b57600080fd5b50518101613e4761240b565b1015613e9a576040805162461bcd60e51b815260206004820152601860248201527f6e6f7420656e6f7567682065706f636873207061737365640000000000000000604482015290519081900360640190fd5b6001600160a01b0387166000908152607160209081526040808320898452825280832088845290915281206002015490613ed38861297f565b90506000613ef58383607a60008d81526020019081526020016000205461553a565b6001600160a01b038b1660009081526071602090815260408083208d845282528083208c845290915281208181556001810182905560020155606e8054820190559050808311613f8c576040805162461bcd60e51b815260206004820152601660248201527f7374616b652069732066756c6c7920736c617368656400000000000000000000604482015290519081900360640190fd5b60006001600160a01b038816613fa8858463ffffffff6130c716565b604051600081818185875af1925050503d8060008114613fe4576040519150601f19603f3d011682016040523d82523d6000602084013e613fe9565b606091505b505090508061403f576040805162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642046544d0000000000000000000000000000604482015290519081900360640190fd5b614048826142f3565b888a8c6001600160a01b03167f75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21876040518082815260200191505060405180910390a45050505050505050505050565b670de0b6b3a764000090565b4290565b6001600160a01b03881660009081526069602052604090205415614113576040805162461bcd60e51b815260206004820152601860248201527f76616c696461746f7220616c7265616479206578697374730000000000000000604482015290519081900360640190fd5b6001600160a01b03881660008181526069602090815260408083208b90558a8352606882528083208981556004810189905560058101889055600181018690556002810187905560060180547fffffffffffffffffffffffff000000000000000000000000000000000000000016909417909355606a8152919020875161419c92890190615c26565b508551602080880191909120600090815260868252604090819020899055805186815291820185905280516001600160a01b038b16928a927f49bca1ed2666922f9f1690c26a569e1299c2a715fe57647d77e81adfabbf25bf92918290030190a3811561423e576040805183815260208101839052815189927fac4801c32a6067ff757446524ee4e7a373797278ac3c883eac5c693b4ad72e47928290030190a25b841561427b5760408051868152905188917fcd35267e7654194727477d6c78b541a553483cff7f92a055d17868d3da6e953e919081900360200190a25b5050505050505050565b61428d615c05565b614295615c05565b61429f8484614ea3565b6001600160a01b0385166000908152606f6020908152604080832087845282529182902082516060810184528154815260018201549281019290925260020154918101919091529091506121a79082615036565b80156125c65760405160009082156108fc0290839083818181858288f19350505050158015614326573d6000803e3d6000fd5b506040805182815290517f8918bd6046d08b314e457977f29562c5d76a7030d79b1edba66e8a5da0b77ae89181900360200190a150565b606b8054600101908190556116a8838284600061437861240b565b6143806140a4565b6000806140a8565b6143918561340e565b156143e3576040805162461bcd60e51b815260206004820152600a60248201527f7265646972656374656400000000000000000000000000000000000000000000604482015290519081900360640190fd5b6143ed858561111d565b821115614441576040805162461bcd60e51b815260206004820152601060248201527f6e6f7420656e6f756768207374616b6500000000000000000000000000000000604482015290519081900360640190fd5b600084815260686020526040902054156144a2576040805162461bcd60e51b815260206004820152601660248201527f76616c696461746f722069736e27742061637469766500000000000000000000604482015290519081900360640190fd5b608160009054906101000a90046001600160a01b03166001600160a01b0316630d7b26096040518163ffffffff1660e01b815260040160206040518083038186803b1580156144f057600080fd5b505afa158015614504573d6000803e3d6000fd5b505050506040513d602081101561451a57600080fd5b505183108015906145a45750608160009054906101000a90046001600160a01b03166001600160a01b0316630d4955e36040518163ffffffff1660e01b815260040160206040518083038186803b15801561457457600080fd5b505afa158015614588573d6000803e3d6000fd5b505050506040513d602081101561459e57600080fd5b50518311155b6145f5576040805162461bcd60e51b815260206004820152601260248201527f696e636f7272656374206475726174696f6e0000000000000000000000000000604482015290519081900360640190fd5b6000614603846110076140a4565b6000868152606860205260409020600601549091506001600160a01b039081169087168114614696576001600160a01b038116600090815260736020908152604080832089845290915290206002015462278d00018211156146965760405162461bcd60e51b8152600401808060200182810382526025815260200180615d0e6025913960400191505060405180910390fd5b6146a0878761348c565b506146ab87876122c6565b6001600160a01b03871660009081526073602090815260408083208984529091529020831561480b576001600160a01b03881660009081526084602090815260408083208a84529091528120825490919061470a908b908b90806153bb565b905080156148085760408051808201909152818152600280850154602080840191825285546001818101885560008881529290922094519302909301918255519101558154601e10156147a4576040805162461bcd60e51b815260206004820152601860248201527f746f6f206d616e79206f6e676f696e672072656c6f636b730000000000000000604482015290519081900360640190fd5b8254606490048711806147b957508154600310155b806147cd5750826002015462127500018510155b6148085760405162461bcd60e51b8152600401808060200182810382526067815260200180615d546067913960800191505060405180910390fd5b50505b8060030154861015614864576040805162461bcd60e51b815260206004820152601f60248201527f6c6f636b7570206475726174696f6e2063616e6e6f7420646563726561736500604482015290519081900360640190fd5b8054614876908663ffffffff612f3c16565b815561488061240b565b600182015560028101839055600381018690556040805187815260208101879052815189926001600160a01b038c16927f138940e95abffcd789b497bf6188bba3afa5fbd22fb5c42c2f6018d1bf0f4e78929081900390910190a35050505050505050565b600090815260686020526040902060050154151590565b6001600160a01b0381166149415760405162461bcd60e51b8152600401808060200182810382526026815260200180615cbf6026913960400191505060405180910390fd5b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6000614a6e6149c2614098565b608154604080517f2265f2840000000000000000000000000000000000000000000000000000000081529051614a62926001600160a01b031691632265f284916004808301926020929190829003018186803b158015614a2157600080fd5b505afa158015614a35573d6000803e3d6000fd5b505050506040513d6020811015614a4b57600080fd5b5051614a5686612135565b9063ffffffff61559916565b9063ffffffff6155f216565b60008381526068602052604090206003015411159050919050565b6000606080614a96614bb8565b909250905060005b8251811015614afa57828181518110614ab357fe5b60200260200101516001600160a01b0316856001600160a01b03161415614af257818181518110614ae057fe5b60200260200101519350505050612166565b600101614a9e565b505050506001600160a01b039081166000908152608960205260409020541690565b60008184841115614bab5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614b70578181015183820152602001614b58565b50505050905090810190601f168015614b9d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50508183035b9392505050565b60408051600580825260c08201909252606091829182916020820160a080388339505060408051600580825260c0820190925292935060609291506020820160a0803883390190505090508051825114614c0e57fe5b7393419fcb5d9dc7989439f0512d4f737421ed48d982600081518110614c3057fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507380f93310709624636852d0111fd6c4a6e02ed0aa81600081518110614c7257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fbcae1b28ca5039dafec4f10a89e022bc811839482600181518110614cb457fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507391b20102dfd2ff1b00d0915266584009d0b1ae3981600181518110614cf657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507315c2ec517905fb3282f26f3ac3e12889755a2ed782600281518110614d3857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073ca3c54c11172a7263300a801e9937780b5143c0881600281518110614d7a57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bdaaec5f9317cc63d26fd7d79ad17372ccd7d76382600381518110614dbc57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735a1cad027eace4c052f5dee0f42da6c62e39b77981600381518110614dfe57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073f72148504819a1d1b038694b02d299f65bfa312d82600481518110614e4057fe5b60200260200101906001600160a01b031690816001600160a01b031681525050734a15b527475977d9b0cb3fcfe825d6aa7428fafc81600481518110614e8257fe5b6001600160a01b039092166020928302919091019091015290925090509091565b614eab615c05565b6001600160a01b038316600090815260706020908152604080832085845290915281205490614ed984614fdb565b90506000614ee78686615634565b905081811115614ef45750805b82811015614eff5750815b6001600160a01b03861660008181526073602090815260408083208984528252808320938352607282528083208984529091528120548254909190614f4b90839063ffffffff6130c716565b90506000614f5f84600001548a8988615711565b9050614f69615c05565b614f7782866003015461577f565b9050614f85838b8a89615711565b9150614f8f615c05565b614f9a83600061577f565b9050614fa8858c898b615711565b9250614fb2615c05565b614fbd84600061577f565b9050614fca838383615940565b9d9c50505050505050505050505050565b6000818152606860205260408120600201541561502e5760008281526068602052604090206002015460675410156150165750606754612166565b50600081815260686020526040902060020154612166565b505060675490565b61503e615c05565b604080516060810190915282518451829161505f919063ffffffff612f3c16565b815260200161507f84602001518660200151612f3c90919063ffffffff16565b815260200161509f84604001518660400151612f3c90919063ffffffff16565b90529392505050565b6001600160a01b0382166000908152607460209081526040808320848452909152902060018101548154600290910401806150e4575050611c8d565b6001600160a01b0384166000908152607360209081526040808320868452909152812060038101548154919290919061511c8361595b565b90508061512e57505050505050611c8d565b600061514784614a62637cbe4cbf614a568c89886159e1565b6001600160a01b038a1660009081526072602090815260408083208c845290915281205491925061522b615179614098565b614a6261520d608160009054906101000a90046001600160a01b03166001600160a01b031663a77865156040518163ffffffff1660e01b815260040160206040518083038186803b1580156151cd57600080fd5b505afa1580156151e1573d6000803e3d6000fd5b505050506040513d60208110156151f757600080fd5b5051615201614098565b9063ffffffff6130c716565b614a56670de0b6b3a7640000614a628c838a8c63ffffffff61559916565b60008b8152606860205260409020600601549091506001600160a01b038c81169116141561532657600061526a600a614a62600b614a568f8c8b615a92565b9050615320615277614098565b608154604080517fa77865150000000000000000000000000000000000000000000000000000000081529051614a62926001600160a01b03169163a7786515916004808301926020929190829003018186803b1580156152d657600080fd5b505afa1580156152ea573d6000803e3d6000fd5b505050506040513d602081101561530057600080fd5b5051614a56670de0b6b3a7640000614a628d83898d63ffffffff61559916565b82019150505b61533a82614a62838863ffffffff61559916565b9050615344615c05565b61534e828861577f565b905060008160000151600283602001518161536557fe5b04019050808a11156153ac578a54615389908b90614a62908463ffffffff61559916565b8b5560018b01546153a6908b90614a62908463ffffffff61559916565b60018c01555b50505050505050505050505050565b6001600160a01b03841660009081526074602090815260408083208684529091528120805482906153f8908590614a62908863ffffffff61559916565b9050600061541785614a6288866001015461559990919063ffffffff16565b83549091506002820483019061542d90846130c7565b84556001840154615444908363ffffffff6130c716565b6001909401939093555090915050949350505050565b600061546685856122c6565b6001600160a01b0385166000908152608460209081526040808320878452909152812090805b825481101561552f5760006154c886614a62898786815481106154ab57fe5b60009182526020909120600290910201549063ffffffff61559916565b90506154f7818584815481106154da57fe5b60009182526020909120600290910201549063ffffffff6130c716565b84838154811061550357fe5b6000918252602090912060029091020155615524838263ffffffff612f3c16565b92505060010161548c565b509695505050505050565b6000821580615550575061554c614098565b8210155b1561555d57506000614bb1565b615588600161100761556d614098565b614a6286615579614098565b8a91900363ffffffff61559916565b905083811115614bb1575082614bb1565b6000826155a8575060006111a0565b828202828482816155b557fe5b041461119d5760405162461bcd60e51b8152600401808060200182810382526021815260200180615d336021913960400191505060405180910390fd5b600061119d83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250615b2a565b6001600160a01b0382166000908152607360209081526040808320848452909152812060010154606754615669858583615b85565b156156775791506111a09050565b615682858584615b85565b615691576000925050506111a0565b808211156156a4576000925050506111a0565b808210156156d7576002818301046156bd868683615b85565b156156cd578060010192506156d1565b8091505b506156a4565b806156e7576000925050506111a0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01949350505050565b6000818310615722575060006121a7565b600083815260776020818152604080842088855260019081018352818520548786529383528185208986520190915290912054615774615760614098565b614a6289614a56858763ffffffff6130c716565b979650505050505050565b615787615c05565b60405180606001604052806000815260200160008152602001600081525090506000608160009054906101000a90046001600160a01b03166001600160a01b0316635e2308d26040518163ffffffff1660e01b815260040160206040518083038186803b1580156157f757600080fd5b505afa15801561580b573d6000803e3d6000fd5b505050506040513d602081101561582157600080fd5b50519050821561591957600081615836614098565b03905060006158c8608160009054906101000a90046001600160a01b03166001600160a01b0316630d4955e36040518163ffffffff1660e01b815260040160206040518083038186803b15801561588c57600080fd5b505afa1580156158a0573d6000803e3d6000fd5b505050506040513d60208110156158b657600080fd5b5051614a62848863ffffffff61559916565b905060006158e96158d7614098565b614a628987860163ffffffff61559916565b90506159066158f6614098565b614a62898763ffffffff61559916565b602086018190529003845250612f359050565b615934615924614098565b614a62868463ffffffff61559916565b60408301525092915050565b615948615c05565b6121a76159558585615036565b83615036565b60008060056067548161596a57fe5b04905061271081111561597c57506127105b60675481900360009081526077602052604090206007015480158061599f575081155b156159af57600092505050612166565b6000826159be836152016140a4565b816159c557fe5b04905080600f0260010185816159d757fe5b0495945050505050565b6000806159f0846152016140a4565b9050600080805b601e8111615a745760675481870290036000818152607760205260409020600781015486811015615a415760028411615a395789975050505050505050614bb1565b505050615a74565b60008b81526002830160205260409020548015615a645795508585615a64578095505b5050600190920191506159f79050565b50818103600560048802048111156157745786945050505050614bb1565b600083815260686020526040812060030154600182615ab3866152016140a4565b905060015b601e8111615b1457606754818702900360008181526077602052604090206007810154841115615ae9575050615b14565b60008a8152602082905260409020548015615b0957958601956001909501945b505050600101615ab8565b50818381615b1e57fe5b04979650505050505050565b60008183615b795760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315614b70578181015183820152602001614b58565b5060008385816159d757fe5b6001600160a01b038316600090815260736020908152604080832085845290915281206001015482108015906121a757506001600160a01b0384166000908152607360209081526040808320868452909152902060020154615be683615bf0565b1115949350505050565b60009081526077602052604090206007015490565b60405180606001604052806000815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10615c6757805160ff1916838001178555615c94565b82800160010185558215615c94579182015b82811115615c94578251825591602001919060010190615c79565b50615ca0929150615ca4565b5090565b61241291905b80821115615ca05760008155600101615caa56fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737363616c6c6572206973206e6f7420746865204e6f64654472697665724175746820636f6e747261637476616c696461746f722773206c6f636b75702077696c6c20656e6420746f6f206561726c79536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77746f6f206672657175656e742072656c6f636b7320286769746875622e636f6d2f46616e746f6d2d666f756e646174696f6e2f6f706572612d7366632f77696b692f4c6f636b75702d63616c6c732d7265666572656e63652372652d6c6f636b2d7374616b65296d757374206265206c657373207468616e206f7220657175616c20746f20312e3076616c696461746f7227732064656c65676174696f6e73206c696d69742069732065786365656465646c6f636b6564207374616b652069732067726561746572207468616e207468652077686f6c65207374616b65a265627a7a72315820355bb02dc9bdc4f689b18b8f31f5621f2a48f106f86eca94e61db7659872453664736f6c63430005110032

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

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

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