Contract 0xfc00face00000000000000000000000000000000 6

 
My Name Tag:
Not Available, login to update

ContractCreator:
GENESIS at txn GENESIS_0
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xd64d8edd8c29508bf2bd0dbef539b878ca9116d7274ec6daefb52fdcc12409c5Withdraw552600012023-02-07 12:27:3617 secs ago0x71b0585eba2025fd2f9b87b38618837499863503 IN Fantom: SFC0 FTM0.002917815189
0x50f77529fac4304c1916840fd1f2706684f5a43fb28dd29ce74db8e9947452aaWithdraw552599732023-02-07 12:26:5855 secs ago0x71b0585eba2025fd2f9b87b38618837499863503 IN Fantom: SFC0 FTM0.002917815189
0x4f769dd288413580d978a8a22c758f7f7498c21fe7f29bfb4abecd95ea2948a1Withdraw552599532023-02-07 12:26:271 min ago0x71b0585eba2025fd2f9b87b38618837499863503 IN Fantom: SFC0 FTM0.002917815189
0x71ba7bdcb8d3755b312d8d51e67de52d2251ab6dd020df109c34e4fdab98c114Withdraw552599342023-02-07 12:26:031 min ago0x71b0585eba2025fd2f9b87b38618837499863503 IN Fantom: SFC0 FTM0.002917815189
0xf83045ce91c9061f1b1c32a56211a9d2de80db5c0883c5e3bf7ab23ca7759cadLock Stake552596782023-02-07 12:20:037 mins ago0xc14ad35f19612acb087ed72ba9ae0906a4ce02d8 IN Fantom: SFC0 FTM0.00996116693
0x4f09f5d202e972529cef8bf9268e1055a9d17469811bd67ce1fe8080a95faab7Withdraw552596742023-02-07 12:19:587 mins ago0x71b0585eba2025fd2f9b87b38618837499863503 IN Fantom: SFC0 FTM0.002917815189
0x5a32a77bbef3b25a8ba4c678938faad17dc470e7e084576e2a03ecebbc39d358Delegate552596232023-02-07 12:18:588 mins ago0xc14ad35f19612acb087ed72ba9ae0906a4ce02d8 IN Fantom: SFC400 FTM0.010293847563
0xa785ad7b8b479c88db0f656e0d2866ce0847a48357dff25c0f97b78fd7f5a421Restake Rewards552593542023-02-07 12:13:3814 mins ago0x19d9a418b734e4fec031cddaf935711d4622161f IN Fantom: SFC0 FTM0.008974293024
0xaa6b793c8979034512ca6329ecd3812834afcb5b82be8013ebaa9394260358c7Withdraw552592722023-02-07 12:11:4516 mins ago0x71b0585eba2025fd2f9b87b38618837499863503 IN Fantom: SFC0 FTM0.002917815189
0x0b8028d6246d87b729d68fd1a1c3c469d23ac05ac49e5cd3f73432b32e6f06a0Delegate552592472023-02-07 12:11:0816 mins ago0x41dd11769c9114eac509b967b0aeecc23c28c3a4 IN Fantom: SFC500 FTM0.01036601494
0x1407f6c09b1dd89ea8ae56ad829bb3729976580de94a03ca95fdb64bfe336a0aUndelegate552591632023-02-07 12:09:2118 mins ago0x1573a49b410fc4fdeccb5c104abd229141aead74 IN Fantom: SFC0 FTM0.011669879669
0xdd099087591da86dbd90c6fdb144b8d320540e6deb95c587ae1f948807b2a0b8Withdraw552591612023-02-07 12:09:1818 mins ago0x41dd11769c9114eac509b967b0aeecc23c28c3a4 IN Fantom: SFC0 FTM0.003036138395
0x8625782a06f0696f728e0c8cc5a22f4771de7b91cb454c0ea4c14514de0b414dClaim Rewards552591292023-02-07 12:08:3819 mins ago0x1573a49b410fc4fdeccb5c104abd229141aead74 IN Fantom: SFC0 FTM0.008503923161
0xb529ebf7d71cbc78ac0706d329f6d135c6631d0d8fcb6d95bcb60a62c8b29ee4Claim Rewards552587922023-02-07 12:01:1926 mins ago0x679ccbdc053963b997fe84b0dcea0277b48f2e36 IN Fantom: SFC0 FTM0.006721373837
0x0f6afd0824b5705d2bd3c49a368c354d6b366ead68ed0c53d2c487a3d78b94f6Lock Stake552587862023-02-07 12:01:1226 mins ago0x18730295eddc1de0551131b0922253ab841cb142 IN Fantom: SFC0 FTM0.010643205921
0x55e1e4260a7a9a96d1445c01f3f0c6c466318da39a22175e03739f767c3c0402Restake Rewards552587632023-02-07 12:00:3627 mins ago0xb0f9be422505c1664792c98db04c0d88997f4932 IN Fantom: SFC0 FTM0.008891883878
0x88492692189ba792f77805e405b46fb1cbe971d2038e7fade4447f41e50bd626Delegate552587302023-02-07 11:59:2628 mins ago0x18730295eddc1de0551131b0922253ab841cb142 IN Fantom: SFC2,500 FTM0.009813276262
0xbb25b2909f32e428db59da0c5b8a3615292ae01af397b409e632dc3f23a49bf4Restake Rewards552587282023-02-07 11:59:2428 mins ago0xb0f9be422505c1664792c98db04c0d88997f4932 IN Fantom: SFC0 FTM0.008891883878
0xf5960239f9889fe09a5c254d31005168b0e1ff6f687a4c6d49ea2562f4fdfbbfClaim Rewards552586202023-02-07 11:56:5131 mins ago0x87a91d99a306bddb6f3674ea3eb469b8e557846f IN Fantom: SFC0 FTM0.008024039836
0xe39934dacd81b2c87ca451ec4c4d84c6301e019e2f1bdcd10e43c4a4aa3a230aRestake Rewards552585882023-02-07 11:56:0031 mins ago0xa994eb333a57ef5b4fdedaec5554ef8ab0054970 IN Fantom: SFC0 FTM0.008896245517
0x11f8a904c9d5b493385879cbdfa54add35a2f83fe6f5f853befecb1b5a81a8eaRestake Rewards552585852023-02-07 11:55:5831 mins ago0x9962d65a35ee7a9911c0295b2b7a3af84c2552e3 IN Fantom: SFC0 FTM0.00854103628
0x0d96dbf54e58f86a6683a5b18fc784d98561e16a3386c4587081673697a62e21Restake Rewards552583782023-02-07 11:51:2636 mins ago0x6fc708fb81d689af8bacf442e23ed802aa24620d IN Fantom: SFC0 FTM0.009474926041
0x0c805ae45a435f664e58f0adb23b7404193223c1bdab06032738ca1ef425f07fRestake Rewards552577022023-02-07 11:37:1050 mins ago0xfef36e7215ac756d414d3e5a064ddd25ca08667e IN Fantom: SFC0 FTM0.009107251018
0x4fab626671ee1a6e5468f34b38a75d4631e69c20f043517285edf4281aa4e672Restake Rewards552574602023-02-07 11:33:0854 mins ago0x8dcccbdfb0f2752dcca57a5a9fc7e55ad7680ace IN Fantom: SFC0 FTM0.011518326082
0xd643e5e43cfbab5ffba390f8dc9dffda00adbc8843614758df879cf6c09562ceRestake Rewards552574362023-02-07 11:32:4355 mins ago0x8dcccbdfb0f2752dcca57a5a9fc7e55ad7680ace IN Fantom: SFC0 FTM0.011518326082
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xe34738f5c26c1dbc201bf9adceac52be9ef6bf95749954f841739d5836b5fbdb552599712023-02-07 12:26:491 min ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f5.250081760147027385 FTM
0x192be4e0ad1d9ed14d278ef7dc6c376800adbf6ea9e5e17c8da53af1e402abab552596832023-02-07 12:20:127 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f4.389256610255498894 FTM
0x34c14a023d907098d8a6366e52c004b927dc5bb06e6ce9a8f41f91770d745cc9552593482023-02-07 12:13:2114 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f2.723557280948001302 FTM
0x74e5430e7097fae32f12775c870578a0211c9fb0971207432322f8bed38021ca552589302023-02-07 12:04:2223 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f2.742640130714309546 FTM
0x9c6c85404f29f9fd3130ae093e0af290b44cac76e4769fd94ce5d2fc770ebe18552587512023-02-07 12:00:0927 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f2.007482138864041838 FTM
0xa473c4b9ca65a5fa7175ecc72addfc880d969ccc0c99a1f8136481cf6dc0a488552585092023-02-07 11:54:1433 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f2.828065999168292292 FTM
0x6e5dc13554487d406e70d0e10f14ede85bea4d3bb40de321ab10614311b9259c552582152023-02-07 11:47:1840 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f3.654996582769046573 FTM
0xcef5d5920000da152f875c63130334d4095fa82b5e61969ccdaddb4df4a898ce552579632023-02-07 11:42:0645 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f3.964325349398827479 FTM
0x1b53689bf251c5d4ab53095c9529f99bfe30a732d3e0d4beb5081090c92a0322552573832023-02-07 11:31:4156 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f12.332159622175338706 FTM
0x61eb5040237122f11943785b52eb3dc9022e9f56a0bffa4db448cd33b155374b552568602023-02-07 11:22:411 hr 5 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f2.314278344963554992 FTM
0xbbe09643939fe482a152c4c6f2136b38f5033e3d37fb26f5b8ef6d611f26265c552564902023-02-07 11:16:191 hr 11 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f2.648009168410117412 FTM
0xcedfea20d36a9eb9ad491fa9afa05d1b3b399dc15a331a57f6240dbd5476e4f9552559952023-02-07 11:08:151 hr 19 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f3.102763337037336856 FTM
0xe04d72eacc10a2622a9143e6827f6695a5ee635d723532fb9d8aea25be9b2db8552555602023-02-07 11:01:001 hr 26 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f2.622701867882588253 FTM
0xb336e587dccfeebc0c5ada3ff803ce510ada029c56069be61c2e7b810c4c059f552552262023-02-07 10:54:521 hr 33 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f2.439445126207464937 FTM
0x3c99d441f805e3b496911994a050c58f714b5cd77bb3ace18913d4abc79ea496552548112023-02-07 10:47:141 hr 40 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f2.858498515079288854 FTM
0x8f86128c88dac76083f6330ecd44a2edd247f707e60e9af37924cf6df1cb8658552543342023-02-07 10:39:161 hr 48 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f8.151611178652774136 FTM
0x4431b75b8427609edd01044181056738c68405c3d4393f40aee4ba4ef2e0ea59552542092023-02-07 10:36:361 hr 51 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f2.787555456593487144 FTM
0xbd94999404595d037bbc66770522eabc98f9acf3e18053c26a32cb3edbddb800552538202023-02-07 10:29:381 hr 58 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f3.181449440879884866 FTM
0x1724a0097363fdbf030a3c6e0096b9b425cbf9209508998935a3b039dab4bfd5552535202023-02-07 10:23:572 hrs 3 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f3.580568010022410029 FTM
0x92fc06cbc3bb45c59fd99b443019e7e948f490f4252b2fbd85db23f54a8c68d1552532192023-02-07 10:18:222 hrs 9 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f6.530720665486913198 FTM
0x98be18609b88eefff8f8b12d9b9db50c20a1000130408e3e79ff36b5df02ade8552530692023-02-07 10:14:082 hrs 13 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f3.117157101422261447 FTM
0xb1e8b727e25033db80b364b69c4f842af2952ef7478acf7324b2772562505ae6552530142023-02-07 10:12:212 hrs 15 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f5.258734578624487258 FTM
0xb250e12b08ae30762db85dd5d1a8ed2c0890d71474745d3c06c11f53606f3b8b552527812023-02-07 10:06:082 hrs 21 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f5.198635050404913704 FTM
0xd9b7e65cec0562a1dea5f7ea33b32fbec0867c68f288bfaa0b3324b56b78dc68552525002023-02-07 9:59:422 hrs 28 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f3.216794593547352328 FTM
0xe4bc5d43dc2e1d2ce6566289679f1aa5b77bf416f2ffe806e3058733b2be6684552523132023-02-07 9:55:082 hrs 32 mins ago Fantom: SFC 0xb21b972b2aa69a01c6f7e6c19717126c7cd7559f6.540830328199157786 FTM
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SFC

