Contract 0xfc00face00000000000000000000000000000000 6

Contract Overview

Balance:
145,286,586.816993560878889254 FTM

Token:
My Name Tag:
Not Available

ContractCreator:
GENESIS at txn GENESIS_0
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xf413081504ff5749201803cfdf9f3715d8ef378513108b69faa0a42cd5179d2eDelegate109009702022-09-29 2:49:066 days 20 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000001 FTM0.000639772783
0x02cece243321aed6cf93390c7ab19b0a4d7ef80775ea833d505f559d98d3818cRelock Stake108920572022-09-28 15:05:127 days 7 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000693816592
0x1a504221486991160b77c75e7dfa846164057f6edb3b24d671212e666b0a7825Relock Stake108918952022-09-28 14:53:537 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000591559802
0x8e6821231d5fb8fa01bf663c275ce7e28c72500865ede94dd47ee0ff70689ea3Relock Stake108918562022-09-28 14:51:277 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000591584927
0xfa0c81362c7e92a2e71555a48e1f8af131a07e914e5ad8ad53692df60e175180Relock Stake108917022022-09-28 14:44:017 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.00058884122
0x41fbcf4e7505f146bc38adaadec524eaeaff2b32e8001a779157b0cdd40348b0Lock Stake108916562022-09-28 14:41:477 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000719761994
0x5ef53a6e38ddfc708f88d437da72a5fc4bceb106b58816633c8e3475bff3e6ebDelegate108915672022-09-28 14:38:077 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face0000000000000000000000000000000010 FTM0.000698299949
0x237f73ec3c1779f7a0c21c8e32b425979d9733e7df45f29caf5d110b58052cf9Delegate108913622022-09-28 14:29:427 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face0000000000000000000000000000000010 FTM0.000536605779
0xbbc910f805195b6235d408af01b11f616cf1c93fde6b72e85f5b81a191922536Relock Stake108912802022-09-28 14:25:527 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000537293039
0x26af53cbb8e7124553245cd486a2dfabaffb0898928834ff1f764ba0f9fd954cRelock Stake108912642022-09-28 14:25:267 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000537171463
0x1780c80718ab58bc27b5158aeb0392c326ae9abbf138a5a284c4005047e527b1Delegate108912342022-09-28 14:24:107 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face0000000000000000000000000000000010 FTM0.00053653564
0x38ea8384757983e721027f5d37b0ac6c9cb40ab7e89096851e11ca7ed9a3d6bcClaim Rewards108907622022-09-28 14:06:217 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000636863035
0xdfb0f3564892a7ee9fe0520485266d348584849a95f087bc35f08bd222bba2dfClaim Rewards108907402022-09-28 14:05:327 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000636937583
0xeebfb967a12022852fb69d15127aa0eb6c9f1813817651339ce802602d67e760Delegate108907182022-09-28 14:04:407 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000001 FTM0.000536692669
0xfe4e5bd8b8507a00e489fc2b3ceb13806aa53d0f3f4dd719b7e995b1c4024ab0Delegate108906952022-09-28 14:03:267 days 8 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000001 FTM0.000536639802
0xe8f9407919df9ace26281c988812246fb2aaed5fcc503980520009c80823a6faRelock Stake108897052022-09-28 13:06:177 days 9 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000541866746
0x0c88401f72f93bd5655720df9b80331042c7777f6007cddf4bef495f29646a48Withdraw108896602022-09-28 13:04:597 days 9 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000539180921
0xa9f08405d95c9959eefd7155bff153240e8019801c2c265b57906cfe0595fa6eWithdraw108896352022-09-28 13:04:097 days 9 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000539210887
0x21579c36056d09b07e668ba96113d9dcb23ee2a5d55b4e5226bcbf42e2ff3e8dWithdraw108895922022-09-28 13:02:027 days 9 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000539007151
0x172cca48ba5029cd4f577a1e2026999b4c07ae8f864546eee97fc19c679f2d53Withdraw108890452022-09-28 12:29:077 days 10 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000538872568
0xe962445b231f565af1223c31e31ed6dc5d83e9ad9fd74689ec9c3100d71c6e0eUnlock Stake108889562022-09-28 12:23:177 days 10 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.0005376881
0xdf5dd7967a81979e51be4b1f5e2afe4b652c50799ad58ac1e0b11afac81e19c1Relock Stake108888782022-09-28 12:17:467 days 10 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000541703496
0x65ecffb7218b83dd4029db6a0493e1498424da80da4437db5935b80eb9bdd1d2Relock Stake108880932022-09-28 11:31:317 days 11 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000541539898
0x36563c8f86fe0b13a8e3c86c6db44597fc4b30cd35041b55de8b423b5479569cRelock Stake108880822022-09-28 11:30:387 days 11 hrs ago0x56f9fc543b835a8edb7c3df46e92105a5562317a IN 0xfc00face000000000000000000000000000000000 FTM0.000541622845
0x394420ee2aab448d4aa96f950fb49705bd08ffbcaf0844801f4eebe4a5658714Relock Stake108807542022-09-28 3:56:427 days 18 hrs ago0x3febabfee16ad303994097125d5baba1f551c7b9 IN 0xfc00face000000000000000000000000000000000 FTM0.000101516222
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xe0c988d5c61e5dc2949a3be8f112c31a39bacdd8be99db781ccc160a1c0df8d1110236872022-10-05 22:38:1116 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.0235701781209048 FTM
0x572b826c86dbf8553200aef2a365d4d42351ab233685bcf916979c2980fd1fb8110234172022-10-05 22:13:5140 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.0259802652941757 FTM
0xe88bb6ed04dba3dd6239a8c5dabff26638cd8659ae54b392d4a20d16f22289bb110231342022-10-05 21:49:581 hr 4 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.02419337655297994 FTM
0x5cf535bc2ef01b8b463c20db693108cd199c86186a941d79d17988fe0ed9656c110228502022-10-05 21:25:311 hr 29 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.02685640800621421 FTM
0xba2e54eba09aeef6476385806d86646fcc43b2833d9545e41c9c41b6627a0670110225962022-10-05 21:01:441 hr 53 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.0237430261618967 FTM
0x58271ba909e4c96c043273876b30ebe209d4edecbf4e6b084b9c8594992b1f32110222952022-10-05 20:37:412 hrs 17 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.0228499192760252 FTM
0x18dc1aa3ea14a72308d45355ae1b33a2cf263d529d6ae71d98ba603c991209da110220162022-10-05 20:14:012 hrs 40 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.02870522140488763 FTM
0x1153307c9952f9d9638b9fb5018c9a72f2fba85c32a33da327117bed0a62b563110217332022-10-05 19:50:113 hrs 4 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.0217654134455533 FTM
0xb47642aa8cf63b214602391de9b666b2fa1599cfa547da27e99645b4c451012d110214702022-10-05 19:25:413 hrs 29 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.023162172616684015 FTM
0x6830474dd8d446c28333d77539679798b3970416acedc4cd4ebc105463ff3fcb110212042022-10-05 19:03:313 hrs 51 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.0246430856582001 FTM
0x3d4cf6c6df7cf3899d4a90aea63f0b4df76b9e144bd4d73890f0bdf5a73017a3110209102022-10-05 18:39:414 hrs 15 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.02213731112288334 FTM
0xd117a2b69a528e9923e4f4b2ec57598acdf5fbf3a6a2abf3caa1ec355035a60c110206422022-10-05 18:15:514 hrs 38 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.02433063802449084 FTM
0x54dd8743ca64849bc960669ba814457861589e7479185e3a86dec2c4dd3889af110203312022-10-05 17:52:205 hrs 2 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.04483265747335374 FTM
0x9a46e946defd445e3dbf1ed96480b0e6ebbd2e8c64b7e44c68ad633dff948bb8110200012022-10-05 17:31:315 hrs 23 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.0381035134188168 FTM
0x435ace55b651758018cc76810724218a8e7a303d17a7cf92c9f8c31bf9a711b1110197262022-10-05 17:09:215 hrs 45 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.04595903897539659 FTM
0x68eb30a43c073b7d5ec4de194595c7f92fcc3a3c1e76cea70063438347e8624f110194112022-10-05 16:49:466 hrs 5 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.0326993981485598 FTM
0x4babcf79216b28d70efaf698e30fd46d012fae1ecb8358adb3e96f4191904e84110191282022-10-05 16:27:306 hrs 27 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.02578726534197442 FTM
0x2b40ec60372f1837b1bac8402edb62abc12597954a00ba7461fea4a7f460656b110188222022-10-05 16:04:016 hrs 50 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.03384694245126829 FTM
0x686679e856a1d76b511e634d4c000a19eeb885078ab2ca3829e08e09bbed369b110185052022-10-05 15:40:507 hrs 13 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.02456417942184863 FTM
0x51eea8a4b13e521140b3dc603eeae552d11d26b571d39921ae273cbdf7f1cc4a110182242022-10-05 15:16:507 hrs 37 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.04006375891258559 FTM
0xd098b736fd987c038b46d35c4d551257ef94f8fe2d1f9d1e17141a08059a32ac110179392022-10-05 14:52:448 hrs 2 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.0219224837813729 FTM
0xe47b2fec7c89789f6ab5ec84afcb9d4bab4370659fb6ede4f5c89557d2230c03110176672022-10-05 14:28:218 hrs 26 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.08309484782575996 FTM
0x29007798661ba9683721e9c95c4a1ca7f5d790b93cc14207b946b7556385687b110173582022-10-05 14:04:218 hrs 50 mins ago 0xfc00face000000000000000000000000000000000x58355621a10b985ca3d6cf26b3d63334d29b040a0.09883650908666082 FTM
0xb2430b81608395316559716c2813fe05ddc6b3950a8c6e4256a11e1ce6b1d854110173462022-10-05 14:03:508 hrs 50 mins ago 0x2cd300f9f6ceae3a078a0236238c929b846e7cfa0xfc00face000000000000000000000000000000002.024628045570974009 FTM
0xb2430b81608395316559716c2813fe05ddc6b3950a8c6e4256a11e1ce6b1d854110173462022-10-05 14:03:508 hrs 50 mins ago 0x2cd300f9f6ceae3a078a0236238c929b846e7cfa0xfc00face000000000000000000000000000000002.024628045570974009 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