Contract 0x0B1B2132f54BA36Bea3b471406EBeeC257e9Fa61

 
Ad
Ad
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xa8858aeb7b768f9776c836a30045123bc8f17d5e0664346fa2deca1b73a8dbeaTransfer Ownersh...218060352021-11-26 2:22:496 days 19 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.00086175
0x0f50c650569b46fceea5ef3b7fdd22eeeea186cc4c3f1a1a5d81f92ee12f7077Change Pool Weig...218057712021-11-26 2:11:376 days 19 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.00158502
0xa0f930a4159fdee53d13d1eeae1bc7e92afd69c363c35ad6fa8bdadc4ba64196Change Pool Weig...218057652021-11-26 2:11:256 days 19 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.00158502
0x26589abcb33a497046570f214afc80eee8f1b861a8c68e1460ba18ddb95b9f5dChange Pool Weig...218057482021-11-26 2:10:476 days 19 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.00158502
0xd34b004ae1d5b4023de114b8d158cc9d45c721a067355b101c5bbc0e45ee854bChange Pool Weig...218057402021-11-26 2:10:316 days 19 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.00158502
0x051d37b2664eca97574821d5cb337f742ffdfd414447802477ac9e2af3e4d1efSet Unstake Burn...218013362021-11-25 23:19:506 days 22 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.00139098
0x0f942fd2a3f129533fad57705ccd785ce8b0728e64fd677aa210c6dda1417f7fSet Unstake Burn...218013282021-11-25 23:19:346 days 22 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.00139098
0xb4ec8e66727ab43ce1ca0c463e3e8b31af8c50d280d311dc01a974d83870032eSet Reward Lock ...218010302021-11-25 23:05:056 days 22 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.00093243
0x43bef90b2b0ad709491db8aad6990c8004d926e30fd582d0605242a0edea0cbdChange Pool Weig...217998402021-11-25 22:19:426 days 23 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.00158502
0x9c6de3037373f7e7bec67917f772797f3c305e08c35a369f287e254a2855adcbChange Pool Weig...217998272021-11-25 22:19:166 days 23 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.00158502
0xf585a1a89a114da661a0f2bf5bae4224d223bc5c01f4bc1e9152b3f6bb667e92Register Pool217997662021-11-25 22:17:106 days 23 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.0027246
0x2f7f7aeb73a61b2ac00f9628f8b50596455d9146a44b6ec4a58993810a8ea73eRegister Pool217997472021-11-25 22:16:326 days 23 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.0027246
0x446a53dd4e540c58d890d219ffdb48a813e197d2215457a258b8589b087eca46Register Pool217997402021-11-25 22:16:146 days 23 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.0027246
0x15cad4b6131cb4666e78dba068df9855169c891dfbc9f15014c44cdb0b61ab1cRegister Pool217997312021-11-25 22:15:566 days 23 hrs ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  0x0b1b2132f54ba36bea3b471406ebeec257e9fa610 MATIC0.0027246
0x6c9a923e9915792a0616f5472c6d0cdfca38d495917a988c8e0d53675f17560c0x60806040217979372021-11-25 21:07:527 days 37 mins ago0x283b3b8f340e8fb94d55b17e906744a74074cd07 IN  Contract Creation0 MATIC0.15219858
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ElixirPoolFactory

Compiler Version
v0.8.2+commit.661d1103

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at polygonscan.com on 2021-11-25
*/

// File: interfaces/IPool.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.2;

/**
 * @title Illuvium Pool
 *
 * @notice An abstraction representing a pool, see IlluviumPoolBase for details
 *
 * @author Pedro Bergamini, reviewed by Basil Gorin
 */
interface IPool {
    /**
     * @dev Deposit is a key data structure used in staking,
     *      it represents a unit of stake with its amount, weight and term (time interval)
     */
    struct Deposit {
        // @dev token amount staked
        uint256 tokenAmount;
        // @dev stake weight
        uint256 weight;
        // @dev liquid percentage;
        uint256 liquidPercentage;
        // @dev locking period - from
        uint64 lockedFrom;
        // @dev locking period - until
        uint64 lockedUntil;
        // @dev indicates if the stake was created as a yield reward
        bool isYield;
    }

    function eli() external view returns (address);

    function poolToken() external view returns (address);

    function weight() external view returns (uint32);

    function lastYieldDistribution() external view returns (uint64);

    function yieldRewardsPerWeight() external view returns (uint256);

    function usersLockingWeight() external view returns (uint256);

    function pendingYieldRewards(address _user) external view returns (uint256);

    function balanceOf(address _user) external view returns (uint256);

    function getDeposit(address _user, uint256 _depositId)
        external
        view
        returns (Deposit memory);

    function getDepositsLength(address _user) external view returns (uint256);

    function stake(uint256 _amount, uint64 _lockedUntil) external;

    function unstake(uint256 _depositId, uint256 _amount) external;

    function sync() external;

    function processRewards() external;

    function setWeight(uint32 _weight) external;
}

// File: interfaces/IFactory.sol

pragma solidity 0.8.2;

interface IFactory {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function endBlock() external view returns (uint32);

    function eliPerBlock() external view returns (uint192);

    function totalWeight() external view returns (uint32);

    function transferYieldTo(
        address _to,
        uint256 _amount,
        uint256 _liquidRewardAmount
    ) external;

    function unstakeBurnFee(address _tokenAddress)
        external
        view
        returns (uint256);

    function burnAddress() external view returns (address);

    function shouldUpdateRatio() external view returns (bool);

    function updateELIPerBlock() external;

    function getPoolAddress(address poolToken) external view returns (address);

    function owner() external view returns (address);

    function maximumRewardLock() external view returns (uint256);

    function minimumRewardLock() external view returns (uint256);

    function poolExists(address) external view returns (bool);
}

// File: @openzeppelin/contracts/proxy/utils/Initializable.sol

pragma solidity ^0.8.0;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract 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 protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }
}

// File: interfaces/ICorePool.sol

pragma solidity 0.8.2;


interface ICorePool is IPool {
    function vaultRewardsPerToken() external view returns (uint256);

    function poolTokenReserve() external view returns (uint256);

    function stakeAsPool(
        address _staker,
        uint256 _amount,
        uint256 _liquidPercent,
        uint256 _lockTime
    ) external;

    function receiveVaultRewards(uint256 _amount) external;
}

// File: contracts/ReentrancyGuard.sol

// https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/security/ReentrancyGuard.sol
// #24a0bc23cfe3fbc76f8f2510b78af1e948ae6651

pragma solidity 0.8.2;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

// File: interfaces/IERC20.sol

pragma solidity 0.8.2;

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

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

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

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

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

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

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

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

// File: utils/Address.sol

pragma solidity 0.8.2;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(
            address(this).balance >= amount,
            "Address: insufficient balance"
        );

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

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data)
        internal
        returns (bytes memory)
    {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return
            functionCallWithValue(
                target,
                data,
                value,
                "Address: low-level call with value failed"
            );
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(
            address(this).balance >= value,
            "Address: insufficient balance for call"
        );
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{value: value}(
            data
        );
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data)
        internal
        view
        returns (bytes memory)
    {
        return
            functionStaticCall(
                target,
                data,
                "Address: low-level static call failed"
            );
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.3._
     */
    function functionDelegateCall(address target, bytes memory data)
        internal
        returns (bytes memory)
    {
        return
            functionDelegateCall(
                target,
                data,
                "Address: low-level delegate call failed"
            );
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.3._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) private pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// File: utils/SafeERC20.sol

pragma solidity 0.8.2;



/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(
            token,
            abi.encodeWithSelector(token.transfer.selector, to, value)
        );
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(
            token,
            abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
        );
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(
            token,
            abi.encodeWithSelector(token.approve.selector, spender, value)
        );
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(
            token,
            abi.encodeWithSelector(
                token.approve.selector,
                spender,
                newAllowance
            )
        );
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) - value;
        _callOptionalReturn(
            token,
            abi.encodeWithSelector(
                token.approve.selector,
                spender,
                newAllowance
            )
        );
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(
            data,
            "SafeERC20: low-level call failed"
        );
        if (returndata.length > 0) {
            // Return data is optional
            // solhint-disable-next-line max-line-length
            require(
                abi.decode(returndata, (bool)),
                "SafeERC20: ERC20 operation did not succeed"
            );
        }
    }
}

// File: contracts/ElixirPoolBase.sol

pragma solidity 0.8.2;






/**
 * @title Elixir Pool Base
 *
 * @notice An abstract contract containing common logic for any pool,
 *      be it a flash pool (temporary pool like SNX) or a core pool (permanent pool like ELIXIR/ETH or ELIXIR pool)
 *
 * @dev Deployment and initialization.
 *      Any pool deployed must be bound to the deployed pool factory (IFactory)
 *      Additionally, 3 token instance addresses must be defined on deployment:
 *          - ELIXIR token address
 *          - pool token address, it can be ELIXIR token address, ELIXIR/ETH pair address, and others
 *
 * @dev Pool weight defines the fraction of the yield current pool receives among the other pools,
 *      pool factory is responsible for the weight synchronization between the pools.
 * @dev The weight is logically 10% for ELIXIR pool and 90% for ELIXIR/ETH pool.
 *      Since Solidity doesn't support fractions the weight is defined by the division of
 *      pool weight by total pools weight (sum of all registered pools within the factory)
 * @dev For ELIXIR Pool we use 100 as weight and for ELIXIR/ETH pool - 900.
 *
 */