Compiler Version
v0.5.17+commit.d19bba13

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity)

/**
 *Submitted for verification at FtmScan.com on 2022-04-05
*/

pragma solidity ^0.5.0;

// SPDX-License-Identifier: GPL-3.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;
    }
}

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

contract StakersConstants {
    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;

    /**
     * @dev Minimum amount of stake for a validator, i.e., 500000 FTM
     */
    function minSelfStake() public pure returns (uint256) {
        // 500000 FTM
        return 500000 * 1e18;
    }

    /**
     * @dev Maximum ratio of delegations a validator can have, say, 15 times of self-stake
     */
    function maxDelegatedRatio() public pure returns (uint256) {
        // 1600%
        return 16 * Decimal.unit();
    }

    /**
     * @dev The commission fee in percentage a validator will get from a delegation, e.g., 15%
     */
    function validatorCommission() public pure returns (uint256) {
        // 15%
        return (15 * Decimal.unit()) / 100;
    }

    /**
     * @dev The commission fee in percentage a validator will get from a contract, e.g., 30%
     */
    function contractCommission() public pure returns (uint256) {
        // 30%
        return (30 * Decimal.unit()) / 100;
    }

    /**
     * @dev The ratio of the reward rate at base rate (no lock), e.g., 30%
     */
    function unlockedRewardRatio() public pure returns (uint256) {
        // 30%
        return (30 * Decimal.unit()) / 100;
    }

    /**
     * @dev The minimum duration of a stake/delegation lockup, e.g. 2 weeks
     */
    function minLockupDuration() public pure returns (uint256) {
        return 86400 * 14;
    }

    /**
     * @dev The maximum duration of a stake/delegation lockup, e.g. 1 year
     */
    function maxLockupDuration() public pure returns (uint256) {
        return 86400 * 365;
    }

    /**
     * @dev the number of epochs that stake is locked
     */
    function withdrawalPeriodEpochs() public pure returns (uint256) {
        return 3;
    }

    function withdrawalPeriodTime() public pure returns (uint256) {
        // 7 days
        return 60 * 60 * 24 * 7;
    }
}

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

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

/**
 * @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.2
        return "302";
    }
}

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

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

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

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

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

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

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 _sfc, address _sFTMTokenAddress)
        public
        initializer
    {
        sfc = SFC(_sfc);
        sFTMTokenAddress = _sFTMTokenAddress;
    }

    function mintSFTM(uint256 toValidatorID) external {
        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 allowedToWithdrawStake(address sender, uint256 validatorID)
        public
        view
        returns (bool)
    {
        return outstandingSFTM[sender][validatorID] == 0;
    }
}

/**
 * @dev Stakers contract defines data structure and methods for validators / validators.
 */