abstract contract ElixirPoolBase is IPool, ReentrancyGuard {
    address public override eli;
    /// @dev Data structure representing token holder using a pool
    struct User {
        // @dev Total staked amount
        uint256 tokenAmount;
        //@dev Total Liquid Staked Amount
        uint256 liquidAmount;
        // @dev Total weight
        uint256 totalWeight;
        // @dev Liquid weight;
        uint256 liquidWeight;
        // @dev Auxiliary variable for yield calculation
        uint256 subYieldRewards;
        // @dev Auxiliary variable for vault rewards calculation
        uint256 subVaultRewards;
        // @dev An array of holder's deposits
        Deposit[] deposits;
    }

    address[] public history;
    /// @dev Token holder storage, maps token holder address to their data record
    mapping(address => User) public users;

    /// @dev Link to the pool factory IFactory instance
    IFactory public factory;

    /// @dev Link to the pool token instance, for example ELIXIR or ELIXIR/ETH pair
    address public override poolToken;

    /// @dev Pool weight, 100 for ELIXIR pool or 900 for ELIXIR/ETH
    uint32 public override weight;

    /// @dev Block number of the last yield distribution event
    uint64 public override lastYieldDistribution;

    /// @dev Used to calculate yield rewards
    /// @dev This value is different from "reward per token" used in locked pool
    /// @dev Note: stakes are different in duration and "weight" reflects that
    uint256 public override yieldRewardsPerWeight;

    /// @dev Used to calculate yield rewards, keeps track of the tokens weight locked in staking
    uint256 public override usersLockingWeight;

    /**
     * @dev Stake weight is proportional to deposit amount and time locked, precisely
     *      "deposit amount wei multiplied by (fraction of the year locked plus one)"
     * @dev To avoid significant precision loss due to multiplication by "fraction of the year" [0, 1],
     *      weight is stored multiplied by 1e6 constant, as an integer
     * @dev Corner case 1: if time locked is zero, weight is deposit amount multiplied by 1e6
     * @dev Corner case 2: if time locked is one year, fraction of the year locked is one, and
     *      weight is a deposit amount multiplied by 2 * 1e6
     */
    uint256 internal WEIGHT_MULTIPLIER = 1e6;

    /** @dev Stake weight for Liquid Guys (its 1/10 of Normal Weight Multiplier)
     */
    uint256 internal LIQUID_MULTIPLIER = 1e5;

    /**
     * @dev When we know beforehand that staking is done for a year, and fraction of the year locked is one,
     *      we use simplified calculation and use the following constant instead previos one
     */
    uint256 internal YEAR_STAKE_WEIGHT_MULTIPLIER = 2 * WEIGHT_MULTIPLIER;

    /**
     * @dev Rewards per weight are stored multiplied by 1e12, as integers.
     */
    uint256 internal REWARD_PER_WEIGHT_MULTIPLIER = 1e12;

    /**
     * @dev Fired in _stake() and stake()
     *
     * @param _by an address which performed an operation, usually token holder
     * @param _from token holder address, the tokens will be returned to that address
     * @param amount amount of tokens staked
     */
    event Staked(address indexed _by, address indexed _from, uint256 amount);

    /**
     * @dev Fired in _updateStakeLock() and updateStakeLock()
     *
     * @param _by an address which performed an operation
     * @param depositId updated deposit ID
     * @param lockedFrom deposit locked from value
     * @param lockedUntil updated deposit locked until value
     */
    event StakeLockUpdated(
        address indexed _by,
        uint256 depositId,
        uint64 lockedFrom,
        uint64 lockedUntil
    );

    /**
     * @dev Fired in _unstake() and unstake()
     *
     * @param _by an address which performed an operation, usually token holder
     * @param _to an address which received the unstaked tokens, usually token holder
     * @param amount amount of tokens unstaked
     */
    event Unstaked(address indexed _by, address indexed _to, uint256 amount);

    /**
     * @dev Fired in _sync(), sync() and dependent functions (stake, unstake, etc.)
     *
     * @param _by an address which performed an operation
     * @param yieldRewardsPerWeight updated yield rewards per weight value
     * @param lastYieldDistribution usually, current block number
     */
    event Synchronized(
        address indexed _by,
        uint256 yieldRewardsPerWeight,
        uint64 lastYieldDistribution
    );

    /**
     * @dev Fired in _processRewards(), processRewards() and dependent functions (stake, unstake, etc.)
     *
     * @param _by an address which performed an operation
     * @param _to an address which claimed the yield reward
     * @param amount amount of yield paid
     */
    event YieldClaimed(
        address indexed _by,
        address indexed _to,
        uint256 amount
    );

    /**
     * @dev Fired in setWeight()
     *
     * @param _by an address which performed an operation, always a factory
     * @param _fromVal old pool weight value
     * @param _toVal new pool weight value
     */
    event PoolWeightUpdated(
        address indexed _by,
        uint32 _fromVal,
        uint32 _toVal
    );

    /**
     * @dev Overridden in sub-contracts to construct the pool
     *
     * @param _eli ELI ERC20 Token ElixirERC20 address
     * @param _factory Pool factory IFactory instance/address
     * @param _poolToken token the pool operates on, for example ELIXIR or ELIXIR/ETH pair
     * @param _initBlock initial block used to calculate the rewards
     *      note: _initBlock can be set to the future effectively meaning _sync() calls will do nothing
     * @param _weight number representing a weight of the pool, actual weight fraction
     *      is calculated as that number divided by the total pools weight and doesn't exceed one
     */
    constructor(
        address _eli,
        IFactory _factory,
        address _poolToken,
        uint64 _initBlock,
        uint32 _weight
    ) {
        require(
            address(_factory) != address(0),
            "ELIXIR Pool fct address not set"
        );
        require(_poolToken != address(0), "pool token address not set");
        require(_initBlock > 0, "init block not set");
        require(_weight > 0, "pool weight not set");

        // save the inputs into internal state variables
        eli = _eli;
        factory = _factory;
        poolToken = _poolToken;
        weight = _weight;

        // init the dependent internal state variables
        lastYieldDistribution = _initBlock;
    }

    function initConfig(
        address _eli,
        IFactory _factory,
        address _poolToken,
        uint64 _initBlock,
        uint32 _weight
    ) internal {
        eli = _eli;
        factory = _factory;
        poolToken = _poolToken;
        weight = _weight;
        lastYieldDistribution = _initBlock;
    }

    /**
     * @notice Calculates current yield rewards value available for address specified
     *
     * @param _staker an address to calculate yield rewards value for
     * @return calculated yield reward value for the given address
     */
    function pendingYieldRewards(address _staker)
        external
        view
        override
        returns (uint256)
    {
        // `newYieldRewardsPerWeight` will store stored or recalculated value for `yieldRewardsPerWeight`
        uint256 newYieldRewardsPerWeight;

        // if smart contract state was not updated recently, `yieldRewardsPerWeight` value
        // is outdated and we need to recalculate it in order to calculate pending rewards correctly
        if (blockNumber() > lastYieldDistribution && usersLockingWeight != 0) {
            uint256 endBlock = factory.endBlock();
            uint256 multiplier = blockNumber() > endBlock
                ? endBlock - lastYieldDistribution
                : blockNumber() - lastYieldDistribution;
            uint256 eliRewards = (multiplier * weight * factory.eliPerBlock()) /
                factory.totalWeight();

            // recalculated value for `yieldRewardsPerWeight`
            newYieldRewardsPerWeight =
                rewardToWeight(eliRewards, usersLockingWeight) +
                yieldRewardsPerWeight;
        } else {
            // if smart contract state is up to date, we don't recalculate
            newYieldRewardsPerWeight = yieldRewardsPerWeight;
        }

        // based on the rewards per weight value, calculate pending rewards;
        User memory user = users[_staker];
        uint256 pending = weightToReward(
            user.totalWeight,
            newYieldRewardsPerWeight
        ) - user.subYieldRewards;

        return pending;
    }

    /**
     * @notice Returns total staked token balance for the given address
     *
     * @param _user an address to query balance for
     * @return total staked token balance
     */
    function balanceOf(address _user) external view override returns (uint256) {
        // read specified user token amount and return
        return users[_user].tokenAmount;
    }

    /**
     * @notice Returns information on the given deposit for the given address
     *
     * @dev See getDepositsLength
     *
     * @param _user an address to query deposit for
     * @param _depositId zero-indexed deposit ID for the address specified
     * @return deposit info as Deposit structure
     */
    function getDeposit(address _user, uint256 _depositId)
        external
        view
        override
        returns (Deposit memory)
    {
        // read deposit at specified index and return
        return users[_user].deposits[_depositId];
    }

    /**
     * @notice Returns number of deposits for the given address. Allows iteration over deposits.
     *
     * @dev See getDeposit
     *
     * @param _user an address to query deposit length for
     * @return number of deposits for the given address
     */
    function getDepositsLength(address _user)
        external
        view
        override
        returns (uint256)
    {
        // read deposits array length and return
        return users[_user].deposits.length;
    }

    /**
     * @notice Stakes specified amount of tokens for the specified amount of time,
     *      and pays pending yield rewards if any
     *
     * @dev Requires amount to stake to be greater than zero
     *
     * @param _amount amount of tokens to stake
     * @param _lockUntil stake period as unix timestamp; zero means no locking
     */
    function stake(uint256 _amount, uint64 _lockUntil) external override {
        // delegate call to an internal function
        _stake(msg.sender, _amount, _lockUntil, false, 0);
        history.push(msg.sender);
    }

    /**
     * @notice Unstakes specified amount of tokens, and pays pending yield rewards if any
     *
     * @dev Requires amount to unstake to be greater than zero
     *
     * @param _depositId deposit ID to unstake from, zero-indexed
     * @param _amount amount of tokens to unstake
     */
    function unstake(uint256 _depositId, uint256 _amount) external override {
        // delegate call to an internal function
        _unstake(msg.sender, _depositId, _amount);
        history.push(msg.sender);
    }

    /**
     * @notice Extends locking period for a given deposit
     *
     * @dev Requires new lockedUntil value to be:
     *      higher than the current one, and
     *      in the future, but
     *      no more than 1 year in the future
     *
     * @param depositId updated deposit ID
     * @param lockedUntil updated deposit locked until value
     */
    function updateStakeLock(uint256 depositId, uint64 lockedUntil) external {
        // sync and call processRewards
        _sync();
        _processRewards(msg.sender, false);
        // delegate call to an internal function
        _updateStakeLock(msg.sender, depositId, lockedUntil);
    }

    /**
     * @notice Service function to synchronize pool state with current time
     *
     * @dev Can be executed by anyone at any time, but has an effect only when
     *      at least one block passes between synchronizations
     * @dev Executed internally when staking, unstaking, processing rewards in order
     *      for calculations to be correct and to reflect state progress of the contract
     * @dev When timing conditions are not met (executed too frequently, or after factory
     *      end block), function doesn't throw and exits silently
     */
    function sync() external override {
        // delegate call to an internal function
        _sync();
    }

    /**
     * @notice Service function to calculate and pay pending yield rewards to the sender
     *
     * @dev Can be executed by anyone at any time, but has an effect only when
     *      executed by deposit holder and when at least one block passes from the
     *      previous reward processing
     * @dev Executed internally when staking and unstaking, executes sync() under the hood
     *      before making further calculations and payouts
     * @dev When timing conditions are not met (executed too frequently, or after factory
     *      end block), function doesn't throw and exits silently
     *
     */
    function processRewards() external virtual override {
        // delegate call to an internal function
        _processRewards(msg.sender, true);
    }

    /**
     * @dev Executed by the factory to modify pool weight; the factory is expected
     *      to keep track of the total pools weight when updating
     *
     * @dev Set weight to zero to disable the pool
     *
     * @param _weight new weight to set for the pool
     */
    function setWeight(uint32 _weight) external override {
        // verify function is executed by the factory
        require(msg.sender == address(factory), "access denied");

        // emit an event logging old and new weight values
        emit PoolWeightUpdated(msg.sender, weight, _weight);

        // set the new weight value
        weight = _weight;
    }

    /**
     * @dev Similar to public pendingYieldRewards, but performs calculations based on
     *      current smart contract state only, not taking into account any additional
     *      time/blocks which might have passed
     *
     * @param _staker an address to calculate yield rewards value for
     * @return pending calculated yield reward value for the given address
     */
    function _pendingYieldRewards(address _staker)
        internal
        view
        returns (uint256 pending)
    {
        // read user data structure into memory
        User memory user = users[_staker];

        // and perform the calculation using the values read
        return
            weightToReward(user.totalWeight, yieldRewardsPerWeight) -
            user.subYieldRewards;
    }

    /**
     * @dev Used internally, mostly by children implementations, see stake()
     *
     * @param _staker an address which stakes tokens and which will receive them back
     * @param _amount amount of tokens to stake
     * @param _lockUntil stake period as unix timestamp; zero means no locking
     * @param _isYield a flag indicating if that stake is created to store yield reward
     *      from the previously unstaked stake
     */
    function _stake(
        address _staker,
        uint256 _amount,
        uint64 _lockUntil,
        bool _isYield,
        uint256 _liquidPercent
    ) internal virtual {
        // validate the inputs
        require(_amount > 0, "zero amount");
        require(
            _lockUntil == 0 ||
                (_lockUntil > now256() && _lockUntil - now256() <= 365 days),
            "invalid lock interval"
        );

        // update smart contract state
        _sync();

        // get a link to user data struct, we will write to it later
        User storage user = users[_staker];
        // process current pending rewards if any
        if (user.tokenAmount > 0) {
            _processRewards(_staker, false);
        }

        // in most of the cases added amount `addedAmount` is simply `_amount`
        // however for deflationary tokens this can be different

        // read the current balance
        uint256 previousBalance = IERC20(poolToken).balanceOf(address(this));
        // transfer `_amount`; note: some tokens may get burnt here
        transferPoolTokenFrom(address(msg.sender), address(this), _amount);
        // read new balance, usually this is just the difference `previousBalance - _amount`
        uint256 newBalance = IERC20(poolToken).balanceOf(address(this));
        // calculate real amount taking into account deflation
        uint256 addedAmount = newBalance - previousBalance;

        // set the `lockFrom` and `lockUntil` taking into account that
        // zero value for `_lockUntil` means "no locking" and leads to zero values
        // for both `lockFrom` and `lockUntil`
        uint64 lockFrom = _lockUntil > 0 ? uint64(now256()) : 0;
        uint64 lockUntil = _lockUntil;

        uint256 weightMultiplier = _lockUntil > 0
            ? WEIGHT_MULTIPLIER
            : LIQUID_MULTIPLIER;

        // stake weight formula rewards for locking
        uint256 stakeWeight = (((lockUntil - lockFrom) * weightMultiplier) /
            365 days +
            weightMultiplier) * addedAmount;

        // makes sure stakeWeight is valid
        assert(stakeWeight > 0);

        // create and save the deposit (append it to deposits array)
        Deposit memory deposit = Deposit({
            tokenAmount: addedAmount,
            weight: stakeWeight,
            liquidPercentage: _isYield ? _liquidPercent : 0,
            lockedFrom: lockFrom,
            lockedUntil: lockUntil,
            isYield: _isYield
        });
        // deposit ID is an index of the deposit in `deposits` array
        user.deposits.push(deposit);

        // update user record
        user.tokenAmount += addedAmount;
        user.totalWeight += stakeWeight;
        if (_lockUntil == 0) {
            user.liquidAmount += addedAmount;
            user.liquidWeight += stakeWeight;
        }
        user.subYieldRewards = weightToReward(
            user.totalWeight,
            yieldRewardsPerWeight
        );

        // update global variable
        usersLockingWeight += stakeWeight;

        // emit an event
        emit Staked(msg.sender, _staker, _amount);
    }

    /**
     * @dev Used internally, mostly by children implementations, see unstake()
     *
     * @param _staker an address which unstakes tokens (which previously staked them)
     * @param _depositId deposit ID to unstake from, zero-indexed
     * @param _amount amount of tokens to unstake
     */
    function _unstake(
        address _staker,
        uint256 _depositId,
        uint256 _amount
    ) internal virtual {
        // verify an amount is set
        require(_amount > 0, "zero amount");

        // get a link to user data struct, we will write to it later
        User storage user = users[_staker];
        // get a link to the corresponding deposit, we may write to it later
        Deposit storage stakeDeposit = user.deposits[_depositId];
        // deposit structure may get deleted, so we save isYield flag to be able to use it
        bool isYield = stakeDeposit.isYield;

        // verify available balance
        // if staker address ot deposit doesn't exist this check will fail as well
        require(stakeDeposit.tokenAmount >= _amount, "amount exceeds stake");

        // update smart contract state
        _sync();
        // and process current pending rewards if any
        _processRewards(_staker, false);

        // recalculate deposit weight
        uint256 previousWeight = stakeDeposit.weight;
        uint256 stakeWeightMultiplier = stakeDeposit.lockedUntil == 0
            ? LIQUID_MULTIPLIER
            : WEIGHT_MULTIPLIER;
        uint256 newWeight = (((stakeDeposit.lockedUntil -
            stakeDeposit.lockedFrom) * stakeWeightMultiplier) /
            365 days +
            stakeWeightMultiplier) * (stakeDeposit.tokenAmount - _amount);

        // update the deposit, or delete it if its depleted
        if (stakeDeposit.tokenAmount - _amount == 0) {
            delete user.deposits[_depositId];
        } else {
            stakeDeposit.tokenAmount -= _amount;
            stakeDeposit.weight = newWeight;
        }

        // update user record
        user.tokenAmount -= _amount;
        if (stakeDeposit.lockedUntil == 0) {
            user.liquidAmount -= _amount;
            user.liquidWeight = user.liquidWeight - previousWeight + newWeight;
        }
        user.totalWeight = user.totalWeight - previousWeight + newWeight;
        user.subYieldRewards = weightToReward(
            user.totalWeight,
            yieldRewardsPerWeight
        );

        // update global variable
        usersLockingWeight = usersLockingWeight - previousWeight + newWeight;

        // if the deposit was created by the pool itself as a yield reward
        if (isYield) {
            // transfer the yield via the factory
            uint256 liquidRewardAmount = (_amount *
                stakeDeposit.liquidPercentage) / 100;
            factory.transferYieldTo(msg.sender, _amount, liquidRewardAmount);
        } else {
            uint256 _burnAmount = (_amount *
                factory.unstakeBurnFee(poolToken)) / 100;
            // otherwise just return tokens back to holder
            if (_burnAmount > 0) {
                transferPoolToken(factory.burnAddress(), _burnAmount);
            }
            if (_burnAmount < _amount) {
                transferPoolToken(msg.sender, _amount - _burnAmount);
            }
        }

        // emit an event
        emit Unstaked(msg.sender, _staker, _amount);
    }

    /**
     * @dev Used internally, mostly by children implementations, see sync()
     *
     * @dev Updates smart contract state (`yieldRewardsPerWeight`, `lastYieldDistribution`),
     *      updates factory state via `updateELIPerBlock`
     */
    function _sync() internal virtual {
        // update ELIXIR per block value in factory if required
        if (factory.shouldUpdateRatio()) {
            factory.updateELIPerBlock();
        }

        // check bound conditions and if these are not met -
        // exit silently, without emitting an event
        uint256 endBlock = factory.endBlock();
        if (lastYieldDistribution >= endBlock) {
            return;
        }
        if (blockNumber() <= lastYieldDistribution) {
            return;
        }
        // if locking weight is zero - update only `lastYieldDistribution` and exit
        if (usersLockingWeight == 0) {
            lastYieldDistribution = uint64(blockNumber());
            return;
        }

        // to calculate the reward we need to know how many blocks passed, and reward per block
        uint256 currentBlock = blockNumber() > endBlock
            ? endBlock
            : blockNumber();
        uint256 blocksPassed = currentBlock - lastYieldDistribution;
        uint256 eliPerBlock = factory.eliPerBlock();

        // calculate the reward
        uint256 eliReward = (blocksPassed * eliPerBlock * weight) /
            factory.totalWeight();

        // update rewards per weight and `lastYieldDistribution`
        yieldRewardsPerWeight += rewardToWeight(eliReward, usersLockingWeight);
        lastYieldDistribution = uint64(currentBlock);

        // emit an event
        emit Synchronized(
            msg.sender,
            yieldRewardsPerWeight,
            lastYieldDistribution
        );
    }

    /**
     * @dev Used internally, mostly by children implementations, see processRewards()
     *
     * @param _staker an address which receives the reward (which has staked some tokens earlier)
     * @param _withUpdate flag allowing to disable synchronization (see sync()) if set to false
     * @return pendingYield the rewards calculated and optionally re-staked
     */
    function _processRewards(address _staker, bool _withUpdate)
        internal
        virtual
        returns (uint256 pendingYield)
    {
        // update smart contract state if required
        if (_withUpdate) {
            _sync();
        }

        // calculate pending yield rewards, this value will be returned
        pendingYield = _pendingYieldRewards(_staker);

        // if pending yield is zero - just return silently
        if (pendingYield == 0) return 0;

        // get link to a user data structure, we will write into it later
        User storage user = users[_staker];

        if (poolToken == eli) {
            // calculate pending yield weight,
            // 2e6 is the bonus weight when staking for 1 year
            uint256 depositWeight = pendingYield * YEAR_STAKE_WEIGHT_MULTIPLIER;

            // if the pool is ELIXIR Pool - create new ELIXIR deposit
            // and save it - push it into deposits array
            Deposit memory newDeposit = Deposit({
                tokenAmount: pendingYield,
                lockedFrom: uint64(now256()),
                lockedUntil: uint64(now256() + getRewardLockPeriod(_staker)), // staking yield for Reward Lock Period
                weight: depositWeight,
                liquidPercentage: (user.liquidWeight * 100) / user.totalWeight,
                isYield: true
            });
            user.deposits.push(newDeposit);

            // update user record
            user.tokenAmount += pendingYield;
            user.totalWeight += depositWeight;

            // update global variable
            usersLockingWeight += depositWeight;
        } else {
            // for other pools - stake as pool
            address eliPool = factory.getPoolAddress(eli);
            ICorePool(eliPool).stakeAsPool(
                _staker,
                pendingYield,
                (user.liquidWeight * 100) / user.totalWeight,
                getRewardLockPeriod(_staker)
            );
        }

        // update users's record for `subYieldRewards` if requested
        if (_withUpdate) {
            user.subYieldRewards = weightToReward(
                user.totalWeight,
                yieldRewardsPerWeight
            );
        }

        // emit an event
        emit YieldClaimed(msg.sender, _staker, pendingYield);
    }

    /**
     * @dev See updateStakeLock()
     *
     * @param _staker an address to update stake lock
     * @param _depositId updated deposit ID
     * @param _lockedUntil updated deposit locked until value
     */
    function _updateStakeLock(
        address _staker,
        uint256 _depositId,
        uint64 _lockedUntil
    ) internal {
        // validate the input time
        require(_lockedUntil > now256(), "lock should be in the future");

        // get a link to user data struct, we will write to it later
        User storage user = users[_staker];
        // get a link to the corresponding deposit, we may write to it later
        Deposit storage stakeDeposit = user.deposits[_depositId];

        // validate the input against deposit structure
        require(_lockedUntil > stakeDeposit.lockedUntil, "invalid new lock");

        // verify locked from and locked until values
        if (stakeDeposit.lockedFrom == 0) {
            require(
                _lockedUntil - now256() <= 365 days,
                "max lock period is 365 days"
            );
            stakeDeposit.lockedFrom = uint64(now256());
        } else {
            require(
                _lockedUntil - stakeDeposit.lockedFrom <= 365 days,
                "max lock period is 365 days"
            );
        }

        // update locked until value, calculate new weight
        stakeDeposit.lockedUntil = _lockedUntil;
        uint256 newWeight = (((stakeDeposit.lockedUntil -
            stakeDeposit.lockedFrom) * WEIGHT_MULTIPLIER) /
            365 days +
            WEIGHT_MULTIPLIER) * stakeDeposit.tokenAmount;

        // save previous weight
        uint256 previousWeight = stakeDeposit.weight;
        // update weight
        stakeDeposit.weight = newWeight;

        // update user total weight and global locking weight
        user.totalWeight = user.totalWeight - previousWeight + newWeight;
        usersLockingWeight = usersLockingWeight - previousWeight + newWeight;

        // emit an event
        emit StakeLockUpdated(
            _staker,
            _depositId,
            stakeDeposit.lockedFrom,
            _lockedUntil
        );
    }

    /**
     * @dev Converts stake weight (not to be mixed with the pool weight) to
     *      ELIXIR reward value, applying the 10^12 division on weight
     *
     * @param _weight stake weight
     * @param rewardPerWeight ELIXIR reward per weight
     * @return reward value normalized to 10^12
     */
    function weightToReward(uint256 _weight, uint256 rewardPerWeight)
        public
        view
        returns (uint256)
    {
        // apply the formula and return
        return (_weight * rewardPerWeight) / REWARD_PER_WEIGHT_MULTIPLIER;
    }

    /**
     * @dev Converts reward ELIXIR value to stake weight (not to be mixed with the pool weight),
     *      applying the 10^12 multiplication on the reward
     *      - OR -
     * @dev Converts reward ELIXIR value to reward/weight if stake weight is supplied as second
     *      function parameter instead of reward/weight
     *
     * @param reward yield reward
     * @param rewardPerWeight reward/weight (or stake weight)
     * @return stake weight (or reward/weight)
     */
    function rewardToWeight(uint256 reward, uint256 rewardPerWeight)
        public
        view
        returns (uint256)
    {
        // apply the reverse formula and return
        return (reward * REWARD_PER_WEIGHT_MULTIPLIER) / rewardPerWeight;
    }

    /**
     * @dev Testing time-dependent functionality is difficult and the best way of
     *      doing it is to override block number in helper test smart contracts
     *
     * @return `block.number` in mainnet, custom values in testnets (if overridden)
     */
    function blockNumber() public view virtual returns (uint256) {
        // return current block number
        return block.number;
    }

    /**
     * @dev Testing time-dependent functionality is difficult and the best way of
     *      doing it is to override time in helper test smart contracts
     *
     * @return `block.timestamp` in mainnet, custom values in testnets (if overridden)
     */
    function now256() public view virtual returns (uint256) {
        // return current block timestamp
        return block.timestamp;
    }

    /**
     * @dev Executes SafeERC20.safeTransfer on a pool token
     *
     * @dev Reentrancy safety enforced via `ReentrancyGuard.nonReentrant`
     */
    function transferPoolToken(address _to, uint256 _value)
        internal
        nonReentrant
    {
        // just delegate call to the target
        SafeERC20.safeTransfer(IERC20(poolToken), _to, _value);
    }

    /**
     * @dev Executes SafeERC20.safeTransferFrom on a pool token
     *
     * @dev Reentrancy safety enforced via `ReentrancyGuard.nonReentrant`
     */
    function transferPoolTokenFrom(
        address _from,
        address _to,
        uint256 _value
    ) internal nonReentrant {
        // just delegate call to the target
        SafeERC20.safeTransferFrom(IERC20(poolToken), _from, _to, _value);
    }

    /** @dev Get History Length */
    function getHistoryLength() external view returns (uint256) {
        return history.length;
    }

    /** @dev Clearing History for more updates */
    function clearHistory() external {
        require(msg.sender == factory.owner(), "access denied");
        delete history;
    }

    function setConfiguration(
        uint256 _rewardPerWeightMultiplier,
        uint256 _yearStakeWeightMultiplier,
        uint256 _weightMultiplier,
        uint256 _liquidMultiplier
    ) external {
        require(msg.sender == factory.owner(), "access denied");
        REWARD_PER_WEIGHT_MULTIPLIER = _rewardPerWeightMultiplier;
        YEAR_STAKE_WEIGHT_MULTIPLIER = _yearStakeWeightMultiplier;
        WEIGHT_MULTIPLIER = _weightMultiplier;
        LIQUID_MULTIPLIER = _liquidMultiplier;
    }

    function setInitialSettings(address _factory, address _poolToken) external {
        require(msg.sender == factory.owner(), "access denied");
        factory = IFactory(_factory);
        poolToken = _poolToken;
    }

    /** @dev Get Reward Lock Time */
    function getRewardLockPeriod(address _staker)
        public
        view
        returns (uint256)
    {
        User storage user = users[_staker];
        if (user.tokenAmount == 0) {
            return factory.maximumRewardLock();
        }

        uint256 i;
        uint256 totalSum = 0;
        for (i = 0; i < user.deposits.length; i++) {
            Deposit storage stakeDeposit = user.deposits[i];
            if (!stakeDeposit.isYield) {
                totalSum =
                    totalSum +
                    stakeDeposit.tokenAmount *
                    (stakeDeposit.lockedUntil - stakeDeposit.lockedFrom);
            }
        }
        uint256 averageLocked = factory.maximumRewardLock() -
            totalSum /
            user.tokenAmount;
        if (averageLocked < factory.minimumRewardLock()) {
            return factory.minimumRewardLock();
        }
        if (averageLocked > factory.maximumRewardLock()) {
            return factory.maximumRewardLock();
        }
        return averageLocked;
    }
}