contract SFC is Initializable, Ownable, StakersConstants, Version {
    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;

    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;

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

    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 public baseRewardPerSecond;
    uint256 public totalSupply;
    mapping(uint256 => EpochSnapshot) public getEpochSnapshot;

    uint256 offlinePenaltyThresholdBlocksNum;
    uint256 offlinePenaltyThresholdTime;

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

    address public stakeTokenizerAddress;

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

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

    event CreatedValidator(
        uint256 indexed validatorID,
        address indexed auth,
        uint256 createdEpoch,
        uint256 createdTime
    );
    event DeactivatedValidator(
        uint256 indexed validatorID,
        uint256 deactivatedEpoch,
        uint256 deactivatedTime
    );
    event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);
    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 InflatedFTM(
        address indexed receiver,
        uint256 amount,
        string justification
    );
    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 UpdatedBaseRewardPerSec(uint256 value);
    event UpdatedOfflinePenaltyThreshold(uint256 blocksNum, uint256 period);
    event UpdatedSlashingRefundRatio(
        uint256 indexed validatorID,
        uint256 refundRatio
    );
    event RefundedSlashedLegacyDelegation(
        address indexed delegator,
        uint256 indexed validatorID,
        uint256 amount
    );

    /*
    Getters
    */

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

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

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

    /*
    Constructor
    */

    function initialize(
        uint256 sealedEpoch,
        uint256 _totalSupply,
        address nodeDriver,
        address owner
    ) external initializer {
        Ownable.initialize(owner);
        currentSealedEpoch = sealedEpoch;
        node = NodeDriverAuth(nodeDriver);
        totalSupply = _totalSupply;
        baseRewardPerSecond = 6.183414351851851852 * 1e18;
        offlinePenaltyThresholdBlocksNum = 1000;
        offlinePenaltyThresholdTime = 3 days;
        getEpochSnapshot[sealedEpoch].endTime = _now();
    }

    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);
        _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 >= minSelfStake(), "insufficient self-stake");
        require(pubkey.length > 0, "empty pubkey");
        _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;

        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(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);
        require(
            _checkDelegatedStakeLimit(toValidatorID),
            "validator's delegations limit is exceeded"
        );
    }

    function _rawDelegate(
        address delegator,
        uint256 toValidatorID,
        uint256 amount
    ) 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);
    }

    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 _rawUndelegate(
        address delegator,
        uint256 toValidatorID,
        uint256 amount
    ) 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) {
            require(
                selfStakeAfterwards >= minSelfStake(),
                "insufficient self-stake"
            );
            require(
                _checkDelegatedStakeLimit(toValidatorID),
                "validator's delegations limit is exceeded"
            );
        } else {
            _setValidatorDeactivated(toValidatorID, WITHDRAWN_BIT);
        }
    }

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

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

    function isSlashed(uint256 validatorID) public view 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(uint256 toValidatorID, uint256 wrID) public {
        address payable delegator = msg.sender;
        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 + withdrawalPeriodTime(),
            "not enough time passed"
        );
        require(
            currentEpoch() >= requestEpoch + 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, ) = delegator.call.value(amount.sub(penalty))("");
        require(sent, "Failed to send FTM");

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

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

        _setValidatorDeactivated(validatorID, status);
        _syncValidator(validatorID, false);
    }

    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 _calcRawValidatorEpochTxReward(
        uint256 epochFee,
        uint256 txRewardWeight,
        uint256 totalTxRewardWeight
    ) internal pure returns (uint256) {
        if (txRewardWeight == 0) {
            return 0;
        }
        uint256 txReward = epochFee.mul(txRewardWeight).div(
            totalTxRewardWeight
        );
        // fee reward except contractCommission
        return
            txReward.mul(Decimal.unit() - contractCommission()).div(
                Decimal.unit()
            );
    }

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

    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 _scaleLockupReward(uint256 fullReward, uint256 lockupDuration)
        internal
        pure
        returns (Rewards memory reward)
    {
        reward = Rewards(0, 0, 0);
        if (lockupDuration != 0) {
            uint256 maxLockupExtraRatio = Decimal.unit() -
                unlockedRewardRatio();
            uint256 lockupExtraRatio = maxLockupExtraRatio
                .mul(lockupDuration)
                .div(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 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 _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];
        }
        return
            nonStashedReward.lockupBaseReward != 0 ||
            nonStashedReward.lockupExtraReward != 0 ||
            nonStashedReward.unlockedReward != 0;
    }

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

    function _claimRewards(address delegator, uint256 toValidatorID)
        internal
        returns (Rewards memory rewards)
    {
        _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, ) = 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
        );
    }

    // _syncValidator updates the validator data on node
    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)
        internal
        view
        returns (bool)
    {
        return getValidator[validatorID].createdTime != 0;
    }

    function offlinePenaltyThreshold()
        public
        view
        returns (uint256 blocksNum, uint256 time)
    {
        return (offlinePenaltyThresholdBlocksNum, offlinePenaltyThresholdTime);
    }

    function updateBaseRewardPerSecond(uint256 value) external onlyOwner {
        require(
            value <= 32.967977168935185184 * 1e18,
            "too large reward per second"
        );
        baseRewardPerSecond = value;
        emit UpdatedBaseRewardPerSec(value);
    }

    function updateOfflinePenaltyThreshold(uint256 blocksNum, uint256 time)
        external
        onlyOwner
    {
        offlinePenaltyThresholdTime = time;
        offlinePenaltyThresholdBlocksNum = blocksNum;
        emit UpdatedOfflinePenaltyThreshold(blocksNum, time);
    }

    function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio)
        external
        onlyOwner
    {
        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 updateStakeTokenizerAddress(address addr) external onlyOwner {
        stakeTokenizerAddress = addr;
    }

    // updateTotalSupply allows to fix the different between actual total supply and totalSupply field due to the
    // bug fixed in 3c828b56b7cd32ea058a954fad3cd726e193cc77
    function updateTotalSupply(int256 diff) external onlyOwner {
        if (diff >= 0) {
            totalSupply += uint256(diff);
        } else {
            totalSupply -= uint256(-diff);
        }
    }

    // mintFTM allows SFC owner to mint an arbitrary amount of FTM tokens
    // justification is a human readable description of why tokens were minted (e.g. because ERC20 FTM tokens were burnt)
    function mintFTM(
        address payable receiver,
        uint256 amount,
        string calldata justification
    ) external onlyOwner {
        _mintNativeToken(amount);
        receiver.transfer(amount);
        emit InflatedFTM(receiver, amount, justification);
    }

    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] > offlinePenaltyThresholdBlocksNum &&
                offlineTime[i] >= 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 epochDuration;
        uint256 epochFee;
    }

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

        ctx.epochDuration = 1;
        if (_now() > prevSnapshot.endTime) {
            ctx.epochDuration = _now() - prevSnapshot.endTime;
        }

        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]) /
                ctx.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]) /
                    ctx.epochDuration) * uptimes[i]) /
                ctx.epochDuration;
            ctx.totalBaseRewardWeight = ctx.totalBaseRewardWeight.add(
                ctx.baseRewardWeights[i]
            );
        }

        for (uint256 i = 0; i < validatorIDs.length; i++) {
            uint256 rawReward = _calcRawValidatorEpochBaseReward(
                ctx.epochDuration,
                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,
                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;
    }

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

        _sealEpoch_offline(snapshot, validatorIDs, offlineTime, offlineBlocks);
        _sealEpoch_rewards(snapshot, validatorIDs, uptimes, originatedTxsFee);

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

    function sealEpochValidators(uint256[] calldata nextValidatorIDs)
        external
        onlyDriver
    {
        // fill data for the next snapshot
        EpochSnapshot storage snapshot = getEpochSnapshot[currentEpoch()];
        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;
    }

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

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

    function isLockedUp(address delegator, uint256 toValidatorID)
        public
        view
        returns (bool)
    {
        return
            getLockupInfo[delegator][toValidatorID].endTime != 0 &&
            getLockupInfo[delegator][toValidatorID].lockedStake != 0 &&
            _now() <= getLockupInfo[delegator][toValidatorID].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
    ) internal {
        require(
            amount <= getUnlockedStake(delegator, toValidatorID),
            "not enough stake"
        );
        require(
            getValidator[toValidatorID].status == OK_STATUS,
            "validator isn't active"
        );

        require(
            lockupDuration >= minLockupDuration() &&
                lockupDuration <= maxLockupDuration(),
            "incorrect duration"
        );
        uint256 endTime = _now().add(lockupDuration);
        address validatorAddr = getValidator[toValidatorID].auth;
        if (delegator != validatorAddr) {
            require(
                getLockupInfo[validatorAddr][toValidatorID].endTime >= endTime,
                "validator lockup period will end earlier"
            );
        }

        _stashRewards(delegator, toValidatorID);

        // check lockup duration after _stashRewards, which has erased previous lockup if it has unlocked already
        LockedDelegation storage ld = getLockupInfo[delegator][toValidatorID];
        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);
    }

    function relockStake(
        uint256 toValidatorID,
        uint256 lockupDuration,
        uint256 amount
    ) public {
        address delegator = msg.sender;
        _lockStake(delegator, toValidatorID, lockupDuration, amount);
    }

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

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

        _stashRewards(delegator, toValidatorID);

        uint256 penalty = _popDelegationUnlockPenalty(
            delegator,
            toValidatorID,
            amount,
            ld.lockedStake
        );

        ld.lockedStake -= amount;
        _rawUndelegate(delegator, toValidatorID, penalty);

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

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

    SFC internal sfc;
    NodeDriver internal driver;

    // Initialize NodeDriverAuth, NodeDriver and SFC in one call to allow fewer genesis transactions
    function initialize(
        address _sfc,
        address _driver,
        address _owner
    ) external initializer {
        Ownable.initialize(_owner);
        driver = NodeDriver(_driver);
        sfc = SFC(_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 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 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
    ) external onlyDriver {
        sfc.sealEpoch(offlineTimes, offlineBlocks, uptimes, originatedTxsFee);
    }

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

contract NodeDriver is Initializable {
    SFC internal sfc;
    NodeDriver internal backend;
    EVMWriter internal evmWriter;

    event UpdatedBackend(address indexed backend);

    function setBackend(address _backend) external onlyBackend {
        emit UpdatedBackend(_backend);
        backend = NodeDriver(_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 = NodeDriver(_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
        );
    }
}

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

Contract Security Audit

Contract ABI

[{"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":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"string","name":"justification","type":"string"}],"name":"InflatedFTM","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":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"UpdatedBaseRewardPerSec","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"blocksNum","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"period","type":"uint256"}],"name":"UpdatedOfflinePenaltyThreshold","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":"uint256","name":"validatorID","type":"uint256"},{"internalType":"bool","name":"syncPubkey","type":"bool"}],"name":"_syncValidator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"baseRewardPerSecond","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"claimRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"contractCommission","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","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":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochAccumulatedOriginatedTxsFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochAccumulatedRewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochAccumulatedUptime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochOfflineBlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochOfflineTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochReceivedStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","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":"uint256","name":"epoch","type":"uint256"}],"name":"getEpochValidatorIDs","outputs":[{"internalType":"uint256[]","name":"","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":"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":"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":false,"inputs":[{"internalType":"uint256","name":"sealedEpoch","type":"uint256"},{"internalType":"uint256","name":"_totalSupply","type":"uint256"},{"internalType":"address","name":"nodeDriver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","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":"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":"maxDelegatedRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"maxLockupDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"minLockupDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"minSelfStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"justification","type":"string"}],"name":"mintFTM","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"offlinePenaltyThreshold","outputs":[{"internalType":"uint256","name":"blocksNum","type":"uint256"},{"internalType":"uint256","name":"time","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":"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":true,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"rewardsStash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256[]","name":"offlineTime","type":"uint256[]"},{"internalType":"uint256[]","name":"offlineBlocks","type":"uint256[]"},{"internalType":"uint256[]","name":"uptimes","type":"uint256[]"},{"internalType":"uint256[]","name":"originatedTxsFee","type":"uint256[]"}],"name":"sealEpoch","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256[]","name":"nextValidatorIDs","type":"uint256[]"}],"name":"sealEpochValidators","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":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":true,"inputs":[],"name":"unlockedRewardRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"updateBaseRewardPerSecond","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"blocksNum","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"}],"name":"updateOfflinePenaltyThreshold","outputs":[],"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":false,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"updateStakeTokenizerAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"int256","name":"diff","type":"int256"}],"name":"updateTotalSupply","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"validatorCommission","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"payable":false,"stateMutability":"pure","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"},{"constant":true,"inputs":[],"name":"withdrawalPeriodEpochs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"withdrawalPeriodTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"}]