// File: contracts/ElixirCorePool.sol

pragma solidity 0.8.2;



/**
 * @title Elixir Core Pool
 *
 * @notice Core pools represent permanent pools like ELIXIR or ELIXIR/ETH Pair pool,
 *      core pools allow staking for arbitrary periods of time up to 1 year
 *
 * @dev See ElixirPoolBase for more details
 *
 */
contract ElixirCorePool is ElixirPoolBase, Initializable {
    /// @dev Link to deployed ElixirVault instance
    address public vault;

    /// @dev Used to calculate vault rewards
    /// @dev This value is different from "reward per token" used in locked pool
    /// @dev Note: stakes are different in duration and "weight" reflects that
    uint256 public vaultRewardsPerWeight;

    /// @dev Pool tokens value available in the pool;
    ///      pool token examples are ELIXIR (ELIXIR core pool) or ELIXIR/ETH pair (LP core pool)
    /// @dev For LP core pool this value doesnt' count for ELIXIR tokens received as Vault rewards
    ///      while for ELIXIR core pool it does count for such tokens as well
    uint256 public poolTokenReserve;

    /**
     * @dev Fired in receiveVaultRewards()
     *
     * @param _by an address that sent the rewards, always a vault
     * @param amount amount of tokens received
     */
    event VaultRewardsReceived(address indexed _by, uint256 amount);

    /**
     * @dev Fired in _processVaultRewards() and dependent functions, like processRewards()
     *
     * @param _by an address which executed the function
     * @param _to an address which received a reward
     * @param amount amount of reward received
     */
    event VaultRewardsClaimed(
        address indexed _by,
        address indexed _to,
        uint256 amount
    );

    /**
     * @dev Fired in setVault()
     *
     * @param _by an address which executed the function, always a factory owner
     */
    event VaultUpdated(address indexed _by, address _fromVal, address _toVal);

    /**
     * @dev Creates/deploys an instance of the core pool
     *
     * @param _eli ELI ERC20 Token ElixirERC20 address
     * @param _factory Pool factory ElixirPoolFactory instance/address
     * @param _poolToken token the pool operates on, for example ELIXIR or ELIXIR/ETH pair
     * @param _initBlock initial block used to calculate the rewards
     * @param _weight number representing a weight of the pool, actual weight fraction
     *      is calculated as that number divided by the total pools weight and doesn't exceed one
     */
    constructor(
        address _eli,
        IFactory _factory,
        address _poolToken,
        uint64 _initBlock,
        uint32 _weight
    ) ElixirPoolBase(_eli, _factory, _poolToken, _initBlock, _weight) {}

    function initialize(
        address _eli,
        IFactory _factory,
        address _poolToken,
        uint64 _initBlock,
        uint32 _weight
    ) public initializer {
        super.initConfig(_eli, _factory, _poolToken, _initBlock, _weight);
    }

    /**
     * @notice Calculates current vault rewards value available for address specified
     *
     * @dev Performs calculations based on current smart contract state only,
     *      not taking into account any additional time/blocks which might have passed
     *
     * @param _staker an address to calculate vault rewards value for
     * @return pending calculated vault reward value for the given address
     */
    function pendingVaultRewards(address _staker)
        public
        view
        returns (uint256 pending)
    {
        User memory user = users[_staker];

        return
            weightToReward(user.totalWeight, vaultRewardsPerWeight) -
            user.subVaultRewards;
    }

    /**
     * @dev Executed only by the factory owner to Set the vault
     *
     * @param _vault an address of deployed ElixirVault instance
     */
    function setVault(address _vault) external {
        // verify function is executed by the factory owner
        require(factory.owner() == msg.sender, "access denied");

        // verify input is set
        require(_vault != address(0), "zero input");

        // emit an event
        emit VaultUpdated(msg.sender, vault, _vault);

        // update vault address
        vault = _vault;
    }

    /**
     * @dev Executed by the vault to transfer vault rewards ELIXIR from the vault
     *      into the pool
     *
     * @dev This function is executed only for ELIXIR core pools
     *
     * @param _rewardsAmount amount of ELIXIR rewards to transfer into the pool
     */
    function receiveVaultRewards(uint256 _rewardsAmount) external {
        require(msg.sender == vault, "access denied");
        // return silently if there is no reward to receive
        if (_rewardsAmount == 0) {
            return;
        }
        require(usersLockingWeight > 0, "zero locking weight");

        transferEliFrom(msg.sender, address(this), _rewardsAmount);

        vaultRewardsPerWeight += rewardToWeight(
            _rewardsAmount,
            usersLockingWeight
        );

        // update `poolTokenReserve` only if this is a ELIXIR Core Pool
        if (poolToken == eli) {
            poolTokenReserve += _rewardsAmount;
        }

        emit VaultRewardsReceived(msg.sender, _rewardsAmount);
    }

    /**
     * @notice Service function to calculate and pay pending vault and yield rewards to the sender
     *
     * @dev Internally executes similar function `_processRewards` from the parent smart contract
     *      to calculate and pay yield rewards; adds vault rewards processing
     *
     * @dev Can be executed by anyone at any time, but has an effect only when
     *      executed by deposit holder and when at least one block passes from the
     *      previous reward processing
     * @dev Executed internally when "staking as a pool" (`stakeAsPool`)
     * @dev When timing conditions are not met (executed too frequently, or after factory
     *      end block), function doesn't throw and exits silently
     *
     */
    function processRewards() external override {
        _processRewards(msg.sender, true);
    }

    /**
     * @dev Executed internally by the pool itself (from the parent `ElixirPoolBase` smart contract)
     *      as part of yield rewards processing logic (`ElixirPoolBase._processRewards` function)
     *
     * @param _staker an address which stakes (the yield reward)
     * @param _amount amount to be staked (yield reward amount)
     */
    function stakeAsPool(
        address _staker,
        uint256 _amount,
        uint256 _liquidPercent,
        uint256 _rewardLockPeriod
    ) external {
        require(factory.poolExists(msg.sender), "access denied");
        _sync();
        User storage user = users[_staker];
        if (user.tokenAmount > 0) {
            _processRewards(_staker, false);
        }
        uint256 depositWeight = _amount * YEAR_STAKE_WEIGHT_MULTIPLIER;
        Deposit memory newDeposit = Deposit({
            tokenAmount: _amount,
            lockedFrom: uint64(now256()),
            lockedUntil: uint64(now256() + _rewardLockPeriod),
            weight: depositWeight,
            liquidPercentage: _liquidPercent,
            isYield: true
        });
        user.tokenAmount += _amount;
        user.totalWeight += depositWeight;
        user.deposits.push(newDeposit);

        usersLockingWeight += depositWeight;

        user.subYieldRewards = weightToReward(
            user.totalWeight,
            yieldRewardsPerWeight
        );
        user.subVaultRewards = weightToReward(
            user.totalWeight,
            vaultRewardsPerWeight
        );

        // update `poolTokenReserve` only if this is a LP Core Pool (stakeAsPool can be executed only for LP pool)
        poolTokenReserve += _amount;
    }

    /**
     * @inheritdoc ElixirPoolBase
     *
     * @dev Additionally to the parent smart contract, updates vault rewards of the holder,
     *      and updates (increases) pool token reserve (pool tokens value available in the pool)
     */
    function _stake(
        address _staker,
        uint256 _amount,
        uint64 _lockedUntil,
        bool _isYield,
        uint256 _liquidPercent
    ) internal override {
        super._stake(_staker, _amount, _lockedUntil, _isYield, _liquidPercent);
        User storage user = users[_staker];
        user.subVaultRewards = weightToReward(
            user.totalWeight,
            vaultRewardsPerWeight
        );

        poolTokenReserve += _amount;
    }

    /**
     * @inheritdoc ElixirPoolBase
     *
     * @dev Additionally to the parent smart contract, updates vault rewards of the holder,
     *      and updates (decreases) pool token reserve (pool tokens value available in the pool)
     */
    function _unstake(
        address _staker,
        uint256 _depositId,
        uint256 _amount
    ) internal override {
        User storage user = users[_staker];
        Deposit memory stakeDeposit = user.deposits[_depositId];
        require(
            stakeDeposit.lockedFrom == 0 || now256() > stakeDeposit.lockedUntil,
            "deposit not yet unlocked"
        );
        poolTokenReserve -= _amount;
        super._unstake(_staker, _depositId, _amount);
        user.subVaultRewards = weightToReward(
            user.totalWeight,
            vaultRewardsPerWeight
        );
    }

    /**
     * @inheritdoc ElixirPoolBase
     *
     * @dev Additionally to the parent smart contract, processes vault rewards of the holder,
     *      and for ELIXIR pool updates (increases) pool token reserve (pool tokens value available in the pool)
     */
    function _processRewards(address _staker, bool _withUpdate)
        internal
        override
        returns (uint256 pendingYield)
    {
        _processVaultRewards(_staker);
        pendingYield = super._processRewards(_staker, _withUpdate);

        // update `poolTokenReserve` only if this is a ELIXIR Core Pool
        if (poolToken == eli) {
            poolTokenReserve += pendingYield;
        }
    }

    /**
     * @dev Used internally to process vault rewards for the staker
     *
     * @param _staker address of the user (staker) to process rewards for
     */
    function _processVaultRewards(address _staker) private {
        User storage user = users[_staker];
        uint256 pendingVaultClaim = pendingVaultRewards(_staker);
        if (pendingVaultClaim == 0) return;
        // read ELIXIR token balance of the pool via standard ERC20 interface
        uint256 eliBalance = IERC20(eli).balanceOf(address(this));
        require(
            eliBalance >= pendingVaultClaim,
            "contract ELI balance too low"
        );

        // update `poolTokenReserve` only if this is a ELIXIR Core Pool
        if (poolToken == eli) {
            // protects against rounding errors
            poolTokenReserve -= pendingVaultClaim > poolTokenReserve
                ? poolTokenReserve
                : pendingVaultClaim;
        }

        user.subVaultRewards = weightToReward(
            user.totalWeight,
            vaultRewardsPerWeight
        );

        // transfer fails if pool ELIXIR balance is not enough - which is a desired behavior
        transferEli(_staker, pendingVaultClaim);

        emit VaultRewardsClaimed(msg.sender, _staker, pendingVaultClaim);
    }

    /**
     * @dev Executes SafeERC20.safeTransfer on a pool token
     *
     * @dev Reentrancy safety enforced via `ReentrancyGuard.nonReentrant`
     */
    function transferEli(address _to, uint256 _value) internal nonReentrant {
        // just delegate call to the target
        SafeERC20.safeTransfer(IERC20(eli), _to, _value);
    }

    /**
     * @dev Executes SafeERC20.safeTransferFrom on a pool token
     *
     * @dev Reentrancy safety enforced via `ReentrancyGuard.nonReentrant`
     */
    function transferEliFrom(
        address _from,
        address _to,
        uint256 _value
    ) internal nonReentrant {
        // just delegate call to the target
        SafeERC20.safeTransferFrom(IERC20(eli), _from, _to, _value);
    }
}

// File: utils/Ownable.sol

pragma solidity 0.8.2;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        address msgSender = msg.sender;
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == msg.sender, "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(
            newOwner != address(0),
            "Ownable: new owner is the zero address"
        );
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

// File: utils/ERC20.sol

pragma solidity 0.8.2;


/**
 * @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 {ERC20PresetMinterPauser}.
 *
 * 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}.
 */

// Copied from Open Zeppelin