608060405234801561001057600080fd5b50615bcc80620000216000396000f3fe6080604052600436106104805760003560e01c80637cacb1d61161025e578063b88a37e211610143578063d9a7c1f9116100bb578063e08d7e661161008a578063e2f8c3361161006f578063e2f8c33614611259578063ebdf104c146112eb578063f2fde38b1461145e57610480565b8063e08d7e66146111ac578063e261641a1461122957610480565b8063d9a7c1f914611101578063dc31e1af14611116578063de67f21514611146578063df00c9221461117c57610480565b8063c65ee0e111610112578063cc8343aa116100f7578063cc8343aa1461105d578063cfd476631461108f578063cfdbb7cd146110c857610480565b8063c65ee0e11461101e578063c7be95de1461104857610480565b8063b88a37e214610f2f578063bd14d90714610fa9578063c3de580e14610fdf578063c5f530af1461100957610480565b8063a198d229116101d6578063a86a056f116101a5578063b6d9edd51161018a578063b6d9edd514610eb7578063b810e41114610ee1578063b82b842714610f1a57610480565b8063a86a056f14610e13578063b5d8962714610e4c57610480565b8063a198d22914610d2b578063a2f6e6bc14610d5b578063a5a470ad14610d8e578063a778651514610dfe57610480565b80638cddb0151161022d5780638f32d59b116102125780638f32d59b14610c8657806396c7ee4614610caf5780639fa6dd3514610d0e57610480565b80638cddb01514610c385780638da5cb5b14610c7157610480565b80637cacb1d614610b3f578063854873e114610b545780638b0e9f3f14610bf35780638b1a0d1114610c0857610480565b8063346bdcfb116103845780635e2308d2116102fc578063650acd66116102cb5780636f498663116102b05780636f49866314610adc578063715018a614610b155780637667180814610b2a57610480565b8063650acd6614610a8e578063670322f814610aa357610480565b80635e2308d2146107475780635fab23a814610a105780636099ecb214610a2557806361e53fcc14610a5e57610480565b80634f864df41161035357806354fd4d501161033857806354fd4d501461096c5780635601fe01146109b657806358f95b80146109e057610480565b80634f864df41461088b5780634feb92f3146108c157610480565b8063346bdcfb1461079f57806339b80c00146107c9578063441a3e701461082b5780634f7c4efb1461085b57610480565b806318160ddd116104175780631f270152116103e65780632709275e116103cb5780632709275e1461074757806328f731481461075c5780632cedb0971461077157610480565b80631f270152146106d55780632265f2841461073257610480565b806318160ddd146105fb57806318f628d4146106105780631d3ac42c146106755780631e702f83146106a557610480565b80630d4955e3116104535780630d4955e3146105675780630d7b26091461057c5780630e559d821461059157806312622d0e146105c257610480565b80630135b1db14610485578063019e2729146104ca57806308c36874146105135780630962ef791461053d575b600080fd5b34801561049157600080fd5b506104b8600480360360208110156104a857600080fd5b50356001600160a01b0316611491565b60408051918252519081900360200190f35b3480156104d657600080fd5b50610511600480360360808110156104ed57600080fd5b508035906020810135906001600160a01b03604082013581169160600135166114a3565b005b34801561051f57600080fd5b506105116004803603602081101561053657600080fd5b503561161a565b34801561054957600080fd5b506105116004803603602081101561056057600080fd5b50356116e6565b34801561057357600080fd5b506104b8611831565b34801561058857600080fd5b506104b861183a565b34801561059d57600080fd5b506105a6611841565b604080516001600160a01b039092168252519081900360200190f35b3480156105ce57600080fd5b506104b8600480360360408110156105e557600080fd5b506001600160a01b038135169060200135611850565b34801561060757600080fd5b506104b86118d9565b34801561061c57600080fd5b50610511600480360361012081101561063457600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060a08101359060c08101359060e08101359061010001356118df565b34801561068157600080fd5b506104b86004803603604081101561069857600080fd5b5080359060200135611a3f565b3480156106b157600080fd5b50610511600480360360408110156106c857600080fd5b5080359060200135611c3e565b3480156106e157600080fd5b50610714600480360360608110156106f857600080fd5b506001600160a01b038135169060208101359060400135611ced565b60408051938452602084019290925282820152519081900360600190f35b34801561073e57600080fd5b506104b8611d1f565b34801561075357600080fd5b506104b8611d31565b34801561076857600080fd5b506104b8611d4d565b34801561077d57600080fd5b50610786611d53565b6040805192835260208301919091528051918290030190f35b3480156107ab57600080fd5b50610511600480360360208110156107c257600080fd5b5035611d5d565b3480156107d557600080fd5b506107f3600480360360208110156107ec57600080fd5b5035611ddc565b604080519788526020880196909652868601949094526060860192909252608085015260a084015260c0830152519081900360e00190f35b34801561083757600080fd5b506105116004803603604081101561084e57600080fd5b5080359060200135611e1e565b34801561086757600080fd5b506105116004803603604081101561087e57600080fd5b5080359060200135612241565b34801561089757600080fd5b50610511600480360360608110156108ae57600080fd5b5080359060208101359060400135612385565b3480156108cd57600080fd5b5061051160048036036101008110156108e557600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561091557600080fd5b82018360208201111561092757600080fd5b8035906020019184600183028401116401000000008311171561094957600080fd5b91935091508035906020810135906040810135906060810135906080013561261e565b34801561097857600080fd5b506109816126c4565b604080517fffffff00000000000000000000000000000000000000000000000000000000009092168252519081900360200190f35b3480156109c257600080fd5b506104b8600480360360208110156109d957600080fd5b50356126e8565b3480156109ec57600080fd5b506104b860048036036040811015610a0357600080fd5b508035906020013561271e565b348015610a1c57600080fd5b506104b861273b565b348015610a3157600080fd5b506104b860048036036040811015610a4857600080fd5b506001600160a01b038135169060200135612741565b348015610a6a57600080fd5b506104b860048036036040811015610a8157600080fd5b508035906020013561277f565b348015610a9a57600080fd5b506104b86127a0565b348015610aaf57600080fd5b506104b860048036036040811015610ac657600080fd5b506001600160a01b0381351690602001356127a5565b348015610ae857600080fd5b506104b860048036036040811015610aff57600080fd5b506001600160a01b0381351690602001356127e6565b348015610b2157600080fd5b50610511612850565b348015610b3657600080fd5b506104b861290b565b348015610b4b57600080fd5b506104b8612914565b348015610b6057600080fd5b50610b7e60048036036020811015610b7757600080fd5b503561291a565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610bb8578181015183820152602001610ba0565b50505050905090810190601f168015610be55780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b348015610bff57600080fd5b506104b86129d3565b348015610c1457600080fd5b5061051160048036036040811015610c2b57600080fd5b50803590602001356129d9565b348015610c4457600080fd5b5061051160048036036040811015610c5b57600080fd5b506001600160a01b038135169060200135612a7b565b348015610c7d57600080fd5b506105a6612ad6565b348015610c9257600080fd5b50610c9b612ae5565b604080519115158252519081900360200190f35b348015610cbb57600080fd5b50610ce860048036036040811015610cd257600080fd5b506001600160a01b038135169060200135612af6565b604080519485526020850193909352838301919091526060830152519081900360800190f35b61051160048036036020811015610d2457600080fd5b5035612b28565b348015610d3757600080fd5b506104b860048036036040811015610d4e57600080fd5b5080359060200135612b33565b348015610d6757600080fd5b5061051160048036036020811015610d7e57600080fd5b50356001600160a01b0316612b54565b61051160048036036020811015610da457600080fd5b810190602081018135640100000000811115610dbf57600080fd5b820183602082011115610dd157600080fd5b80359060200191846001830284011164010000000083111715610df357600080fd5b509092509050612be7565b348015610e0a57600080fd5b506104b8612ce2565b348015610e1f57600080fd5b506104b860048036036040811015610e3657600080fd5b506001600160a01b038135169060200135612cf8565b348015610e5857600080fd5b50610e7660048036036020811015610e6f57600080fd5b5035612d15565b604080519788526020880196909652868601949094526060860192909252608085015260a08401526001600160a01b031660c0830152519081900360e00190f35b348015610ec357600080fd5b5061051160048036036020811015610eda57600080fd5b5035612d5b565b348015610eed57600080fd5b5061071460048036036040811015610f0457600080fd5b506001600160a01b038135169060200135612e4d565b348015610f2657600080fd5b506104b8612e79565b348015610f3b57600080fd5b50610f5960048036036020811015610f5257600080fd5b5035612e80565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610f95578181015183820152602001610f7d565b505050509050019250505060405180910390f35b348015610fb557600080fd5b5061051160048036036060811015610fcc57600080fd5b5080359060208101359060400135612ee5565b348015610feb57600080fd5b50610c9b6004803603602081101561100257600080fd5b5035612ef8565b34801561101557600080fd5b506104b8612f0f565b34801561102a57600080fd5b506104b86004803603602081101561104157600080fd5b5035612f1d565b34801561105457600080fd5b506104b8612f2f565b34801561106957600080fd5b506105116004803603604081101561108057600080fd5b50803590602001351515612f35565b34801561109b57600080fd5b506104b8600480360360408110156110b257600080fd5b506001600160a01b03813516906020013561316e565b3480156110d457600080fd5b50610c9b600480360360408110156110eb57600080fd5b506001600160a01b03813516906020013561318b565b34801561110d57600080fd5b506104b8613221565b34801561112257600080fd5b506104b86004803603604081101561113957600080fd5b5080359060200135613227565b34801561115257600080fd5b506105116004803603606081101561116957600080fd5b5080359060208101359060400135613248565b34801561118857600080fd5b506104b86004803603604081101561119f57600080fd5b5080359060200135613303565b3480156111b857600080fd5b50610511600480360360208110156111cf57600080fd5b8101906020810181356401000000008111156111ea57600080fd5b8201836020820111156111fc57600080fd5b8035906020019184602083028401116401000000008311171561121e57600080fd5b509092509050613324565b34801561123557600080fd5b506104b86004803603604081101561124c57600080fd5b50803590602001356133fe565b34801561126557600080fd5b506105116004803603606081101561127c57600080fd5b6001600160a01b03823516916020810135918101906060810160408201356401000000008111156112ac57600080fd5b8201836020820111156112be57600080fd5b803590602001918460018302840111640100000000831117156112e057600080fd5b50909250905061341f565b3480156112f757600080fd5b506105116004803603608081101561130e57600080fd5b81019060208101813564010000000081111561132957600080fd5b82018360208201111561133b57600080fd5b8035906020019184602083028401116401000000008311171561135d57600080fd5b91939092909160208101903564010000000081111561137b57600080fd5b82018360208201111561138d57600080fd5b803590602001918460208302840111640100000000831117156113af57600080fd5b9193909290916020810190356401000000008111156113cd57600080fd5b8201836020820111156113df57600080fd5b8035906020019184602083028401116401000000008311171561140157600080fd5b91939092909160208101903564010000000081111561141f57600080fd5b82018360208201111561143157600080fd5b8035906020019184602083028401116401000000008311171561145357600080fd5b50909250905061354e565b34801561146a57600080fd5b506105116004803603602081101561148157600080fd5b50356001600160a01b031661372a565b60696020526000908152604090205481565b600054610100900460ff16806114bc57506114bc61378c565b806114ca575060005460ff16155b6115055760405162461bcd60e51b815260040180806020018281038252602e815260200180615acc602e913960400191505060405180910390fd5b600054610100900460ff1615801561156b57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff909116610100171660011790555b61157482613792565b6067859055606680547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03851617905560768490556755cfe697852e904c6075556103e86078556203f4806079556115d26138f3565b600086815260776020526040902060070155801561161357600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1690555b5050505050565b33611623615931565b61162d82846138f7565b602081015181519192506000916116499163ffffffff6139fc16565b905061166c83856116678560400151856139fc90919063ffffffff16565b613a56565b6001600160a01b0383166000818152607360209081526040808320888452825291829020805485019055845185820151868401518451928352928201528083019190915290518692917f4119153d17a36f9597d40e3ab4148d03261a439dddbec4e91799ab7159608e26919081900360600190a350505050565b336116ef615931565b6116f982846138f7565b90506000826001600160a01b0316611736836040015161172a856020015186600001516139fc90919063ffffffff16565b9063ffffffff6139fc16565b604051600081818185875af1925050503d8060008114611772576040519150601f19603f3d011682016040523d82523d6000602084013e611777565b606091505b50509050806117cd576040805162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642046544d0000000000000000000000000000604482015290519081900360640190fd5b83836001600160a01b03167fc1d8eb6e444b89fb8ff0991c19311c070df704ccb009e210d1462d5b2410bf4584600001518560200151866040015160405180848152602001838152602001828152602001935050505060405180910390a350505050565b6301e133805b90565b6212750090565b607b546001600160a01b031681565b600061185c838361318b565b61188a57506001600160a01b03821660009081526072602090815260408083208484529091529020546118d3565b6001600160a01b0383166000818152607360209081526040808320868452825280832054938352607282528083208684529091529020546118d09163ffffffff613b6016565b90505b92915050565b60765481565b6118e833613ba2565b6119235760405162461bcd60e51b8152600401808060200182810382526029815260200180615a826029913960400191505060405180910390fd5b61192e898989613bb6565b6001600160a01b0389166000908152606f602090815260408083208b8452909152902060020181905561196087613d2d565b8515611a3457868611156119a55760405162461bcd60e51b815260040180806020018281038252602c815260200180615b6c602c913960400191505060405180910390fd5b6001600160a01b03891660008181526073602090815260408083208c845282528083208a8155600181018a90556002810189905560038101889055848452607483528184208d855283529281902086905580518781529182018a9052805192938c9390927f138940e95abffcd789b497bf6188bba3afa5fbd22fb5c42c2f6018d1bf0f4e7892908290030190a3505b505050505050505050565b336000818152607360209081526040808320868452909152812090919083611aae576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b611ab8828661318b565b611b09576040805162461bcd60e51b815260206004820152600d60248201527f6e6f74206c6f636b656420757000000000000000000000000000000000000000604482015290519081900360640190fd5b8054841115611b5f576040805162461bcd60e51b815260206004820152601760248201527f6e6f7420656e6f756768206c6f636b6564207374616b65000000000000000000604482015290519081900360640190fd5b611b698286613dcb565b611bba576040805162461bcd60e51b815260206004820152601860248201527f6f75747374616e64696e67207346544d2062616c616e63650000000000000000604482015290519081900360640190fd5b611bc48286613e86565b506000611bd78387878560000154614051565b825486900383559050611beb838783614182565b85836001600160a01b03167fef6c0c14fe9aa51af36acd791464dec3badbde668b63189b47bfa4e25be9b2b98784604051808381526020018281526020019250505060405180910390a395945050505050565b611c4733613ba2565b611c825760405162461bcd60e51b8152600401808060200182810382526029815260200180615a826029913960400191505060405180910390fd5b80611cd4576040805162461bcd60e51b815260206004820152600c60248201527f77726f6e67207374617475730000000000000000000000000000000000000000604482015290519081900360640190fd5b611cde82826142d8565b611ce9826000612f35565b5050565b607160209081526000938452604080852082529284528284209052825290208054600182015460029092015490919083565b6000611d29614402565b601002905090565b60006064611d3d614402565b601e0281611d4757fe5b04905090565b606d5481565b6078546079549091565b611d65612ae5565b611db6576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b60008112611dcb576076805482019055611dd9565b607680546000839003900390555b50565b607760205280600052604060002060009150905080600701549080600801549080600901549080600a01549080600b01549080600c01549080600d0154905087565b33611e27615931565b506001600160a01b038116600090815260716020908152604080832086845282528083208584528252918290208251606081018452815480825260018301549382019390935260029091015492810192909252611ecb576040805162461bcd60e51b815260206004820152601560248201527f7265717565737420646f65736e27742065786973740000000000000000000000604482015290519081900360640190fd5b611ed58285613dcb565b611f26576040805162461bcd60e51b815260206004820152601860248201527f6f75747374616e64696e67207346544d2062616c616e63650000000000000000604482015290519081900360640190fd5b60208082015182516000878152606890935260409092206001015490919015801590611f62575060008681526068602052604090206001015482115b15611f83575050600084815260686020526040902060018101546002909101545b611f8b612e79565b8201611f956138f3565b1015611fe8576040805162461bcd60e51b815260206004820152601660248201527f6e6f7420656e6f7567682074696d652070617373656400000000000000000000604482015290519081900360640190fd5b611ff06127a0565b8101611ffa61290b565b101561204d576040805162461bcd60e51b815260206004820152601860248201527f6e6f7420656e6f7567682065706f636873207061737365640000000000000000604482015290519081900360640190fd5b6001600160a01b038416600090815260716020908152604080832089845282528083208884529091528120600201549061208688612ef8565b905060006120a88383607a60008d81526020019081526020016000205461440e565b6001600160a01b03881660009081526071602090815260408083208d845282528083208c845290915281208181556001810182905560020155606e805482019055905080831161213f576040805162461bcd60e51b815260206004820152601660248201527f7374616b652069732066756c6c7920736c617368656400000000000000000000604482015290519081900360640190fd5b60006001600160a01b03881661215b858463ffffffff613b6016565b604051600081818185875af1925050503d8060008114612197576040519150601f19603f3d011682016040523d82523d6000602084013e61219c565b606091505b50509050806121f2576040805162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642046544d0000000000000000000000000000604482015290519081900360640190fd5b888a896001600160a01b03167f75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21876040518082815260200191505060405180910390a450505050505050505050565b612249612ae5565b61229a576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6122a382612ef8565b6122f4576040805162461bcd60e51b815260206004820152601760248201527f76616c696461746f722069736e277420736c6173686564000000000000000000604482015290519081900360640190fd5b6122fc614402565b81111561233a5760405162461bcd60e51b8152600401808060200182810382526021815260200180615afa6021913960400191505060405180910390fd5b6000828152607a60209081526040918290208390558151838152915184927f047575f43f09a7a093d94ec483064acfc61b7e25c0de28017da442abf99cb91792908290030190a25050565b336123908185613e86565b50600082116123e6576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b6123f08185611850565b821115612444576040805162461bcd60e51b815260206004820152601960248201527f6e6f7420656e6f75676820756e6c6f636b6564207374616b6500000000000000604482015290519081900360640190fd5b61244e8185613dcb565b61249f576040805162461bcd60e51b815260206004820152601860248201527f6f75747374616e64696e67207346544d2062616c616e63650000000000000000604482015290519081900360640190fd5b6001600160a01b0381166000908152607160209081526040808320878452825280832086845290915290206002015415612520576040805162461bcd60e51b815260206004820152601360248201527f7772494420616c72656164792065786973747300000000000000000000000000604482015290519081900360640190fd5b61252b818584614182565b6001600160a01b03811660009081526071602090815260408083208784528252808320868452909152902060020182905561256461290b565b6001600160a01b038216600090815260716020908152604080832088845282528083208784529091529020556125986138f3565b6001600160a01b038216600090815260716020908152604080832088845282528083208784529091528120600101919091556125d5908590612f35565b8284826001600160a01b03167fd3bb4e423fbea695d16b982f9f682dc5f35152e5411646a8a5a79a6b02ba8d57856040518082815260200191505060405180910390a450505050565b61262733613ba2565b6126625760405162461bcd60e51b8152600401808060200182810382526029815260200180615a826029913960400191505060405180910390fd5b6126aa898989898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b92508a91508990508888614470565b606b54881115611a3457606b889055505050505050505050565b7f333032000000000000000000000000000000000000000000000000000000000090565b6000818152606860209081526040808320600601546001600160a01b03168352607282528083208484529091529020545b919050565b600091825260776020908152604080842092845291905290205490565b606e5481565b600061274b615931565b6127558484614637565b8051602082015160408301519293506127779261172a9163ffffffff6139fc16565b949350505050565b60009182526077602090815260408084209284526001909201905290205490565b600390565b60006127b1838361318b565b6127bd575060006118d3565b506001600160a01b03919091166000908152607360209081526040808320938352929052205490565b60006127f0615931565b506001600160a01b0383166000908152606f602090815260408083208584528252918290208251606081018452815480825260018301549382018490526002909201549381018490529261277792909161172a919063ffffffff6139fc16565b612858612ae5565b6128a9576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b60675460010190565b60675481565b606a6020908152600091825260409182902080548351601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100600186161502019093169290920491820184900484028101840190945280845290918301828280156129cb5780601f106129a0576101008083540402835291602001916129cb565b820191906000526020600020905b8154815290600101906020018083116129ae57829003601f168201915b505050505081565b606c5481565b6129e1612ae5565b612a32576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b60798190556078829055604080518381526020810183905281517f702756a07c05d0bbfd06fc17b67951a5f4deb7bb6b088407e68a58969daf2a34929181900390910190a15050565b612a858282613e86565b611ce9576040805162461bcd60e51b815260206004820152601060248201527f6e6f7468696e6720746f20737461736800000000000000000000000000000000604482015290519081900360640190fd5b6033546001600160a01b031690565b6033546001600160a01b0316331490565b607360209081526000928352604080842090915290825290208054600182015460028301546003909301549192909184565b611dd9338234613a56565b60009182526077602090815260408084209284526005909201905290205490565b612b5c612ae5565b612bad576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b607b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b612bef612f0f565b341015612c43576040805162461bcd60e51b815260206004820152601760248201527f696e73756666696369656e742073656c662d7374616b65000000000000000000604482015290519081900360640190fd5b80612c95576040805162461bcd60e51b815260206004820152600c60248201527f656d707479207075626b65790000000000000000000000000000000000000000604482015290519081900360640190fd5b612cd53383838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506146a592505050565b611ce933606b5434613a56565b60006064612cee614402565b600f0281611d4757fe5b607060209081526000928352604080842090915290825290205481565b606860205260009081526040902080546001820154600283015460038401546004850154600586015460069096015494959394929391929091906001600160a01b031687565b612d63612ae5565b612db4576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6801c985c8903591eb20811115612e12576040805162461bcd60e51b815260206004820152601b60248201527f746f6f206c617267652072657761726420706572207365636f6e640000000000604482015290519081900360640190fd5b60758190556040805182815290517f8cd9dae1bbea2bc8a5e80ffce2c224727a25925130a03ae100619a8861ae23969181900360200190a150565b607460209081526000928352604080842090915290825290208054600182015460029092015490919083565b62093a8090565b600081815260776020908152604091829020600601805483518184028101840190945280845260609392830182828015612ed957602002820191906000526020600020905b815481526020019060010190808311612ec5575b50505050509050919050565b33612ef2818585856146d0565b50505050565b600090815260686020526040902054608016151590565b6969e10de76676d080000090565b607a6020526000908152604090205481565b606b5481565b612f3e826149a1565b612f8f576040805162461bcd60e51b815260206004820152601760248201527f76616c696461746f7220646f65736e2774206578697374000000000000000000604482015290519081900360640190fd5b60008281526068602052604090206003810154905415612fad575060005b606654604080517fa4066fbe000000000000000000000000000000000000000000000000000000008152600481018690526024810184905290516001600160a01b039092169163a4066fbe9160448082019260009290919082900301818387803b15801561301a57600080fd5b505af115801561302e573d6000803e3d6000fd5b5050505081801561303e57508015155b15613169576066546000848152606a60205260409081902081517f242a6e3f0000000000000000000000000000000000000000000000000000000081526004810187815260248201938452825460027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001831615610100020190911604604483018190526001600160a01b039095169463242a6e3f9489949390916064909101908490801561312f5780601f106131045761010080835404028352916020019161312f565b820191906000526020600020905b81548152906001019060200180831161311257829003601f168201915b50509350505050600060405180830381600087803b15801561315057600080fd5b505af1158015613164573d6000803e3d6000fd5b505050505b505050565b607260209081526000928352604080842090915290825290205481565b6001600160a01b0382166000908152607360209081526040808320848452909152812060020154158015906131e257506001600160a01b038316600090815260736020908152604080832085845290915290205415155b80156118d057506001600160a01b03831660009081526073602090815260408083208584529091529020600201546132186138f3565b11159392505050565b60755481565b60009182526077602090815260408084209284526003909201905290205490565b338161329b576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b6132a5818561318b565b156132f7576040805162461bcd60e51b815260206004820152601160248201527f616c7265616479206c6f636b6564207570000000000000000000000000000000604482015290519081900360640190fd5b612ef2818585856146d0565b60009182526077602090815260408084209284526002909201905290205490565b61332d33613ba2565b6133685760405162461bcd60e51b8152600401808060200182810382526029815260200180615a826029913960400191505060405180910390fd5b60006077600061337661290b565b8152602001908152602001600020905060008090505b828110156133ef5760008484838181106133a257fe5b60209081029290920135600081815260688452604080822060030154948890529020839055600c8601549093506133e091508263ffffffff6139fc16565b600c850155505060010161338c565b50612ef2600682018484615952565b60009182526077602090815260408084209284526004909201905290205490565b613427612ae5565b613478576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b61348183613d2d565b6040516001600160a01b0385169084156108fc029085906000818181858888f193505050501580156134b7573d6000803e3d6000fd5b50836001600160a01b03167f9eec469b348bcf64bbfb60e46ce7b160e2e09bf5421496a2cdbc43714c28b8ad84848460405180848152602001806020018281038252848482818152602001925080828437600083820152604051601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909201829003965090945050505050a250505050565b61355733613ba2565b6135925760405162461bcd60e51b8152600401808060200182810382526029815260200180615a826029913960400191505060405180910390fd5b6000607760006135a061290b565b8152602001908152602001600020905060608160060180548060200260200160405190810160405280929190818152602001828054801561360057602002820191906000526020600020905b8154815260200190600101908083116135ec575b5050505050905061368782828c8c80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508b8b808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506149b892505050565b6136f6828288888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a918291850190849080828437600092019190915250614ac792505050565b6136fe61290b565b6067556137096138f3565b600783015550607554600b820155607654600d909101555050505050505050565b613732612ae5565b613783576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b611dd98161510d565b303b1590565b600054610100900460ff16806137ab57506137ab61378c565b806137b9575060005460ff16155b6137f45760405162461bcd60e51b815260040180806020018281038252602e815260200180615acc602e913960400191505060405180910390fd5b600054610100900460ff1615801561385a57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff909116610100171660011790555b603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384811691909117918290556040519116906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a38015611ce957600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1690555050565b4290565b6138ff615931565b6139098383613e86565b50506001600160a01b0382166000908152606f60209081526040808320848452825280832081516060810183528154808252600183015494820185905260029092015492810183905293926139679261172a9163ffffffff6139fc16565b9050806139bb576040805162461bcd60e51b815260206004820152600c60248201527f7a65726f20726577617264730000000000000000000000000000000000000000604482015290519081900360640190fd5b6001600160a01b0384166000908152606f60209081526040808320868452909152812081815560018101829055600201556139f581613d2d565b5092915050565b6000828201838110156118d0576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b613a5f826149a1565b613ab0576040805162461bcd60e51b815260206004820152601760248201527f76616c696461746f7220646f65736e2774206578697374000000000000000000604482015290519081900360640190fd5b60008281526068602052604090205415613b11576040805162461bcd60e51b815260206004820152601660248201527f76616c696461746f722069736e27742061637469766500000000000000000000604482015290519081900360640190fd5b613b1c838383613bb6565b613b25826151c6565b6131695760405162461bcd60e51b8152600401808060200182810382526029815260200180615b436029913960400191505060405180910390fd5b60006118d083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061520e565b6066546001600160a01b0390811691161490565b60008111613c0b576040805162461bcd60e51b815260206004820152600b60248201527f7a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b613c158383613e86565b506001600160a01b0383166000908152607260209081526040808320858452909152902054613c4a908263ffffffff6139fc16565b6001600160a01b0384166000908152607260209081526040808320868452825280832093909355606890522060030154613c8a818363ffffffff6139fc16565b600084815260686020526040902060030155606c54613caf908363ffffffff6139fc16565b606c55600083815260686020526040902054613cdc57606d54613cd8908363ffffffff6139fc16565b606d555b613ce7838215612f35565b60408051838152905184916001600160a01b038716917f9a8f44850296624dadfd9c246d17e47171d35727a181bd090aa14bbbe00238bb9181900360200190a350505050565b606654604080517f66e7ea0f0000000000000000000000000000000000000000000000000000000081523060048201526024810184905290516001600160a01b03909216916366e7ea0f9160448082019260009290919082900301818387803b158015613d9957600080fd5b505af1158015613dad573d6000803e3d6000fd5b5050607654613dc5925090508263ffffffff6139fc16565b60765550565b607b546000906001600160a01b0316613de6575060016118d3565b607b54604080517f21d585c30000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015260248201869052915191909216916321d585c3916044808301926020929190829003018186803b158015613e5357600080fd5b505afa158015613e67573d6000803e3d6000fd5b505050506040513d6020811015613e7d57600080fd5b50519392505050565b6000613e90615931565b613e9a84846152a5565b9050613ea5836153dd565b6001600160a01b0385166000818152607060209081526040808320888452825280832094909455918152606f825282812086825282528290208251606081018452815481526001820154928101929092526002015491810191909152613f0b9082615438565b6001600160a01b0385166000818152606f6020908152604080832088845282528083208551815585830151600180830191909155958201516002918201559383526074825280832088845282529182902082516060810184528154815294810154918501919091529091015490820152613f859082615438565b6001600160a01b038516600090815260746020908152604080832087845282529182902083518155908301516001820155910151600290910155613fc9848461318b565b61402c576001600160a01b0384166000818152607360209081526040808320878452825280832083815560018082018590556002808301869055600390920185905594845260748352818420888552909252822082815592830182905591909101555b602081015115158061403e5750805115155b8061277757506040015115159392505050565b6001600160a01b0384166000908152607460209081526040808320868452909152812054819061409990849061408d908763ffffffff6154aa16565b9063ffffffff61550316565b6001600160a01b0387166000908152607460209081526040808320898452909152812060010154919250906140da90859061408d908863ffffffff6154aa16565b6001600160a01b03881660009081526074602090815260408083208a8452909152902054909150600282048301906141129084613b60565b6001600160a01b03891660009081526074602090815260408083208b84529091529020908155600101546141469083613b60565b6001600160a01b03891660009081526074602090815260408083208b84529091529020600101558581106141775750845b979650505050505050565b6001600160a01b038316600090815260726020908152604080832085845282528083208054859003905560689091529020600301546141c7908263ffffffff613b6016565b600083815260686020526040902060030155606c546141ec908263ffffffff613b6016565b606c5560008281526068602052604090205461421957606d54614215908263ffffffff613b6016565b606d555b6000614224836126e8565b905080156142d157614234612f0f565b811015614288576040805162461bcd60e51b815260206004820152601760248201527f696e73756666696369656e742073656c662d7374616b65000000000000000000604482015290519081900360640190fd5b614291836151c6565b6142cc5760405162461bcd60e51b8152600401808060200182810382526029815260200180615b436029913960400191505060405180910390fd5b612ef2565b612ef28360015b6000828152606860205260409020541580156142f357508015155b1561432057600082815260686020526040902060030154606d5461431c9163ffffffff613b6016565b606d555b600082815260686020526040902054811115611ce9576000828152606860205260409020818155600201546143c85761435761290b565b6000838152606860205260409020600201556143716138f3565b6000838152606860209081526040918290206001810184905560020154825190815290810192909252805184927fac4801c32a6067ff757446524ee4e7a373797278ac3c883eac5c693b4ad72e4792908290030190a25b60408051828152905183917fcd35267e7654194727477d6c78b541a553483cff7f92a055d17868d3da6e953e919081900360200190a25050565b670de0b6b3a764000090565b60008215806144245750614420614402565b8210155b1561443157506000614469565b61445c600161172a614441614402565b61408d8661444d614402565b8a91900363ffffffff6154aa16565b9050838111156144695750825b9392505050565b6001600160a01b038816600090815260696020526040902054156144db576040805162461bcd60e51b815260206004820152601860248201527f76616c696461746f7220616c7265616479206578697374730000000000000000604482015290519081900360640190fd5b6001600160a01b03881660008181526069602090815260408083208b90558a8352606882528083208981556004810189905560058101889055600181018690556002810187905560060180547fffffffffffffffffffffffff000000000000000000000000000000000000000016909417909355606a815291902087516145649289019061599d565b50876001600160a01b0316877f49bca1ed2666922f9f1690c26a569e1299c2a715fe57647d77e81adfabbf25bf8686604051808381526020018281526020019250505060405180910390a381156145f0576040805183815260208101839052815189927fac4801c32a6067ff757446524ee4e7a373797278ac3c883eac5c693b4ad72e47928290030190a25b841561462d5760408051868152905188917fcd35267e7654194727477d6c78b541a553483cff7f92a055d17868d3da6e953e919081900360200190a25b5050505050505050565b61463f615931565b614647615931565b61465184846152a5565b6001600160a01b0385166000908152606f6020908152604080832087845282529182902082516060810184528154815260018201549281019290925260020154918101919091529091506127779082615438565b606b80546001019081905561316983828460006146c061290b565b6146c86138f3565b600080614470565b6146da8484611850565b81111561472e576040805162461bcd60e51b815260206004820152601060248201527f6e6f7420656e6f756768207374616b6500000000000000000000000000000000604482015290519081900360640190fd5b6000838152606860205260409020541561478f576040805162461bcd60e51b815260206004820152601660248201527f76616c696461746f722069736e27742061637469766500000000000000000000604482015290519081900360640190fd5b61479761183a565b82101580156147ad57506147a9611831565b8211155b6147fe576040805162461bcd60e51b815260206004820152601260248201527f696e636f7272656374206475726174696f6e0000000000000000000000000000604482015290519081900360640190fd5b600061480c8361172a6138f3565b6000858152606860205260409020600601549091506001600160a01b03908116908616811461489a576001600160a01b038116600090815260736020908152604080832088845290915290206002015482111561489a5760405162461bcd60e51b8152600401808060200182810382526028815260200180615b1b6028913960400191505060405180910390fd5b6148a48686613e86565b506001600160a01b038616600090815260736020908152604080832088845290915290206003810154851015614921576040805162461bcd60e51b815260206004820152601f60248201527f6c6f636b7570206475726174696f6e2063616e6e6f7420646563726561736500604482015290519081900360640190fd5b8054614933908563ffffffff6139fc16565b815561493d61290b565b600182015560028101839055600381018590556040805186815260208101869052815188926001600160a01b038b16927f138940e95abffcd789b497bf6188bba3afa5fbd22fb5c42c2f6018d1bf0f4e78929081900390910190a350505050505050565b600090815260686020526040902060050154151590565b60005b8351811015611613576078548282815181106149d357fe5b60200260200101511180156149fd57506079548382815181106149f257fe5b602002602001015110155b15614a3e57614a20848281518110614a1157fe5b602002602001015160086142d8565b614a3e848281518110614a2f57fe5b60200260200101516000612f35565b828181518110614a4a57fe5b6020026020010151856004016000868481518110614a6457fe5b6020026020010151815260200190815260200160002081905550818181518110614a8a57fe5b6020026020010151856005016000868481518110614aa457fe5b6020908102919091018101518252810191909152604001600020556001016149bb565b614acf615a0b565b6040518060c001604052808551604051908082528060200260200182016040528015614b05578160200160208202803883390190505b508152602001600081526020018551604051908082528060200260200182016040528015614b3d578160200160208202803883390190505b508152602001600081526020016000815260200160008152509050600060776000614b776001614b6b61290b565b9063ffffffff613b6016565b81526020810191909152604001600020600160808401526007810154909150614b9e6138f3565b1115614bb8578060070154614bb16138f3565b0360808301525b60005b8551811015614cc0576000826003016000888481518110614bd857fe5b60200260200101518152602001908152602001600020549050600080905081868481518110614c0357fe5b60200260200101511115614c2a5781868481518110614c1e57fe5b60200260200101510390505b8460800151878481518110614c3b57fe5b6020026020010151820281614c4c57fe5b0485604001518481518110614c5d57fe5b602002602001018181525050614c9785604001518481518110614c7c57fe5b602002602001015186606001516139fc90919063ffffffff16565b606086015260a0850151614cb1908263ffffffff6139fc16565b60a08601525050600101614bbb565b5060005b8551811015614d91578260800151858281518110614cde57fe5b60200260200101518460800151878481518110614cf757fe5b60200260200101518a60000160008b8781518110614d1157fe5b60200260200101518152602001908152602001600020540281614d3057fe5b040281614d3957fe5b0483600001518281518110614d4a57fe5b602002602001018181525050614d8483600001518281518110614d6957fe5b602002602001015184602001516139fc90919063ffffffff16565b6020840152600101614cc4565b5060005b85518110156150e5576000614dcd846080015160755486600001518581518110614dbb57fe5b60200260200101518760200151615545565b9050614e09614dfc8560a0015186604001518581518110614dea57fe5b60200260200101518760600151615586565b829063ffffffff6139fc16565b90506000878381518110614e1957fe5b6020908102919091018101516000818152606890925260408220600601549092506001600160a01b031690614e5584614e50612ce2565b6155e3565b6001600160a01b03831660009081526072602090815260408083208784529091529020549091508015614ffc57600081614e8f85876127a5565b840281614e9857fe5b049050808303614ea6615931565b6001600160a01b03861660009081526073602090815260408083208a8452909152902060030154614ed8908490615600565b9050614ee2615931565b614eed836000615600565b6001600160a01b0388166000908152606f602090815260408083208c84528252918290208251606081018452815481526001820154928101929092526002015491810191909152909150614f429083836156f1565b6001600160a01b0388166000818152606f602090815260408083208d84528252808320855181558583015160018083019190915595820151600291820155938352607482528083208d845282529182902082516060810184528154815294810154918501919091529091015490820152614fbd9083836156f1565b6001600160a01b03881660009081526074602090815260408083208c845282529182902083518155908301516001820155910151600290910155505050505b60008481526068602052604081206003015483870391811561502e5781615021614402565b84028161502a57fe5b0490505b808a600101600089815260200190815260200160002054018f6001016000898152602001908152602001600020819055508b898151811061506b57fe5b60200260200101518f6003016000898152602001908152602001600020819055508c898151811061509857fe5b60200260200101518a600201600089815260200190815260200160002054018f60020160008981526020019081526020016000208190555050505050505050508080600101915050614d95565b505060a081015160088601556020810151600986015560600151600a90940193909355505050565b6001600160a01b0381166151525760405162461bcd60e51b8152600401808060200182810382526026815260200180615a5c6026913960400191505060405180910390fd5b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b60006151f36151d3614402565b61408d6151de611d1f565b6151e7866126e8565b9063ffffffff6154aa16565b60008381526068602052604090206003015411159050919050565b6000818484111561529d5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561526257818101518382015260200161524a565b50505050905090810190601f16801561528f5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6152ad615931565b6001600160a01b0383166000908152607060209081526040808320858452909152812054906152db846153dd565b905060006152e9868661570c565b9050818111156152f65750805b828110156153015750815b6001600160a01b0386166000818152607360209081526040808320898452825280832093835260728252808320898452909152812054825490919061534d90839063ffffffff613b6016565b9050600061536184600001548a89886157e9565b905061536b615931565b615379828660030154615600565b9050615387838b8a896157e9565b9150615391615931565b61539c836000615600565b90506153aa858c898b6157e9565b92506153b4615931565b6153bf846000615600565b90506153cc8383836156f1565b9d9c50505050505050505050505050565b600081815260686020526040812060020154156154305760008281526068602052604090206002015460675410156154185750606754612719565b50600081815260686020526040902060020154612719565b505060675490565b615440615931565b6040805160608101909152825184518291615461919063ffffffff6139fc16565b8152602001615481846020015186602001516139fc90919063ffffffff16565b81526020016154a1846040015186604001516139fc90919063ffffffff16565b90529392505050565b6000826154b9575060006118d3565b828202828482816154c657fe5b04146118d05760405162461bcd60e51b8152600401808060200182810382526021815260200180615aab6021913960400191505060405180910390fd5b60006118d083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061584c565b60008261555457506000612777565b6000615566868663ffffffff6154aa16565b905061557c8361408d838763ffffffff6154aa16565b9695505050505050565b60008261559557506000614469565b60006155ab8361408d878763ffffffff6154aa16565b90506155da6155b8614402565b61408d6155c3611d31565b6155cb614402565b8591900363ffffffff6154aa16565b95945050505050565b60006118d06155f0614402565b61408d858563ffffffff6154aa16565b615608615931565b6040518060600160405280600081526020016000815260200160008152509050816000146156c357600061563a611d31565b615642614402565b0390506000615662615652611831565b61408d848763ffffffff6154aa16565b9050600061568b615671614402565b61408d8461567d611d31565b8a910163ffffffff6154aa16565b90506156b0615698614402565b61408d6156a3611d31565b899063ffffffff6154aa16565b6020850181905290038352506118d39050565b6156e66156ce614402565b61408d6156d9611d31565b869063ffffffff6154aa16565b604082015292915050565b6156f9615931565b6127776157068585615438565b83615438565b6001600160a01b03821660009081526073602090815260408083208484529091528120600101546067546157418585836158b1565b1561574f5791506118d39050565b61575a8585846158b1565b615769576000925050506118d3565b8082111561577c576000925050506118d3565b808210156157af576002818301046157958686836158b1565b156157a5578060010192506157a9565b8091505b5061577c565b806157bf576000925050506118d3565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01949350505050565b60008183106157fa57506000612777565b600083815260776020818152604080842088855260019081018352818520548786529383528185208986520190915290912054614177615838614402565b61408d896151e7858763ffffffff613b6016565b6000818361589b5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561526257818101518382015260200161524a565b5060008385816158a757fe5b0495945050505050565b6001600160a01b0383166000908152607360209081526040808320858452909152812060010154821080159061277757506001600160a01b03841660009081526073602090815260408083208684529091529020600201546159128361591c565b1115949350505050565b60009081526077602052604090206007015490565b60405180606001604052806000815260200160008152602001600081525090565b82805482825590600052602060002090810192821561598d579160200282015b8281111561598d578235825591602001919060010190615972565b50615999929150615a41565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106159de57805160ff191683800117855561598d565b8280016001018555821561598d579182015b8281111561598d5782518255916020019190600101906159f0565b6040518060c001604052806060815260200160008152602001606081526020016000815260200160008152602001600081525090565b61183791905b808211156159995760008155600101615a4756fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737363616c6c6572206973206e6f7420746865204e6f64654472697665724175746820636f6e7472616374536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a65646d757374206265206c657373207468616e206f7220657175616c20746f20312e3076616c696461746f72206c6f636b757020706572696f642077696c6c20656e64206561726c69657276616c696461746f7227732064656c65676174696f6e73206c696d69742069732065786365656465646c6f636b6564207374616b652069732067726561746572207468616e207468652077686f6c65207374616b65a265627a7a723158201e2c0c2613e9f5b1b982f1d341fd2892182615eda299d96c66dad0b19a60772564736f6c63430005110032

Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Validator ID :
0 FTM

Amount Staked
0

Amount Delegated
0

Staking Total
0

Staking Start Epoch
0

Staking Start Time
0

Proof of Importance
0

Origination Score
0

Validation Score
0

Active
0

Online
0

Downtime
0 s
Address Amount claimed Rewards Created On Epoch Created On
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.