contract ERC20 is IERC20 {
    mapping(address => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @notice Token creator is responsible for creating (minting)
     *      tokens to an arbitrary address
     * @dev Role ROLE_TOKEN_CREATOR allows minting tokens
     *      (calling `mint` function)
     */
    uint32 public constant ROLE_TOKEN_CREATOR = 0x0001_0000;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return _decimals;
    }

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

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account)
        public
        view
        virtual
        override
        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
        virtual
        override
        returns (bool)
    {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

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

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount)
        public
        virtual
        override
        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 virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, msg.sender, _allowances[sender][msg.sender] - amount);
        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
        virtual
        returns (bool)
    {
        _approve(
            msg.sender,
            spender,
            _allowances[msg.sender][spender] + 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
        virtual
        returns (bool)
    {
        _approve(
            msg.sender,
            spender,
            _allowances[msg.sender][spender] - subtractedValue
        );
        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 virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender] - amount;
        _balances[recipient] = _balances[recipient] + 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 virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply = _totalSupply + amount;
        _balances[account] = _balances[account] + 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 virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

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

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This 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 virtual {
        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 Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

// File: contracts/ElixirPoolFactory.sol

pragma solidity 0.8.2;






/**
 * @title Elixir Pool Factory
 *
 * @notice ELI Pool Factory manages Elixir Yield farming pools, provides a single
 *      public interface to access the pools, provides an interface for the pools
 *      to mint yield rewards, access pool-related info, update weights, etc.
 *
 * @notice The factory is authorized (via its owner) to register new pools, change weights
 *      of the existing pools, removing the pools (by changing their weights to zero)
 *
 * @dev The factory requires ROLE_TOKEN_CREATOR permission on the ELI token to mint yield
 *      (see `transferYieldTo` function)
 *
 * @author Pedro Bergamini, reviewed by Basil Gorin
 */
contract ElixirPoolFactory is Ownable {
    /**
     * @dev Smart contract unique identifier, a random number
     * @dev Should be regenerated each time smart contact source code is changed
     *      and changes smart contract itself is to be redeployed
     * @dev Generated using https://www.random.org/bytes/
     */
    address public eli;
    address public start;

    /// @dev Unstaking Burn Fee for each pool
    mapping(address => uint256) public unstakeBurnFee;

    /// @dev Auxiliary data structure used only in getPoolData() view function
    struct PoolData {
        // @dev pool token address (like ELI)
        address poolToken;
        // @dev pool address (like deployed core pool instance)
        address poolAddress;
        // @dev pool weight (200 for ELI pools, 800 for ELI/ETH pools - set during deployment)
        uint32 weight;
    }

    /**
     * @dev ELI/block determines yield farming reward base
     *      used by the yield pools controlled by the factory
     */
    uint192 public eliPerBlock;

    /**
     * @dev Percent of Reward Reduce Per Block
     */
    uint192 public rewardReducePercent = 97;

    /**
     * @dev The yield is distributed proportionally to pool weights;
     *      total weight is here to help in determining the proportion
     */
    uint32 public totalWeight;

    /**
     * @dev ELI/block decreases by 3% every blocks/update (set to 91252 blocks during deployment);
     *      an update is triggered by executing `updateELIPerBlock` public function
     */
    uint32 public blocksPerUpdate;

    /**
     * @dev End block is the last block when ELI/block can be decreased;
     *      it is implied that yield farming stops after that block
     */
    uint32 public endBlock;

    /** @dev Burn Address */
    address public constant burnAddress =
        0x000000000000000000000000000000000000dEaD;

    /** @dev Burn Percentage */
    uint256 public burnFee = 250; // 2.5% of Liquid Reward will be burned

    /** @dev Maximum Reward Lock Period */
    uint256 public maximumRewardLock = 365 days;
    uint256 public minimumRewardLock = 30 days;

    /**
     * @dev Each time the ELI/block ratio gets updated, the block number
     *      when the operation has occurred gets recorded into `lastRatioUpdate`
     * @dev This block number is then used to check if blocks/update `blocksPerUpdate`
     *      has passed when decreasing yield reward by 3%
     */
    uint32 public lastRatioUpdate;

    /// @dev Maps pool token address (like ELI) -> pool address (like core pool instance)
    mapping(address => address) public pools;

    /// @dev Keeps track of registered pool addresses, maps pool address -> exists flag
    mapping(address => bool) public poolExists;

    /**
     * @dev Fired in createPool() and registerPool()
     *
     * @param _by an address which executed an action
     * @param poolToken pool token address (like ELI)
     * @param poolAddress deployed pool instance address
     * @param weight pool weight
     */
    event PoolRegistered(
        address indexed _by,
        address indexed poolToken,
        address indexed poolAddress,
        uint64 weight
    );

    /**
     * @dev Fired in changePoolWeight()
     *
     * @param _by an address which executed an action
     * @param poolAddress deployed pool instance address
     * @param weight new pool weight
     */
    event WeightUpdated(
        address indexed _by,
        address indexed poolAddress,
        uint32 weight
    );

    /**
     * @dev Fired in updateELIPerBlock()
     *
     * @param _by an address which executed an action
     * @param newEliPerBlock new ELI/block value
     */
    event EliRatioUpdated(address indexed _by, uint256 newEliPerBlock);

    /**
     * @dev Creates/deploys a factory instance
     *
     * @param _eli ELI ERC20 token address
     * @param _eliPerBlock initial ELI/block value for rewards
     * @param _blocksPerUpdate how frequently the rewards gets updated (decreased by 3%), blocks
     * @param _initBlock block number to measure _blocksPerUpdate from
     * @param _endBlock block number when farming stops and rewards cannot be updated anymore
     */
    constructor(
        address _eli,
        uint192 _eliPerBlock,
        uint32 _blocksPerUpdate,
        uint32 _initBlock,
        uint32 _endBlock
    ) {
        // verify the inputs are set
        require(_eliPerBlock > 0, "ELI/block not set");
        require(_blocksPerUpdate > 0, "blocks/update not set");
        require(_initBlock > 0, "init block not set");
        require(
            _endBlock > _initBlock,
            "invalid end block: must be greater than init block"
        );

        // save the inputs into internal state variables
        eli = _eli;
        eliPerBlock = _eliPerBlock;
        blocksPerUpdate = _blocksPerUpdate;
        lastRatioUpdate = _initBlock;
        endBlock = _endBlock;
    }

    /**
     * @notice Given a pool token retrieves corresponding pool address
     *
     * @dev A shortcut for `pools` mapping
     *
     * @param poolToken pool token address (like ELI) to query pool address for
     * @return pool address for the token specified
     */
    function getPoolAddress(address poolToken) external view returns (address) {
        // read the mapping and return
        return pools[poolToken];
    }

    /**
     * @notice Reads pool information for the pool defined by its pool token address,
     *      designed to simplify integration with the front ends
     *
     * @param _poolToken pool token address to query pool information for
     * @return pool information packed in a PoolData struct
     */
    function getPoolData(address _poolToken)
        public
        view
        returns (PoolData memory)
    {
        // get the pool address from the mapping
        address poolAddr = pools[_poolToken];

        // throw if there is no pool registered for the token specified
        require(poolAddr != address(0), "pool not found");

        // read pool information from the pool smart contract
        // via the pool interface (IPool)
        address poolToken = IPool(poolAddr).poolToken();
        uint32 weight = IPool(poolAddr).weight();

        // create the in-memory structure and return it
        return
            PoolData({
                poolToken: poolToken,
                poolAddress: poolAddr,
                weight: weight
            });
    }

    /**
     * @dev Verifies if `blocksPerUpdate` has passed since last ELI/block
     *      ratio update and if ELI/block reward can be decreased by 3%
     *
     * @return true if enough time has passed and `updateELIPerBlock` can be executed
     */
    function shouldUpdateRatio() public view returns (bool) {
        // if yield farming period has ended
        if (blockNumber() > endBlock) {
            // ELI/block reward cannot be updated anymore
            return false;
        }

        // check if blocks/update (91252 blocks) have passed since last update
        return blockNumber() >= lastRatioUpdate + blocksPerUpdate;
    }

    /**
     * @dev Creates a core pool (ElixirCorePool) and registers it within the factory
     *
     * @dev Can be executed by the pool factory owner only
     *
     * @param poolToken pool token address (like ELI, or ELI/ETH pair)
     * @param initBlock init block to be used for the pool created
     * @param weight weight of the pool to be created
     */
    function createPool(
        address poolToken,
        uint64 initBlock,
        uint32 weight
    ) external virtual onlyOwner {
        // create/deploy new core pool instance
        IPool pool = new ElixirCorePool(
            eli,
            IFactory(address(this)),
            poolToken,
            initBlock,
            weight
        );

        // register it within a factory
        registerPool(address(pool));
    }

    /**
     * @dev Registers an already deployed pool instance within the factory
     *
     * @dev Can be executed by the pool factory owner only
     *
     * @param poolAddr address of the already deployed pool instance
     */
    function registerPool(address poolAddr) public onlyOwner {
        // read pool information from the pool smart contract
        // via the pool interface (IPool)
        address poolToken = IPool(poolAddr).poolToken();
        uint32 weight = IPool(poolAddr).weight();

        // ensure that the pool is not already registered within the factory
        require(
            pools[poolToken] == address(0),
            "this pool is already registered"
        );

        // create pool structure, register it within the factory
        pools[poolToken] = poolAddr;
        poolExists[poolAddr] = true;
        // update total pool weight of the factory
        totalWeight += weight;

        // emit an event
        emit PoolRegistered(msg.sender, poolToken, poolAddr, weight);
    }

    /**
     * @notice Decreases ELI/block reward by 3%, can be executed
     *      no more than once per `blocksPerUpdate` blocks
     */
    function updateELIPerBlock() external {
        // checks if ratio can be updated i.e. if blocks/update (91252 blocks) have passed
        require(shouldUpdateRatio(), "too frequent");

        // decreases ELI/block reward by RewardReducePercent
        eliPerBlock = (eliPerBlock * rewardReducePercent) / 100;

        // set current block as the last ratio update block
        lastRatioUpdate = uint32(blockNumber());

        // emit an event
        emit EliRatioUpdated(msg.sender, eliPerBlock);
    }

    /**
     * @dev Transfer ELI tokens; executed by ELI Pool only
     *
     * @dev Requires factory to have ROLE_TOKEN_CREATOR permission
     *      on the ELI ERC20 token instance
     *
     * @param _to an address to mint tokens to
     * @param _amount amount of ELI tokens to mint
     */
    function transferYieldTo(
        address _to,
        uint256 _amount,
        uint256 _liquidRewardAmount
    ) external {
        // verify that sender is a pool registered withing the factory
        require(poolExists[msg.sender], "access denied");

        uint256 burnAmount = (_liquidRewardAmount * burnFee) / 10000;

        require(_amount > burnAmount, "burn amount is big");

        uint256 rewardAmount = _amount - burnAmount;

        IERC20(eli).transfer(burnAddress, burnAmount);
        // send ELI tokens as required
        IERC20(eli).transfer(_to, rewardAmount);
    }

    /**
     * @dev Changes the weight of the pool;
     *      executed by the pool itself or by the factory owner
     *
     * @param poolAddr address of the pool to change weight for
     * @param weight new weight value to set to
     */
    function changePoolWeight(address poolAddr, uint32 weight)
        external
        onlyOwner
    {
        // recalculate total weight
        totalWeight = totalWeight + weight - IPool(poolAddr).weight();

        // set the new pool weight
        IPool(poolAddr).setWeight(weight);

        // emit an event
        emit WeightUpdated(msg.sender, poolAddr, weight);
    }

    /** @dev set burn fee function */
    function setBurnFee(uint256 _burnFee) external onlyOwner {
        burnFee = _burnFee;
    }

    /** @dev set reward lock period */

    function setRewardLockLimit(uint256 _minLock, uint256 _maxLock)
        external
        onlyOwner
    {
        minimumRewardLock = _minLock;
        maximumRewardLock = _maxLock;
    }

    function setAddresses(address _eli, address _start) external onlyOwner {
        eli = _eli;
        start = _start;
    }

    function setUnstakeBurnFee(address _poolToken, uint256 _burnFee)
        external
        onlyOwner
    {
        unstakeBurnFee[_poolToken] = _burnFee;
    }

    function setGeneralInfo(
        address _eli,
        uint192 _eliPerBlock,
        uint192 _rewardReducePercent,
        uint32 _blocksPerUpdate,
        uint32 _initBlock,
        uint32 _endBlock
    ) external onlyOwner {
        // save the inputs into internal state variables
        eli = _eli;
        eliPerBlock = _eliPerBlock;
        rewardReducePercent = _rewardReducePercent;
        blocksPerUpdate = _blocksPerUpdate;
        lastRatioUpdate = _initBlock;
        endBlock = _endBlock;
    }

    function emergencyWithdraw(address _to) external onlyOwner {
        IERC20(eli).transfer(_to, IERC20(eli).balanceOf(address(this)));
    }

    /**
     * @dev Testing time-dependent functionality is difficult and the best way of
     *      doing it is to override block number in helper test smart contracts
     *
     * @return `block.number` in mainnet, custom values in testnets (if overridden)
     */
    function blockNumber() public view virtual returns (uint256) {
        // return current block number
        return block.number;
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_eli","type":"address"},{"internalType":"uint192","name":"_eliPerBlock","type":"uint192"},{"internalType":"uint32","name":"_blocksPerUpdate","type":"uint32"},{"internalType":"uint32","name":"_initBlock","type":"uint32"},{"internalType":"uint32","name":"_endBlock","type":"uint32"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":false,"internalType":"uint256","name":"newEliPerBlock","type":"uint256"}],"name":"EliRatioUpdated","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":"_by","type":"address"},{"indexed":true,"internalType":"address","name":"poolToken","type":"address"},{"indexed":true,"internalType":"address","name":"poolAddress","type":"address"},{"indexed":false,"internalType":"uint64","name":"weight","type":"uint64"}],"name":"PoolRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":true,"internalType":"address","name":"poolAddress","type":"address"},{"indexed":false,"internalType":"uint32","name":"weight","type":"uint32"}],"name":"WeightUpdated","type":"event"},{"inputs":[],"name":"blockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blocksPerUpdate","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddr","type":"address"},{"internalType":"uint32","name":"weight","type":"uint32"}],"name":"changePoolWeight","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolToken","type":"address"},{"internalType":"uint64","name":"initBlock","type":"uint64"},{"internalType":"uint32","name":"weight","type":"uint32"}],"name":"createPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eli","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"eliPerBlock","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endBlock","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolToken","type":"address"}],"name":"getPoolAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_poolToken","type":"address"}],"name":"getPoolData","outputs":[{"components":[{"internalType":"address","name":"poolToken","type":"address"},{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint32","name":"weight","type":"uint32"}],"internalType":"struct ElixirPoolFactory.PoolData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRatioUpdate","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maximumRewardLock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumRewardLock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"poolExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddr","type":"address"}],"name":"registerPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardReducePercent","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_eli","type":"address"},{"internalType":"address","name":"_start","type":"address"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_burnFee","type":"uint256"}],"name":"setBurnFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_eli","type":"address"},{"internalType":"uint192","name":"_eliPerBlock","type":"uint192"},{"internalType":"uint192","name":"_rewardReducePercent","type":"uint192"},{"internalType":"uint32","name":"_blocksPerUpdate","type":"uint32"},{"internalType":"uint32","name":"_initBlock","type":"uint32"},{"internalType":"uint32","name":"_endBlock","type":"uint32"}],"name":"setGeneralInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minLock","type":"uint256"},{"internalType":"uint256","name":"_maxLock","type":"uint256"}],"name":"setRewardLockLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_poolToken","type":"address"},{"internalType":"uint256","name":"_burnFee","type":"uint256"}],"name":"setUnstakeBurnFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shouldUpdateRatio","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"start","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalWeight","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_liquidRewardAmount","type":"uint256"}],"name":"transferYieldTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unstakeBurnFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateELIPerBlock","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6080604052600580546001600160c01b031916606117905560fa6007556301e1338060085562278d006009553480156200003857600080fd5b5060405162005a4538038062005a458339810160408190526200005b91620002b1565b600080546001600160a01b031916339081178255604051909182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506000846001600160c01b031611620000ef5760405162461bcd60e51b81526020600482015260116024820152701153124bd89b1bd8dac81b9bdd081cd95d607a1b60448201526064015b60405180910390fd5b60008363ffffffff1611620001475760405162461bcd60e51b815260206004820152601560248201527f626c6f636b732f757064617465206e6f742073657400000000000000000000006044820152606401620000e6565b60008263ffffffff1611620001945760405162461bcd60e51b81526020600482015260126024820152711a5b9a5d08189b1bd8dac81b9bdd081cd95d60721b6044820152606401620000e6565b8163ffffffff168163ffffffff16116200020c5760405162461bcd60e51b815260206004820152603260248201527f696e76616c696420656e6420626c6f636b3a206d7573742062652067726561746044820152716572207468616e20696e697420626c6f636b60701b6064820152608401620000e6565b600180546001600160a01b0319166001600160a01b039690961695909517909455600480546001600160c01b0319166001600160c01b039490941693909317909255600580546001600160e01b0316600160e01b63ffffffff93841602179055600a805463ffffffff1990811693831693909317905560068054909216921691909117905562000339565b805163ffffffff81168114620002ac57600080fd5b919050565b600080600080600060a08688031215620002c9578081fd5b85516001600160a01b0381168114620002e0578182fd5b60208701519095506001600160c01b0381168114620002fd578182fd5b93506200030d6040870162000297565b92506200031d6060870162000297565b91506200032d6080870162000297565b90509295509295909350565b6156fc80620003496000396000f3fe60806040523480156200001157600080fd5b50600436106200022c5760003560e01c806396c82e571162000135578063d7302bb911620000bd578063f10885901162000087578063f10885901462000524578063f2fde38b146200053b578063fce589d81462000552578063fcee95d4146200055c578063fcf4e7c51462000566576200022c565b8063d7302bb914620004d0578063e6cbfd0214620004e7578063eb89d59414620004f8578063ed6453e9146200050c576200022c565b8063abd9084611620000ff578063abd908461462000477578063bb9900eb146200048e578063be9a655514620004a5578063d078e14114620004b9576200022c565b806396c82e5714620004125780639e50389b146200042a5780639f1dc9bd1462000441578063a4063dbc146200044b576200022c565b806357e871e711620001b957806371c41d93116200018357806371c41d9314620003c85780638172407514620003d25780638da5cb5b14620003e957806390107afe14620003fb576200022c565b806357e871e714620003935780636ff1c9bc146200039d57806370d5ae0514620003b4578063715018a614620003be576200022c565b806313d21cdf11620001fb57806313d21cdf14620002c857806316c5091b14620003165780631e1c6a0714620003435780634bf2c7c9146200037a576200022c565b8063065458c41462000231578063083c632314620002675780630d819e70146200028e5780631228cbee1462000298575b600080fd5b62000254620002423660046200148c565b60036020526000908152604090205481565b6040519081526020015b60405180910390f35b600654620002789063ffffffff1681565b60405163ffffffff90911681526020016200025e565b6200025460095481565b620002af620002a93660046200148c565b6200057a565b6040516001600160a01b0390911681526020016200025e565b620002df620002d93660046200148c565b6200059b565b6040805182516001600160a01b039081168252602080850151909116908201529181015163ffffffff16908201526060016200025e565b6005546200032a906001600160c01b031681565b6040516001600160c01b0390911681526020016200025e565b62000369620003543660046200148c565b600c6020526000908152604090205460ff1681565b60405190151581526020016200025e565b620003916200038b366004620016ad565b6200073b565b005b620002546200077d565b62000391620003ae3660046200148c565b62000782565b620002af61dead81565b62000391620008ce565b6200025460085481565b62000391620003e3366004620015fd565b62000955565b6000546001600160a01b0316620002af565b620003916200040c366004620014d1565b62000af6565b6005546200027890600160c01b900463ffffffff1681565b620003916200043b3660046200162f565b62000b61565b6200036962000c32565b620002af6200045c3660046200148c565b600b602052600090815260409020546001600160a01b031681565b62000391620004883660046200148c565b62000c91565b620003916200049f366004620015c6565b62000f17565b600254620002af906001600160a01b031681565b62000391620004ca36600462001598565b620010fd565b62000391620004e1366004620016df565b62001156565b600a54620002789063ffffffff1681565b6004546200032a906001600160c01b031681565b6005546200027890600160e01b900463ffffffff1681565b62000391620005353660046200150e565b6200119e565b620003916200054c3660046200148c565b6200126b565b6200025460075481565b620003916200136a565b600154620002af906001600160a01b031681565b6001600160a01b038082166000908152600b6020526040902054165b919050565b60408051606081018252600080825260208201819052918101919091526001600160a01b038083166000908152600b60205260409020541680620006175760405162461bcd60e51b815260206004820152600e60248201526d1c1bdbdb081b9bdd08199bdd5b9960921b60448201526064015b60405180910390fd5b6000816001600160a01b031663cbdf382c6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200065357600080fd5b505afa15801562000668573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200068e9190620014b2565b90506000826001600160a01b031663a1aab33f6040518163ffffffff1660e01b815260040160206040518083038186803b158015620006cc57600080fd5b505afa158015620006e1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000707919062001701565b604080516060810182526001600160a01b03948516815294909316602085015263ffffffff16918301919091525092915050565b336200074f6000546001600160a01b031690565b6001600160a01b031614620007785760405162461bcd60e51b81526004016200060e9062001720565b600755565b435b90565b33620007966000546001600160a01b031690565b6001600160a01b031614620007bf5760405162461bcd60e51b81526004016200060e9062001720565b6001546040516370a0823160e01b81523060048201526001600160a01b039091169063a9059cbb90839083906370a082319060240160206040518083038186803b1580156200080d57600080fd5b505afa15801562000822573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620008489190620016c6565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381600087803b1580156200088f57600080fd5b505af1158015620008a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620008ca91906200168b565b5050565b33620008e26000546001600160a01b031690565b6001600160a01b0316146200090b5760405162461bcd60e51b81526004016200060e9062001720565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b33620009696000546001600160a01b031690565b6001600160a01b031614620009925760405162461bcd60e51b81526004016200060e9062001720565b816001600160a01b031663a1aab33f6040518163ffffffff1660e01b815260040160206040518083038186803b158015620009cc57600080fd5b505afa158015620009e1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000a07919062001701565b60055462000a24908390600160c01b900463ffffffff1662001755565b62000a3091906200182e565b6005805463ffffffff60c01b1916600160c01b63ffffffff93841602179055604051634087aeb760e01b815290821660048201526001600160a01b03831690634087aeb790602401600060405180830381600087803b15801562000a9357600080fd5b505af115801562000aa8573d6000803e3d6000fd5b505060405163ffffffff841681526001600160a01b03851692503391507fa2aeff1f58da6bc6dec93ece72a8664ae7f764789ec6fe9eeab5bd12ad39b43c9060200160405180910390a35050565b3362000b0a6000546001600160a01b031690565b6001600160a01b03161462000b335760405162461bcd60e51b81526004016200060e9062001720565b600180546001600160a01b039384166001600160a01b03199182161790915560028054929093169116179055565b3362000b756000546001600160a01b031690565b6001600160a01b03161462000b9e5760405162461bcd60e51b81526004016200060e9062001720565b6001546040516000916001600160a01b031690309086908690869062000bc49062001466565b6001600160a01b039586168152938516602085015293909116604083015267ffffffffffffffff16606082015263ffffffff909116608082015260a001604051809103906000f08015801562000c1e573d6000803e3d6000fd5b50905062000c2c8162000c91565b50505050565b60065460009063ffffffff1662000c486200077d565b111562000c58575060006200077f565b600554600a5462000c7a9163ffffffff600160e01b9091048116911662001755565b63ffffffff1662000c8a6200077d565b1015905090565b3362000ca56000546001600160a01b031690565b6001600160a01b03161462000cce5760405162461bcd60e51b81526004016200060e9062001720565b6000816001600160a01b031663cbdf382c6040518163ffffffff1660e01b815260040160206040518083038186803b15801562000d0a57600080fd5b505afa15801562000d1f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000d459190620014b2565b90506000826001600160a01b031663a1aab33f6040518163ffffffff1660e01b815260040160206040518083038186803b15801562000d8357600080fd5b505afa15801562000d98573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000dbe919062001701565b6001600160a01b038381166000908152600b6020526040902054919250161562000e2b5760405162461bcd60e51b815260206004820152601f60248201527f7468697320706f6f6c20697320616c726561647920726567697374657265640060448201526064016200060e565b6001600160a01b038281166000908152600b6020908152604080832080546001600160a01b0319169488169485179055928252600c905220805460ff191660011790556005805482919060189062000e92908490600160c01b900463ffffffff1662001755565b92506101000a81548163ffffffff021916908363ffffffff160217905550826001600160a01b0316826001600160a01b0316336001600160a01b03167f3907c92c9819fbb942b4c7c293d5ce6893312ea85adecf01ae1188f469dd73de8460405162000f0a919063ffffffff91909116815260200190565b60405180910390a4505050565b336000908152600c602052604090205460ff1662000f685760405162461bcd60e51b815260206004820152600d60248201526c1858d8d95cdcc819195b9a5959609a1b60448201526064016200060e565b60006127106007548362000f7d9190620017f2565b62000f899190620017a9565b905080831162000fd15760405162461bcd60e51b81526020600482015260126024820152716275726e20616d6f756e742069732062696760701b60448201526064016200060e565b600062000fdf828562001814565b60015460405163a9059cbb60e01b815261dead6004820152602481018590529192506001600160a01b03169063a9059cbb90604401602060405180830381600087803b1580156200102f57600080fd5b505af115801562001044573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200106a91906200168b565b5060015460405163a9059cbb60e01b81526001600160a01b038781166004830152602482018490529091169063a9059cbb90604401602060405180830381600087803b158015620010ba57600080fd5b505af1158015620010cf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620010f591906200168b565b505050505050565b33620011116000546001600160a01b031690565b6001600160a01b0316146200113a5760405162461bcd60e51b81526004016200060e9062001720565b6001600160a01b03909116600090815260036020526040902055565b336200116a6000546001600160a01b031690565b6001600160a01b031614620011935760405162461bcd60e51b81526004016200060e9062001720565b600991909155600855565b33620011b26000546001600160a01b031690565b6001600160a01b031614620011db5760405162461bcd60e51b81526004016200060e9062001720565b600180546001600160a01b0319166001600160a01b039790971696909617909555600480546001600160c01b03199081166001600160c01b03968716179091556005805490911693909416929092176001600160e01b0316600160e01b63ffffffff9283160217909255600a805463ffffffff199081169284169290921790556006805490911691909216179055565b336200127f6000546001600160a01b031690565b6001600160a01b031614620012a85760405162461bcd60e51b81526004016200060e9062001720565b6001600160a01b0381166200130f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016200060e565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6200137462000c32565b620013b15760405162461bcd60e51b815260206004820152600c60248201526b1d1bdbc8199c995c5d595b9d60a21b60448201526064016200060e565b600554600454606491620013d2916001600160c01b039182169116620017c0565b620013de919062001780565b600480546001600160c01b0319166001600160c01b0392909216919091179055620014086200077d565b600a805463ffffffff191663ffffffff929092169190911790556004546040516001600160c01b03909116815233907f1afea7e95d60c3463fa1b490257be0fca8d89bd3a6f8a1e45fd99e5f0220a0a99060200160405180910390a2565b613e1880620018af83390190565b80356001600160c01b03811681146200059657600080fd5b6000602082840312156200149e578081fd5b8135620014ab8162001882565b9392505050565b600060208284031215620014c4578081fd5b8151620014ab8162001882565b60008060408385031215620014e4578081fd5b8235620014f18162001882565b91506020830135620015038162001882565b809150509250929050565b60008060008060008060c0878903121562001527578182fd5b8635620015348162001882565b9550620015446020880162001474565b9450620015546040880162001474565b9350606087013562001566816200189b565b9250608087013562001578816200189b565b915060a08701356200158a816200189b565b809150509295509295509295565b60008060408385031215620015ab578182fd5b8235620015b88162001882565b946020939093013593505050565b600080600060608486031215620015db578283fd5b8335620015e88162001882565b95602085013595506040909401359392505050565b6000806040838503121562001610578182fd5b82356200161d8162001882565b9150602083013562001503816200189b565b60008060006060848603121562001644578283fd5b8335620016518162001882565b9250602084013567ffffffffffffffff811681146200166e578283fd5b9150604084013562001680816200189b565b809150509250925092565b6000602082840312156200169d578081fd5b81518015158114620014ab578182fd5b600060208284031215620016bf578081fd5b5035919050565b600060208284031215620016d8578081fd5b5051919050565b60008060408385031215620016f2578182fd5b50508035926020909101359150565b60006020828403121562001713578081fd5b8151620014ab816200189b565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b600063ffffffff80831681851680830382111562001777576200177762001856565b01949350505050565b60006001600160c01b03838116806200179d576200179d6200186c565b92169190910492915050565b600082620017bb57620017bb6200186c565b500490565b60006001600160c01b0382811684821681151582840482111615620017e957620017e962001856565b02949350505050565b60008160001904831182151516156200180f576200180f62001856565b500290565b60008282101562001829576200182962001856565b500390565b600063ffffffff838116908316818110156200184e576200184e62001856565b039392505050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6001600160a01b03811681146200189857600080fd5b50565b63ffffffff811681146200189857600080fdfe6080604052620f42406008819055620186a060095562000021906002620002db565b600a5564e8d4a51000600b553480156200003a57600080fd5b5060405162003e1838038062003e188339810160408190526200005d916200024b565b600160005584848484846001600160a01b038416620000c35760405162461bcd60e51b815260206004820152601f60248201527f454c4958495220506f6f6c206663742061646472657373206e6f74207365740060448201526064015b60405180910390fd5b6001600160a01b0383166200011b5760405162461bcd60e51b815260206004820152601a60248201527f706f6f6c20746f6b656e2061646472657373206e6f74207365740000000000006044820152606401620000ba565b6000826001600160401b0316116200016b5760405162461bcd60e51b81526020600482015260126024820152711a5b9a5d08189b1bd8dac81b9bdd081cd95d60721b6044820152606401620000ba565b60008163ffffffff1611620001c35760405162461bcd60e51b815260206004820152601360248201527f706f6f6c20776569676874206e6f7420736574000000000000000000000000006044820152606401620000ba565b600180546001600160a01b039687166001600160a01b0319918216179091556004805495871695821695909517909455600580546001600160401b03909316600160c01b026001600160c01b0363ffffffff909316600160a01b0263ffffffff60a01b1995909716939095169290921792909216939093171617905550620003209350505050565b600080600080600060a0868803121562000263578081fd5b8551620002708162000307565b6020870151909550620002838162000307565b6040870151909450620002968162000307565b60608701519093506001600160401b0381168114620002b3578182fd5b608087015190925063ffffffff81168114620002cd578182fd5b809150509295509295909350565b60008160001904831182151516156200030257634e487b7160e01b81526011600452602481fd5b500290565b6001600160a01b03811681146200031d57600080fd5b50565b613ae880620003306000396000f3fe608060405234801561001057600080fd5b506004361061021b5760003560e01c8063a156dc2811610125578063e8d3cad5116100ad578063fbfa77cf1161007c578063fbfa77cf1461055f578063fc27076f14610578578063fcf4e7c51461058b578063feef6c681461059e578063fff6cae9146105b15761021b565b8063e8d3cad51461050f578063f44e781b1461053b578063f9fc0d071461054e578063fa213bd6146105565761021b565b8063beb0ed6c116100f4578063beb0ed6c146104c4578063c45a0155146104cd578063cbdf382c146104e0578063ce111541146104f3578063de31f7cb146104fc5761021b565b8063a156dc28146103e3578063a1aab33f146103f6578063a7a38f0b14610422578063a87430ba1461044d5761021b565b80635478f468116101a857806370a082311161017757806370a082311461036e578063806fcedf146103975780638e169d47146103aa578063952e68cf146103bd5780639e2c8a5b146103d05761021b565b80635478f4681461034557806356b176e11461034d57806357e871e7146103555780636817031b1461035b5761021b565b80632726b506116101ef5780632726b5061461027757806329eb5f2c146102e75780633021a560146103195780634087aeb71461032c5780634ce0f9a61461033f5761021b565b8062ed1b0814610220578063054723581461024657806315188a1b1461024f5780631984db9914610264575b600080fd5b61023361022e36600461367c565b6105b9565b6040519081526020015b60405180910390f35b610233600d5481565b61026261025d366004613888565b61097f565b005b61023361027236600461367c565b6109a2565b61028a61028536600461375a565b610d4d565b60405161023d9190600060c08201905082518252602083015160208301526040830151604083015260608301516001600160401b038082166060850152806080860151166080850152505060a0830151151560a083015292915050565b60055461030190600160c01b90046001600160401b031681565b6040516001600160401b03909116815260200161023d565b610262610327366004613806565b610e2a565b61026261033a3660046138b3565b610f4c565b42610233565b600254610233565b610262610fe9565b43610233565b61026261036936600461367c565b6110a1565b61023361037c36600461367c565b6001600160a01b031660009081526003602052604090205490565b6102626103a5366004613785565b611200565b6102336103b8366004613836565b611468565b6102626103cb366004613888565b611489565b6102626103de366004613836565b6114dd565b6102336103f1366004613836565b6114e8565b60055461040d90600160a01b900463ffffffff1681565b60405163ffffffff909116815260200161023d565b610435610430366004613806565b6114f9565b6040516001600160a01b03909116815260200161023d565b61049761045b36600461367c565b60036020528060005260406000206000915090508060000154908060010154908060020154908060030154908060040154908060050154905086565b604080519687526020870195909552938501929092526060840152608083015260a082015260c00161023d565b61023360075481565b600454610435906001600160a01b031681565b600554610435906001600160a01b031681565b610233600e5481565b61026261050a366004613857565b611523565b61023361051d36600461367c565b6001600160a01b031660009081526003602052604090206006015490565b6102626105493660046136b4565b6115e1565b6102626116b9565b61023360065481565b600c54610435906201000090046001600160a01b031681565b61023361058636600461367c565b6116c4565b600154610435906001600160a01b031681565b6102626105ac3660046136ec565b6117e8565b610262611919565b6001600160a01b038116600090815260036020526040812080546106595760048054604080516371c41d9360e01b815290516001600160a01b03909216926371c41d93928282019260209290829003018186803b15801561061957600080fd5b505afa15801561062d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610651919061381e565b91505061097a565b6000805b600683015482101561070b57600083600601838154811061068e57634e487b7160e01b600052603260045260246000fd5b906000526020600020906004020190508060030160109054906101000a900460ff166106f85760038101546106d6906001600160401b0380821691600160401b900416613a06565b81546106eb916001600160401b0316906139d0565b6106f59083613998565b91505b508161070381613a5a565b92505061065d565b825460009061071a90836139b0565b60048054604080516371c41d9360e01b815290516001600160a01b03909216926371c41d93928282019260209290829003018186803b15801561075c57600080fd5b505afa158015610770573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610794919061381e565b61079e91906139ef565b9050600460009054906101000a90046001600160a01b03166001600160a01b0316630d819e706040518163ffffffff1660e01b815260040160206040518083038186803b1580156107ee57600080fd5b505afa158015610802573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610826919061381e565b8110156108b157600480546040805162d819e760e41b815290516001600160a01b0390921692630d819e70928282019260209290829003018186803b15801561086e57600080fd5b505afa158015610882573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a6919061381e565b94505050505061097a565b60048054604080516371c41d9360e01b815290516001600160a01b03909216926371c41d93928282019260209290829003018186803b1580156108f357600080fd5b505afa158015610907573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061092b919061381e565b8111156109745760048054604080516371c41d9360e01b815290516001600160a01b03909216926371c41d93928282019260209290829003018186803b15801561086e57600080fd5b93505050505b919050565b61098761191d565b610992336000611d15565b5061099e338383611d63565b5050565b6005546000908190600160c01b90046001600160401b03166109c14390565b1180156109cf575060075415155b15610c29576000600460009054906101000a90046001600160a01b03166001600160a01b031663083c63236040518163ffffffff1660e01b815260040160206040518083038186803b158015610a2457600080fd5b505afa158015610a38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a5c91906138cf565b63ffffffff1690506000814311610a9657600554600160c01b90046001600160401b0316610a874390565b610a9191906139ef565b610ab3565b600554610ab390600160c01b90046001600160401b0316836139ef565b90506000600460009054906101000a90046001600160a01b03166001600160a01b03166396c82e576040518163ffffffff1660e01b815260040160206040518083038186803b158015610b0557600080fd5b505afa158015610b19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3d91906138cf565b63ffffffff16600460009054906101000a90046001600160a01b03166001600160a01b031663eb89d5946040518163ffffffff1660e01b815260040160206040518083038186803b158015610b9157600080fd5b505afa158015610ba5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc991906137df565b6005546001600160c01b039190911690610bf090600160a01b900463ffffffff16856139d0565b610bfa91906139d0565b610c0491906139b0565b9050600654610c15826007546114e8565b610c1f9190613998565b9350505050610c2e565b506006545b6001600160a01b0383166000908152600360208181526040808420815160e0810183528154815260018201548185015260028201548184015293810154606085015260048101546080850152600581015460a0850152600681018054835181860281018601909452808452919360c08601939290879084015b82821015610d245760008481526020908190206040805160c081018252600486029092018054835260018082015484860152600282015492840192909252600301546001600160401b038082166060850152600160401b8204166080840152600160801b900460ff16151560a08301529083529092019101610ca7565b5050505081525050905060008160800151610d43836040015185611468565b61097491906139ef565b6040805160c08101825260008082526020808301829052828401829052606083018290526080830182905260a083018290526001600160a01b03861682526003905291909120600601805483908110610db657634e487b7160e01b600052603260045260246000fd5b60009182526020918290206040805160c0810182526004909302909101805483526001810154938301939093526002830154908201526003909101546001600160401b038082166060840152600160401b820416608083015260ff600160801b90910416151560a082015290505b92915050565b600c546201000090046001600160a01b03163314610e635760405162461bcd60e51b8152600401610e5a9061393a565b60405180910390fd5b80610e6d57610f49565b600060075411610eb55760405162461bcd60e51b81526020600482015260136024820152721e995c9bc81b1bd8dada5b99c81dd95a59da1d606a1b6044820152606401610e5a565b610ec03330836120b7565b610ecc816007546114e8565b600d6000828254610edd9190613998565b90915550506001546005546001600160a01b0390811691161415610f135780600e6000828254610f0d9190613998565b90915550505b60405181815233907fe1745dfad8f400852fcec0e4b23dabb3b55a98c67df52ee99c5385887277d72f9060200160405180910390a25b50565b6004546001600160a01b03163314610f765760405162461bcd60e51b8152600401610e5a9061393a565b60055460408051600160a01b90920463ffffffff90811683528316602083015233917f06555fe9dc8cbe328585a0c60ae1b7aafe71c28a706c2769d6cb4ee6e3e44e46910160405180910390a26005805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b6004805460408051638da5cb5b60e01b815290516001600160a01b0390921692638da5cb5b928282019260209290829003018186803b15801561102b57600080fd5b505afa15801561103f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110639190613698565b6001600160a01b0316336001600160a01b0316146110935760405162461bcd60e51b8152600401610e5a9061393a565b61109f60026000613633565b565b6004805460408051638da5cb5b60e01b8152905133936001600160a01b0390931692638da5cb5b92808201926020929091829003018186803b1580156110e657600080fd5b505afa1580156110fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061111e9190613698565b6001600160a01b0316146111445760405162461bcd60e51b8152600401610e5a9061393a565b6001600160a01b0381166111875760405162461bcd60e51b815260206004820152600a6024820152691e995c9bc81a5b9c1d5d60b21b6044820152606401610e5a565b600c54604080516001600160a01b036201000090930483168152918316602083015233917f2afec66505e0ceed692012e3833f6609d4933ded34732135bc05f28423744065910160405180910390a2600c80546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b60048054604051631e1c6a0760e01b815233928101929092526001600160a01b031690631e1c6a079060240160206040518083038186803b15801561124457600080fd5b505afa158015611258573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127c91906137bf565b6112985760405162461bcd60e51b8152600401610e5a9061393a565b6112a061191d565b6001600160a01b03841660009081526003602052604090208054156112cc576112ca856000611d15565b505b6000600a54856112dc91906139d0565b905060006040518060c001604052808781526020018381526020018681526020016113044290565b6001600160401b0316815260200161131c8642613998565b6001600160401b03168152602001600115158152509050858360000160008282546113479190613998565b92505081905550818360020160008282546113629190613998565b9091555050600683018054600181810183556000928352602080842085516004909402019283558401519082015560408301516002820155606083015160039091018054608085015160a08601511515600160801b0260ff60801b196001600160401b03928316600160401b0267ffffffffffffffff60401b199390961667ffffffffffffffff199094169390931791909116939093171691909117905560078054849290611412908490613998565b925050819055506114298360020154600654611468565b60048401556002830154600d546114409190611468565b836005018190555085600e600082825461145a9190613998565b909155505050505050505050565b600b5460009061147883856139d0565b61148291906139b0565b9392505050565b611497338383600080612101565b5050600280546001810182556000919091527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0180546001600160a01b03191633179055565b61149733838361215f565b600081600b548461147891906139d0565b6002818154811061150957600080fd5b6000918252602090912001546001600160a01b0316905081565b6004805460408051638da5cb5b60e01b815290516001600160a01b0390921692638da5cb5b928282019260209290829003018186803b15801561156557600080fd5b505afa158015611579573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061159d9190613698565b6001600160a01b0316336001600160a01b0316146115cd5760405162461bcd60e51b8152600401610e5a9061393a565b600b93909355600a91909155600855600955565b6004805460408051638da5cb5b60e01b815290516001600160a01b0390921692638da5cb5b928282019260209290829003018186803b15801561162357600080fd5b505afa158015611637573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061165b9190613698565b6001600160a01b0316336001600160a01b03161461168b5760405162461bcd60e51b8152600401610e5a9061393a565b600480546001600160a01b039384166001600160a01b03199182161790915560058054929093169116179055565b610f49336001611d15565b6001600160a01b0381166000908152600360208181526040808420815160e0810183528154815260018201548185015260028201548184015293810154606085015260048101546080850152600581015460a0850152600681018054835181860281018601909452808452869594929360c086019390929190879084015b828210156117bf5760008481526020908190206040805160c081018252600486029092018054835260018082015484860152600282015492840192909252600301546001600160401b038082166060850152600160401b8204166080840152600160801b900460ff16151560a08301529083529092019101611742565b505050508152505090508060a001516117de8260400151600d54611468565b61148291906139ef565b600c54610100900460ff16806118015750600c5460ff16155b6118645760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610e5a565b600c54610100900460ff1615801561188f57600c805460ff1961ff0019909116610100171660011790555b600180546001600160a01b03199081166001600160a01b03808a1691909117909255600480548216838916179055600580549091169186169190911763ffffffff60a01b1916600160a01b63ffffffff851602176001600160c01b0316600160c01b6001600160401b03861602179055801561191157600c805461ff00191690555b505050505050565b61109f5b6004805460408051639f1dc9bd60e01b815290516001600160a01b0390921692639f1dc9bd928282019260209290829003018186803b15801561195f57600080fd5b505afa158015611973573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199791906137bf565b156119f8576004805460408051633f3ba57560e21b815290516001600160a01b039092169263fcee95d492828201926000929082900301818387803b1580156119df57600080fd5b505af11580156119f3573d6000803e3d6000fd5b505050505b6000600460009054906101000a90046001600160a01b03166001600160a01b031663083c63236040518163ffffffff1660e01b815260040160206040518083038186803b158015611a4857600080fd5b505afa158015611a5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8091906138cf565b60055463ffffffff919091169150600160c01b90046001600160401b03168111611aaa575061109f565b600554600160c01b90046001600160401b0316611ac44390565b11611acf575061109f565b600754611afc5750600580546001600160c01b0316600160c01b436001600160401b03160217905561109f565b6000814311611b0b5743611b0d565b815b600554909150600090611b3090600160c01b90046001600160401b0316836139ef565b90506000600460009054906101000a90046001600160a01b03166001600160a01b031663eb89d5946040518163ffffffff1660e01b815260040160206040518083038186803b158015611b8257600080fd5b505afa158015611b96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bba91906137df565b6001600160c01b031690506000600460009054906101000a90046001600160a01b03166001600160a01b03166396c82e576040518163ffffffff1660e01b815260040160206040518083038186803b158015611c1557600080fd5b505afa158015611c29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4d91906138cf565b60055463ffffffff91821691600160a01b90910416611c6c84866139d0565b611c7691906139d0565b611c8091906139b0565b9050611c8e816007546114e8565b60066000828254611c9f9190613998565b9091555050600580546001600160c01b0316600160c01b6001600160401b03878116820292909217928390556006546040805191825291909304909116602083015233917f5ffbf9ce09d035b92503aad17a31b3d37ca5cd887b63701ddc2200be77d9ccc7910160405180910390a25050505050565b6000611d20836122b7565b611d2a838361245d565b6001546005549192506001600160a01b0391821691161415610e245780600e6000828254611d589190613998565b909155505092915050565b42816001600160401b031611611dbb5760405162461bcd60e51b815260206004820152601c60248201527f6c6f636b2073686f756c6420626520696e2074686520667574757265000000006044820152606401610e5a565b6001600160a01b038316600090815260036020526040812060068101805491929185908110611dfa57634e487b7160e01b600052603260045260246000fd5b906000526020600020906004020190508060030160089054906101000a90046001600160401b03166001600160401b0316836001600160401b031611611e755760405162461bcd60e51b815260206004820152601060248201526f696e76616c6964206e6577206c6f636b60801b6044820152606401610e5a565b60038101546001600160401b0316611f10576301e13380611e9f426001600160401b0386166139ef565b1115611eed5760405162461bcd60e51b815260206004820152601b60248201527f6d6178206c6f636b20706572696f6420697320333635206461797300000000006044820152606401610e5a565b60038101805467ffffffffffffffff1916426001600160401b0316179055611f85565b60038101546301e1338090611f2e906001600160401b031685613a06565b6001600160401b03161115611f855760405162461bcd60e51b815260206004820152601b60248201527f6d6178206c6f636b20706572696f6420697320333635206461797300000000006044820152606401610e5a565b60038101805467ffffffffffffffff60401b1916600160401b6001600160401b03868116820292909217928390558354600854600094919390926301e13380928492611fd692828216920416613a06565b6001600160401b0316611fe991906139d0565b611ff391906139b0565b611ffd9190613998565b61200791906139d0565b6001830180549082905560028501549192509082906120279083906139ef565b6120319190613998565b600285015560075482906120469083906139ef565b6120509190613998565b6007556003830154604080518881526001600160401b039283166020820152918716908201526001600160a01b038816907f85daa0d8a4afa74e5bd57c0f5d2cddf52920ec882a02b8d3f646c972b4cfb6b49060600160405180910390a250505050505050565b600260005414156120da5760405162461bcd60e51b8152600401610e5a90613961565b60026000556001546120f7906001600160a01b03168484846127b6565b5050600160005550565b61210e8585858585612827565b6001600160a01b03851660009081526003602052604090206002810154600d546121389190611468565b816005018190555084600e60008282546121529190613998565b9091555050505050505050565b6001600160a01b03831660009081526003602052604081206006810180549192918590811061219e57634e487b7160e01b600052603260045260246000fd5b60009182526020918290206040805160c081018252600493909302909101805483526001810154938301939093526002830154908201526003909101546001600160401b0380821660608401819052600160401b83049091166080840152600160801b90910460ff16151560a08301529091501580612229575060808101516001600160401b031642115b6122755760405162461bcd60e51b815260206004820152601860248201527f6465706f736974206e6f742079657420756e6c6f636b656400000000000000006044820152606401610e5a565b82600e600082825461228791906139ef565b909155506122989050858585612cad565b6122a88260020154600d54611468565b82600501819055505050505050565b6001600160a01b0381166000908152600360205260408120906122d9836116c4565b9050806122e7575050610f49565b6001546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b15801561232b57600080fd5b505afa15801561233f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612363919061381e565b9050818110156123b55760405162461bcd60e51b815260206004820152601c60248201527f636f6e747261637420454c492062616c616e636520746f6f206c6f77000000006044820152606401610e5a565b6001546005546001600160a01b03908116911614156123f857600e5482116123dd57816123e1565b600e545b600e60008282546123f291906139ef565b90915550505b6124088360020154600d54611468565b600584015561241784836131d7565b6040518281526001600160a01b0385169033907f291d65ce1109b491b8b6540dabe662b3478f4c808f76bb4130c3f7afa2fab59c9060200160405180910390a350505050565b6000811561246d5761246d61191d565b6124768361321f565b90508061248557506000610e24565b6001600160a01b038084166000908152600360205260409020600154600554919291821691161415612625576000600a54836124c191906139d0565b905060006040518060c001604052808581526020018381526020018460020154856003015460646124f291906139d0565b6124fc91906139b0565b8152602001426001600160401b03168152602001612519886105b9565b6125239042613998565b6001600160401b03908116825260016020928301819052600687018054808301825560009182528482208651600490920201908155938501519184019190915560408401516002840155606084015160039093018054608086015160a08701511515600160801b0260ff60801b19918616600160401b0267ffffffffffffffff60401b199790961667ffffffffffffffff199093169290921795909516939093179390931691909117909155845491925085918591906125e4908490613998565b92505081905550818360020160008282546125ff9190613998565b9250508190555081600760008282546126189190613998565b9091555061275392505050565b6004805460015460405163091465f760e11b81526001600160a01b0391821693810193909352600092911690631228cbee9060240160206040518083038186803b15801561267257600080fd5b505afa158015612686573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126aa9190613698565b9050806001600160a01b031663806fcedf86858560020154866003015460646126d391906139d0565b6126dd91906139b0565b6126e68a6105b9565b6040516001600160e01b031960e087901b1681526001600160a01b039094166004850152602484019290925260448301526064820152608401600060405180830381600087803b15801561273957600080fd5b505af115801561274d573d6000803e3d6000fd5b50505050505b821561276f576127698160020154600654611468565b60048201555b6040518281526001600160a01b0385169033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd79060200160405180910390a35092915050565b6040516001600160a01b03808516602483015283166044820152606481018290526128219085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613339565b50505050565b600084116128655760405162461bcd60e51b815260206004820152600b60248201526a1e995c9bc8185b5bdd5b9d60aa1b6044820152606401610e5a565b6001600160401b03831615806128a4575042836001600160401b03161180156128a457506301e133806128a1426001600160401b0386166139ef565b11155b6128e85760405162461bcd60e51b81526020600482015260156024820152741a5b9d985b1a59081b1bd8dac81a5b9d195c9d985b605a1b6044820152606401610e5a565b6128f061191d565b6001600160a01b038516600090815260036020526040902080541561291c5761291a866000611d15565b505b6005546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b15801561296057600080fd5b505afa158015612974573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612998919061381e565b90506129a5333088613410565b6005546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b1580156129e957600080fd5b505afa1580156129fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a21919061381e565b90506000612a2f83836139ef565b9050600080886001600160401b031611612a4a576000612a4c565b425b90508760006001600160401b038216612a6757600954612a6b565b6008545b9050600084826301e1338081612a818888613a06565b6001600160401b0316612a9491906139d0565b612a9e91906139b0565b612aa89190613998565b612ab291906139d0565b905060008111612ad257634e487b7160e01b600052600160045260246000fd5b60006040518060c001604052808781526020018381526020018c612af7576000612af9565b8b5b81526001600160401b038781166020808401919091528782166040808501919091528f151560609485015260068e018054600180820183556000928352848320885160049093020191825593870151938101939093559085015160028301559284015160039091018054608086015160a087015167ffffffffffffffff199092169385169390931767ffffffffffffffff60401b1916600160401b93909416929092029290921760ff60801b1916600160801b911515919091021790558a5491925087918b9190612bcb908490613998565b9250508190555081896002016000828254612be69190613998565b90915550506001600160401b038c16612c2e5785896001016000828254612c0d9190613998565b9250508190555081896003016000828254612c289190613998565b90915550505b612c3e8960020154600654611468565b89600401819055508160076000828254612c589190613998565b90915550506040518d81526001600160a01b038f169033907f5dac0c1b1112564a045ba943c9d50270893e8e826c49be8e7073adc713ab7bd79060200160405180910390a35050505050505050505050505050565b60008111612ceb5760405162461bcd60e51b815260206004820152600b60248201526a1e995c9bc8185b5bdd5b9d60aa1b6044820152606401610e5a565b6001600160a01b038316600090815260036020526040812060068101805491929185908110612d2a57634e487b7160e01b600052603260045260246000fd5b6000918252602090912060049091020160038101548154919250600160801b900460ff1690841115612d955760405162461bcd60e51b8152602060048201526014602482015273616d6f756e742065786365656473207374616b6560601b6044820152606401610e5a565b612d9d61191d565b612da8866000611d15565b5060018201546003830154600090600160401b90046001600160401b031615612dd357600854612dd7565b6009545b90506000868560000154612deb91906139ef565b600386015483906301e13380908290612e17906001600160401b0380821691600160401b900416613a06565b6001600160401b0316612e2a91906139d0565b612e3491906139b0565b612e3e9190613998565b612e4891906139d0565b8554909150612e589088906139ef565b612ec057856006018881548110612e7f57634e487b7160e01b600052603260045260246000fd5b600091825260208220600490910201818155600181018290556002810191909155600301805470ffffffffffffffffffffffffffffffffff19169055612ee1565b86856000016000828254612ed491906139ef565b9091555050600185018190555b86866000016000828254612ef591906139ef565b90915550506003850154600160401b90046001600160401b0316612f4e5786866001016000828254612f2791906139ef565b909155505060038601548190612f3e9085906139ef565b612f489190613998565b60038701555b80838760020154612f5f91906139ef565b612f699190613998565b60028701819055600654612f7d9190611468565b60048701556007548190612f929085906139ef565b612f9c9190613998565b600755831561303a5760006064866002015489612fb991906139d0565b612fc391906139b0565b6004805460405163bb9900eb60e01b81523392810192909252602482018b9052604482018390529192506001600160a01b039091169063bb9900eb90606401600060405180830381600087803b15801561301c57600080fd5b505af1158015613030573d6000803e3d6000fd5b505050505061318c565b60048054600554604051630195163160e21b81526001600160a01b0391821693810193909352600092606492919091169063065458c49060240160206040518083038186803b15801561308c57600080fd5b505afa1580156130a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130c4919061381e565b6130ce908a6139d0565b6130d891906139b0565b9050801561316f5761316f600460009054906101000a90046001600160a01b03166001600160a01b03166370d5ae056040518163ffffffff1660e01b815260040160206040518083038186803b15801561313157600080fd5b505afa158015613145573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131699190613698565b82613450565b8781101561318a5761318a33613185838b6139ef565b613450565b505b6040518781526001600160a01b038a169033907fd8654fcc8cf5b36d30b3f5e4688fc78118e6d68de60b9994e09902268b57c3e39060200160405180910390a3505050505050505050565b600260005414156131fa5760405162461bcd60e51b8152600401610e5a90613961565b6002600055600154613216906001600160a01b0316838361348b565b50506001600055565b6001600160a01b0381166000908152600360208181526040808420815160e0810183528154815260018201548185015260028201548184015293810154606085015260048101546080850152600581015460a0850152600681018054835181860281018601909452808452869594929360c086019390929190879084015b8282101561331a5760008481526020908190206040805160c081018252600486029092018054835260018082015484860152600282015492840192909252600301546001600160401b038082166060850152600160401b8204166080840152600160801b900460ff16151560a0830152908352909201910161329d565b5050505081525050905080608001516117de8260400151600654611468565b600061338e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166134bb9092919063ffffffff16565b80519091501561340b57808060200190518101906133ac91906137bf565b61340b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610e5a565b505050565b600260005414156134335760405162461bcd60e51b8152600401610e5a90613961565b60026000556005546120f7906001600160a01b03168484846127b6565b600260005414156134735760405162461bcd60e51b8152600401610e5a90613961565b6002600055600554613216906001600160a01b031683835b6040516001600160a01b03831660248201526044810182905261340b90849063a9059cbb60e01b906064016127ea565b60606134ca84846000856134d2565b949350505050565b6060824710156135335760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610e5a565b843b6135815760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610e5a565b600080866001600160a01b0316858760405161359d91906138eb565b60006040518083038185875af1925050503d80600081146135da576040519150601f19603f3d011682016040523d82523d6000602084013e6135df565b606091505b50915091506135ef8282866135fa565b979650505050505050565b60608315613609575081611482565b8251156136195782518084602001fd5b8160405162461bcd60e51b8152600401610e5a9190613907565b5080546000825590600052602060002090810190610f4991905b80821115613661576000815560010161364d565b5090565b80356001600160401b038116811461097a57600080fd5b60006020828403121561368d578081fd5b813561148281613a8b565b6000602082840312156136a9578081fd5b815161148281613a8b565b600080604083850312156136c6578081fd5b82356136d181613a8b565b915060208301356136e181613a8b565b809150509250929050565b600080600080600060a08688031215613703578081fd5b853561370e81613a8b565b9450602086013561371e81613a8b565b9350604086013561372e81613a8b565b925061373c60608701613665565b9150608086013561374c81613aa0565b809150509295509295909350565b6000806040838503121561376c578182fd5b823561377781613a8b565b946020939093013593505050565b6000806000806080858703121561379a578384fd5b84356137a581613a8b565b966020860135965060408601359560600135945092505050565b6000602082840312156137d0578081fd5b81518015158114611482578182fd5b6000602082840312156137f0578081fd5b81516001600160c01b0381168114611482578182fd5b600060208284031215613817578081fd5b5035919050565b60006020828403121561382f578081fd5b5051919050565b60008060408385031215613848578182fd5b50508035926020909101359150565b6000806000806080858703121561386c578384fd5b5050823594602084013594506040840135936060013592509050565b6000806040838503121561389a578182fd5b823591506138aa60208401613665565b90509250929050565b6000602082840312156138c4578081fd5b813561148281613aa0565b6000602082840312156138e0578081fd5b815161148281613aa0565b600082516138fd818460208701613a2e565b9190910192915050565b6000602082528251806020840152613926816040850160208701613a2e565b601f01601f19169190910160400192915050565b6020808252600d908201526c1858d8d95cdcc819195b9a5959609a1b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b600082198211156139ab576139ab613a75565b500190565b6000826139cb57634e487b7160e01b81526012600452602481fd5b500490565b60008160001904831182151516156139ea576139ea613a75565b500290565b600082821015613a0157613a01613a75565b500390565b60006001600160401b0383811690831681811015613a2657613a26613a75565b039392505050565b60005b83811015613a49578181015183820152602001613a31565b838111156128215750506000910152565b6000600019821415613a6e57613a6e613a75565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b0381168114610f4957600080fd5b63ffffffff81168114610f4957600080fdfea264697066735822122086b1e478b87de881e6564d7cef43d7cbf29b628283d78374687535f904dea73c64736f6c63430008020033a2646970667358221220d4ac322e5dcc534047748e6d14c717bb164ae71359e0b2333877cc8bb26c79c164736f6c63430008020033000000000000000000000000ed0bb69f9f96e8c5fffdf16c468ecec385134ea500000000000000000000000000000000000000000000000004064976a8dd0000000000000000000000000000000000000000000000000000000000000001647400000000000000000000000000000000000000000000000000000000014d27ef00000000000000000000000000000000000000000000000000000000032dcf67

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000ed0bb69f9f96e8c5fffdf16c468ecec385134ea500000000000000000000000000000000000000000000000004064976a8dd0000000000000000000000000000000000000000000000000000000000000001647400000000000000000000000000000000000000000000000000000000014d27ef00000000000000000000000000000000000000000000000000000000032dcf67

-----Decoded View---------------
Arg [0] : _eli (address): 0xed0bb69f9f96e8c5fffdf16c468ecec385134ea5
Arg [1] : _eliPerBlock (uint192): 290000000000000000
Arg [2] : _blocksPerUpdate (uint32): 91252
Arg [3] : _initBlock (uint32): 21833711
Arg [4] : _endBlock (uint32): 53333863

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000ed0bb69f9f96e8c5fffdf16c468ecec385134ea5
Arg [1] : 00000000000000000000000000000000000000000000000004064976a8dd0000
Arg [2] : 0000000000000000000000000000000000000000000000000000000000016474
Arg [3] : 00000000000000000000000000000000000000000000000000000000014d27ef
Arg [4] : 00000000000000000000000000000000000000000000000000000000032dcf67


Deployed ByteCode Sourcemap

86660:13227:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;87095:49;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;9665:25:1;;;9653:2;9638:18;87095:49:0;;;;;;;;88441:22;;;;;;;;;;;;9875:10:1;9863:23;;;9845:42;;9833:2;9818:18;88441:22:0;9800:93:1;88804:42:0;;;;;;92024:157;;;;;;:::i;:::-;;:::i;:::-;;;-1:-1:-1;;;;;5060:32:1;;;5042:51;;5030:2;5015:18;92024:157:0;4997:102:1;92504:795:0;;;;;;:::i;:::-;;:::i;:::-;;;;8921:13:1;;-1:-1:-1;;;;;8917:22:1;;;8899:41;;9000:4;8988:17;;;8982:24;8978:33;;;8956:20;;;8949:63;9060:17;;;9054:24;9080:10;9050:41;9028:20;;;9021:71;8849:2;8834:18;92504:795:0;8816:282:1;87797:39:0;;;;;-1:-1:-1;;;;;87797:39:0;;;;;;-1:-1:-1;;;;;9267:32:1;;;9249:51;;9237:2;9222:18;87797:39:0;9204:102:1;89443:42:0;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;6135:14:1;;6128:22;6110:41;;6098:2;6083:18;89443:42:0;6065:92:1;98141:94:0;;;;;;:::i;:::-;;:::i;:::-;;99745:139;;;:::i;99321:141::-;;;;;;:::i;:::-;;:::i;88502:89::-;;88549:42;88502:89;;73784:148;;;:::i;88754:43::-;;;;;;97707:387;;;;;;:::i;:::-;;:::i;73135:87::-;73181:7;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;98485:125;;;;;;:::i;:::-;;:::i;88005:25::-;;;;;-1:-1:-1;;;88005:25:0;;;;;;94349:449;;;;;;:::i;:::-;;:::i;93568:398::-;;;:::i;89305:40::-;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;89305:40:0;;;95046:809;;;;;;:::i;:::-;;:::i;96842:607::-;;;;;;:::i;:::-;;:::i;87019:20::-;;;;;-1:-1:-1;;;;;87019:20:0;;;98618:163;;;;;;:::i;:::-;;:::i;98285:192::-;;;;;;:::i;:::-;;:::i;89176:29::-;;;;;;;;;87696:26;;;;;-1:-1:-1;;;;;87696:26:0;;;88242:29;;;;;-1:-1:-1;;;88242:29:0;;;;;;98789:524;;;;;;:::i;:::-;;:::i;74087:281::-;;;;;;:::i;:::-;;:::i;88633:28::-;;;;;;96007:520;;;:::i;86994:18::-;;;;;-1:-1:-1;;;;;86994:18:0;;;92024:157;-1:-1:-1;;;;;92157:16:0;;;92090:7;92157:16;;;:5;:16;;;;;;;92024:157;;;;:::o;92504:795::-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;92695:17:0;;;92676:16;92695:17;;;:5;:17;;;;;;;92806:22;92798:49;;;;-1:-1:-1;;;92798:49:0;;7112:2:1;92798:49:0;;;7094:21:1;7151:2;7131:18;;;7124:30;-1:-1:-1;;;7170:18:1;;;7163:44;7224:18;;92798:49:0;;;;;;;;;92966:17;92992:8;-1:-1:-1;;;;;92986:25:0;;:27;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;92966:47;;93024:13;93046:8;-1:-1:-1;;;;;93040:22:0;;:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;93154:137;;;;;;;;-1:-1:-1;;;;;93154:137:0;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;93154:137:0;92504:795;-1:-1:-1;;92504:795:0:o;98141:94::-;73366:10;73355:7;73181;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;73355:7;-1:-1:-1;;;;;73355:21:0;;73347:66;;;;-1:-1:-1;;;73347:66:0;;;;;;;:::i;:::-;98209:7:::1;:18:::0;98141:94::o;99745:139::-;99864:12;99745:139;;:::o;99321:141::-;73366:10;73355:7;73181;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;73355:7;-1:-1:-1;;;;;73355:21:0;;73347:66;;;;-1:-1:-1;;;73347:66:0;;;;;;;:::i;:::-;99398:3:::1;::::0;99417:36:::1;::::0;-1:-1:-1;;;99417:36:0;;99447:4:::1;99417:36;::::0;::::1;5042:51:1::0;-1:-1:-1;;;;;99398:3:0;;::::1;::::0;99391:20:::1;::::0;99412:3;;99398;;99417:21:::1;::::0;5015:18:1;;99417:36:0::1;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;99391:63;::::0;-1:-1:-1;;;;;;99391:63:0::1;::::0;;;;;;-1:-1:-1;;;;;5883:32:1;;;99391:63:0::1;::::0;::::1;5865:51:1::0;5932:18;;;5925:34;5838:18;;99391:63:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;99321:141:::0;:::o;73784:148::-;73366:10;73355:7;73181;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;73355:7;-1:-1:-1;;;;;73355:21:0;;73347:66;;;;-1:-1:-1;;;73347:66:0;;;;;;;:::i;:::-;73891:1:::1;73875:6:::0;;73854:40:::1;::::0;-1:-1:-1;;;;;73875:6:0;;::::1;::::0;73854:40:::1;::::0;73891:1;;73854:40:::1;73922:1;73905:19:::0;;-1:-1:-1;;;;;;73905:19:0::1;::::0;;73784:148::o;97707:387::-;73366:10;73355:7;73181;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;73355:7;-1:-1:-1;;;;;73355:21:0;;73347:66;;;;-1:-1:-1;;;73347:66:0;;;;;;;:::i;:::-;97899:8:::1;-1:-1:-1::0;;;;;97893:22:0::1;;:24;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;97870:11;::::0;:20:::1;::::0;97884:6;;-1:-1:-1;;;97870:11:0;::::1;;;:20;:::i;:::-;:47;;;;:::i;:::-;97856:11;:61:::0;;-1:-1:-1;;;;97856:61:0::1;-1:-1:-1::0;;;97856:61:0::1;::::0;;::::1;;;::::0;;97966:33:::1;::::0;-1:-1:-1;;;97966:33:0;;9863:23:1;;;97966:33:0::1;::::0;::::1;9845:42:1::0;-1:-1:-1;;;;;97966:25:0;::::1;::::0;::::1;::::0;9818:18:1;;97966:33:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;98043:43:0::1;::::0;9875:10:1;9863:23;;9845:42;;-1:-1:-1;;;;;98043:43:0;::::1;::::0;-1:-1:-1;98057:10:0::1;::::0;-1:-1:-1;98043:43:0::1;::::0;9833:2:1;9818:18;98043:43:0::1;;;;;;;97707:387:::0;;:::o;98485:125::-;73366:10;73355:7;73181;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;73355:7;-1:-1:-1;;;;;73355:21:0;;73347:66;;;;-1:-1:-1;;;73347:66:0;;;;;;;:::i;:::-;98567:3:::1;:10:::0;;-1:-1:-1;;;;;98567:10:0;;::::1;-1:-1:-1::0;;;;;;98567:10:0;;::::1;;::::0;;;98588:5:::1;:14:::0;;;;;::::1;::::0;::::1;;::::0;;98485:125::o;94349:449::-;73366:10;73355:7;73181;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;73355:7;-1:-1:-1;;;;;73355:21:0;;73347:66;;;;-1:-1:-1;;;73347:66:0;;;;;;;:::i;:::-;94588:3:::1;::::0;94555:154:::1;::::0;94542:10:::1;::::0;-1:-1:-1;;;;;94588:3:0::1;::::0;94623:4:::1;::::0;94644:9;;94668;;94692:6;;94555:154:::1;::::0;::::1;:::i;:::-;-1:-1:-1::0;;;;;5431:15:1;;;5413:34;;5483:15;;;5478:2;5463:18;;5456:43;5535:15;;;;5530:2;5515:18;;5508:43;5599:18;5587:31;5582:2;5567:18;;5560:59;5668:10;5656:23;;;5650:3;5635:19;;5628:52;5362:3;5347:19;94555:154:0::1;;;;;;;;;;;;;;;;::::0;::::1;;;;;;94542:167;;94763:27;94784:4;94763:12;:27::i;:::-;73424:1;94349:449:::0;;;:::o;93568:398::-;93701:8;;93618:4;;93701:8;;93685:13;:11;:13::i;:::-;:24;93681:128;;;-1:-1:-1;93792:5:0;93785:12;;93681:128;93943:15;;93925;;:33;;93943:15;-1:-1:-1;;;93943:15:0;;;;;;93925;:33;:::i;:::-;93908:50;;:13;:11;:13::i;:::-;:50;;93901:57;;93568:398;:::o;95046:809::-;73366:10;73355:7;73181;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;73355:7;-1:-1:-1;;;;;73355:21:0;;73347:66;;;;-1:-1:-1;;;73347:66:0;;;;;;;:::i;:::-;95220:17:::1;95246:8;-1:-1:-1::0;;;;;95240:25:0::1;;:27;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;95220:47;;95278:13;95300:8;-1:-1:-1::0;;;;;95294:22:0::1;;:24;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;95431:16:0;;::::1;95459:1;95431:16:::0;;;:5:::1;:16;::::0;;;;;95278:40;;-1:-1:-1;95431:16:0::1;:30:::0;95409:111:::1;;;::::0;-1:-1:-1;;;95409:111:0;;8163:2:1;95409:111:0::1;::::0;::::1;8145:21:1::0;8202:2;8182:18;;;8175:30;8241:33;8221:18;;;8214:61;8292:18;;95409:111:0::1;8135:181:1::0;95409:111:0::1;-1:-1:-1::0;;;;;95599:16:0;;::::1;;::::0;;;:5:::1;:16;::::0;;;;;;;:27;;-1:-1:-1;;;;;;95599:27:0::1;::::0;;::::1;::::0;;::::1;::::0;;95637:20;;;:10:::1;:20:::0;;;:27;;-1:-1:-1;;95637:27:0::1;-1:-1:-1::0;95637:27:0::1;::::0;;95727:11:::1;:21:::0;;95742:6;;95727:11;::::1;::::0;:21:::1;::::0;95742:6;;-1:-1:-1;;;95727:21:0;::::1;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;95830:8;-1:-1:-1::0;;;;;95792:55:0::1;95819:9;-1:-1:-1::0;;;;;95792:55:0::1;95807:10;-1:-1:-1::0;;;;;95792:55:0::1;;95840:6;95792:55;;;;;9875:10:1::0;9863:23;;;;9845:42;;9833:2;9818:18;;9800:93;95792:55:0::1;;;;;;;;73424:1;;95046:809:::0;:::o;96842:607::-;97071:10;97060:22;;;;:10;:22;;;;;;;;97052:48;;;;-1:-1:-1;;;97052:48:0;;8523:2:1;97052:48:0;;;8505:21:1;8562:2;8542:18;;;8535:30;-1:-1:-1;;;8581:18:1;;;8574:43;8634:18;;97052:48:0;8495:163:1;97052:48:0;97113:18;97168:5;97157:7;;97135:19;:29;;;;:::i;:::-;97134:39;;;;:::i;:::-;97113:60;;97204:10;97194:7;:20;97186:51;;;;-1:-1:-1;;;97186:51:0;;7455:2:1;97186:51:0;;;7437:21:1;7494:2;7474:18;;;7467:30;-1:-1:-1;;;7513:18:1;;;7506:48;7571:18;;97186:51:0;7427:168:1;97186:51:0;97250:20;97273;97283:10;97273:7;:20;:::i;:::-;97313:3;;97306:45;;-1:-1:-1;;;97306:45:0;;88549:42;97306:45;;;5865:51:1;5932:18;;;5925:34;;;97250:43:0;;-1:-1:-1;;;;;;97313:3:0;;97306:20;;5838:18:1;;97306:45:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;97409:3:0;;97402:39;;-1:-1:-1;;;97402:39:0;;-1:-1:-1;;;;;5883:32:1;;;97402:39:0;;;5865:51:1;5932:18;;;5925:34;;;97409:3:0;;;;97402:20;;5838:18:1;;97402:39:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;96842:607;;;;;:::o;98618:163::-;73366:10;73355:7;73181;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;73355:7;-1:-1:-1;;;;;73355:21:0;;73347:66;;;;-1:-1:-1;;;73347:66:0;;;;;;;:::i;:::-;-1:-1:-1;;;;;98736:26:0;;::::1;;::::0;;;:14:::1;:26;::::0;;;;:37;98618:163::o;98285:192::-;73366:10;73355:7;73181;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;73355:7;-1:-1:-1;;;;;73355:21:0;;73347:66;;;;-1:-1:-1;;;73347:66:0;;;;;;;:::i;:::-;98402:17:::1;:28:::0;;;;98441:17:::1;:28:::0;98285:192::o;98789:524::-;73366:10;73355:7;73181;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;73355:7;-1:-1:-1;;;;;73355:21:0;;73347:66;;;;-1:-1:-1;;;73347:66:0;;;;;;;:::i;:::-;99090:3:::1;:10:::0;;-1:-1:-1;;;;;;99090:10:0::1;-1:-1:-1::0;;;;;99090:10:0;;;::::1;::::0;;;::::1;::::0;;;99111:11:::1;:26:::0;;-1:-1:-1;;;;;;99111:26:0;;::::1;-1:-1:-1::0;;;;;99111:26:0;;::::1;;::::0;;;99148:19:::1;:42:::0;;;;::::1;::::0;;;::::1;::::0;;;::::1;-1:-1:-1::0;;;;;99201:34:0::1;-1:-1:-1::0;;;99201:34:0::1;::::0;;::::1;;;::::0;;;99246:15:::1;:28:::0;;-1:-1:-1;;99246:28:0;;::::1;::::0;;::::1;::::0;;;::::1;::::0;;99285:8:::1;:20:::0;;;;::::1;::::0;;;::::1;;::::0;;98789:524::o;74087:281::-;73366:10;73355:7;73181;73208:6;-1:-1:-1;;;;;73208:6:0;73135:87;;73355:7;-1:-1:-1;;;;;73355:21:0;;73347:66;;;;-1:-1:-1;;;73347:66:0;;;;;;;:::i;:::-;-1:-1:-1;;;;;74190:22:0;::::1;74168:110;;;::::0;-1:-1:-1;;;74168:110:0;;6705:2:1;74168:110:0::1;::::0;::::1;6687:21:1::0;6744:2;6724:18;;;6717:30;6783:34;6763:18;;;6756:62;-1:-1:-1;;;6834:18:1;;;6827:36;6880:19;;74168:110:0::1;6677:228:1::0;74168:110:0::1;74315:6;::::0;;74294:38:::1;::::0;-1:-1:-1;;;;;74294:38:0;;::::1;::::0;74315:6;::::1;::::0;74294:38:::1;::::0;::::1;74343:6;:17:::0;;-1:-1:-1;;;;;;74343:17:0::1;-1:-1:-1::0;;;;;74343:17:0;;;::::1;::::0;;;::::1;::::0;;74087:281::o;96007:520::-;96156:19;:17;:19::i;:::-;96148:44;;;;-1:-1:-1;;;96148:44:0;;6364:2:1;96148:44:0;;;6346:21:1;6403:2;6383:18;;;6376:30;-1:-1:-1;;;6422:18:1;;;6415:42;6474:18;;96148:44:0;6336:162:1;96148:44:0;96296:19;;96282:11;;96319:3;;96282:33;;-1:-1:-1;;;;;96296:19:0;;;;96282:11;:33;:::i;:::-;96281:41;;;;:::i;:::-;96267:11;:55;;-1:-1:-1;;;;;;96267:55:0;-1:-1:-1;;;;;96267:55:0;;;;;;;;;;96421:13;:11;:13::i;:::-;96396:15;:39;;-1:-1:-1;;96396:39:0;;;;;;;;;;;;96507:11;;96479:40;;-1:-1:-1;;;;;96507:11:0;;;9249:51:1;;96495:10:0;;96479:40;;9237:2:1;9222:18;96479:40:0;;;;;;;96007:520::o;-1:-1:-1:-;;;;;;;;:::o;14:173:1:-;82:20;;-1:-1:-1;;;;;131:31:1;;121:42;;111:2;;177:1;174;167:12;192:257;;304:2;292:9;283:7;279:23;275:32;272:2;;;325:6;317;310:22;272:2;369:9;356:23;388:31;413:5;388:31;:::i;:::-;438:5;262:187;-1:-1:-1;;;262:187:1:o;454:261::-;;577:2;565:9;556:7;552:23;548:32;545:2;;;598:6;590;583:22;545:2;635:9;629:16;654:31;679:5;654:31;:::i;720:398::-;;;849:2;837:9;828:7;824:23;820:32;817:2;;;870:6;862;855:22;817:2;914:9;901:23;933:31;958:5;933:31;:::i;:::-;983:5;-1:-1:-1;1040:2:1;1025:18;;1012:32;1053:33;1012:32;1053:33;:::i;:::-;1105:7;1095:17;;;807:311;;;;;:::o;1123:825::-;;;;;;;1317:3;1305:9;1296:7;1292:23;1288:33;1285:2;;;1339:6;1331;1324:22;1285:2;1383:9;1370:23;1402:31;1427:5;1402:31;:::i;:::-;1452:5;-1:-1:-1;1476:38:1;1510:2;1495:18;;1476:38;:::i;:::-;1466:48;;1533:38;1567:2;1556:9;1552:18;1533:38;:::i;:::-;1523:48;;1623:2;1612:9;1608:18;1595:32;1636;1660:7;1636:32;:::i;:::-;1687:7;-1:-1:-1;1746:3:1;1731:19;;1718:33;1760:32;1718:33;1760:32;:::i;:::-;1811:7;-1:-1:-1;1870:3:1;1855:19;;1842:33;1884:32;1842:33;1884:32;:::i;:::-;1935:7;1925:17;;;1275:673;;;;;;;;:::o;1953:325::-;;;2082:2;2070:9;2061:7;2057:23;2053:32;2050:2;;;2103:6;2095;2088:22;2050:2;2147:9;2134:23;2166:31;2191:5;2166:31;:::i;:::-;2216:5;2268:2;2253:18;;;;2240:32;;-1:-1:-1;;;2040:238:1:o;2283:393::-;;;;2429:2;2417:9;2408:7;2404:23;2400:32;2397:2;;;2450:6;2442;2435:22;2397:2;2494:9;2481:23;2513:31;2538:5;2513:31;:::i;:::-;2563:5;2615:2;2600:18;;2587:32;;-1:-1:-1;2666:2:1;2651:18;;;2638:32;;2387:289;-1:-1:-1;;;2387:289:1:o;2681:396::-;;;2809:2;2797:9;2788:7;2784:23;2780:32;2777:2;;;2830:6;2822;2815:22;2777:2;2874:9;2861:23;2893:31;2918:5;2893:31;:::i;:::-;2943:5;-1:-1:-1;3000:2:1;2985:18;;2972:32;3013;2972;3013;:::i;3082:586::-;;;;3226:2;3214:9;3205:7;3201:23;3197:32;3194:2;;;3247:6;3239;3232:22;3194:2;3291:9;3278:23;3310:31;3335:5;3310:31;:::i;:::-;3360:5;-1:-1:-1;3417:2:1;3402:18;;3389:32;3465:18;3452:32;;3440:45;;3430:2;;3504:6;3496;3489:22;3430:2;3532:7;-1:-1:-1;3591:2:1;3576:18;;3563:32;3604;3563;3604;:::i;:::-;3655:7;3645:17;;;3184:484;;;;;:::o;3673:297::-;;3793:2;3781:9;3772:7;3768:23;3764:32;3761:2;;;3814:6;3806;3799:22;3761:2;3851:9;3845:16;3904:5;3897:13;3890:21;3883:5;3880:32;3870:2;;3931:6;3923;3916:22;3975:190;;4087:2;4075:9;4066:7;4062:23;4058:32;4055:2;;;4108:6;4100;4093:22;4055:2;-1:-1:-1;4136:23:1;;4045:120;-1:-1:-1;4045:120:1:o;4170:194::-;;4293:2;4281:9;4272:7;4268:23;4264:32;4261:2;;;4314:6;4306;4299:22;4261:2;-1:-1:-1;4342:16:1;;4251:113;-1:-1:-1;4251:113:1:o;4369:258::-;;;4498:2;4486:9;4477:7;4473:23;4469:32;4466:2;;;4519:6;4511;4504:22;4466:2;-1:-1:-1;;4547:23:1;;;4617:2;4602:18;;;4589:32;;-1:-1:-1;4456:171:1:o;4632:259::-;;4754:2;4742:9;4733:7;4729:23;4725:32;4722:2;;;4775:6;4767;4760:22;4722:2;4812:9;4806:16;4831:30;4855:5;4831:30;:::i;7600:356::-;7802:2;7784:21;;;7821:18;;;7814:30;7880:34;7875:2;7860:18;;7853:62;7947:2;7932:18;;7774:182::o;10095:228::-;;10162:10;10199:2;10196:1;10192:10;10229:2;10226:1;10222:10;10260:3;10256:2;10252:12;10247:3;10244:21;10241:2;;;10268:18;;:::i;:::-;10304:13;;10142:181;-1:-1:-1;;;;10142:181:1:o;10328:201::-;;-1:-1:-1;;;;;10433:10:1;;;;10452:2;;10469:18;;:::i;:::-;10507:10;;10503:20;;;;;10374:155;-1:-1:-1;;10374:155:1:o;10534:120::-;;10600:1;10590:2;;10605:18;;:::i;:::-;-1:-1:-1;10639:9:1;;10580:74::o;10659:272::-;;-1:-1:-1;;;;;10770:10:1;;;10800;;;10833:11;;10826:19;10855:12;;;10847:21;;10822:47;10819:2;;;10872:18;;:::i;:::-;10912:13;;10711:220;-1:-1:-1;;;;10711:220:1:o;10936:168::-;;11042:1;11038;11034:6;11030:14;11027:1;11024:21;11019:1;11012:9;11005:17;11001:45;10998:2;;;11049:18;;:::i;:::-;-1:-1:-1;11089:9:1;;10988:116::o;11109:125::-;;11177:1;11174;11171:8;11168:2;;;11182:18;;:::i;:::-;-1:-1:-1;11219:9:1;;11158:76::o;11239:221::-;;11307:10;11367;;;;11337;;11389:12;;;11386:2;;;11404:18;;:::i;:::-;11441:13;;11287:173;-1:-1:-1;;;11287:173:1:o;11465:127::-;11526:10;11521:3;11517:20;11514:1;11507:31;11557:4;11554:1;11547:15;11581:4;11578:1;11571:15;11597:127;11658:10;11653:3;11649:20;11646:1;11639:31;11689:4;11686:1;11679:15;11713:4;11710:1;11703:15;11729:131;-1:-1:-1;;;;;11804:31:1;;11794:42;;11784:2;;11850:1;11847;11840:12;11784:2;11774:86;:::o;11865:121::-;11950:10;11943:5;11939:22;11932:5;11929:33;11919:2;;11976:1;11973;11966:12

Swarm Source

ipfs://d4ac322e5dcc534047748e6d14c717bb164ae71359e0b2333877cc8bb26c79c1
Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.