ETH Price: $3,398.25 (+1.91%)

Contract

0x309AC8840f9b4C7eEB5bAb1e89669d8dbb86c060
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Mint FPI147929972022-05-17 14:17:35956 days ago1652797055IN
0x309AC884...dbb86c060
0 ETH0.005926841.87904555
Mint FPI147767442022-05-15 0:12:03959 days ago1652573523IN
0x309AC884...dbb86c060
0 ETH0.0048009830.99770285
Redeem FPI147720522022-05-14 6:23:37960 days ago1652509417IN
0x309AC884...dbb86c060
0 ETH0.0044932926.53229052
Redeem FPI147657842022-05-13 6:30:48961 days ago1652423448IN
0x309AC884...dbb86c060
0 ETH0.0156654192.45842195
Mint FPI147643822022-05-13 1:03:11961 days ago1652403791IN
0x309AC884...dbb86c060
0 ETH0.01557971113.14899481
Redeem FPI147622202022-05-12 16:50:44961 days ago1652374244IN
0x309AC884...dbb86c060
0 ETH0.02494609147.50440613
Redeem FPI147599832022-05-12 8:12:19962 days ago1652343139IN
0x309AC884...dbb86c060
0 ETH0.05217787397.18863983
Redeem FPI147597792022-05-12 7:21:00962 days ago1652340060IN
0x309AC884...dbb86c060
0 ETH0.14625059982.93956855
Redeem FPI147595112022-05-12 6:22:56962 days ago1652336576IN
0x309AC884...dbb86c060
0 ETH0.05968037319.96426923
Redeem FPI147594962022-05-12 6:19:38962 days ago1652336378IN
0x309AC884...dbb86c060
0 ETH0.05361755360.11035469
Redeem FPI147590902022-05-12 4:42:08962 days ago1652330528IN
0x309AC884...dbb86c060
0 ETH0.04446588337.62244573
Redeem FPI147541892022-05-11 9:58:50963 days ago1652263130IN
0x309AC884...dbb86c060
0 ETH0.0107390181.55447364
Redeem FPI147532982022-05-11 6:34:19963 days ago1652250859IN
0x309AC884...dbb86c060
0 ETH0.0060366645.77287704
Mint FPI147506212022-05-10 20:21:13963 days ago1652214073IN
0x309AC884...dbb86c060
0 ETH0.0037546227.20764117
Redeem FPI147502742022-05-10 19:05:29963 days ago1652209529IN
0x309AC884...dbb86c060
0 ETH0.0100378959.24788064
Redeem FPI147471582022-05-10 6:55:53964 days ago1652165753IN
0x309AC884...dbb86c060
0 ETH0.0074747350.20576584
Redeem FPI147454352022-05-10 0:19:37964 days ago1652141977IN
0x309AC884...dbb86c060
0 ETH0.01746995125.89687632
Redeem FPI147442472022-05-09 19:43:53964 days ago1652125433IN
0x309AC884...dbb86c060
0 ETH0.01717913130.69250223
Redeem FPI147442292022-05-09 19:39:29964 days ago1652125169IN
0x309AC884...dbb86c060
0 ETH0.0314184168.43438144
Redeem FPI147442122022-05-09 19:35:42964 days ago1652124942IN
0x309AC884...dbb86c060
0 ETH0.02979931159.76299649
Redeem FPI147436642022-05-09 17:27:40964 days ago1652117260IN
0x309AC884...dbb86c060
0 ETH0.01453371110.19155057
Redeem FPI147434312022-05-09 16:33:15964 days ago1652113995IN
0x309AC884...dbb86c060
0 ETH0.01815065107.20557099
Mint FPI147430962022-05-09 15:12:56964 days ago1652109176IN
0x309AC884...dbb86c060
0 ETH0.01482202107.50412157
Redeem FPI147407932022-05-09 6:10:41965 days ago1652076641IN
0x309AC884...dbb86c060
0 ETH0.0031262221
Redeem FPI147372772022-05-08 17:04:28965 days ago1652029468IN
0x309AC884...dbb86c060
0 ETH0.0125611374.19673534
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FPIControllerPool

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 100000 runs

Other Settings:
default evmVersion, GNU GPLv2 license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2022-04-05
*/

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

// Sources flattened with hardhat v2.8.4 https://hardhat.org

// File @openzeppelin/contracts/token/ERC20/[email protected]

// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)


/**
 * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        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 @openzeppelin/contracts/token/ERC20/extensions/[email protected]

// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)


/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}


// File @openzeppelin/contracts/utils/[email protected]

// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)


/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}


// File @openzeppelin/contracts/token/ERC20/[email protected]

// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)




/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {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 Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override 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 this function is
     * overridden;
     *
     * 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 override returns (uint8) {
        return 18;
    }

    /**
     * @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:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, 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}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, 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}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, 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) {
        address owner = _msgSender();
        _approve(owner, spender, _allowances[owner][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) {
        address owner = _msgSender();
        uint256 currentAllowance = _allowances[owner][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This 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:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, 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:
     *
     * - `account` 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 += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

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

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(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 Spend `amount` form the allowance of `owner` toward `spender`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

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

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been 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 _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}


// File @openzeppelin/contracts/token/ERC20/extensions/[email protected]

// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)



/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20Burnable is Context, ERC20 {
    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
     * allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `amount`.
     */
    function burnFrom(address account, uint256 amount) public virtual {
        _spendAllowance(account, _msgSender(), amount);
        _burn(account, amount);
    }
}


// File @openzeppelin/contracts/security/[email protected]

// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)


/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}


// File @openzeppelin/contracts/access/[email protected]

// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)


/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}


// File @openzeppelin/contracts/utils/[email protected]

// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)


/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}


// File @openzeppelin/contracts/utils/introspection/[email protected]

// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)


/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}


// File @openzeppelin/contracts/utils/introspection/[email protected]

// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)


/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}


// File @openzeppelin/contracts/access/[email protected]

// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)





/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(uint160(account), 20),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}


// File contracts/Staking/Owned.sol


// https://docs.synthetix.io/contracts/Owned
contract Owned {
    address public owner;
    address public nominatedOwner;

    constructor (address _owner) public {
        require(_owner != address(0), "Owner address cannot be 0");
        owner = _owner;
        emit OwnerChanged(address(0), _owner);
    }

    function nominateNewOwner(address _owner) external onlyOwner {
        nominatedOwner = _owner;
        emit OwnerNominated(_owner);
    }

    function acceptOwnership() external {
        require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
        emit OwnerChanged(owner, nominatedOwner);
        owner = nominatedOwner;
        nominatedOwner = address(0);
    }

    modifier onlyOwner {
        require(msg.sender == owner, "Only the contract owner may perform this action");
        _;
    }

    event OwnerNominated(address newOwner);
    event OwnerChanged(address oldOwner, address newOwner);
}


// File contracts/ERC20/ERC20PermissionedMint.sol







contract ERC20PermissionedMint is ERC20, ERC20Burnable, Owned {

    // Core
    address public timelock_address;

    // Minters
    address[] public minters_array; // Allowed to mint
    mapping(address => bool) public minters; // Mapping is also used for faster verification

    /* ========== CONSTRUCTOR ========== */

    constructor(
        address _creator_address,
        address _timelock_address,
        string memory _name,
        string memory _symbol
    ) 
    ERC20(_name, _symbol) 
    Owned(_creator_address)
    {
      timelock_address = _timelock_address;
    }


    /* ========== MODIFIERS ========== */

    modifier onlyByOwnGov() {
        require(msg.sender == timelock_address || msg.sender == owner, "Not owner or timelock");
        _;
    }

    modifier onlyMinters() {
       require(minters[msg.sender] == true, "Only minters");
        _;
    } 

    /* ========== RESTRICTED FUNCTIONS ========== */

    // Used by minters when user redeems
    function minter_burn_from(address b_address, uint256 b_amount) public onlyMinters {
        super.burnFrom(b_address, b_amount);
        emit TokenMinterBurned(b_address, msg.sender, b_amount);
    }

    // This function is what other minters will call to mint new tokens 
    function minter_mint(address m_address, uint256 m_amount) public onlyMinters {
        super._mint(m_address, m_amount);
        emit TokenMinterMinted(msg.sender, m_address, m_amount);
    }

    // Adds whitelisted minters 
    function addMinter(address minter_address) public onlyByOwnGov {
        require(minter_address != address(0), "Zero address detected");

        require(minters[minter_address] == false, "Address already exists");
        minters[minter_address] = true; 
        minters_array.push(minter_address);

        emit MinterAdded(minter_address);
    }

    // Remove a minter 
    function removeMinter(address minter_address) public onlyByOwnGov {
        require(minter_address != address(0), "Zero address detected");
        require(minters[minter_address] == true, "Address nonexistant");
        
        // Delete from the mapping
        delete minters[minter_address];

        // 'Delete' from the array by setting the address to 0x0
        for (uint i = 0; i < minters_array.length; i++){ 
            if (minters_array[i] == minter_address) {
                minters_array[i] = address(0); // This will leave a null in the array and keep the indices the same
                break;
            }
        }

        emit MinterRemoved(minter_address);
    }

    /* ========== EVENTS ========== */
    
    event TokenMinterBurned(address indexed from, address indexed to, uint256 amount);
    event TokenMinterMinted(address indexed from, address indexed to, uint256 amount);
    event MinterAdded(address minter_address);
    event MinterRemoved(address minter_address);
}


// File contracts/FPI/FPI.sol


// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ================================ FPI ===============================
// ====================================================================
// Frax Price Index
// Initial peg target is the US CPI-U (Consumer Price Index, All Urban Consumers)

// Frax Finance: https://github.com/FraxFinance

// Primary Author(s)
// Travis Moore: https://github.com/FortisFortuna
// Jack Corddry: https://github.com/corddry

// Reviewer(s) / Contributor(s)
// Sam Kazemian: https://github.com/samkazemian
// Rich Gee: https://github.com/zer0blockchain
// Dennis: https://github.com/denett
contract FPI is ERC20PermissionedMint {

    /* ========== CONSTRUCTOR ========== */

    constructor(
      address _creator_address,
      address _timelock_address
    ) 
    ERC20PermissionedMint(_creator_address, _timelock_address, "Frax Price Index", "FPI") 
    {
      _mint(_creator_address, 100000000e18); // Genesis mint
    }

}


// File contracts/Frax/IFrax.sol


interface IFrax {
  function COLLATERAL_RATIO_PAUSER() external view returns (bytes32);
  function DEFAULT_ADMIN_ADDRESS() external view returns (address);
  function DEFAULT_ADMIN_ROLE() external view returns (bytes32);
  function addPool(address pool_address ) external;
  function allowance(address owner, address spender ) external view returns (uint256);
  function approve(address spender, uint256 amount ) external returns (bool);
  function balanceOf(address account ) external view returns (uint256);
  function burn(uint256 amount ) external;
  function burnFrom(address account, uint256 amount ) external;
  function collateral_ratio_paused() external view returns (bool);
  function controller_address() external view returns (address);
  function creator_address() external view returns (address);
  function decimals() external view returns (uint8);
  function decreaseAllowance(address spender, uint256 subtractedValue ) external returns (bool);
  function eth_usd_consumer_address() external view returns (address);
  function eth_usd_price() external view returns (uint256);
  function frax_eth_oracle_address() external view returns (address);
  function frax_info() external view returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256);
  function frax_pools(address ) external view returns (bool);
  function frax_pools_array(uint256 ) external view returns (address);
  function frax_price() external view returns (uint256);
  function frax_step() external view returns (uint256);
  function fxs_address() external view returns (address);
  function fxs_eth_oracle_address() external view returns (address);
  function fxs_price() external view returns (uint256);
  function genesis_supply() external view returns (uint256);
  function getRoleAdmin(bytes32 role ) external view returns (bytes32);
  function getRoleMember(bytes32 role, uint256 index ) external view returns (address);
  function getRoleMemberCount(bytes32 role ) external view returns (uint256);
  function globalCollateralValue() external view returns (uint256);
  function global_collateral_ratio() external view returns (uint256);
  function grantRole(bytes32 role, address account ) external;
  function hasRole(bytes32 role, address account ) external view returns (bool);
  function increaseAllowance(address spender, uint256 addedValue ) external returns (bool);
  function last_call_time() external view returns (uint256);
  function minting_fee() external view returns (uint256);
  function name() external view returns (string memory);
  function owner_address() external view returns (address);
  function pool_burn_from(address b_address, uint256 b_amount ) external;
  function pool_mint(address m_address, uint256 m_amount ) external;
  function price_band() external view returns (uint256);
  function price_target() external view returns (uint256);
  function redemption_fee() external view returns (uint256);
  function refreshCollateralRatio() external;
  function refresh_cooldown() external view returns (uint256);
  function removePool(address pool_address ) external;
  function renounceRole(bytes32 role, address account ) external;
  function revokeRole(bytes32 role, address account ) external;
  function setController(address _controller_address ) external;
  function setETHUSDOracle(address _eth_usd_consumer_address ) external;
  function setFRAXEthOracle(address _frax_oracle_addr, address _weth_address ) external;
  function setFXSAddress(address _fxs_address ) external;
  function setFXSEthOracle(address _fxs_oracle_addr, address _weth_address ) external;
  function setFraxStep(uint256 _new_step ) external;
  function setMintingFee(uint256 min_fee ) external;
  function setOwner(address _owner_address ) external;
  function setPriceBand(uint256 _price_band ) external;
  function setPriceTarget(uint256 _new_price_target ) external;
  function setRedemptionFee(uint256 red_fee ) external;
  function setRefreshCooldown(uint256 _new_cooldown ) external;
  function setTimelock(address new_timelock ) external;
  function symbol() external view returns (string memory);
  function timelock_address() external view returns (address);
  function toggleCollateralRatio() external;
  function totalSupply() external view returns (uint256);
  function transfer(address recipient, uint256 amount ) external returns (bool);
  function transferFrom(address sender, address recipient, uint256 amount ) external returns (bool);
  function weth_address() external view returns (address);
}


// File contracts/Frax/IFraxAMOMinter.sol


// MAY need to be updated
interface IFraxAMOMinter {
  function FRAX() external view returns(address);
  function FXS() external view returns(address);
  function acceptOwnership() external;
  function addAMO(address amo_address, bool sync_too) external;
  function allAMOAddresses() external view returns(address[] memory);
  function allAMOsLength() external view returns(uint256);
  function amos(address) external view returns(bool);
  function amos_array(uint256) external view returns(address);
  function burnFraxFromAMO(uint256 frax_amount) external;
  function burnFxsFromAMO(uint256 fxs_amount) external;
  function col_idx() external view returns(uint256);
  function collatDollarBalance() external view returns(uint256);
  function collatDollarBalanceStored() external view returns(uint256);
  function collat_borrow_cap() external view returns(int256);
  function collat_borrowed_balances(address) external view returns(int256);
  function collat_borrowed_sum() external view returns(int256);
  function collateral_address() external view returns(address);
  function collateral_token() external view returns(address);
  function correction_offsets_amos(address, uint256) external view returns(int256);
  function custodian_address() external view returns(address);
  function dollarBalances() external view returns(uint256 frax_val_e18, uint256 collat_val_e18);
  // function execute(address _to, uint256 _value, bytes _data) external returns(bool, bytes);
  function fraxDollarBalanceStored() external view returns(uint256);
  function fraxTrackedAMO(address amo_address) external view returns(int256);
  function fraxTrackedGlobal() external view returns(int256);
  function frax_mint_balances(address) external view returns(int256);
  function frax_mint_cap() external view returns(int256);
  function frax_mint_sum() external view returns(int256);
  function fxs_mint_balances(address) external view returns(int256);
  function fxs_mint_cap() external view returns(int256);
  function fxs_mint_sum() external view returns(int256);
  function giveCollatToAMO(address destination_amo, uint256 collat_amount) external;
  function min_cr() external view returns(uint256);
  function mintFraxForAMO(address destination_amo, uint256 frax_amount) external;
  function mintFxsForAMO(address destination_amo, uint256 fxs_amount) external;
  function missing_decimals() external view returns(uint256);
  function nominateNewOwner(address _owner) external;
  function nominatedOwner() external view returns(address);
  function oldPoolCollectAndGive(address destination_amo) external;
  function oldPoolRedeem(uint256 frax_amount) external;
  function old_pool() external view returns(address);
  function owner() external view returns(address);
  function pool() external view returns(address);
  function receiveCollatFromAMO(uint256 usdc_amount) external;
  function recoverERC20(address tokenAddress, uint256 tokenAmount) external;
  function removeAMO(address amo_address, bool sync_too) external;
  function setAMOCorrectionOffsets(address amo_address, int256 frax_e18_correction, int256 collat_e18_correction) external;
  function setCollatBorrowCap(uint256 _collat_borrow_cap) external;
  function setCustodian(address _custodian_address) external;
  function setFraxMintCap(uint256 _frax_mint_cap) external;
  function setFraxPool(address _pool_address) external;
  function setFxsMintCap(uint256 _fxs_mint_cap) external;
  function setMinimumCollateralRatio(uint256 _min_cr) external;
  function setTimelock(address new_timelock) external;
  function syncDollarBalances() external;
  function timelock_address() external view returns(address);
}


// File contracts/Oracle/AggregatorV3Interface.sol


interface AggregatorV3Interface {

  function decimals() external view returns (uint8);
  function description() external view returns (string memory);
  function version() external view returns (uint256);

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

}


// File @chainlink/contracts/src/v0.8/vendor/[email protected]


/**
 * @dev A library for working with mutable byte buffers in Solidity.
 *
 * Byte buffers are mutable and expandable, and provide a variety of primitives
 * for writing to them. At any time you can fetch a bytes object containing the
 * current contents of the buffer. The bytes object should not be stored between
 * operations, as it may change due to resizing of the buffer.
 */
library BufferChainlink {
  /**
   * @dev Represents a mutable buffer. Buffers have a current value (buf) and
   *      a capacity. The capacity may be longer than the current value, in
   *      which case it can be extended without the need to allocate more memory.
   */
  struct buffer {
    bytes buf;
    uint256 capacity;
  }

  /**
   * @dev Initializes a buffer with an initial capacity.
   * @param buf The buffer to initialize.
   * @param capacity The number of bytes of space to allocate the buffer.
   * @return The buffer, for chaining.
   */
  function init(buffer memory buf, uint256 capacity) internal pure returns (buffer memory) {
    if (capacity % 32 != 0) {
      capacity += 32 - (capacity % 32);
    }
    // Allocate space for the buffer data
    buf.capacity = capacity;
    assembly {
      let ptr := mload(0x40)
      mstore(buf, ptr)
      mstore(ptr, 0)
      mstore(0x40, add(32, add(ptr, capacity)))
    }
    return buf;
  }

  /**
   * @dev Initializes a new buffer from an existing bytes object.
   *      Changes to the buffer may mutate the original value.
   * @param b The bytes object to initialize the buffer with.
   * @return A new buffer.
   */
  function fromBytes(bytes memory b) internal pure returns (buffer memory) {
    buffer memory buf;
    buf.buf = b;
    buf.capacity = b.length;
    return buf;
  }

  function resize(buffer memory buf, uint256 capacity) private pure {
    bytes memory oldbuf = buf.buf;
    init(buf, capacity);
    append(buf, oldbuf);
  }

  function max(uint256 a, uint256 b) private pure returns (uint256) {
    if (a > b) {
      return a;
    }
    return b;
  }

  /**
   * @dev Sets buffer length to 0.
   * @param buf The buffer to truncate.
   * @return The original buffer, for chaining..
   */
  function truncate(buffer memory buf) internal pure returns (buffer memory) {
    assembly {
      let bufptr := mload(buf)
      mstore(bufptr, 0)
    }
    return buf;
  }

  /**
   * @dev Writes a byte string to a buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param off The start offset to write to.
   * @param data The data to append.
   * @param len The number of bytes to copy.
   * @return The original buffer, for chaining.
   */
  function write(
    buffer memory buf,
    uint256 off,
    bytes memory data,
    uint256 len
  ) internal pure returns (buffer memory) {
    require(len <= data.length);

    if (off + len > buf.capacity) {
      resize(buf, max(buf.capacity, len + off) * 2);
    }

    uint256 dest;
    uint256 src;
    assembly {
      // Memory address of the buffer data
      let bufptr := mload(buf)
      // Length of existing buffer data
      let buflen := mload(bufptr)
      // Start address = buffer address + offset + sizeof(buffer length)
      dest := add(add(bufptr, 32), off)
      // Update buffer length if we're extending it
      if gt(add(len, off), buflen) {
        mstore(bufptr, add(len, off))
      }
      src := add(data, 32)
    }

    // Copy word-length chunks while possible
    for (; len >= 32; len -= 32) {
      assembly {
        mstore(dest, mload(src))
      }
      dest += 32;
      src += 32;
    }

    // Copy remaining bytes
    unchecked {
      uint256 mask = (256**(32 - len)) - 1;
      assembly {
        let srcpart := and(mload(src), not(mask))
        let destpart := and(mload(dest), mask)
        mstore(dest, or(destpart, srcpart))
      }
    }

    return buf;
  }

  /**
   * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @param len The number of bytes to copy.
   * @return The original buffer, for chaining.
   */
  function append(
    buffer memory buf,
    bytes memory data,
    uint256 len
  ) internal pure returns (buffer memory) {
    return write(buf, buf.buf.length, data, len);
  }

  /**
   * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @return The original buffer, for chaining.
   */
  function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) {
    return write(buf, buf.buf.length, data, data.length);
  }

  /**
   * @dev Writes a byte to the buffer. Resizes if doing so would exceed the
   *      capacity of the buffer.
   * @param buf The buffer to append to.
   * @param off The offset to write the byte at.
   * @param data The data to append.
   * @return The original buffer, for chaining.
   */
  function writeUint8(
    buffer memory buf,
    uint256 off,
    uint8 data
  ) internal pure returns (buffer memory) {
    if (off >= buf.capacity) {
      resize(buf, buf.capacity * 2);
    }

    assembly {
      // Memory address of the buffer data
      let bufptr := mload(buf)
      // Length of existing buffer data
      let buflen := mload(bufptr)
      // Address = buffer address + sizeof(buffer length) + off
      let dest := add(add(bufptr, off), 32)
      mstore8(dest, data)
      // Update buffer length if we extended it
      if eq(off, buflen) {
        mstore(bufptr, add(buflen, 1))
      }
    }
    return buf;
  }

  /**
   * @dev Appends a byte to the buffer. Resizes if doing so would exceed the
   *      capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @return The original buffer, for chaining.
   */
  function appendUint8(buffer memory buf, uint8 data) internal pure returns (buffer memory) {
    return writeUint8(buf, buf.buf.length, data);
  }

  /**
   * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would
   *      exceed the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param off The offset to write at.
   * @param data The data to append.
   * @param len The number of bytes to write (left-aligned).
   * @return The original buffer, for chaining.
   */
  function write(
    buffer memory buf,
    uint256 off,
    bytes32 data,
    uint256 len
  ) private pure returns (buffer memory) {
    if (len + off > buf.capacity) {
      resize(buf, (len + off) * 2);
    }

    unchecked {
      uint256 mask = (256**len) - 1;
      // Right-align data
      data = data >> (8 * (32 - len));
      assembly {
        // Memory address of the buffer data
        let bufptr := mload(buf)
        // Address = buffer address + sizeof(buffer length) + off + len
        let dest := add(add(bufptr, off), len)
        mstore(dest, or(and(mload(dest), not(mask)), data))
        // Update buffer length if we extended it
        if gt(add(off, len), mload(bufptr)) {
          mstore(bufptr, add(off, len))
        }
      }
    }
    return buf;
  }

  /**
   * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the
   *      capacity of the buffer.
   * @param buf The buffer to append to.
   * @param off The offset to write at.
   * @param data The data to append.
   * @return The original buffer, for chaining.
   */
  function writeBytes20(
    buffer memory buf,
    uint256 off,
    bytes20 data
  ) internal pure returns (buffer memory) {
    return write(buf, off, bytes32(data), 20);
  }

  /**
   * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @return The original buffer, for chhaining.
   */
  function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) {
    return write(buf, buf.buf.length, bytes32(data), 20);
  }

  /**
   * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @return The original buffer, for chaining.
   */
  function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) {
    return write(buf, buf.buf.length, data, 32);
  }

  /**
   * @dev Writes an integer to the buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param off The offset to write at.
   * @param data The data to append.
   * @param len The number of bytes to write (right-aligned).
   * @return The original buffer, for chaining.
   */
  function writeInt(
    buffer memory buf,
    uint256 off,
    uint256 data,
    uint256 len
  ) private pure returns (buffer memory) {
    if (len + off > buf.capacity) {
      resize(buf, (len + off) * 2);
    }

    uint256 mask = (256**len) - 1;
    assembly {
      // Memory address of the buffer data
      let bufptr := mload(buf)
      // Address = buffer address + off + sizeof(buffer length) + len
      let dest := add(add(bufptr, off), len)
      mstore(dest, or(and(mload(dest), not(mask)), data))
      // Update buffer length if we extended it
      if gt(add(off, len), mload(bufptr)) {
        mstore(bufptr, add(off, len))
      }
    }
    return buf;
  }

  /**
   * @dev Appends a byte to the end of the buffer. Resizes if doing so would
   * exceed the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @return The original buffer.
   */
  function appendInt(
    buffer memory buf,
    uint256 data,
    uint256 len
  ) internal pure returns (buffer memory) {
    return writeInt(buf, buf.buf.length, data, len);
  }
}


// File @chainlink/contracts/src/v0.8/vendor/[email protected]


library CBORChainlink {
  using BufferChainlink for BufferChainlink.buffer;

  uint8 private constant MAJOR_TYPE_INT = 0;
  uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1;
  uint8 private constant MAJOR_TYPE_BYTES = 2;
  uint8 private constant MAJOR_TYPE_STRING = 3;
  uint8 private constant MAJOR_TYPE_ARRAY = 4;
  uint8 private constant MAJOR_TYPE_MAP = 5;
  uint8 private constant MAJOR_TYPE_TAG = 6;
  uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7;

  uint8 private constant TAG_TYPE_BIGNUM = 2;
  uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3;

  function encodeFixedNumeric(BufferChainlink.buffer memory buf, uint8 major, uint64 value) private pure {
    if(value <= 23) {
      buf.appendUint8(uint8((major << 5) | value));
    } else if (value <= 0xFF) {
      buf.appendUint8(uint8((major << 5) | 24));
      buf.appendInt(value, 1);
    } else if (value <= 0xFFFF) {
      buf.appendUint8(uint8((major << 5) | 25));
      buf.appendInt(value, 2);
    } else if (value <= 0xFFFFFFFF) {
      buf.appendUint8(uint8((major << 5) | 26));
      buf.appendInt(value, 4);
    } else {
      buf.appendUint8(uint8((major << 5) | 27));
      buf.appendInt(value, 8);
    }
  }

  function encodeIndefiniteLengthType(BufferChainlink.buffer memory buf, uint8 major) private pure {
    buf.appendUint8(uint8((major << 5) | 31));
  }

  function encodeUInt(BufferChainlink.buffer memory buf, uint value) internal pure {
    if(value > 0xFFFFFFFFFFFFFFFF) {
      encodeBigNum(buf, value);
    } else {
      encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value));
    }
  }

  function encodeInt(BufferChainlink.buffer memory buf, int value) internal pure {
    if(value < -0x10000000000000000) {
      encodeSignedBigNum(buf, value);
    } else if(value > 0xFFFFFFFFFFFFFFFF) {
      encodeBigNum(buf, uint(value));
    } else if(value >= 0) {
      encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(uint256(value)));
    } else {
      encodeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(uint256(-1 - value)));
    }
  }

  function encodeBytes(BufferChainlink.buffer memory buf, bytes memory value) internal pure {
    encodeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length));
    buf.append(value);
  }

  function encodeBigNum(BufferChainlink.buffer memory buf, uint value) internal pure {
    buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM));
    encodeBytes(buf, abi.encode(value));
  }

  function encodeSignedBigNum(BufferChainlink.buffer memory buf, int input) internal pure {
    buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM));
    encodeBytes(buf, abi.encode(uint256(-1 - input)));
  }

  function encodeString(BufferChainlink.buffer memory buf, string memory value) internal pure {
    encodeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length));
    buf.append(bytes(value));
  }

  function startArray(BufferChainlink.buffer memory buf) internal pure {
    encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY);
  }

  function startMap(BufferChainlink.buffer memory buf) internal pure {
    encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP);
  }

  function endSequence(BufferChainlink.buffer memory buf) internal pure {
    encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE);
  }
}


// File @chainlink/contracts/src/v0.8/[email protected]



/**
 * @title Library for common Chainlink functions
 * @dev Uses imported CBOR library for encoding to buffer
 */
library Chainlink {
  uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase

  using CBORChainlink for BufferChainlink.buffer;

  struct Request {
    bytes32 id;
    address callbackAddress;
    bytes4 callbackFunctionId;
    uint256 nonce;
    BufferChainlink.buffer buf;
  }

  /**
   * @notice Initializes a Chainlink request
   * @dev Sets the ID, callback address, and callback function signature on the request
   * @param self The uninitialized request
   * @param jobId The Job Specification ID
   * @param callbackAddr The callback address
   * @param callbackFunc The callback function signature
   * @return The initialized request
   */
  function initialize(
    Request memory self,
    bytes32 jobId,
    address callbackAddr,
    bytes4 callbackFunc
  ) internal pure returns (Chainlink.Request memory) {
    BufferChainlink.init(self.buf, defaultBufferSize);
    self.id = jobId;
    self.callbackAddress = callbackAddr;
    self.callbackFunctionId = callbackFunc;
    return self;
  }

  /**
   * @notice Sets the data for the buffer without encoding CBOR on-chain
   * @dev CBOR can be closed with curly-brackets {} or they can be left off
   * @param self The initialized request
   * @param data The CBOR data
   */
  function setBuffer(Request memory self, bytes memory data) internal pure {
    BufferChainlink.init(self.buf, data.length);
    BufferChainlink.append(self.buf, data);
  }

  /**
   * @notice Adds a string value to the request with a given key name
   * @param self The initialized request
   * @param key The name of the key
   * @param value The string value to add
   */
  function add(
    Request memory self,
    string memory key,
    string memory value
  ) internal pure {
    self.buf.encodeString(key);
    self.buf.encodeString(value);
  }

  /**
   * @notice Adds a bytes value to the request with a given key name
   * @param self The initialized request
   * @param key The name of the key
   * @param value The bytes value to add
   */
  function addBytes(
    Request memory self,
    string memory key,
    bytes memory value
  ) internal pure {
    self.buf.encodeString(key);
    self.buf.encodeBytes(value);
  }

  /**
   * @notice Adds a int256 value to the request with a given key name
   * @param self The initialized request
   * @param key The name of the key
   * @param value The int256 value to add
   */
  function addInt(
    Request memory self,
    string memory key,
    int256 value
  ) internal pure {
    self.buf.encodeString(key);
    self.buf.encodeInt(value);
  }

  /**
   * @notice Adds a uint256 value to the request with a given key name
   * @param self The initialized request
   * @param key The name of the key
   * @param value The uint256 value to add
   */
  function addUint(
    Request memory self,
    string memory key,
    uint256 value
  ) internal pure {
    self.buf.encodeString(key);
    self.buf.encodeUInt(value);
  }

  /**
   * @notice Adds an array of strings to the request with a given key name
   * @param self The initialized request
   * @param key The name of the key
   * @param values The array of string values to add
   */
  function addStringArray(
    Request memory self,
    string memory key,
    string[] memory values
  ) internal pure {
    self.buf.encodeString(key);
    self.buf.startArray();
    for (uint256 i = 0; i < values.length; i++) {
      self.buf.encodeString(values[i]);
    }
    self.buf.endSequence();
  }
}


// File @chainlink/contracts/src/v0.8/interfaces/[email protected]


interface ENSInterface {
  // Logged when the owner of a node assigns a new owner to a subnode.
  event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);

  // Logged when the owner of a node transfers ownership to a new account.
  event Transfer(bytes32 indexed node, address owner);

  // Logged when the resolver for a node changes.
  event NewResolver(bytes32 indexed node, address resolver);

  // Logged when the TTL of a node changes
  event NewTTL(bytes32 indexed node, uint64 ttl);

  function setSubnodeOwner(
    bytes32 node,
    bytes32 label,
    address owner
  ) external;

  function setResolver(bytes32 node, address resolver) external;

  function setOwner(bytes32 node, address owner) external;

  function setTTL(bytes32 node, uint64 ttl) external;

  function owner(bytes32 node) external view returns (address);

  function resolver(bytes32 node) external view returns (address);

  function ttl(bytes32 node) external view returns (uint64);
}


// File @chainlink/contracts/src/v0.8/interfaces/[email protected]


interface LinkTokenInterface {
  function allowance(address owner, address spender) external view returns (uint256 remaining);

  function approve(address spender, uint256 value) external returns (bool success);

  function balanceOf(address owner) external view returns (uint256 balance);

  function decimals() external view returns (uint8 decimalPlaces);

  function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);

  function increaseApproval(address spender, uint256 subtractedValue) external;

  function name() external view returns (string memory tokenName);

  function symbol() external view returns (string memory tokenSymbol);

  function totalSupply() external view returns (uint256 totalTokensIssued);

  function transfer(address to, uint256 value) external returns (bool success);

  function transferAndCall(
    address to,
    uint256 value,
    bytes calldata data
  ) external returns (bool success);

  function transferFrom(
    address from,
    address to,
    uint256 value
  ) external returns (bool success);
}


// File @chainlink/contracts/src/v0.8/interfaces/[email protected]


interface ChainlinkRequestInterface {
  function oracleRequest(
    address sender,
    uint256 requestPrice,
    bytes32 serviceAgreementID,
    address callbackAddress,
    bytes4 callbackFunctionId,
    uint256 nonce,
    uint256 dataVersion,
    bytes calldata data
  ) external;

  function cancelOracleRequest(
    bytes32 requestId,
    uint256 payment,
    bytes4 callbackFunctionId,
    uint256 expiration
  ) external;
}


// File @chainlink/contracts/src/v0.8/interfaces/[email protected]


interface OracleInterface {
  function fulfillOracleRequest(
    bytes32 requestId,
    uint256 payment,
    address callbackAddress,
    bytes4 callbackFunctionId,
    uint256 expiration,
    bytes32 data
  ) external returns (bool);

  function isAuthorizedSender(address node) external view returns (bool);

  function withdraw(address recipient, uint256 amount) external;

  function withdrawable() external view returns (uint256);
}


// File @chainlink/contracts/src/v0.8/interfaces/[email protected]



interface OperatorInterface is OracleInterface, ChainlinkRequestInterface {
  function operatorRequest(
    address sender,
    uint256 payment,
    bytes32 specId,
    bytes4 callbackFunctionId,
    uint256 nonce,
    uint256 dataVersion,
    bytes calldata data
  ) external;

  function fulfillOracleRequest2(
    bytes32 requestId,
    uint256 payment,
    address callbackAddress,
    bytes4 callbackFunctionId,
    uint256 expiration,
    bytes calldata data
  ) external returns (bool);

  function ownerTransferAndCall(
    address to,
    uint256 value,
    bytes calldata data
  ) external returns (bool success);

  function distributeFunds(address payable[] calldata receivers, uint256[] calldata amounts) external payable;

  function getAuthorizedSenders() external returns (address[] memory);

  function setAuthorizedSenders(address[] calldata senders) external;

  function getForwarder() external returns (address);
}


// File @chainlink/contracts/src/v0.8/interfaces/[email protected]


interface PointerInterface {
  function getAddress() external view returns (address);
}


// File @chainlink/contracts/src/v0.8/vendor/[email protected]


abstract contract ENSResolver_Chainlink {
  function addr(bytes32 node) public view virtual returns (address);
}


// File @chainlink/contracts/src/v0.8/[email protected]








/**
 * @title The ChainlinkClient contract
 * @notice Contract writers can inherit this contract in order to create requests for the
 * Chainlink network
 */
abstract contract ChainlinkClient {
  using Chainlink for Chainlink.Request;

  uint256 internal constant LINK_DIVISIBILITY = 10**18;
  uint256 private constant AMOUNT_OVERRIDE = 0;
  address private constant SENDER_OVERRIDE = address(0);
  uint256 private constant ORACLE_ARGS_VERSION = 1;
  uint256 private constant OPERATOR_ARGS_VERSION = 2;
  bytes32 private constant ENS_TOKEN_SUBNAME = keccak256("link");
  bytes32 private constant ENS_ORACLE_SUBNAME = keccak256("oracle");
  address private constant LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571;

  ENSInterface private s_ens;
  bytes32 private s_ensNode;
  LinkTokenInterface private s_link;
  OperatorInterface private s_oracle;
  uint256 private s_requestCount = 1;
  mapping(bytes32 => address) private s_pendingRequests;

  event ChainlinkRequested(bytes32 indexed id);
  event ChainlinkFulfilled(bytes32 indexed id);
  event ChainlinkCancelled(bytes32 indexed id);

  /**
   * @notice Creates a request that can hold additional parameters
   * @param specId The Job Specification ID that the request will be created for
   * @param callbackAddr address to operate the callback on
   * @param callbackFunctionSignature function signature to use for the callback
   * @return A Chainlink Request struct in memory
   */
  function buildChainlinkRequest(
    bytes32 specId,
    address callbackAddr,
    bytes4 callbackFunctionSignature
  ) internal pure returns (Chainlink.Request memory) {
    Chainlink.Request memory req;
    return req.initialize(specId, callbackAddr, callbackFunctionSignature);
  }

  /**
   * @notice Creates a request that can hold additional parameters
   * @param specId The Job Specification ID that the request will be created for
   * @param callbackFunctionSignature function signature to use for the callback
   * @return A Chainlink Request struct in memory
   */
  function buildOperatorRequest(bytes32 specId, bytes4 callbackFunctionSignature)
    internal
    view
    returns (Chainlink.Request memory)
  {
    Chainlink.Request memory req;
    return req.initialize(specId, address(this), callbackFunctionSignature);
  }

  /**
   * @notice Creates a Chainlink request to the stored oracle address
   * @dev Calls `chainlinkRequestTo` with the stored oracle address
   * @param req The initialized Chainlink Request
   * @param payment The amount of LINK to send for the request
   * @return requestId The request ID
   */
  function sendChainlinkRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) {
    return sendChainlinkRequestTo(address(s_oracle), req, payment);
  }

  /**
   * @notice Creates a Chainlink request to the specified oracle address
   * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to
   * send LINK which creates a request on the target oracle contract.
   * Emits ChainlinkRequested event.
   * @param oracleAddress The address of the oracle for the request
   * @param req The initialized Chainlink Request
   * @param payment The amount of LINK to send for the request
   * @return requestId The request ID
   */
  function sendChainlinkRequestTo(
    address oracleAddress,
    Chainlink.Request memory req,
    uint256 payment
  ) internal returns (bytes32 requestId) {
    uint256 nonce = s_requestCount;
    s_requestCount = nonce + 1;
    bytes memory encodedRequest = abi.encodeWithSelector(
      ChainlinkRequestInterface.oracleRequest.selector,
      SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address
      AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent
      req.id,
      address(this),
      req.callbackFunctionId,
      nonce,
      ORACLE_ARGS_VERSION,
      req.buf.buf
    );
    return _rawRequest(oracleAddress, nonce, payment, encodedRequest);
  }

  /**
   * @notice Creates a Chainlink request to the stored oracle address
   * @dev This function supports multi-word response
   * @dev Calls `sendOperatorRequestTo` with the stored oracle address
   * @param req The initialized Chainlink Request
   * @param payment The amount of LINK to send for the request
   * @return requestId The request ID
   */
  function sendOperatorRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) {
    return sendOperatorRequestTo(address(s_oracle), req, payment);
  }

  /**
   * @notice Creates a Chainlink request to the specified oracle address
   * @dev This function supports multi-word response
   * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to
   * send LINK which creates a request on the target oracle contract.
   * Emits ChainlinkRequested event.
   * @param oracleAddress The address of the oracle for the request
   * @param req The initialized Chainlink Request
   * @param payment The amount of LINK to send for the request
   * @return requestId The request ID
   */
  function sendOperatorRequestTo(
    address oracleAddress,
    Chainlink.Request memory req,
    uint256 payment
  ) internal returns (bytes32 requestId) {
    uint256 nonce = s_requestCount;
    s_requestCount = nonce + 1;
    bytes memory encodedRequest = abi.encodeWithSelector(
      OperatorInterface.operatorRequest.selector,
      SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address
      AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent
      req.id,
      req.callbackFunctionId,
      nonce,
      OPERATOR_ARGS_VERSION,
      req.buf.buf
    );
    return _rawRequest(oracleAddress, nonce, payment, encodedRequest);
  }

  /**
   * @notice Make a request to an oracle
   * @param oracleAddress The address of the oracle for the request
   * @param nonce used to generate the request ID
   * @param payment The amount of LINK to send for the request
   * @param encodedRequest data encoded for request type specific format
   * @return requestId The request ID
   */
  function _rawRequest(
    address oracleAddress,
    uint256 nonce,
    uint256 payment,
    bytes memory encodedRequest
  ) private returns (bytes32 requestId) {
    requestId = keccak256(abi.encodePacked(this, nonce));
    s_pendingRequests[requestId] = oracleAddress;
    emit ChainlinkRequested(requestId);
    require(s_link.transferAndCall(oracleAddress, payment, encodedRequest), "unable to transferAndCall to oracle");
  }

  /**
   * @notice Allows a request to be cancelled if it has not been fulfilled
   * @dev Requires keeping track of the expiration value emitted from the oracle contract.
   * Deletes the request from the `pendingRequests` mapping.
   * Emits ChainlinkCancelled event.
   * @param requestId The request ID
   * @param payment The amount of LINK sent for the request
   * @param callbackFunc The callback function specified for the request
   * @param expiration The time of the expiration for the request
   */
  function cancelChainlinkRequest(
    bytes32 requestId,
    uint256 payment,
    bytes4 callbackFunc,
    uint256 expiration
  ) internal {
    OperatorInterface requested = OperatorInterface(s_pendingRequests[requestId]);
    delete s_pendingRequests[requestId];
    emit ChainlinkCancelled(requestId);
    requested.cancelOracleRequest(requestId, payment, callbackFunc, expiration);
  }

  /**
   * @notice the next request count to be used in generating a nonce
   * @dev starts at 1 in order to ensure consistent gas cost
   * @return returns the next request count to be used in a nonce
   */
  function getNextRequestCount() internal view returns (uint256) {
    return s_requestCount;
  }

  /**
   * @notice Sets the stored oracle address
   * @param oracleAddress The address of the oracle contract
   */
  function setChainlinkOracle(address oracleAddress) internal {
    s_oracle = OperatorInterface(oracleAddress);
  }

  /**
   * @notice Sets the LINK token address
   * @param linkAddress The address of the LINK token contract
   */
  function setChainlinkToken(address linkAddress) internal {
    s_link = LinkTokenInterface(linkAddress);
  }

  /**
   * @notice Sets the Chainlink token address for the public
   * network as given by the Pointer contract
   */
  function setPublicChainlinkToken() internal {
    setChainlinkToken(PointerInterface(LINK_TOKEN_POINTER).getAddress());
  }

  /**
   * @notice Retrieves the stored address of the LINK token
   * @return The address of the LINK token
   */
  function chainlinkTokenAddress() internal view returns (address) {
    return address(s_link);
  }

  /**
   * @notice Retrieves the stored address of the oracle contract
   * @return The address of the oracle contract
   */
  function chainlinkOracleAddress() internal view returns (address) {
    return address(s_oracle);
  }

  /**
   * @notice Allows for a request which was created on another contract to be fulfilled
   * on this contract
   * @param oracleAddress The address of the oracle contract that will fulfill the request
   * @param requestId The request ID used for the response
   */
  function addChainlinkExternalRequest(address oracleAddress, bytes32 requestId) internal notPendingRequest(requestId) {
    s_pendingRequests[requestId] = oracleAddress;
  }

  /**
   * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS
   * @dev Accounts for subnodes having different resolvers
   * @param ensAddress The address of the ENS contract
   * @param node The ENS node hash
   */
  function useChainlinkWithENS(address ensAddress, bytes32 node) internal {
    s_ens = ENSInterface(ensAddress);
    s_ensNode = node;
    bytes32 linkSubnode = keccak256(abi.encodePacked(s_ensNode, ENS_TOKEN_SUBNAME));
    ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_ens.resolver(linkSubnode));
    setChainlinkToken(resolver.addr(linkSubnode));
    updateChainlinkOracleWithENS();
  }

  /**
   * @notice Sets the stored oracle contract with the address resolved by ENS
   * @dev This may be called on its own as long as `useChainlinkWithENS` has been called previously
   */
  function updateChainlinkOracleWithENS() internal {
    bytes32 oracleSubnode = keccak256(abi.encodePacked(s_ensNode, ENS_ORACLE_SUBNAME));
    ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_ens.resolver(oracleSubnode));
    setChainlinkOracle(resolver.addr(oracleSubnode));
  }

  /**
   * @notice Ensures that the fulfillment is valid for this contract
   * @dev Use if the contract developer prefers methods instead of modifiers for validation
   * @param requestId The request ID for fulfillment
   */
  function validateChainlinkCallback(bytes32 requestId)
    internal
    recordChainlinkFulfillment(requestId)
  // solhint-disable-next-line no-empty-blocks
  {

  }

  /**
   * @dev Reverts if the sender is not the oracle of the request.
   * Emits ChainlinkFulfilled event.
   * @param requestId The request ID for fulfillment
   */
  modifier recordChainlinkFulfillment(bytes32 requestId) {
    require(msg.sender == s_pendingRequests[requestId], "Source must be the oracle of the request");
    delete s_pendingRequests[requestId];
    emit ChainlinkFulfilled(requestId);
    _;
  }

  /**
   * @dev Reverts if the request is already pending
   * @param requestId The request ID for fulfillment
   */
  modifier notPendingRequest(bytes32 requestId) {
    require(s_pendingRequests[requestId] == address(0), "Request is already pending");
    _;
  }
}


// File contracts/Math/BokkyPooBahsDateTimeLibrary.sol


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

library BokkyPooBahsDateTimeLibrary {

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

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

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

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

        _days = uint(__days);
    }

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

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

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

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

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

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

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

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

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


// File contracts/Math/BokkyPooBahsDateTimeContract.sol


// ----------------------------------------------------------------------------
// BokkyPooBah's DateTime Library v1.00 - Contract Instance
//
// A gas-efficient Solidity date and time library
//
// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary
//
// Tested date range 1970/01/01 to 2345/12/31
//
// Conventions:
// Unit      | Range         | Notes
// :-------- |:-------------:|:-----
// timestamp | >= 0          | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC
// year      | 1970 ... 2345 |
// month     | 1 ... 12      |
// day       | 1 ... 31      |
// hour      | 0 ... 23      |
// minute    | 0 ... 59      |
// second    | 0 ... 59      |
// dayOfWeek | 1 ... 7       | 1 = Monday, ..., 7 = Sunday
//
//
// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018.
//
// GNU Lesser General Public License 3.0
// https://www.gnu.org/licenses/lgpl-3.0.en.html
// ----------------------------------------------------------------------------

contract BokkyPooBahsDateTimeContract {
    uint public constant SECONDS_PER_DAY = 24 * 60 * 60;
    uint public constant SECONDS_PER_HOUR = 60 * 60;
    uint public constant SECONDS_PER_MINUTE = 60;
    int public constant OFFSET19700101 = 2440588;

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

    function _now() public view returns (uint timestamp) {
        timestamp = block.timestamp;
    }
    function _nowDateTime() public view returns (uint year, uint month, uint day, uint hour, uint minute, uint second) {
        (year, month, day, hour, minute, second) = BokkyPooBahsDateTimeLibrary.timestampToDateTime(block.timestamp);
    }
    function _daysFromDate(uint year, uint month, uint day) public pure returns (uint _days) {
        return BokkyPooBahsDateTimeLibrary._daysFromDate(year, month, day);
    }
    function _daysToDate(uint _days) public pure returns (uint year, uint month, uint day) {
        return BokkyPooBahsDateTimeLibrary._daysToDate(_days);
    }
    function timestampFromDate(uint year, uint month, uint day) public pure returns (uint timestamp) {
        return BokkyPooBahsDateTimeLibrary.timestampFromDate(year, month, day);
    }
    function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) public pure returns (uint timestamp) {
        return BokkyPooBahsDateTimeLibrary.timestampFromDateTime(year, month, day, hour, minute, second);
    }
    function timestampToDate(uint timestamp) public pure returns (uint year, uint month, uint day) {
        (year, month, day) = BokkyPooBahsDateTimeLibrary.timestampToDate(timestamp);
    }
    function timestampToDateTime(uint timestamp) public pure returns (uint year, uint month, uint day, uint hour, uint minute, uint second) {
        (year, month, day, hour, minute, second) = BokkyPooBahsDateTimeLibrary.timestampToDateTime(timestamp);
    }

    function isValidDate(uint year, uint month, uint day) public pure returns (bool valid) {
        valid = BokkyPooBahsDateTimeLibrary.isValidDate(year, month, day);
    }
    function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) public pure returns (bool valid) {
        valid = BokkyPooBahsDateTimeLibrary.isValidDateTime(year, month, day, hour, minute, second);
    }
    function isLeapYear(uint timestamp) public pure returns (bool leapYear) {
        leapYear = BokkyPooBahsDateTimeLibrary.isLeapYear(timestamp);
    }
    function _isLeapYear(uint year) public pure returns (bool leapYear) {
        leapYear = BokkyPooBahsDateTimeLibrary._isLeapYear(year);
    }
    function isWeekDay(uint timestamp) public pure returns (bool weekDay) {
        weekDay = BokkyPooBahsDateTimeLibrary.isWeekDay(timestamp);
    }
    function isWeekEnd(uint timestamp) public pure returns (bool weekEnd) {
        weekEnd = BokkyPooBahsDateTimeLibrary.isWeekEnd(timestamp);
    }

    function getDaysInMonth(uint timestamp) public pure returns (uint daysInMonth) {
        daysInMonth = BokkyPooBahsDateTimeLibrary.getDaysInMonth(timestamp);
    }
    function _getDaysInMonth(uint year, uint month) public pure returns (uint daysInMonth) {
        daysInMonth = BokkyPooBahsDateTimeLibrary._getDaysInMonth(year, month);
    }
    function getDayOfWeek(uint timestamp) public pure returns (uint dayOfWeek) {
        dayOfWeek = BokkyPooBahsDateTimeLibrary.getDayOfWeek(timestamp);
    }

    function getYear(uint timestamp) public pure returns (uint year) {
        year = BokkyPooBahsDateTimeLibrary.getYear(timestamp);
    }
    function getMonth(uint timestamp) public pure returns (uint month) {
        month = BokkyPooBahsDateTimeLibrary.getMonth(timestamp);
    }
    function getDay(uint timestamp) public pure returns (uint day) {
        day = BokkyPooBahsDateTimeLibrary.getDay(timestamp);
    }
    function getHour(uint timestamp) public pure returns (uint hour) {
        hour = BokkyPooBahsDateTimeLibrary.getHour(timestamp);
    }
    function getMinute(uint timestamp) public pure returns (uint minute) {
        minute = BokkyPooBahsDateTimeLibrary.getMinute(timestamp);
    }
    function getSecond(uint timestamp) public pure returns (uint second) {
        second = BokkyPooBahsDateTimeLibrary.getSecond(timestamp);
    }

    function addYears(uint timestamp, uint _years) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.addYears(timestamp, _years);
    }
    function addMonths(uint timestamp, uint _months) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.addMonths(timestamp, _months);
    }
    function addDays(uint timestamp, uint _days) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.addDays(timestamp, _days);
    }
    function addHours(uint timestamp, uint _hours) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.addHours(timestamp, _hours);
    }
    function addMinutes(uint timestamp, uint _minutes) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.addMinutes(timestamp, _minutes);
    }
    function addSeconds(uint timestamp, uint _seconds) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.addSeconds(timestamp, _seconds);
    }

    function subYears(uint timestamp, uint _years) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.subYears(timestamp, _years);
    }
    function subMonths(uint timestamp, uint _months) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.subMonths(timestamp, _months);
    }
    function subDays(uint timestamp, uint _days) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.subDays(timestamp, _days);
    }
    function subHours(uint timestamp, uint _hours) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.subHours(timestamp, _hours);
    }
    function subMinutes(uint timestamp, uint _minutes) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.subMinutes(timestamp, _minutes);
    }
    function subSeconds(uint timestamp, uint _seconds) public pure returns (uint newTimestamp) {
        newTimestamp = BokkyPooBahsDateTimeLibrary.subSeconds(timestamp, _seconds);
    }

    function diffYears(uint fromTimestamp, uint toTimestamp) public pure returns (uint _years) {
        _years = BokkyPooBahsDateTimeLibrary.diffYears(fromTimestamp, toTimestamp);
    }
    function diffMonths(uint fromTimestamp, uint toTimestamp) public pure returns (uint _months) {
        _months = BokkyPooBahsDateTimeLibrary.diffMonths(fromTimestamp, toTimestamp);
    }
    function diffDays(uint fromTimestamp, uint toTimestamp) public pure returns (uint _days) {
        _days = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, toTimestamp);
    }
    function diffHours(uint fromTimestamp, uint toTimestamp) public pure returns (uint _hours) {
        _hours = BokkyPooBahsDateTimeLibrary.diffHours(fromTimestamp, toTimestamp);
    }
    function diffMinutes(uint fromTimestamp, uint toTimestamp) public pure returns (uint _minutes) {
        _minutes = BokkyPooBahsDateTimeLibrary.diffMinutes(fromTimestamp, toTimestamp);
    }
    function diffSeconds(uint fromTimestamp, uint toTimestamp) public pure returns (uint _seconds) {
        _seconds = BokkyPooBahsDateTimeLibrary.diffSeconds(fromTimestamp, toTimestamp);
    }
}


// File contracts/Uniswap/TransferHelper.sol


// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(address token, address to, uint value) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
    }

    function safeTransfer(address token, address to, uint value) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
    }

    function safeTransferFrom(address token, address from, address to, uint value) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
    }

    function safeTransferETH(address to, uint value) internal {
        (bool success,) = to.call{value:value}(new bytes(0));
        require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
    }
}


// File contracts/Oracle/CPITrackerOracle.sol


// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ========================= CPITrackerOracle =========================
// ====================================================================
// Pull in CPI data and track it in Dec 2021 dollars

// Frax Finance: https://github.com/FraxFinance

// Primary Author(s)
// Travis Moore: https://github.com/FortisFortuna

// Reviewer(s) / Contributor(s)
// Sam Kazemian: https://github.com/samkazemian
// Rich Gee: https://github.com/zer0blockchain
// Dennis: https://github.com/denett

// References
// https://docs.chain.link/docs/make-a-http-get-request/#api-consumer-example






contract CPITrackerOracle is Owned, ChainlinkClient {
    using Chainlink for Chainlink.Request;
  
    // Core
    BokkyPooBahsDateTimeContract public time_contract;
    address public timelock_address;
    address public bot_address;

    // Data
    uint256 public cpi_last = 28012600000; // Dec 2021 CPI-U, 280.126 * 100000000
    uint256 public cpi_target = 28193300000; // Jan 2022 CPI-U, 281.933 * 100000000
    uint256 public peg_price_last = 1e18; // Use currPegPrice(). Will always be in Dec 2021 dollars
    uint256 public peg_price_target = 1006450668627688968; // Will always be in Dec 2021 dollars

    // Chainlink
    address public oracle; // Chainlink CPI oracle address
    bytes32 public jobId; // Job ID for the CPI-U date
    uint256 public fee; // LINK token fee

    // Tracking
    uint256 public stored_year = 2022; // Last time (year) the stored CPI data was updated
    uint256 public stored_month = 1; // Last time (month) the stored CPI data was updated
    uint256 public lastUpdateTime = 1644886800; // Last time the stored CPI data was updated.
    uint256 public ramp_period = 28 * 86400; // Apply the CPI delta to the peg price over a set period
    uint256 public future_ramp_period = 28 * 86400;
    CPIObservation[] public cpi_observations; // Historical tracking of CPI data

    // Safety
    uint256 public max_delta_frac = 25000; // 2.5%. Max month-to-month CPI delta. 

    // Misc
    string[13] public month_names; // English names of the 12 months
    uint256 public fulfill_ready_day = 15; // Date of the month that CPI data is expected to by ready by


    /* ========== STRUCTS ========== */
    
    struct CPIObservation {
        uint256 result_year;
        uint256 result_month;
        uint256 cpi_target;
        uint256 peg_price_target;
        uint256 timestamp;
    }

    /* ========== MODIFIERS ========== */

    modifier onlyByOwnGov() {
        require(msg.sender == owner || msg.sender == timelock_address, "Not owner or timelock");
        _;
    }

    modifier onlyByOwnGovBot() {
        require(msg.sender == owner || msg.sender == timelock_address || msg.sender == bot_address, "Not owner, tlck, or bot");
        _;
    }

    /* ========== CONSTRUCTOR ========== */

    constructor (
        address _creator_address,
        address _timelock_address
    ) Owned(_creator_address) {
        timelock_address = _timelock_address;

        // Initialize the array. Cannot be done in the declaration
        month_names = [
            '',
            'January',
            'February',
            'March',
            'April',
            'May',
            'June',
            'July',
            'August',
            'September',
            'October',
            'November',
            'December'
        ];

        // CPI [Ethereum]
        // =================================
        // setPublicChainlinkToken();
        // time_contract = BokkyPooBahsDateTimeContract(0x90503D86E120B3B309CEBf00C2CA013aB3624736);
        // oracle = 0x049Bd8C3adC3fE7d3Fc2a44541d955A537c2A484;
        // jobId = "1c309d42c7084b34b1acf1a89e7b51fc";
        // fee = 50e18; // 50 LINK

        // CPI [Polygon Mainnet]
        // =================================
        // setChainlinkToken(0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39);
        // time_contract = BokkyPooBahsDateTimeContract(0x998da4fCB229Db1AA84395ef6f0c6be6Ef3dbE58);
        // oracle = 0x9B44870bcc35734c08e40F847cC068c0bA618194;
        // jobId = "8107f18343a24980b2fe7d3c8f32630f";
        // fee = 1e17; // 0.1 LINK

        // CPI [Polygon Mumbai]
        // =================================
        setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
        time_contract = BokkyPooBahsDateTimeContract(0x2Dd1B4D4548aCCeA497050619965f91f78b3b532);
        oracle = 0x3c30c5c415B2410326297F0f65f5Cbb32f3aefCc;
        jobId = "32c3e7b12fe44665a4e2bb87aa9779af";
        fee = 1e17; // 0.1 LINK

        // Add the first observation
        cpi_observations.push(CPIObservation(
            2021,
            12,
            cpi_last,
            peg_price_last,
            1642208400 // Dec data observed on Jan 15 2021
        ));

        // Add the second observation
        cpi_observations.push(CPIObservation(
            2022,
            1,
            cpi_target,
            peg_price_target,
            1644886800 // Jan data observed on Feb 15 2022
        ));
    }

    /* ========== VIEWS ========== */
    function upcomingCPIParams() public view returns (
        uint256 upcoming_year,
        uint256 upcoming_month, 
        uint256 upcoming_timestamp
    ) {
        if (stored_month == 12) {
            upcoming_year = stored_year + 1;
            upcoming_month = 1;
        }
        else {
            upcoming_year = stored_year;
            upcoming_month = stored_month + 1;
        }

        // Data is usually released by the 15th day of the next month (fulfill_ready_day)
        // https://www.usinflationcalculator.com/inflation/consumer-price-index-release-schedule/
        upcoming_timestamp = time_contract.timestampFromDate(upcoming_year, upcoming_month, fulfill_ready_day);
    }

    // Display the upcoming CPI month
    function upcomingSerie() external view returns (string memory serie_name) {
        // Get the upcoming CPI params
        (uint256 upcoming_year, uint256 upcoming_month, ) = upcomingCPIParams();

        // Convert to a string
        return string(abi.encodePacked("CUSR0000SA0", " ", month_names[upcoming_month], " ", Strings.toString(upcoming_year)));
    }

    // Delta between the current and previous peg prices
    function currDeltaFracE6() public view returns (int256) {
        return int256(((peg_price_target - peg_price_last) * 1e6) / peg_price_last);
    }

    // Absolute value of the delta between the current and previous peg prices
    function currDeltaFracAbsE6() public view returns (uint256) {
        int256 curr_delta_frac = currDeltaFracE6();
        if (curr_delta_frac > 0) return uint256(curr_delta_frac);
        else return uint256(-curr_delta_frac);
    }

    // Current peg price in E18, accounting for the ramping
    function currPegPrice() external view returns (uint256) {
        uint256 elapsed_time = block.timestamp - lastUpdateTime;
        if (elapsed_time >= ramp_period) {
            return peg_price_target;
        }
        else {
            // Calculate the fraction of the delta to use, based on the elapsed time
            // Can be negative in case of deflation (that never happens right :])
            int256 fractional_price_delta = (int256(peg_price_target - peg_price_last) * int256(elapsed_time)) / int256(ramp_period);
            return uint256(int256(peg_price_last) + int256(fractional_price_delta));
        }
    }

    /* ========== MUTATIVE ========== */

    // Fetch the CPI data from the Chainlink oracle
    function requestCPIData() external onlyByOwnGovBot returns (bytes32 requestId) 
    {
        Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);

        // Get the upcoming CPI params
        (uint256 upcoming_year, uint256 upcoming_month, uint256 upcoming_timestamp) = upcomingCPIParams();

        // Don't update too fast
        require(block.timestamp >= upcoming_timestamp, "Too early");

        request.add("serie", "CUSR0000SA0"); // CPI-U: https://data.bls.gov/timeseries/CUSR0000SA0
        request.add("month", month_names[upcoming_month]);
        request.add("year", Strings.toString(upcoming_year)); 
        return sendChainlinkRequestTo(oracle, request, fee);
    }

    /**
     * Callback function
     */
    //  Called by the Chainlink oracle
    function fulfill(bytes32 _requestId, uint256 result) public recordChainlinkFulfillment(_requestId)
    {
        // Set the stored CPI and price to the old targets
        cpi_last = cpi_target;
        peg_price_last = peg_price_target;

        // Set the target CPI and price based on the results
        cpi_target = result;
        peg_price_target = (peg_price_last * cpi_target) / cpi_last;

        // Make sure the delta isn't too large
        require(currDeltaFracAbsE6() <= max_delta_frac, "Delta too high");

        // Update the timestamp
        lastUpdateTime = block.timestamp;

        // Update the year and month
        (uint256 result_year, uint256 result_month, ) = upcomingCPIParams();
        stored_year = result_year;
        stored_month = result_month;

        // Update the future ramp period, if applicable
        // A ramp cannot be updated mid-month as it will mess up the last_price math;
        ramp_period = future_ramp_period;

        // Add the observation
        cpi_observations.push(CPIObservation(
            result_year,
            result_month,
            cpi_target,
            peg_price_target,
            block.timestamp
        ));

        emit CPIUpdated(result_year, result_month, result, peg_price_target, ramp_period);
    }

    function cancelRequest(
        bytes32 _requestId,
        uint256 _payment,
        bytes4 _callbackFunc,
        uint256 _expiration
    ) external onlyByOwnGovBot {
        cancelChainlinkRequest(_requestId, _payment, _callbackFunc, _expiration);
    }
    
    /* ========== RESTRICTED FUNCTIONS ========== */

    function setTimelock(address _new_timelock_address) external onlyByOwnGov {
        timelock_address = _new_timelock_address;
    }

    function setBot(address _new_bot_address) external onlyByOwnGov {
        bot_address = _new_bot_address;
    }

    function setOracleInfo(address _oracle, bytes32 _jobId, uint256 _fee) external onlyByOwnGov {
        oracle = _oracle;
        jobId = _jobId;
        fee = _fee;
    }

    function setMaxDeltaFrac(uint256 _max_delta_frac) external onlyByOwnGov {
        max_delta_frac = _max_delta_frac; 
    }

    function setFulfillReadyDay(uint256 _fulfill_ready_day) external onlyByOwnGov {
        fulfill_ready_day = _fulfill_ready_day; 
    }

    function setFutureRampPeriod(uint256 _future_ramp_period) external onlyByOwnGov {
        future_ramp_period = _future_ramp_period; // In sec
    }

    // Mainly for recovering LINK
    function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyByOwnGov {
        // Only the owner address can ever receive the recovery withdrawal
        TransferHelper.safeTransfer(tokenAddress, owner, tokenAmount);
    }

    /* ========== EVENTS ========== */
    
    event CPIUpdated(uint256 year, uint256 month, uint256 result, uint256 peg_price_target, uint256 ramp_period);
}


// File contracts/Uniswap_V2_TWAMM/core/interfaces/IUniswapV2PairV5.sol


interface IUniswapV2PairV5 {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;
    function initialize(address, address) external;
}


// File contracts/Uniswap_V2_TWAMM/core/interfaces/IUniV2TWAMMPair.sol


// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ========================= IUniV2TWAMMPair ==========================
// ====================================================================
// TWAMM LP Pair Interface
// Inspired by https://www.paradigm.xyz/2021/07/twamm
// https://github.com/para-dave/twamm

// Frax Finance: https://github.com/FraxFinance

// Primary Author(s)
// Rich Gee: https://github.com/zer0blockchain
// Dennis: https://github.com/denett

// Reviewer(s) / Contributor(s)
// Travis Moore: https://github.com/FortisFortuna
// Sam Kazemian: https://github.com/samkazemian

interface IUniV2TWAMMPair is IUniswapV2PairV5 {
    // TWAMM

    event LongTermSwap0To1(address indexed addr, uint256 orderId, uint256 amount0In, uint256 numberOfTimeIntervals);
    event LongTermSwap1To0(address indexed addr, uint256 orderId, uint256 amount1In, uint256 numberOfTimeIntervals);
    event CancelLongTermOrder(address indexed addr, uint256 orderId, address sellToken, uint256 unsoldAmount, address buyToken, uint256 purchasedAmount);
    event WithdrawProceedsFromLongTermOrder(address indexed addr, uint256 orderId, address indexed proceedToken, uint256 proceeds, bool orderExpired);

    function longTermSwapFrom0To1(uint256 amount0In, uint256 numberOfTimeIntervals) external returns (uint256 orderId);
    function longTermSwapFrom1To0(uint256 amount1In, uint256 numberOfTimeIntervals) external returns (uint256 orderId);
    function cancelLongTermSwap(uint256 orderId) external;
    function withdrawProceedsFromLongTermSwap(uint256 orderId) external returns (bool is_expired, address rewardTkn, uint256 totalReward);
    function executeVirtualOrders(uint256 blockTimestamp) external;

    function orderTimeInterval() external returns (uint256);
    function getTWAPHistoryLength() external view returns (uint);
    function getTwammReserves() external view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast, uint112 _twammReserve0, uint112 _twammReserve1);
    function getReserveAfterTwamm(uint256 blockTimestamp) external view returns (uint112 _reserve0, uint112 _reserve1, uint256 lastVirtualOrderTimestamp, uint112 _twammReserve0, uint112 _twammReserve1);
    function getNextOrderID() external view returns (uint256);
    function getOrderIDsForUser(address user) external view returns (uint256[] memory);
    function getOrderIDsForUserLength(address user) external view returns (uint256);
//    function getDetailedOrdersForUser(address user, uint256 offset, uint256 limit) external view returns (LongTermOrdersLib.Order[] memory detailed_orders);
    function twammUpToDate() external view returns (bool);
    function getTwammState() external view returns (uint256 token0Rate, uint256 token1Rate, uint256 lastVirtualOrderTimestamp, uint256 orderTimeInterval_rtn, uint256 rewardFactorPool0, uint256 rewardFactorPool1);
    function getTwammSalesRateEnding(uint256 _blockTimestamp) external view returns (uint256 orderPool0SalesRateEnding, uint256 orderPool1SalesRateEnding);
    function getTwammRewardFactor(uint256 _blockTimestamp) external view returns (uint256 rewardFactorPool0AtTimestamp, uint256 rewardFactorPool1AtTimestamp);
    function getTwammOrder(uint256 orderId) external view returns (uint256 id, uint256 expirationTimestamp, uint256 saleRate, address owner, address sellTokenAddr, address buyTokenAddr);
    function getTwammOrderProceedsView(uint256 orderId, uint256 blockTimestamp) external view returns (bool orderExpired, uint256 totalReward);
    function getTwammOrderProceeds(uint256 orderId) external returns (bool orderExpired, uint256 totalReward);


    function togglePauseNewSwaps() external;
}


// File contracts/Uniswap_V2_TWAMM/core/interfaces/IUniswapV2FactoryV5.sol


interface IUniswapV2FactoryV5 {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);

    function getPair(address tokenA, address tokenB) external view returns (address pair);
    function allPairs(uint) external view returns (address pair);
    function allPairsLength() external view returns (uint);

    function createPair(address tokenA, address tokenB) external returns (address pair);

    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
}


// File contracts/Uniswap_V2_TWAMM/libraries/Babylonian.sol



// computes square roots using the babylonian method
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
library Babylonian {
    // credit for this implementation goes to
    // https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.sol#L687
    function sqrt(uint256 x) internal pure returns (uint256) {
        if (x == 0) return 0;
        // this block is equivalent to r = uint256(1) << (BitMath.mostSignificantBit(x) / 2);
        // however that code costs significantly more gas
        uint256 xx = x;
        uint256 r = 1;
        if (xx >= 0x100000000000000000000000000000000) {
            xx >>= 128;
            r <<= 64;
        }
        if (xx >= 0x10000000000000000) {
            xx >>= 64;
            r <<= 32;
        }
        if (xx >= 0x100000000) {
            xx >>= 32;
            r <<= 16;
        }
        if (xx >= 0x10000) {
            xx >>= 16;
            r <<= 8;
        }
        if (xx >= 0x100) {
            xx >>= 8;
            r <<= 4;
        }
        if (xx >= 0x10) {
            xx >>= 4;
            r <<= 2;
        }
        if (xx >= 0x8) {
            r <<= 1;
        }
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1; // Seven iterations should be enough
        uint256 r1 = x / r;
        return (r < r1 ? r : r1);
    }
}


// File contracts/Uniswap_V2_TWAMM/libraries/FullMath.sol



/// @notice Math library that facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision.
/// @author Adapted from https://github.com/Uniswap/uniswap-v3-core/blob/main/contracts/libraries/FullMath.sol.
/// @dev Handles "phantom overflow", i.e., allows multiplication and division where an intermediate value overflows 256 bits.
library FullMath {
    /// @notice Calculates floor(a×b÷denominator) with full precision - throws if result overflows an uint256 or denominator == 0.
    /// @param a The multiplicand.
    /// @param b The multiplier.
    /// @param denominator The divisor.
    /// @return result The 256-bit result.
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
    function mulDiv(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
    unchecked {
        // 512-bit multiply [prod1 prod0] = a * b.
        // Compute the product mod 2**256 and mod 2**256 - 1,
        // then use the Chinese Remainder Theorem to reconstruct
        // the 512 bit result. The result is stored in two 256
        // variables such that product = prod1 * 2**256 + prod0.
        uint256 prod0; // Least significant 256 bits of the product.
        uint256 prod1; // Most significant 256 bits of the product.
        assembly {
            let mm := mulmod(a, b, not(0))
            prod0 := mul(a, b)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }
        // Handle non-overflow cases, 256 by 256 division.
        if (prod1 == 0) {
            require(denominator > 0);
            assembly {
                result := div(prod0, denominator)
            }
            return result;
        }
        // Make sure the result is less than 2**256 -
        // also prevents denominator == 0.
        require(denominator > prod1);
        ///////////////////////////////////////////////
        // 512 by 256 division.
        ///////////////////////////////////////////////
        // Make division exact by subtracting the remainder from [prod1 prod0] -
        // compute remainder using mulmod.
        uint256 remainder;
        assembly {
            remainder := mulmod(a, b, denominator)
        }
        // Subtract 256 bit number from 512 bit number.
        assembly {
            prod1 := sub(prod1, gt(remainder, prod0))
            prod0 := sub(prod0, remainder)
        }
        // Factor powers of two out of denominator -
        // compute largest power of two divisor of denominator
        // (always >= 1).
        uint256 twos = uint256(-int256(denominator)) & denominator;
        // Divide denominator by power of two.
        assembly {
            denominator := div(denominator, twos)
        }
        // Divide [prod1 prod0] by the factors of two.
        assembly {
            prod0 := div(prod0, twos)
        }
        // Shift in bits from prod1 into prod0. For this we need
        // to flip `twos` such that it is 2**256 / twos -
        // if twos is zero, then it becomes one.
        assembly {
            twos := add(div(sub(0, twos), twos), 1)
        }
        prod0 |= prod1 * twos;
        // Invert denominator mod 2**256 -
        // now that denominator is an odd number, it has an inverse
        // modulo 2**256 such that denominator * inv = 1 mod 2**256.
        // Compute the inverse by starting with a seed that is correct
        // for four bits. That is, denominator * inv = 1 mod 2**4.
        uint256 inv = (3 * denominator) ^ 2;
        // Now use Newton-Raphson iteration to improve the precision.
        // Thanks to Hensel's lifting lemma, this also works in modular
        // arithmetic, doubling the correct bits in each step.
        inv *= 2 - denominator * inv; // Inverse mod 2**8.
        inv *= 2 - denominator * inv; // Inverse mod 2**16.
        inv *= 2 - denominator * inv; // Inverse mod 2**32.
        inv *= 2 - denominator * inv; // Inverse mod 2**64.
        inv *= 2 - denominator * inv; // Inverse mod 2**128.
        inv *= 2 - denominator * inv; // Inverse mod 2**256.
        // Because the division is now exact we can divide by multiplying
        // with the modular inverse of denominator. This will give us the
        // correct result modulo 2**256. Since the precoditions guarantee
        // that the outcome is less than 2**256, this is the final result.
        // We don't need to compute the high bits of the result and prod1
        // is no longer required.
        result = prod0 * inv;
        return result;
    }
    }

    /// @notice Calculates ceil(a×b÷denominator) with full precision - throws if result overflows an uint256 or denominator == 0.
    /// @param a The multiplicand.
    /// @param b The multiplier.
    /// @param denominator The divisor.
    /// @return result The 256-bit result.
    function mulDivRoundingUp(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        result = mulDiv(a, b, denominator);
    unchecked {
        if (mulmod(a, b, denominator) != 0) {
            require(result < type(uint256).max);
            result++;
        }
    }
    }
}


// File contracts/Uniswap_V2_TWAMM/periphery/libraries/UniswapV2LiquidityMathLibraryMini.sol





// library containing some math for dealing with the liquidity shares of a pair, e.g. computing their exact value
// in terms of the underlying tokens
library UniswapV2LiquidityMathLibraryMini {

    // computes the direction and magnitude of the profit-maximizing trade
    // function computeProfitMaximizingTrade(
    //     uint256 truePriceTokenA,
    //     uint256 truePriceTokenB,
    //     uint256 reserveA,
    //     uint256 reserveB
    // ) pure internal returns (uint256 amountIn) {
    //     bool aToB = ((reserveA * truePriceTokenB) / reserveB) < truePriceTokenA;

    //     uint256 invariant = reserveA * reserveB;

    //     // true price is expressed as a ratio, so both values must be non-zero
    //     require(truePriceTokenA != 0 && truePriceTokenB != 0, "CPMT: ZERO_PRICE");

    //     uint256 leftSide = Babylonian.sqrt(
    //         FullMath.mulDiv(
    //             (invariant * 1000),
    //             aToB ? truePriceTokenA : truePriceTokenB,
    //             (aToB ? truePriceTokenB : truePriceTokenA) * 997
    //         )
    //     );
    //     uint256 rightSide = (aToB ? reserveA * 1000 : reserveB * 1000) / 997;

    //     if (leftSide < rightSide) return (0);

    //     // compute the amount that must be sent to move the price to the profit-maximizing price
    //     amountIn = leftSide - rightSide;
    // }

    function computeProfitMaximizingTrade(
        uint256 inTokenTruePrice,
        uint256 outTokenTruePrice,
        uint256 reserveIn,
        uint256 reserveOut
    ) pure internal returns (uint256 amountIn) {
        uint256 invariant = reserveIn * reserveOut;

        // true price is expressed as a ratio, so both values must be non-zero
        require(inTokenTruePrice != 0 && outTokenTruePrice != 0, "CPMT: ZERO_PRICE");

        uint256 leftSide = Babylonian.sqrt(
            FullMath.mulDiv(
                (invariant * 1000),
                inTokenTruePrice,
                outTokenTruePrice * 997
            )
        );
        uint256 rightSide = (reserveIn * 1000) / 997;

        if (leftSide < rightSide) return (0);

        // compute the amount that must be sent to move the price to the profit-maximizing price
        amountIn = leftSide - rightSide;
    }
}


// File contracts/FPI/FPIControllerPool.sol


// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ========================= FPIControllerPool =========================
// ====================================================================
// Makes sure FPI is targeting the CPI peg
// First method is minting / redeeming with FRAX
// Second is bulk TWAMM trades
// Frax Finance: https://github.com/FraxFinance

// Primary Author(s)
// Travis Moore: https://github.com/FortisFortuna

// Reviewer(s) / Contributor(s)
// Sam Kazemian: https://github.com/samkazemian
// Rich Gee: https://github.com/zer0blockchain
// Dennis: https://github.com/denett
// Jack Corddry: https://github.com/corddry








contract FPIControllerPool is Owned {

    // Core
    address public timelock_address;
    FPI public FPI_TKN;
    IFrax public FRAX;
    IUniV2TWAMMPair public TWAMM;

    // Oracles
    AggregatorV3Interface public priceFeedFRAXUSD;
    AggregatorV3Interface public priceFeedFPIUSD;
    uint256 public chainlink_frax_usd_decimals;
    uint256 public chainlink_fpi_usd_decimals;
    CPITrackerOracle public cpiTracker;

    // Tracking
    uint256 public last_order_id_twamm; // Last TWAMM order ID that was used

   // AMO addresses (lend out FRAX)
    address[] public amos_array;
    mapping(address => bool) public amos; // Mapping is also used for faster verification

    // FRAX borrowed balances
    mapping(address => int256) public frax_borrowed_balances; // Amount of FRAX the contract borrowed, by AMO
    int256 public frax_borrowed_sum = 0; // Across all AMOs
    int256 public frax_borrow_cap = int256(10000000e18); // Max amount of FRAX the contract can borrow from this contract

    // Mint Fee Related
    bool public use_manual_mint_fee = true;
    uint256 public mint_fee_manual = 3000; // E6
    uint256 public mint_fee_multiplier = 1000000; // E6
    
    // Redeem Fee Related
    bool public use_manual_redeem_fee = true;
    uint256 public redeem_fee_manual = 3000; // E6
    uint256 public redeem_fee_multiplier = 1000000; // E6
    
    // Safety
    uint256 public fpi_mint_cap = 110000000e18; // 110M
    uint256 public peg_band_mint_redeem = 50000; // 5%
    uint256 public peg_band_twamm = 100000; // 10%
    uint256 public max_swap_frax_amt_in = 10000000e18; // 10M, mainly fat-finger precautions
    uint256 public max_swap_fpi_amt_in = 10000000e18; // 10M, mainly fat-finger precautions
    bool public mints_paused = false;
    bool public redeems_paused = false;

    // Constants for various precisions
    uint256 public constant PRICE_PRECISION = 1e18;
    uint256 public constant FEE_PRECISION = 1e6;
    uint256 public constant PEG_BAND_PRECISION = 1e6;

    // Misc
    bool public frax_is_token0;
    bool public pending_twamm_order = false;
    uint256 public num_twamm_intervals = 168; // Each interval is default 3600 sec (1 hr)
    uint256 public swap_period = 7 * 86400; // 7 days

    /* ========== MODIFIERS ========== */

    modifier onlyByOwnGov() {
        require(msg.sender == owner || msg.sender == timelock_address, "Not owner or timelock");
        _;
    }

    modifier validAMO(address amo_address) {
        require(amos[amo_address], "Invalid AMO");
        _;
    }

    /* ========== CONSTRUCTOR ========== */

    constructor (
        address _creator_address,
        address _timelock_address,
        address[6] memory _address_pack
    ) Owned(_creator_address) {
        timelock_address = _timelock_address;

        // Set instances
        FRAX = IFrax(_address_pack[0]);
        FPI_TKN = FPI(_address_pack[1]);
        TWAMM = IUniV2TWAMMPair(_address_pack[2]);
        priceFeedFRAXUSD = AggregatorV3Interface(_address_pack[3]);
        priceFeedFPIUSD = AggregatorV3Interface(_address_pack[4]);
        cpiTracker = CPITrackerOracle(_address_pack[5]);

        // Set the oracle decimals
        chainlink_frax_usd_decimals = priceFeedFRAXUSD.decimals();
        chainlink_fpi_usd_decimals = priceFeedFPIUSD.decimals();

        // Need to know which token FRAX is (0 or 1)
        address token0 = TWAMM.token0();
        if (token0 == address(FRAX)) frax_is_token0 = true;
        else frax_is_token0 = false;

        // Get the number of TWAMM intervals. Truncation desired
        num_twamm_intervals = swap_period / TWAMM.orderTimeInterval();
    }


    /* ========== VIEWS ========== */

    // Needed as a FRAX AMO
    function dollarBalances() public view returns (uint256 frax_val_e18, uint256 collat_val_e18) {
        // Dummy values here. FPI is not FRAX and should not be treated as FRAX collateral
        frax_val_e18 = 1e18;
        collat_val_e18 = 1e18;
    }

    // In Chainlink decimals
    function getFRAXPriceE18() public view returns (uint256) {
        (uint80 roundID, int price, , uint256 updatedAt, uint80 answeredInRound) = priceFeedFRAXUSD.latestRoundData();
        require(price >= 0 && updatedAt!= 0 && answeredInRound >= roundID, "Invalid chainlink price");

        return ((uint256(price) * 1e18) / (10 ** chainlink_frax_usd_decimals));
    }

    // In Chainlink decimals    
    function getFPIPriceE18() public view returns (uint256) {
        (uint80 roundID, int price, , uint256 updatedAt, uint80 answeredInRound) = priceFeedFPIUSD.latestRoundData();
        require(price >= 0 && updatedAt!= 0 && answeredInRound >= roundID, "Invalid chainlink price");

        return ((uint256(price) * 1e18) / (10 ** chainlink_fpi_usd_decimals));
    }

    // Reserve spot price (fpi_price is dangerous / flash loan susceptible, so use carefully)    
    function getReservesAndFPISpot() public returns (uint256 reserveFRAX, uint256 reserveFPI, uint256 fpi_price) {
        // Update and get the reserves
        TWAMM.executeVirtualOrders(block.timestamp);
        {
            (uint256 reserveA, uint256 reserveB, ) = TWAMM.getReserves();
            if (frax_is_token0){
                reserveFRAX = reserveA;
                reserveFPI = reserveB;
                
            }
            else {
                reserveFRAX = reserveB;
                reserveFPI = reserveA;
            }
        }

        // Get the TWAMM reserve spot price
        fpi_price = (reserveFRAX * 1e18) / reserveFPI;
    }

    // function getTwammToPegAmt() public returns (uint256 frax_in, uint256 fpi_in) {
    //     // Update and get the reserves
    //     (uint256 reserveFRAX, uint256 reserveFPI, uint256 reservePriceFPI) = getReservesAndFPISpot();
        
    //     // Get the CPI price
    //     uint256 cpi_peg_price = cpiTracker.currPegPrice();

    //     // Sort the pricing. NOTE: IN RATIOS, NOT PRICE
    //     uint256 truePriceFRAX = 1e18;
    //     uint256 truePriceFPI = cpi_peg_price;

    //     // Determine the direction
    //     if (fpi_to_frax) {
    //         return UniswapV2LiquidityMathLibraryMini.computeProfitMaximizingTrade(
    //             truePriceFPI, truePriceFRAX,
    //             reserveFPI, reserveFRAX
    //         );
    //     }
    //     else {
    //         return UniswapV2LiquidityMathLibraryMini.computeProfitMaximizingTrade(
    //             truePriceFRAX, truePriceFPI,
    //             reserveFRAX, reserveFPI
    //         );
    //     }
    // }

    // In E6
    function mint_fee() public view returns (uint256 fee) {
        if (use_manual_mint_fee) fee = mint_fee_manual;
        else {
            // For future variable fees
            fee = 0;

            // Apply the multiplier
            fee = (fee * mint_fee_multiplier) / 1e6;
        }
    }

    // In E6
    function redeem_fee() public view returns (uint256 fee) {
        if (use_manual_redeem_fee) fee = redeem_fee_manual;
        else {
            // For future variable fees
            fee = 0;

            // Apply the multiplier
            fee = (fee * redeem_fee_multiplier) / 1e6;
        }

        
    }

    // Get some info about the peg status
    function pegStatusMntRdm() public view returns (uint256 cpi_peg_price, uint256 diff_frac_abs, bool within_range) {
        uint256 fpi_price = getFPIPriceE18();
        cpi_peg_price = cpiTracker.currPegPrice();

        if (fpi_price > cpi_peg_price){
            diff_frac_abs = ((fpi_price - cpi_peg_price) * PEG_BAND_PRECISION) / fpi_price;
        }
        else {
            diff_frac_abs = ((cpi_peg_price - fpi_price) * PEG_BAND_PRECISION) / fpi_price;
        }

        within_range = (diff_frac_abs <= peg_band_mint_redeem);
    }

    // Get additional info about the peg status
    function price_info() public view returns (
        int256 collat_imbalance, 
        uint256 cpi_peg_price,
        uint256 fpi_price,
        uint256 price_diff_frac_abs
    ) {
        fpi_price = getFPIPriceE18();
        cpi_peg_price = cpiTracker.currPegPrice();
        uint256 fpi_supply = FPI_TKN.totalSupply();

        if (fpi_price > cpi_peg_price){
            collat_imbalance = int256(((fpi_price - cpi_peg_price) * fpi_supply) / PRICE_PRECISION);
            price_diff_frac_abs = ((fpi_price - cpi_peg_price) * PEG_BAND_PRECISION) / fpi_price;
        }
        else {
            collat_imbalance = -1 * int256(((cpi_peg_price - fpi_price) * fpi_supply) / PRICE_PRECISION);
            price_diff_frac_abs = ((cpi_peg_price - fpi_price) * PEG_BAND_PRECISION) / fpi_price;
        }
    }

    /* ========== MUTATIVE ========== */

    // Calculate Mint FPI with FRAX
    function calcMintFPI(uint256 frax_in, uint256 min_fpi_out) public view returns (uint256 fpi_out) {
        require(!mints_paused, "Mints paused");

        // Fetch the CPI price and other info
        (uint256 cpi_peg_price, , bool within_range) = pegStatusMntRdm();

        // Make sure the peg is within range for minting
        // Helps combat oracle errors and megadumping
        require(within_range, "Peg band [Mint]");

        // Calculate the amount of FPI that the incoming FRAX should give
        fpi_out = (frax_in * PRICE_PRECISION) / cpi_peg_price;

        // Apply the fee
        fpi_out -= (fpi_out * mint_fee()) / FEE_PRECISION;

        // Make sure enough FPI is generated
        require(fpi_out >= min_fpi_out, "Slippage [Mint]");

        // Check the mint cap
        require(FPI_TKN.totalSupply() + fpi_out <= fpi_mint_cap, "FPI mint cap");
    }

    // Mint FPI with FRAX
    function mintFPI(uint256 frax_in, uint256 min_fpi_out) external returns (uint256 fpi_out) {
        fpi_out = calcMintFPI(frax_in, min_fpi_out);

        // Pull in the FRAX
        TransferHelper.safeTransferFrom(address(FRAX), msg.sender, address(this), frax_in);

        // Mint FPI to the sender
        FPI_TKN.minter_mint(msg.sender, fpi_out);

        emit FPIMinted(frax_in, fpi_out);
    }

    // Calculate Redeem FPI for FRAX
    function calcRedeemFPI(uint256 fpi_in, uint256 min_frax_out) public view returns (uint256 frax_out) {
        require(!redeems_paused, "Redeems paused");

        // Fetch the CPI price and other info
        (uint256 cpi_peg_price, , bool within_range) = pegStatusMntRdm();

        // Make sure the peg is within range for minting
        // Helps combat oracle errors and megadumping
        require(within_range, "Peg band [Redeem]");

        // Calculate the amount of FRAX that the incoming FPI should give
        frax_out = (fpi_in * cpi_peg_price) / PRICE_PRECISION;

        // Apply the fee
        frax_out -= (frax_out * redeem_fee()) / FEE_PRECISION;

        // Make sure enough FRAX is generated
        require(frax_out >= min_frax_out, "Slippage [Redeem]");
    }

    // Redeem FPI for FRAX
    function redeemFPI(uint256 fpi_in, uint256 min_frax_out) external returns (uint256 frax_out) {
        frax_out = calcRedeemFPI(fpi_in, min_frax_out);

        // Pull in the FPI
        TransferHelper.safeTransferFrom(address(FPI_TKN), msg.sender, address(this), fpi_in);

        // Give FRAX to the sender
        TransferHelper.safeTransfer(address(FRAX), msg.sender, frax_out);

        emit FPIRedeemed(fpi_in, frax_out);
    }

    // Use the TWAMM for bulk peg corrections
    function twammManual(uint256 frax_sell_amt, uint256 fpi_sell_amt, uint256 override_intervals) external onlyByOwnGov returns (uint256 frax_to_use, uint256 fpi_to_use) {
        // Make sure only one direction occurs
        require(!((frax_sell_amt > 0) && (fpi_sell_amt > 0)), "Can only sell in one direction");

        // Update and get the reserves
        // longTermSwapFrom0to1 and longTermSwapFrom1To0 do it automatically
        // TWAMM.executeVirtualOrders(block.timestamp);
        
        // Cancel the previous order (if any) and collect any leftover tokens
        if (pending_twamm_order) TWAMM.cancelLongTermSwap(last_order_id_twamm);

        // Now calculate the imbalance after the burn
        (, , , uint256 price_diff_abs) = price_info();

        // Make sure the FPI oracle price hasn't moved away too much from the target peg price
        require(price_diff_abs <= peg_band_twamm, "Peg band [TWAMM]");

        // Create a new order
        last_order_id_twamm = TWAMM.getNextOrderID(); 
        {
            if (fpi_sell_amt > 0) {
                // Mint FPI and sell for FRAX
                // --------------------------------
                fpi_to_use = fpi_sell_amt;
    
                // Make sure nonzero
                require(fpi_to_use > 0, "FPI sold must be nonzero");

                // Safety check
                require(fpi_to_use <= max_swap_fpi_amt_in, "Too much FPI sold");

                // Mint some FPI
                FPI_TKN.minter_mint(address(this), fpi_to_use);

                // Approve FPI first
                FPI_TKN.approve(address(TWAMM), fpi_to_use);

                // Sell FPI for FRAX
                if (frax_is_token0) {
                    TWAMM.longTermSwapFrom1To0(fpi_to_use, override_intervals > 0 ? override_intervals : num_twamm_intervals);
                }
                else {
                    TWAMM.longTermSwapFrom0To1(fpi_to_use, override_intervals > 0 ? override_intervals : num_twamm_intervals);
                }
            }
            else {
                // Use FRAX to buy FPI
                // --------------------------------
                frax_to_use = frax_sell_amt;

                // Make sure nonzero
                require(frax_to_use > 0, "FRAX sold must be nonzero");

                // Safety check
                require(frax_to_use <= max_swap_frax_amt_in, "Too much FRAX sold");

                // Approve FRAX first
                FRAX.approve(address(TWAMM), frax_to_use);

                // Sell FRAX for FPI
                if (frax_is_token0) {
                    TWAMM.longTermSwapFrom0To1(frax_to_use, override_intervals > 0 ? override_intervals : num_twamm_intervals);
                }
                else {
                    TWAMM.longTermSwapFrom1To0(frax_to_use, override_intervals > 0 ? override_intervals : num_twamm_intervals);
                }
            }
        }

        // Mark that there is a pending order
        pending_twamm_order = true;

        emit TWAMMedToPeg(last_order_id_twamm, frax_to_use, fpi_to_use, block.timestamp);
    }

    function cancelCurrTWAMMOrder(uint256 order_id_override) public onlyByOwnGov {
        // Cancel the order
        TWAMM.cancelLongTermSwap(order_id_override == 0 ? last_order_id_twamm : order_id_override);

        // Clear the pending order indicator
        pending_twamm_order = false;
    }

    function collectCurrTWAMMProceeds(uint256 order_id_override) external onlyByOwnGov {
        // Withdraw current proceeds
        (bool is_expired, , ) = TWAMM.withdrawProceedsFromLongTermSwap(order_id_override == 0 ? last_order_id_twamm : order_id_override);
        
        // If using the last_order_id_twamm and it is expired, clear the pending order indicator
        if (is_expired && (order_id_override == 0)) pending_twamm_order = false;
    }

    /* ========== Burns and givebacks ========== */

    // Burn unneeded or excess FPI.
    function burnFPI(bool burn_all, uint256 fpi_amount) public onlyByOwnGov {
        if (burn_all) {
            // Burn any leftover FPI
            FPI_TKN.burn(FPI_TKN.balanceOf(address(this)));
        }
        else FPI_TKN.burn(fpi_amount);
    }

    // ------------------------------------------------------------------
    // ------------------------------ FRAX ------------------------------
    // ------------------------------------------------------------------

    // Lend the FRAX collateral to an AMO
    function giveFRAXToAMO(address destination_amo, uint256 frax_amount) external onlyByOwnGov validAMO(destination_amo) {
        int256 frax_amount_i256 = int256(frax_amount);

        // Update the balances first
        require((frax_borrowed_sum + frax_amount_i256) <= frax_borrow_cap, "Borrow cap");
        frax_borrowed_balances[destination_amo] += frax_amount_i256;
        frax_borrowed_sum += frax_amount_i256;

        // Give the FRAX to the AMO
        TransferHelper.safeTransfer(address(FRAX), destination_amo, frax_amount);
    }

    // AMO gives back FRAX. Needed for proper accounting
    function receiveFRAXFromAMO(uint256 frax_amount) external validAMO(msg.sender) {
        int256 frax_amt_i256 = int256(frax_amount);

        // Give back first
        TransferHelper.safeTransferFrom(address(FRAX), msg.sender, address(this), frax_amount);

        // Then update the balances
        frax_borrowed_balances[msg.sender] -= frax_amt_i256;
        frax_borrowed_sum -= frax_amt_i256;
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    // Adds an AMO 
    function addAMO(address amo_address) public onlyByOwnGov {
        require(amo_address != address(0), "Zero address detected");

        require(amos[amo_address] == false, "Address already exists");
        amos[amo_address] = true; 
        amos_array.push(amo_address);

        emit AMOAdded(amo_address);
    }

    // Removes an AMO
    function removeAMO(address amo_address) public onlyByOwnGov {
        require(amo_address != address(0), "Zero address detected");
        require(amos[amo_address] == true, "Address nonexistant");
        
        // Delete from the mapping
        delete amos[amo_address];

        // 'Delete' from the array by setting the address to 0x0
        for (uint i = 0; i < amos_array.length; i++){ 
            if (amos_array[i] == amo_address) {
                amos_array[i] = address(0); // This will leave a null in the array and keep the indices the same
                break;
            }
        }

        emit AMORemoved(amo_address);
    }

    function setOracles(address _frax_oracle, address _fpi_oracle, address _cpi_oracle) external onlyByOwnGov {
        priceFeedFRAXUSD = AggregatorV3Interface(_frax_oracle);
        priceFeedFPIUSD = AggregatorV3Interface(_fpi_oracle);
        cpiTracker = CPITrackerOracle(_cpi_oracle);

        // Set the Chainlink oracle decimals
        chainlink_frax_usd_decimals = priceFeedFRAXUSD.decimals();
        chainlink_fpi_usd_decimals = priceFeedFPIUSD.decimals();
    }

    function setTWAMMAndSwapPeriod(address _twamm_addr, uint256 _swap_period) external onlyByOwnGov {
        // Cancel an outstanding order, if present
        if (pending_twamm_order) cancelCurrTWAMMOrder(last_order_id_twamm);
        
        // Change the TWAMM parameters
        TWAMM = IUniV2TWAMMPair(_twamm_addr);
        swap_period = _swap_period;
        num_twamm_intervals = _swap_period / TWAMM.orderTimeInterval();
    }

    function toggleMints() external onlyByOwnGov {
        mints_paused = !mints_paused;
    }

    function toggleRedeems() external onlyByOwnGov {
        redeems_paused = !redeems_paused;
    }

    function setFraxBorrowCap(int256 _frax_borrow_cap) external onlyByOwnGov {
        frax_borrow_cap = _frax_borrow_cap;
    }

    function setMintCap(uint256 _fpi_mint_cap) external onlyByOwnGov {
        fpi_mint_cap = _fpi_mint_cap;
    }

    function setPegBands(uint256 _peg_band_mint_redeem, uint256 _peg_band_twamm) external onlyByOwnGov {
        peg_band_mint_redeem = _peg_band_mint_redeem;
        peg_band_twamm = _peg_band_twamm;
    }

    function setMintRedeemFees(
        bool _use_manual_mint_fee,
        uint256 _mint_fee_manual, 
        uint256 _mint_fee_multiplier, 
        bool _use_manual_redeem_fee,
        uint256 _redeem_fee_manual, 
        uint256 _redeem_fee_multiplier
    ) external onlyByOwnGov {
        use_manual_mint_fee = _use_manual_mint_fee;
        mint_fee_manual = _mint_fee_manual;
        mint_fee_multiplier = _mint_fee_multiplier;
        use_manual_redeem_fee = _use_manual_redeem_fee;
        redeem_fee_manual = _redeem_fee_manual;
        redeem_fee_multiplier = _redeem_fee_multiplier;
    }

    function setTWAMMMaxSwapIn(uint256 _max_swap_frax_amt_in, uint256 _max_swap_fpi_amt_in) external onlyByOwnGov {
        max_swap_frax_amt_in = _max_swap_frax_amt_in;
        max_swap_fpi_amt_in = _max_swap_fpi_amt_in;
    }

    function setTimelock(address _new_timelock_address) external onlyByOwnGov {
        timelock_address = _new_timelock_address;
    }

    // Added to support recovering LP Rewards and other mistaken tokens from other systems to be distributed to holders
    function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyByOwnGov {
        // Only the owner address can ever receive the recovery withdrawal
        TransferHelper.safeTransfer(tokenAddress, owner, tokenAmount);
        emit RecoveredERC20(tokenAddress, tokenAmount);
    }

    /* ========== EVENTS ========== */
    event AMOAdded(address amo_address);
    event AMORemoved(address amo_address);
    event RecoveredERC20(address token, uint256 amount);
    event FPIMinted(uint256 frax_in, uint256 fpi_out);
    event FPIRedeemed(uint256 fpi_in, uint256 frax_out);
    event TWAMMedToPeg(uint256 order_id, uint256 frax_amt, uint256 fpi_amt, uint256 timestamp);
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_creator_address","type":"address"},{"internalType":"address","name":"_timelock_address","type":"address"},{"internalType":"address[6]","name":"_address_pack","type":"address[6]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"amo_address","type":"address"}],"name":"AMOAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"amo_address","type":"address"}],"name":"AMORemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"frax_in","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fpi_out","type":"uint256"}],"name":"FPIMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fpi_in","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"frax_out","type":"uint256"}],"name":"FPIRedeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RecoveredERC20","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"order_id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"frax_amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fpi_amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"TWAMMedToPeg","type":"event"},{"inputs":[],"name":"FEE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FPI_TKN","outputs":[{"internalType":"contract FPI","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FRAX","outputs":[{"internalType":"contract IFrax","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PEG_BAND_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TWAMM","outputs":[{"internalType":"contract IUniV2TWAMMPair","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"amo_address","type":"address"}],"name":"addAMO","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"amos","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"amos_array","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"burn_all","type":"bool"},{"internalType":"uint256","name":"fpi_amount","type":"uint256"}],"name":"burnFPI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"frax_in","type":"uint256"},{"internalType":"uint256","name":"min_fpi_out","type":"uint256"}],"name":"calcMintFPI","outputs":[{"internalType":"uint256","name":"fpi_out","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"fpi_in","type":"uint256"},{"internalType":"uint256","name":"min_frax_out","type":"uint256"}],"name":"calcRedeemFPI","outputs":[{"internalType":"uint256","name":"frax_out","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"order_id_override","type":"uint256"}],"name":"cancelCurrTWAMMOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"chainlink_fpi_usd_decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainlink_frax_usd_decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"order_id_override","type":"uint256"}],"name":"collectCurrTWAMMProceeds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cpiTracker","outputs":[{"internalType":"contract CPITrackerOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dollarBalances","outputs":[{"internalType":"uint256","name":"frax_val_e18","type":"uint256"},{"internalType":"uint256","name":"collat_val_e18","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fpi_mint_cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"frax_borrow_cap","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"frax_borrowed_balances","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"frax_borrowed_sum","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"frax_is_token0","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFPIPriceE18","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFRAXPriceE18","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReservesAndFPISpot","outputs":[{"internalType":"uint256","name":"reserveFRAX","type":"uint256"},{"internalType":"uint256","name":"reserveFPI","type":"uint256"},{"internalType":"uint256","name":"fpi_price","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"destination_amo","type":"address"},{"internalType":"uint256","name":"frax_amount","type":"uint256"}],"name":"giveFRAXToAMO","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"last_order_id_twamm","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"max_swap_fpi_amt_in","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"max_swap_frax_amt_in","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"frax_in","type":"uint256"},{"internalType":"uint256","name":"min_fpi_out","type":"uint256"}],"name":"mintFPI","outputs":[{"internalType":"uint256","name":"fpi_out","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mint_fee","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mint_fee_manual","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mint_fee_multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mints_paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"num_twamm_intervals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pegStatusMntRdm","outputs":[{"internalType":"uint256","name":"cpi_peg_price","type":"uint256"},{"internalType":"uint256","name":"diff_frac_abs","type":"uint256"},{"internalType":"bool","name":"within_range","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"peg_band_mint_redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"peg_band_twamm","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pending_twamm_order","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeedFPIUSD","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeedFRAXUSD","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price_info","outputs":[{"internalType":"int256","name":"collat_imbalance","type":"int256"},{"internalType":"uint256","name":"cpi_peg_price","type":"uint256"},{"internalType":"uint256","name":"fpi_price","type":"uint256"},{"internalType":"uint256","name":"price_diff_frac_abs","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"frax_amount","type":"uint256"}],"name":"receiveFRAXFromAMO","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fpi_in","type":"uint256"},{"internalType":"uint256","name":"min_frax_out","type":"uint256"}],"name":"redeemFPI","outputs":[{"internalType":"uint256","name":"frax_out","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeem_fee","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeem_fee_manual","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeem_fee_multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeems_paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"amo_address","type":"address"}],"name":"removeAMO","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"_frax_borrow_cap","type":"int256"}],"name":"setFraxBorrowCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fpi_mint_cap","type":"uint256"}],"name":"setMintCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_use_manual_mint_fee","type":"bool"},{"internalType":"uint256","name":"_mint_fee_manual","type":"uint256"},{"internalType":"uint256","name":"_mint_fee_multiplier","type":"uint256"},{"internalType":"bool","name":"_use_manual_redeem_fee","type":"bool"},{"internalType":"uint256","name":"_redeem_fee_manual","type":"uint256"},{"internalType":"uint256","name":"_redeem_fee_multiplier","type":"uint256"}],"name":"setMintRedeemFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_frax_oracle","type":"address"},{"internalType":"address","name":"_fpi_oracle","type":"address"},{"internalType":"address","name":"_cpi_oracle","type":"address"}],"name":"setOracles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_peg_band_mint_redeem","type":"uint256"},{"internalType":"uint256","name":"_peg_band_twamm","type":"uint256"}],"name":"setPegBands","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_twamm_addr","type":"address"},{"internalType":"uint256","name":"_swap_period","type":"uint256"}],"name":"setTWAMMAndSwapPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_max_swap_frax_amt_in","type":"uint256"},{"internalType":"uint256","name":"_max_swap_fpi_amt_in","type":"uint256"}],"name":"setTWAMMMaxSwapIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_new_timelock_address","type":"address"}],"name":"setTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swap_period","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timelock_address","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleMints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleRedeems","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"frax_sell_amt","type":"uint256"},{"internalType":"uint256","name":"fpi_sell_amt","type":"uint256"},{"internalType":"uint256","name":"override_intervals","type":"uint256"}],"name":"twammManual","outputs":[{"internalType":"uint256","name":"frax_to_use","type":"uint256"},{"internalType":"uint256","name":"fpi_to_use","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"use_manual_mint_fee","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"use_manual_redeem_fee","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

60806040526000600f556a084595161401484a000000601081905560118054600160ff199182168117909255610bb86012819055620f42406013819055601480549093169093179091556015556016556a5afd67f2dc0e1b2e00000060175561c350601855620186a0601955601a819055601b55601c805463ff00ffff1916905560a8601d5562093a80601e553480156200009957600080fd5b5060405162004b4b38038062004b4b833981016040819052620000bc916200045a565b826001600160a01b038116620001185760405162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f74206265203000000000000000604482015260640160405180910390fd5b600080546001600160a01b0319166001600160a01b03831690811782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a150600280546001600160a01b03199081166001600160a01b0385811691909117909255825160048054831691841691909117815560208085015160038054851691861691909117905560408086015160058054861691871691909117905560608601516006805486169187169182179055608087015160078054871691881691909117905560a0870151600a8054909616961695909517909355825163313ce56760e01b8152925163313ce5679380840193908290030181865afa1580156200023c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000262919062000511565b60ff166008556007546040805163313ce56760e01b815290516001600160a01b039092169163313ce567916004808201926020929091908290030181865afa158015620002b3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002d9919062000511565b60ff1660095560055460408051630dfe168160e01b815290516000926001600160a01b031691630dfe16819160048083019260209291908290030181865afa1580156200032a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200035091906200053d565b6004549091506001600160a01b03908116908216036200038157601c805462ff00001916620100001790556200038e565b601c805462ff0000191690555b600560009054906101000a90046001600160a01b03166001600160a01b031663748fc63b6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015620003e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200040a91906200055b565b601e5462000419919062000575565b601d55506200059892505050565b80516001600160a01b03811681146200043f57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60008060006101008085870312156200047257600080fd5b6200047d8562000427565b935060206200048e81870162000427565b935086605f870112620004a057600080fd5b60405160c081016001600160401b0381118282101715620004c557620004c562000444565b604052918601918088841115620004db57600080fd5b604088015b848110156200050257620004f48162000427565b8252908301908301620004e0565b50508093505050509250925092565b6000602082840312156200052457600080fd5b815160ff811681146200053657600080fd5b9392505050565b6000602082840312156200055057600080fd5b620005368262000427565b6000602082840312156200056e57600080fd5b5051919050565b6000826200059357634e487b7160e01b600052601260045260246000fd5b500490565b6145a380620005a86000396000f3fe608060405234801561001057600080fd5b50600436106104715760003560e01c80638980f11f11610250578063ba92341e11610150578063d9ebcc2f116100c8578063e63a391f11610097578063ea36648a1161007c578063ea36648a14610964578063eb99aff814610981578063ef71c765146109a157600080fd5b8063e63a391f14610964578063e8d5931d1461096e57600080fd5b8063d9ebcc2f14610927578063da610fcf1461092f578063dc6663c714610937578063e24ab97b1461095757600080fd5b8063bf6a89a21161011f578063c70b6ad111610104578063c70b6ad11461090d578063cd080fbd14610915578063d578660a1461091e57600080fd5b8063bf6a89a2146108e7578063c5a36df1146108fa57600080fd5b8063ba92341e1461086c578063bc3d5a8e1461088c578063bda767ab146108b1578063bdacb303146108d457600080fd5b8063a2b2d0e9116101e3578063a7c027da116101b2578063b0e4556f11610197578063b0e4556f1461083a578063b565c0cb1461085a578063b5d7684b1461086357600080fd5b8063a7c027da1461081a578063ac12d5971461082d57600080fd5b8063a2b2d0e9146107ac578063a42b4b25146107d4578063a4c3e73c146107e7578063a63b39e91461080757600080fd5b80639238c0ac1161021f5780639238c0ac1461077857806395082d25146107815780639c97ca1b146107905780639fc397e3146107a357600080fd5b80638980f11f146107295780638da5cb5b1461073c57806390f749c71461075c578063919e06ce1461076557600080fd5b806350fc60cb11610376578063694033f2116102ee57806382cd8713116102bd57806386c8c345116102a257806386c8c345146106e3578063882c95c1146106f657806388b93d9a1461071657600080fd5b806382cd8713146106b75780638583f21c146106da57600080fd5b8063694033f2146106955780636e7813ff1461069e57806379ba5097146106a657806379df3933146106ae57600080fd5b80636195e1f11161034557806365525bf61161032a57806365525bf61461066557806367694bae146106795780636830f5711461068257600080fd5b80636195e1f114610653578063631a5a171461065c57600080fd5b806350fc60cb146105ed57806353a47bb7146106005780635592e9f514610620578063608984611461064057600080fd5b80632f7e992f116104095780633ae2e4a3116103d85780634608fd46116103bd5780634608fd46146105ba57806349696b20146105c75780634d6f9ef2146105da57600080fd5b80633ae2e4a3146105945780634070a0c9146105a757600080fd5b80632f7e992f1461056757806330fd8a461461057057806335f77feb146105825780633a0319d71461058b57600080fd5b80631627540c116104455780631627540c146104dc5780631937bdf9146104ef5780631eaa95321461050f5780632141203e1461052257600080fd5b8062aa08fd1461047657806309761c7e1461048b57806309d32681146104a6578063152129b2146104b9575b600080fd5b610489610484366004613e17565b6109a9565b005b610493610aa5565b6040519081526020015b60405180910390f35b6104896104b4366004613e95565b610c14565b601c546104cc9062010000900460ff1681565b604051901515815260200161049d565b6104896104ea366004613ee0565b610e34565b6104936104fd366004613ee0565b600e6020526000908152604090205481565b61049361051d366004613f04565b610f55565b6007546105429073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161049d565b61049360155481565b601c546104cc90610100900460ff1681565b610493601b5481565b610493601a5481565b6104936105a2366004613f04565b6110fe565b6104896105b5366004613f26565b6113a2565b6011546104cc9060ff1681565b6105426105d5366004613f26565b61144a565b6104936105e8366004613f04565b611481565b6104896105fb366004613f26565b611580565b6001546105429073ffffffffffffffffffffffffffffffffffffffff1681565b600a546105429073ffffffffffffffffffffffffffffffffffffffff1681565b61048961064e366004613f26565b611628565b61049360125481565b61049360135481565b601c546104cc906301000000900460ff1681565b61049360195481565b610489610690366004613f3f565b61170a565b61049360185481565b610493611937565b610489611971565b61049360085481565b6106bf611abc565b6040805193845260208401929092529082015260600161049d565b610493601e5481565b6104896106f1366004613f6b565b611c3b565b6003546105429073ffffffffffffffffffffffffffffffffffffffff1681565b610489610724366004613f26565b611e2d565b610489610737366004613f3f565b611fa2565b6000546105429073ffffffffffffffffffffffffffffffffffffffff1681565b61049360165481565b610489610773366004613ee0565b6120bd565b610493601d5481565b610493670de0b6b3a764000081565b61048961079e366004613f3f565b6123d3565b610493600f5481565b6107b4612566565b60408051948552602085019390935291830152606082015260800161049d565b6104896107e2366004613f04565b61277b565b670de0b6b3a7640000805b6040805192835260208301919091520161049d565b6107f2610815366004613f89565b612829565b610489610828366004613ee0565b6130d5565b6014546104cc9060ff1681565b6004546105429073ffffffffffffffffffffffffffffffffffffffff1681565b61049360095481565b61049360105481565b6005546105429073ffffffffffffffffffffffffffffffffffffffff1681565b61089461335e565b60408051938452602084019290925215159082015260600161049d565b6104cc6108bf366004613ee0565b600d6020526000908152604090205460ff1681565b6104896108e2366004613ee0565b613467565b6104896108f5366004613f26565b613551565b610493610908366004613f04565b61369d565b61048961372b565b610493600b5481565b61049360175481565b610493613800565b61049361394a565b6002546105429073ffffffffffffffffffffffffffffffffffffffff1681565b601c546104cc9060ff1681565b610493620f424081565b61048961097c366004613f04565b613975565b6006546105429073ffffffffffffffffffffffffffffffffffffffff1681565b610489613a23565b60005473ffffffffffffffffffffffffffffffffffffffff163314806109e6575060025473ffffffffffffffffffffffffffffffffffffffff1633145b610a51576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064015b60405180910390fd5b601180549615157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00978816179055601294909455601392909255601480549115159190941617909255601591909155601655565b6000806000806000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610b1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3e9190613fd4565b94509450509350935060008312158015610b5757508115155b8015610b7b57508369ffffffffffffffffffff168169ffffffffffffffffffff1610155b610be1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420636861696e6c696e6b2070726963650000000000000000006044820152606401610a48565b600854610bef90600a614175565b610c0184670de0b6b3a7640000614181565b610c0b91906141be565b94505050505090565b60005473ffffffffffffffffffffffffffffffffffffffff16331480610c51575060025473ffffffffffffffffffffffffffffffffffffffff1633145b610cb7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b6006805473ffffffffffffffffffffffffffffffffffffffff8086167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316811790935560078054868316908416179055600a805491851691909216179055604080517f313ce567000000000000000000000000000000000000000000000000000000008152905163313ce567916004808201926020929091908290030181865afa158015610d6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8f91906141f9565b60ff16600855600754604080517f313ce567000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff9092169163313ce567916004808201926020929091908290030181865afa158015610e05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2991906141f9565b60ff16600955505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610edb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201527f6f726d207468697320616374696f6e00000000000000000000000000000000006064820152608401610a48565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906020015b60405180910390a150565b601c54600090610100900460ff1615610fca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f52656465656d73207061757365640000000000000000000000000000000000006044820152606401610a48565b600080610fd561335e565b925050915080611041576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f5065672062616e64205b52656465656d5d0000000000000000000000000000006044820152606401610a48565b670de0b6b3a76400006110548387614181565b61105e91906141be565b9250620f424061106c611937565b6110769085614181565b61108091906141be565b61108a908461421c565b9250838310156110f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f536c697070616765205b52656465656d5d0000000000000000000000000000006044820152606401610a48565b505092915050565b601c5460009060ff161561116e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4d696e74732070617573656400000000000000000000000000000000000000006044820152606401610a48565b60008061117961335e565b9250509150806111e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5065672062616e64205b4d696e745d00000000000000000000000000000000006044820152606401610a48565b816111f8670de0b6b3a764000087614181565b61120291906141be565b9250620f424061121061394a565b61121a9085614181565b61122491906141be565b61122e908461421c565b92508383101561129a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f536c697070616765205b4d696e745d00000000000000000000000000000000006044820152606401610a48565b601754600354604080517f18160ddd0000000000000000000000000000000000000000000000000000000081529051869273ffffffffffffffffffffffffffffffffffffffff16916318160ddd9160048083019260209291908290030181865afa15801561130c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113309190614233565b61133a919061424c565b11156110f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f465049206d696e742063617000000000000000000000000000000000000000006044820152606401610a48565b60005473ffffffffffffffffffffffffffffffffffffffff163314806113df575060025473ffffffffffffffffffffffffffffffffffffffff1633145b611445576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601755565b600c818154811061145a57600080fd5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b600061148d83836110fe565b6004549091506114b59073ffffffffffffffffffffffffffffffffffffffff16333086613b00565b6003546040517f6a257ebc0000000000000000000000000000000000000000000000000000000081523360048201526024810183905273ffffffffffffffffffffffffffffffffffffffff90911690636a257ebc90604401600060405180830381600087803b15801561152757600080fd5b505af115801561153b573d6000803e3d6000fd5b505060408051868152602081018590527ff4c708dad0640bb97b06e573b39b194c40301c581997805cbc86390ace5848c793500190505b60405180910390a192915050565b60005473ffffffffffffffffffffffffffffffffffffffff163314806115bd575060025473ffffffffffffffffffffffffffffffffffffffff1633145b611623576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601055565b336000818152600d602052604090205460ff166116a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f496e76616c696420414d4f0000000000000000000000000000000000000000006044820152606401610a48565b60045482906116c89073ffffffffffffffffffffffffffffffffffffffff16333084613b00565b336000908152600e6020526040812080548392906116e7908490614264565b9250508190555080600f60008282546117009190614264565b9091555050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331480611747575060025473ffffffffffffffffffffffffffffffffffffffff1633145b6117ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600d6020526040902054829060ff1661183e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f496e76616c696420414d4f0000000000000000000000000000000000000000006044820152606401610a48565b601054600f548391906118529083906142d8565b13156118ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f426f72726f7720636170000000000000000000000000000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600e6020526040812080548392906118ef9084906142d8565b9250508190555080600f600082825461190891906142d8565b90915550506004546119319073ffffffffffffffffffffffffffffffffffffffff168585613c96565b50505050565b60145460009060ff161561194c575060155490565b60009050620f4240601654826119629190614181565b61196c91906141be565b905090565b60015473ffffffffffffffffffffffffffffffffffffffff163314611a18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527f2063616e20616363657074206f776e65727368697000000000000000000000006064820152608401610a48565b6000546001546040805173ffffffffffffffffffffffffffffffffffffffff93841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff841617909155169055565b6005546040517f2e0ae3750000000000000000000000000000000000000000000000000000000081524260048201526000918291829173ffffffffffffffffffffffffffffffffffffffff1690632e0ae37590602401600060405180830381600087803b158015611b2c57600080fd5b505af1158015611b40573d6000803e3d6000fd5b50505050600080600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015611bb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bd8919061436a565b50601c546dffffffffffffffffffffffffffff92831694509116915062010000900460ff1615611c0d57819450809350611c14565b8094508193505b50829050611c2a84670de0b6b3a7640000614181565b611c3491906141be565b9050909192565b60005473ffffffffffffffffffffffffffffffffffffffff16331480611c78575060025473ffffffffffffffffffffffffffffffffffffffff1633145b611cde576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b8115611dd2576003546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906342966c689082906370a0823190602401602060405180830381865afa158015611d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d7e9190614233565b6040518263ffffffff1660e01b8152600401611d9c91815260200190565b600060405180830381600087803b158015611db657600080fd5b505af1158015611dca573d6000803e3d6000fd5b505050505050565b6003546040517f42966c680000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff909116906342966c6890602401611d9c565b5050565b60005473ffffffffffffffffffffffffffffffffffffffff16331480611e6a575060025473ffffffffffffffffffffffffffffffffffffffff1633145b611ed0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b60055460009073ffffffffffffffffffffffffffffffffffffffff166381fd0a468315611efd5783611f01565b600b545b6040518263ffffffff1660e01b8152600401611f1f91815260200190565b6060604051808303816000875af1158015611f3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f6291906143af565b50509050808015611f71575081155b15611e2957601c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffff1690555050565b60005473ffffffffffffffffffffffffffffffffffffffff16331480611fdf575060025473ffffffffffffffffffffffffffffffffffffffff1633145b612045576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b60005461206a90839073ffffffffffffffffffffffffffffffffffffffff1683613c96565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018390527f55350610fe57096d8c0ffa30beede987326bccfcb0b4415804164d0dd50ce8b1910160405180910390a15050565b60005473ffffffffffffffffffffffffffffffffffffffff163314806120fa575060025473ffffffffffffffffffffffffffffffffffffffff1633145b612160576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166121dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f5a65726f206164647265737320646574656374656400000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600d602052604090205460ff161515600114612271576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f41646472657373206e6f6e6578697374616e74000000000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600d6020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555b600c5481101561238c578173ffffffffffffffffffffffffffffffffffffffff16600c82815481106122f1576122f16143f2565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff160361237a576000600c828154811061232d5761232d6143f2565b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061238c565b8061238481614421565b9150506122bd565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527fcfdb9256a3d90cc6d479617ca43d8c8d264a9f5fe7f480029c4d918862c46db490602001610f4a565b60005473ffffffffffffffffffffffffffffffffffffffff16331480612410575060025473ffffffffffffffffffffffffffffffffffffffff1633145b612476576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601c546301000000900460ff161561249357612493600b54613551565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8416908117909155601e829055604080517f748fc63b000000000000000000000000000000000000000000000000000000008152905163748fc63b9160048082019260209290919082900301816000875af1158015612531573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125559190614233565b61255f90826141be565b601d555050565b600080600080612574613800565b9150600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633c09868e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126079190614233565b92506000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612678573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061269c9190614233565b9050838311156126fa57670de0b6b3a7640000816126ba868661421c565b6126c49190614181565b6126ce91906141be565b945082620f42406126df868361421c565b6126e99190614181565b6126f391906141be565b9150612774565b670de0b6b3a76400008161270e858761421c565b6127189190614181565b61272291906141be565b61274c907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614459565b945082620f424061275d828761421c565b6127679190614181565b61277191906141be565b91505b5090919293565b60005473ffffffffffffffffffffffffffffffffffffffff163314806127b8575060025473ffffffffffffffffffffffffffffffffffffffff1633145b61281e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601891909155601955565b60008054819073ffffffffffffffffffffffffffffffffffffffff16331480612869575060025473ffffffffffffffffffffffffffffffffffffffff1633145b6128cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b6000851180156128df5750600084115b15612946576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f43616e206f6e6c792073656c6c20696e206f6e6520646972656374696f6e00006044820152606401610a48565b601c546301000000900460ff16156129e657600554600b546040517f1f4f5b4200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90921691631f4f5b42916129b39160040190815260200190565b600060405180830381600087803b1580156129cd57600080fd5b505af11580156129e1573d6000803e3d6000fd5b505050505b60006129f0612566565b9350505050601954811115612a61576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5065672062616e64205b5457414d4d5d000000000000000000000000000000006044820152606401610a48565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166378dd02986040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ace573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612af29190614233565b600b558415612dee5784915060008211612b68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f46504920736f6c64206d757374206265206e6f6e7a65726f00000000000000006044820152606401610a48565b601b54821115612bd4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f546f6f206d7563682046504920736f6c640000000000000000000000000000006044820152606401610a48565b6003546040517f6a257ebc0000000000000000000000000000000000000000000000000000000081523060048201526024810184905273ffffffffffffffffffffffffffffffffffffffff90911690636a257ebc90604401600060405180830381600087803b158015612c4657600080fd5b505af1158015612c5a573d6000803e3d6000fd5b50506003546005546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152602481018790529116925063095ea7b391506044016020604051808303816000875af1158015612cd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cfd9190614515565b50601c5462010000900460ff1615612dc25760055473ffffffffffffffffffffffffffffffffffffffff166381ca79988386612d3b57601d54612d3d565b865b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b168152600481019290925260248201526044016020604051808303816000875af1158015612d98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dbc9190614233565b50613055565b60055473ffffffffffffffffffffffffffffffffffffffff1663c9738a0d8386612d3b57601d54612d3d565b85925060008311612e5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4652415820736f6c64206d757374206265206e6f6e7a65726f000000000000006044820152606401610a48565b601a54831115612ec7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f546f6f206d756368204652415820736f6c6400000000000000000000000000006044820152606401610a48565b600480546005546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182169381019390935260248301869052169063095ea7b3906044016020604051808303816000875af1158015612f44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f689190614515565b50601c5462010000900460ff1615612fa65760055473ffffffffffffffffffffffffffffffffffffffff1663c9738a0d8486612d3b57601d54612d3d565b60055473ffffffffffffffffffffffffffffffffffffffff166381ca79988486612fd257601d54612fd4565b865b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b168152600481019290925260248201526044016020604051808303816000875af115801561302f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130539190614233565b505b601c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffff166301000000179055600b54604080519182526020820185905281018390524260608201527fc26bbc8178bf327125d683aff5eff4547698645ff5efff7b616fe3fac345637a9060800160405180910390a150935093915050565b60005473ffffffffffffffffffffffffffffffffffffffff16331480613112575060025473ffffffffffffffffffffffffffffffffffffffff1633145b613178576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166131f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f5a65726f206164647265737320646574656374656400000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600d602052604090205460ff1615613285576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4164647265737320616c726561647920657869737473000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166000818152600d6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117909155600c805491820181559093527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c790920180547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905590519182527faa5bd6bda335b0c74f281b4b10d444ed06cd74963d7d77daa9a274eb4a7b36399101610f4a565b60008060008061336c613800565b9050600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633c09868e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ff9190614233565b9350838111156134335780620f4240613418868361421c565b6134229190614181565b61342c91906141be565b9250613459565b80620f4240613442828761421c565b61344c9190614181565b61345691906141be565b92505b601854831115915050909192565b60005473ffffffffffffffffffffffffffffffffffffffff163314806134a4575060025473ffffffffffffffffffffffffffffffffffffffff1633145b61350a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60005473ffffffffffffffffffffffffffffffffffffffff1633148061358e575060025473ffffffffffffffffffffffffffffffffffffffff1633145b6135f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b60055473ffffffffffffffffffffffffffffffffffffffff16631f4f5b42821561361e5782613622565b600b545b6040518263ffffffff1660e01b815260040161364091815260200190565b600060405180830381600087803b15801561365a57600080fd5b505af115801561366e573d6000803e3d6000fd5b5050601c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffff169055505050565b60006136a98383610f55565b6003549091506136d19073ffffffffffffffffffffffffffffffffffffffff16333086613b00565b6004546136f59073ffffffffffffffffffffffffffffffffffffffff163383613c96565b60408051848152602081018390527fa7c2e65a1acb60d7ad1de1c523988ccedba739d1184defb9c84afaeb6479b2619101611572565b60005473ffffffffffffffffffffffffffffffffffffffff16331480613768575060025473ffffffffffffffffffffffffffffffffffffffff1633145b6137ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00811660ff90911615179055565b6000806000806000600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613875573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138999190613fd4565b945094505093509350600083121580156138b257508115155b80156138d657508369ffffffffffffffffffff168169ffffffffffffffffffff1610155b61393c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420636861696e6c696e6b2070726963650000000000000000006044820152606401610a48565b600954610bef90600a614175565b60115460009060ff161561395f575060125490565b60009050620f4240601354826119629190614181565b60005473ffffffffffffffffffffffffffffffffffffffff163314806139b2575060025473ffffffffffffffffffffffffffffffffffffffff1633145b613a18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601a91909155601b55565b60005473ffffffffffffffffffffffffffffffffffffffff16331480613a60575060025473ffffffffffffffffffffffffffffffffffffffff1633145b613ac6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff81166101009182900460ff1615909102179055565b6040805173ffffffffffffffffffffffffffffffffffffffff85811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790529151600092839290881691613b9f9190614532565b6000604051808303816000865af19150503d8060008114613bdc576040519150601f19603f3d011682016040523d82523d6000602084013e613be1565b606091505b5091509150818015613c0b575080511580613c0b575080806020019051810190613c0b9190614515565b611dca576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f464160448201527f494c4544000000000000000000000000000000000000000000000000000000006064820152608401610a48565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790529151600092839290871691613d2d9190614532565b6000604051808303816000865af19150503d8060008114613d6a576040519150601f19603f3d011682016040523d82523d6000602084013e613d6f565b606091505b5091509150818015613d99575080511580613d99575080806020019051810190613d999190614515565b613dff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c4544006044820152606401610a48565b5050505050565b8015158114613e1457600080fd5b50565b60008060008060008060c08789031215613e3057600080fd5b8635613e3b81613e06565b955060208701359450604087013593506060870135613e5981613e06565b9598949750929560808101359460a0909101359350915050565b73ffffffffffffffffffffffffffffffffffffffff81168114613e1457600080fd5b600080600060608486031215613eaa57600080fd5b8335613eb581613e73565b92506020840135613ec581613e73565b91506040840135613ed581613e73565b809150509250925092565b600060208284031215613ef257600080fd5b8135613efd81613e73565b9392505050565b60008060408385031215613f1757600080fd5b50508035926020909101359150565b600060208284031215613f3857600080fd5b5035919050565b60008060408385031215613f5257600080fd5b8235613f5d81613e73565b946020939093013593505050565b60008060408385031215613f7e57600080fd5b8235613f5d81613e06565b600080600060608486031215613f9e57600080fd5b505081359360208301359350604090920135919050565b805169ffffffffffffffffffff81168114613fcf57600080fd5b919050565b600080600080600060a08688031215613fec57600080fd5b613ff586613fb5565b945060208601519350604086015192506060860151915061401860808701613fb5565b90509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600181815b808511156140ac57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561409257614092614024565b8085161561409f57918102915b93841c9390800290614058565b509250929050565b6000826140c35750600161416f565b816140d05750600061416f565b81600181146140e657600281146140f05761410c565b600191505061416f565b60ff84111561410157614101614024565b50506001821b61416f565b5060208310610133831016604e8410600b841016171561412f575081810a61416f565b6141398383614053565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561416b5761416b614024565b0290505b92915050565b6000613efd83836140b4565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156141b9576141b9614024565b500290565b6000826141f4577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60006020828403121561420b57600080fd5b815160ff81168114613efd57600080fd5b60008282101561422e5761422e614024565b500390565b60006020828403121561424557600080fd5b5051919050565b6000821982111561425f5761425f614024565b500190565b6000808312837f80000000000000000000000000000000000000000000000000000000000000000183128115161561429e5761429e614024565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183138116156142d2576142d2614024565b50500390565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0384138115161561431257614312614024565b827f800000000000000000000000000000000000000000000000000000000000000003841281161561434657614346614024565b50500190565b80516dffffffffffffffffffffffffffff81168114613fcf57600080fd5b60008060006060848603121561437f57600080fd5b6143888461434c565b92506143966020850161434c565b9150604084015163ffffffff81168114613ed557600080fd5b6000806000606084860312156143c457600080fd5b83516143cf81613e06565b60208501519093506143e081613e73565b80925050604084015190509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361445257614452614024565b5060010190565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60008413600084138583048511828216161561449a5761449a614024565b7f800000000000000000000000000000000000000000000000000000000000000060008712868205881281841616156144d5576144d5614024565b600087129250878205871284841616156144f1576144f1614024565b8785058712818416161561450757614507614024565b505050929093029392505050565b60006020828403121561452757600080fd5b8151613efd81613e06565b6000825160005b818110156145535760208186018101518583015201614539565b81811115614562576000828501525b50919091019291505056fea2646970667358221220208fed16333a2046b1f9866232ee4988528031799d47c7654db9f03ac633a27964736f6c634300080d003300000000000000000000000026ce2091749059a66703cd4b998156d94ec393ef0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000853d955acef822db058eb8505911ed77f175b99e0000000000000000000000005ca135cb8527d76e932f34b5145575f9d8cbe08e0000000000000000000000007d95b33cbec441917d4f617482d4b5c7e6c35902000000000000000000000000b9e1e3a9feff48998e45fa90847ed4d467e8bcfd00000000000000000000000059985d79e1e69f659f4ab97db07a35ce73d9174b0000000000000000000000007086f2acb5558043ff9ce3df346d8e3fb4f4f452

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106104715760003560e01c80638980f11f11610250578063ba92341e11610150578063d9ebcc2f116100c8578063e63a391f11610097578063ea36648a1161007c578063ea36648a14610964578063eb99aff814610981578063ef71c765146109a157600080fd5b8063e63a391f14610964578063e8d5931d1461096e57600080fd5b8063d9ebcc2f14610927578063da610fcf1461092f578063dc6663c714610937578063e24ab97b1461095757600080fd5b8063bf6a89a21161011f578063c70b6ad111610104578063c70b6ad11461090d578063cd080fbd14610915578063d578660a1461091e57600080fd5b8063bf6a89a2146108e7578063c5a36df1146108fa57600080fd5b8063ba92341e1461086c578063bc3d5a8e1461088c578063bda767ab146108b1578063bdacb303146108d457600080fd5b8063a2b2d0e9116101e3578063a7c027da116101b2578063b0e4556f11610197578063b0e4556f1461083a578063b565c0cb1461085a578063b5d7684b1461086357600080fd5b8063a7c027da1461081a578063ac12d5971461082d57600080fd5b8063a2b2d0e9146107ac578063a42b4b25146107d4578063a4c3e73c146107e7578063a63b39e91461080757600080fd5b80639238c0ac1161021f5780639238c0ac1461077857806395082d25146107815780639c97ca1b146107905780639fc397e3146107a357600080fd5b80638980f11f146107295780638da5cb5b1461073c57806390f749c71461075c578063919e06ce1461076557600080fd5b806350fc60cb11610376578063694033f2116102ee57806382cd8713116102bd57806386c8c345116102a257806386c8c345146106e3578063882c95c1146106f657806388b93d9a1461071657600080fd5b806382cd8713146106b75780638583f21c146106da57600080fd5b8063694033f2146106955780636e7813ff1461069e57806379ba5097146106a657806379df3933146106ae57600080fd5b80636195e1f11161034557806365525bf61161032a57806365525bf61461066557806367694bae146106795780636830f5711461068257600080fd5b80636195e1f114610653578063631a5a171461065c57600080fd5b806350fc60cb146105ed57806353a47bb7146106005780635592e9f514610620578063608984611461064057600080fd5b80632f7e992f116104095780633ae2e4a3116103d85780634608fd46116103bd5780634608fd46146105ba57806349696b20146105c75780634d6f9ef2146105da57600080fd5b80633ae2e4a3146105945780634070a0c9146105a757600080fd5b80632f7e992f1461056757806330fd8a461461057057806335f77feb146105825780633a0319d71461058b57600080fd5b80631627540c116104455780631627540c146104dc5780631937bdf9146104ef5780631eaa95321461050f5780632141203e1461052257600080fd5b8062aa08fd1461047657806309761c7e1461048b57806309d32681146104a6578063152129b2146104b9575b600080fd5b610489610484366004613e17565b6109a9565b005b610493610aa5565b6040519081526020015b60405180910390f35b6104896104b4366004613e95565b610c14565b601c546104cc9062010000900460ff1681565b604051901515815260200161049d565b6104896104ea366004613ee0565b610e34565b6104936104fd366004613ee0565b600e6020526000908152604090205481565b61049361051d366004613f04565b610f55565b6007546105429073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161049d565b61049360155481565b601c546104cc90610100900460ff1681565b610493601b5481565b610493601a5481565b6104936105a2366004613f04565b6110fe565b6104896105b5366004613f26565b6113a2565b6011546104cc9060ff1681565b6105426105d5366004613f26565b61144a565b6104936105e8366004613f04565b611481565b6104896105fb366004613f26565b611580565b6001546105429073ffffffffffffffffffffffffffffffffffffffff1681565b600a546105429073ffffffffffffffffffffffffffffffffffffffff1681565b61048961064e366004613f26565b611628565b61049360125481565b61049360135481565b601c546104cc906301000000900460ff1681565b61049360195481565b610489610690366004613f3f565b61170a565b61049360185481565b610493611937565b610489611971565b61049360085481565b6106bf611abc565b6040805193845260208401929092529082015260600161049d565b610493601e5481565b6104896106f1366004613f6b565b611c3b565b6003546105429073ffffffffffffffffffffffffffffffffffffffff1681565b610489610724366004613f26565b611e2d565b610489610737366004613f3f565b611fa2565b6000546105429073ffffffffffffffffffffffffffffffffffffffff1681565b61049360165481565b610489610773366004613ee0565b6120bd565b610493601d5481565b610493670de0b6b3a764000081565b61048961079e366004613f3f565b6123d3565b610493600f5481565b6107b4612566565b60408051948552602085019390935291830152606082015260800161049d565b6104896107e2366004613f04565b61277b565b670de0b6b3a7640000805b6040805192835260208301919091520161049d565b6107f2610815366004613f89565b612829565b610489610828366004613ee0565b6130d5565b6014546104cc9060ff1681565b6004546105429073ffffffffffffffffffffffffffffffffffffffff1681565b61049360095481565b61049360105481565b6005546105429073ffffffffffffffffffffffffffffffffffffffff1681565b61089461335e565b60408051938452602084019290925215159082015260600161049d565b6104cc6108bf366004613ee0565b600d6020526000908152604090205460ff1681565b6104896108e2366004613ee0565b613467565b6104896108f5366004613f26565b613551565b610493610908366004613f04565b61369d565b61048961372b565b610493600b5481565b61049360175481565b610493613800565b61049361394a565b6002546105429073ffffffffffffffffffffffffffffffffffffffff1681565b601c546104cc9060ff1681565b610493620f424081565b61048961097c366004613f04565b613975565b6006546105429073ffffffffffffffffffffffffffffffffffffffff1681565b610489613a23565b60005473ffffffffffffffffffffffffffffffffffffffff163314806109e6575060025473ffffffffffffffffffffffffffffffffffffffff1633145b610a51576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064015b60405180910390fd5b601180549615157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00978816179055601294909455601392909255601480549115159190941617909255601591909155601655565b6000806000806000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610b1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3e9190613fd4565b94509450509350935060008312158015610b5757508115155b8015610b7b57508369ffffffffffffffffffff168169ffffffffffffffffffff1610155b610be1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420636861696e6c696e6b2070726963650000000000000000006044820152606401610a48565b600854610bef90600a614175565b610c0184670de0b6b3a7640000614181565b610c0b91906141be565b94505050505090565b60005473ffffffffffffffffffffffffffffffffffffffff16331480610c51575060025473ffffffffffffffffffffffffffffffffffffffff1633145b610cb7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b6006805473ffffffffffffffffffffffffffffffffffffffff8086167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316811790935560078054868316908416179055600a805491851691909216179055604080517f313ce567000000000000000000000000000000000000000000000000000000008152905163313ce567916004808201926020929091908290030181865afa158015610d6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8f91906141f9565b60ff16600855600754604080517f313ce567000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff9092169163313ce567916004808201926020929091908290030181865afa158015610e05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2991906141f9565b60ff16600955505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610edb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201527f6f726d207468697320616374696f6e00000000000000000000000000000000006064820152608401610a48565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906020015b60405180910390a150565b601c54600090610100900460ff1615610fca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f52656465656d73207061757365640000000000000000000000000000000000006044820152606401610a48565b600080610fd561335e565b925050915080611041576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f5065672062616e64205b52656465656d5d0000000000000000000000000000006044820152606401610a48565b670de0b6b3a76400006110548387614181565b61105e91906141be565b9250620f424061106c611937565b6110769085614181565b61108091906141be565b61108a908461421c565b9250838310156110f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f536c697070616765205b52656465656d5d0000000000000000000000000000006044820152606401610a48565b505092915050565b601c5460009060ff161561116e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4d696e74732070617573656400000000000000000000000000000000000000006044820152606401610a48565b60008061117961335e565b9250509150806111e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5065672062616e64205b4d696e745d00000000000000000000000000000000006044820152606401610a48565b816111f8670de0b6b3a764000087614181565b61120291906141be565b9250620f424061121061394a565b61121a9085614181565b61122491906141be565b61122e908461421c565b92508383101561129a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f536c697070616765205b4d696e745d00000000000000000000000000000000006044820152606401610a48565b601754600354604080517f18160ddd0000000000000000000000000000000000000000000000000000000081529051869273ffffffffffffffffffffffffffffffffffffffff16916318160ddd9160048083019260209291908290030181865afa15801561130c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113309190614233565b61133a919061424c565b11156110f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f465049206d696e742063617000000000000000000000000000000000000000006044820152606401610a48565b60005473ffffffffffffffffffffffffffffffffffffffff163314806113df575060025473ffffffffffffffffffffffffffffffffffffffff1633145b611445576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601755565b600c818154811061145a57600080fd5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b600061148d83836110fe565b6004549091506114b59073ffffffffffffffffffffffffffffffffffffffff16333086613b00565b6003546040517f6a257ebc0000000000000000000000000000000000000000000000000000000081523360048201526024810183905273ffffffffffffffffffffffffffffffffffffffff90911690636a257ebc90604401600060405180830381600087803b15801561152757600080fd5b505af115801561153b573d6000803e3d6000fd5b505060408051868152602081018590527ff4c708dad0640bb97b06e573b39b194c40301c581997805cbc86390ace5848c793500190505b60405180910390a192915050565b60005473ffffffffffffffffffffffffffffffffffffffff163314806115bd575060025473ffffffffffffffffffffffffffffffffffffffff1633145b611623576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601055565b336000818152600d602052604090205460ff166116a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f496e76616c696420414d4f0000000000000000000000000000000000000000006044820152606401610a48565b60045482906116c89073ffffffffffffffffffffffffffffffffffffffff16333084613b00565b336000908152600e6020526040812080548392906116e7908490614264565b9250508190555080600f60008282546117009190614264565b9091555050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331480611747575060025473ffffffffffffffffffffffffffffffffffffffff1633145b6117ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600d6020526040902054829060ff1661183e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f496e76616c696420414d4f0000000000000000000000000000000000000000006044820152606401610a48565b601054600f548391906118529083906142d8565b13156118ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f426f72726f7720636170000000000000000000000000000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600e6020526040812080548392906118ef9084906142d8565b9250508190555080600f600082825461190891906142d8565b90915550506004546119319073ffffffffffffffffffffffffffffffffffffffff168585613c96565b50505050565b60145460009060ff161561194c575060155490565b60009050620f4240601654826119629190614181565b61196c91906141be565b905090565b60015473ffffffffffffffffffffffffffffffffffffffff163314611a18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527f2063616e20616363657074206f776e65727368697000000000000000000000006064820152608401610a48565b6000546001546040805173ffffffffffffffffffffffffffffffffffffffff93841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff841617909155169055565b6005546040517f2e0ae3750000000000000000000000000000000000000000000000000000000081524260048201526000918291829173ffffffffffffffffffffffffffffffffffffffff1690632e0ae37590602401600060405180830381600087803b158015611b2c57600080fd5b505af1158015611b40573d6000803e3d6000fd5b50505050600080600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015611bb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bd8919061436a565b50601c546dffffffffffffffffffffffffffff92831694509116915062010000900460ff1615611c0d57819450809350611c14565b8094508193505b50829050611c2a84670de0b6b3a7640000614181565b611c3491906141be565b9050909192565b60005473ffffffffffffffffffffffffffffffffffffffff16331480611c78575060025473ffffffffffffffffffffffffffffffffffffffff1633145b611cde576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b8115611dd2576003546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906342966c689082906370a0823190602401602060405180830381865afa158015611d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d7e9190614233565b6040518263ffffffff1660e01b8152600401611d9c91815260200190565b600060405180830381600087803b158015611db657600080fd5b505af1158015611dca573d6000803e3d6000fd5b505050505050565b6003546040517f42966c680000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff909116906342966c6890602401611d9c565b5050565b60005473ffffffffffffffffffffffffffffffffffffffff16331480611e6a575060025473ffffffffffffffffffffffffffffffffffffffff1633145b611ed0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b60055460009073ffffffffffffffffffffffffffffffffffffffff166381fd0a468315611efd5783611f01565b600b545b6040518263ffffffff1660e01b8152600401611f1f91815260200190565b6060604051808303816000875af1158015611f3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f6291906143af565b50509050808015611f71575081155b15611e2957601c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffff1690555050565b60005473ffffffffffffffffffffffffffffffffffffffff16331480611fdf575060025473ffffffffffffffffffffffffffffffffffffffff1633145b612045576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b60005461206a90839073ffffffffffffffffffffffffffffffffffffffff1683613c96565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018390527f55350610fe57096d8c0ffa30beede987326bccfcb0b4415804164d0dd50ce8b1910160405180910390a15050565b60005473ffffffffffffffffffffffffffffffffffffffff163314806120fa575060025473ffffffffffffffffffffffffffffffffffffffff1633145b612160576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166121dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f5a65726f206164647265737320646574656374656400000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600d602052604090205460ff161515600114612271576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f41646472657373206e6f6e6578697374616e74000000000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600d6020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555b600c5481101561238c578173ffffffffffffffffffffffffffffffffffffffff16600c82815481106122f1576122f16143f2565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff160361237a576000600c828154811061232d5761232d6143f2565b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061238c565b8061238481614421565b9150506122bd565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527fcfdb9256a3d90cc6d479617ca43d8c8d264a9f5fe7f480029c4d918862c46db490602001610f4a565b60005473ffffffffffffffffffffffffffffffffffffffff16331480612410575060025473ffffffffffffffffffffffffffffffffffffffff1633145b612476576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601c546301000000900460ff161561249357612493600b54613551565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8416908117909155601e829055604080517f748fc63b000000000000000000000000000000000000000000000000000000008152905163748fc63b9160048082019260209290919082900301816000875af1158015612531573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125559190614233565b61255f90826141be565b601d555050565b600080600080612574613800565b9150600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633c09868e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126079190614233565b92506000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612678573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061269c9190614233565b9050838311156126fa57670de0b6b3a7640000816126ba868661421c565b6126c49190614181565b6126ce91906141be565b945082620f42406126df868361421c565b6126e99190614181565b6126f391906141be565b9150612774565b670de0b6b3a76400008161270e858761421c565b6127189190614181565b61272291906141be565b61274c907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614459565b945082620f424061275d828761421c565b6127679190614181565b61277191906141be565b91505b5090919293565b60005473ffffffffffffffffffffffffffffffffffffffff163314806127b8575060025473ffffffffffffffffffffffffffffffffffffffff1633145b61281e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601891909155601955565b60008054819073ffffffffffffffffffffffffffffffffffffffff16331480612869575060025473ffffffffffffffffffffffffffffffffffffffff1633145b6128cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b6000851180156128df5750600084115b15612946576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f43616e206f6e6c792073656c6c20696e206f6e6520646972656374696f6e00006044820152606401610a48565b601c546301000000900460ff16156129e657600554600b546040517f1f4f5b4200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90921691631f4f5b42916129b39160040190815260200190565b600060405180830381600087803b1580156129cd57600080fd5b505af11580156129e1573d6000803e3d6000fd5b505050505b60006129f0612566565b9350505050601954811115612a61576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5065672062616e64205b5457414d4d5d000000000000000000000000000000006044820152606401610a48565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166378dd02986040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ace573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612af29190614233565b600b558415612dee5784915060008211612b68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f46504920736f6c64206d757374206265206e6f6e7a65726f00000000000000006044820152606401610a48565b601b54821115612bd4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f546f6f206d7563682046504920736f6c640000000000000000000000000000006044820152606401610a48565b6003546040517f6a257ebc0000000000000000000000000000000000000000000000000000000081523060048201526024810184905273ffffffffffffffffffffffffffffffffffffffff90911690636a257ebc90604401600060405180830381600087803b158015612c4657600080fd5b505af1158015612c5a573d6000803e3d6000fd5b50506003546005546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152602481018790529116925063095ea7b391506044016020604051808303816000875af1158015612cd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cfd9190614515565b50601c5462010000900460ff1615612dc25760055473ffffffffffffffffffffffffffffffffffffffff166381ca79988386612d3b57601d54612d3d565b865b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b168152600481019290925260248201526044016020604051808303816000875af1158015612d98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dbc9190614233565b50613055565b60055473ffffffffffffffffffffffffffffffffffffffff1663c9738a0d8386612d3b57601d54612d3d565b85925060008311612e5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4652415820736f6c64206d757374206265206e6f6e7a65726f000000000000006044820152606401610a48565b601a54831115612ec7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f546f6f206d756368204652415820736f6c6400000000000000000000000000006044820152606401610a48565b600480546005546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182169381019390935260248301869052169063095ea7b3906044016020604051808303816000875af1158015612f44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f689190614515565b50601c5462010000900460ff1615612fa65760055473ffffffffffffffffffffffffffffffffffffffff1663c9738a0d8486612d3b57601d54612d3d565b60055473ffffffffffffffffffffffffffffffffffffffff166381ca79988486612fd257601d54612fd4565b865b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b168152600481019290925260248201526044016020604051808303816000875af115801561302f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130539190614233565b505b601c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffff166301000000179055600b54604080519182526020820185905281018390524260608201527fc26bbc8178bf327125d683aff5eff4547698645ff5efff7b616fe3fac345637a9060800160405180910390a150935093915050565b60005473ffffffffffffffffffffffffffffffffffffffff16331480613112575060025473ffffffffffffffffffffffffffffffffffffffff1633145b613178576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166131f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f5a65726f206164647265737320646574656374656400000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600d602052604090205460ff1615613285576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4164647265737320616c726561647920657869737473000000000000000000006044820152606401610a48565b73ffffffffffffffffffffffffffffffffffffffff81166000818152600d6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117909155600c805491820181559093527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c790920180547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905590519182527faa5bd6bda335b0c74f281b4b10d444ed06cd74963d7d77daa9a274eb4a7b36399101610f4a565b60008060008061336c613800565b9050600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633c09868e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ff9190614233565b9350838111156134335780620f4240613418868361421c565b6134229190614181565b61342c91906141be565b9250613459565b80620f4240613442828761421c565b61344c9190614181565b61345691906141be565b92505b601854831115915050909192565b60005473ffffffffffffffffffffffffffffffffffffffff163314806134a4575060025473ffffffffffffffffffffffffffffffffffffffff1633145b61350a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60005473ffffffffffffffffffffffffffffffffffffffff1633148061358e575060025473ffffffffffffffffffffffffffffffffffffffff1633145b6135f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b60055473ffffffffffffffffffffffffffffffffffffffff16631f4f5b42821561361e5782613622565b600b545b6040518263ffffffff1660e01b815260040161364091815260200190565b600060405180830381600087803b15801561365a57600080fd5b505af115801561366e573d6000803e3d6000fd5b5050601c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffff169055505050565b60006136a98383610f55565b6003549091506136d19073ffffffffffffffffffffffffffffffffffffffff16333086613b00565b6004546136f59073ffffffffffffffffffffffffffffffffffffffff163383613c96565b60408051848152602081018390527fa7c2e65a1acb60d7ad1de1c523988ccedba739d1184defb9c84afaeb6479b2619101611572565b60005473ffffffffffffffffffffffffffffffffffffffff16331480613768575060025473ffffffffffffffffffffffffffffffffffffffff1633145b6137ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00811660ff90911615179055565b6000806000806000600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613875573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138999190613fd4565b945094505093509350600083121580156138b257508115155b80156138d657508369ffffffffffffffffffff168169ffffffffffffffffffff1610155b61393c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420636861696e6c696e6b2070726963650000000000000000006044820152606401610a48565b600954610bef90600a614175565b60115460009060ff161561395f575060125490565b60009050620f4240601354826119629190614181565b60005473ffffffffffffffffffffffffffffffffffffffff163314806139b2575060025473ffffffffffffffffffffffffffffffffffffffff1633145b613a18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601a91909155601b55565b60005473ffffffffffffffffffffffffffffffffffffffff16331480613a60575060025473ffffffffffffffffffffffffffffffffffffffff1633145b613ac6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b00000000000000000000006044820152606401610a48565b601c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff81166101009182900460ff1615909102179055565b6040805173ffffffffffffffffffffffffffffffffffffffff85811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790529151600092839290881691613b9f9190614532565b6000604051808303816000865af19150503d8060008114613bdc576040519150601f19603f3d011682016040523d82523d6000602084013e613be1565b606091505b5091509150818015613c0b575080511580613c0b575080806020019051810190613c0b9190614515565b611dca576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f464160448201527f494c4544000000000000000000000000000000000000000000000000000000006064820152608401610a48565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790529151600092839290871691613d2d9190614532565b6000604051808303816000865af19150503d8060008114613d6a576040519150601f19603f3d011682016040523d82523d6000602084013e613d6f565b606091505b5091509150818015613d99575080511580613d99575080806020019051810190613d999190614515565b613dff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c4544006044820152606401610a48565b5050505050565b8015158114613e1457600080fd5b50565b60008060008060008060c08789031215613e3057600080fd5b8635613e3b81613e06565b955060208701359450604087013593506060870135613e5981613e06565b9598949750929560808101359460a0909101359350915050565b73ffffffffffffffffffffffffffffffffffffffff81168114613e1457600080fd5b600080600060608486031215613eaa57600080fd5b8335613eb581613e73565b92506020840135613ec581613e73565b91506040840135613ed581613e73565b809150509250925092565b600060208284031215613ef257600080fd5b8135613efd81613e73565b9392505050565b60008060408385031215613f1757600080fd5b50508035926020909101359150565b600060208284031215613f3857600080fd5b5035919050565b60008060408385031215613f5257600080fd5b8235613f5d81613e73565b946020939093013593505050565b60008060408385031215613f7e57600080fd5b8235613f5d81613e06565b600080600060608486031215613f9e57600080fd5b505081359360208301359350604090920135919050565b805169ffffffffffffffffffff81168114613fcf57600080fd5b919050565b600080600080600060a08688031215613fec57600080fd5b613ff586613fb5565b945060208601519350604086015192506060860151915061401860808701613fb5565b90509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600181815b808511156140ac57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561409257614092614024565b8085161561409f57918102915b93841c9390800290614058565b509250929050565b6000826140c35750600161416f565b816140d05750600061416f565b81600181146140e657600281146140f05761410c565b600191505061416f565b60ff84111561410157614101614024565b50506001821b61416f565b5060208310610133831016604e8410600b841016171561412f575081810a61416f565b6141398383614053565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561416b5761416b614024565b0290505b92915050565b6000613efd83836140b4565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156141b9576141b9614024565b500290565b6000826141f4577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60006020828403121561420b57600080fd5b815160ff81168114613efd57600080fd5b60008282101561422e5761422e614024565b500390565b60006020828403121561424557600080fd5b5051919050565b6000821982111561425f5761425f614024565b500190565b6000808312837f80000000000000000000000000000000000000000000000000000000000000000183128115161561429e5761429e614024565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183138116156142d2576142d2614024565b50500390565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0384138115161561431257614312614024565b827f800000000000000000000000000000000000000000000000000000000000000003841281161561434657614346614024565b50500190565b80516dffffffffffffffffffffffffffff81168114613fcf57600080fd5b60008060006060848603121561437f57600080fd5b6143888461434c565b92506143966020850161434c565b9150604084015163ffffffff81168114613ed557600080fd5b6000806000606084860312156143c457600080fd5b83516143cf81613e06565b60208501519093506143e081613e73565b80925050604084015190509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361445257614452614024565b5060010190565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60008413600084138583048511828216161561449a5761449a614024565b7f800000000000000000000000000000000000000000000000000000000000000060008712868205881281841616156144d5576144d5614024565b600087129250878205871284841616156144f1576144f1614024565b8785058712818416161561450757614507614024565b505050929093029392505050565b60006020828403121561452757600080fd5b8151613efd81613e06565b6000825160005b818110156145535760208186018101518583015201614539565b81811115614562576000828501525b50919091019291505056fea2646970667358221220208fed16333a2046b1f9866232ee4988528031799d47c7654db9f03ac633a27964736f6c634300080d0033

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

00000000000000000000000026ce2091749059a66703cd4b998156d94ec393ef0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000853d955acef822db058eb8505911ed77f175b99e0000000000000000000000005ca135cb8527d76e932f34b5145575f9d8cbe08e0000000000000000000000007d95b33cbec441917d4f617482d4b5c7e6c35902000000000000000000000000b9e1e3a9feff48998e45fa90847ed4d467e8bcfd00000000000000000000000059985d79e1e69f659f4ab97db07a35ce73d9174b0000000000000000000000007086f2acb5558043ff9ce3df346d8e3fb4f4f452

-----Decoded View---------------
Arg [0] : _creator_address (address): 0x26Ce2091749059a66703CD4B998156d94eC393ef
Arg [1] : _timelock_address (address): 0x0000000000000000000000000000000000000000
Arg [2] : _address_pack (address[6]): 0x853d955aCEf822Db058eb8505911ED77F175b99e,0x5Ca135cB8527d76e932f34B5145575F9d8cbE08E,0x7d95b33cbEC441917d4f617482d4b5c7e6c35902,0xB9E1E3A9feFf48998E45Fa90847ed4D467E8BcfD,0x59985D79E1e69f659f4aB97Db07A35cE73D9174B,0x7086F2aCB5558043fF9cE3df346D8E3FB4F4f452

-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 00000000000000000000000026ce2091749059a66703cd4b998156d94ec393ef
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [2] : 000000000000000000000000853d955acef822db058eb8505911ed77f175b99e
Arg [3] : 0000000000000000000000005ca135cb8527d76e932f34b5145575f9d8cbe08e
Arg [4] : 0000000000000000000000007d95b33cbec441917d4f617482d4b5c7e6c35902
Arg [5] : 000000000000000000000000b9e1e3a9feff48998e45fa90847ed4d467e8bcfd
Arg [6] : 00000000000000000000000059985d79e1e69f659f4ab97db07a35ce73d9174b
Arg [7] : 0000000000000000000000007086f2acb5558043ff9ce3df346d8e3fb4f4f452


Deployed Bytecode Sourcemap

139818:21802:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;159809:607;;;;;;:::i;:::-;;:::i;:::-;;143925:372;;;:::i;:::-;;;939:25:1;;;927:2;912:18;143925:372:0;;;;;;;;158203:477;;;;;;:::i;:::-;;:::i;141885:26::-;;;;;;;;;;;;;;;1833:14:1;;1826:22;1808:41;;1796:2;1781:18;141885:26:0;1668:187:1;35902:141:0;;;;;;:::i;:::-;;:::i;140551:56::-;;;;;;:::i;:::-;;;;;;;;;;;;;;150145:800;;;;;;:::i;:::-;;:::i;140068:44::-;;;;;;;;;;;;2751:42:1;2739:55;;;2721:74;;2709:2;2694:18;140068:44:0;2545:256:1;141105:39:0;;;;;;141628:34;;;;;;;;;;;;141496:48;;;;;;141402:49;;;;;;148757:898;;;;;;:::i;:::-;;:::i;159476:112::-;;;;;;:::i;:::-;;:::i;140873:38::-;;;;;;;;;140393:27;;;;;;:::i;:::-;;:::i;149690:409::-;;;;;;:::i;:::-;;:::i;159342:126::-;;;;;;:::i;:::-;;:::i;35670:29::-;;;;;;;;;140216:34;;;;;;;;;156678:413;;;;;;:::i;:::-;;:::i;140918:37::-;;;;;;140968:44;;;;;;141918:39;;;;;;;;;;;;141350:38;;;;;;156060:552;;;;;;:::i;:::-;;:::i;141294:43::-;;;;;;146861:322;;;:::i;36051:271::-;;;:::i;140119:42::-;;;;;;144815:675;;;:::i;:::-;;;;4184:25:1;;;4240:2;4225:18;;4218:34;;;;4268:18;;;4261:34;4172:2;4157:18;144815:675:0;3982:319:1;142055:38:0;;;;;;155527:255;;;;;;:::i;:::-;;:::i;139914:18::-;;;;;;;;;154969:458;;;;;;:::i;:::-;;:::i;160920:300::-;;;;;;:::i;:::-;;:::i;35643:20::-;;;;;;;;;141157:46;;;;;;157530:665;;;;;;:::i;:::-;;:::i;141964:40::-;;;;;;141712:46;;141754:4;141712:46;;158688:440;;;;;;:::i;:::-;;:::i;140662:35::-;;;;;;147845:823;;;:::i;:::-;;;;5092:25:1;;;5148:2;5133:18;;5126:34;;;;5176:18;;;5169:34;5234:2;5219:18;;5212:34;5079:3;5064:19;147845:823:0;4863:389:1;159596:205:0;;;;;;:::i;:::-;;:::i;143632:255::-;143843:4;;143632:255;;;;5431:25:1;;;5487:2;5472:18;;5465:34;;;;5404:18;143632:255:0;5257:248:1;151479:3173:0;;;;;;:::i;:::-;;:::i;157176:323::-;;;;;;:::i;:::-;;:::i;141058:40::-;;;;;;;;;139939:17;;;;;;;;;140168:41;;;;;;140723:51;;;;;;139963:28;;;;;;;;;147234:554;;;:::i;:::-;;;;6527:25:1;;;6583:2;6568:18;;6561:34;;;;6638:14;6631:22;6611:18;;;6604:50;6515:2;6500:18;147234:554:0;6331:329:1;140427:36:0;;;;;;:::i;:::-;;;;;;;;;;;;;;;;160658:133;;;;;;:::i;:::-;;:::i;154660:301::-;;;;;;:::i;:::-;;:::i;150981:443::-;;;;;;:::i;:::-;;:::i;159136:92::-;;;:::i;140276:34::-;;;;;;141237:42;;;;;;144339:369;;;:::i;146537:302::-;;;:::i;139876:31::-;;;;;;;;;141589:32;;;;;;;;;141765:43;;141805:3;141765:43;;160424:226;;;;;;:::i;:::-;;:::i;140016:45::-;;;;;;;;;159236:98;;;:::i;159809:607::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;;;;;;;;;160105:19:::1;:42:::0;;;::::1;;::::0;;;::::1;;::::0;;160158:15:::1;:34:::0;;;;160203:19:::1;:42:::0;;;;160256:21:::1;:46:::0;;;::::1;;::::0;;;::::1;;::::0;;;160313:17:::1;:38:::0;;;;160362:21:::1;:46:::0;159809:607::o;143925:372::-;143973:7;143994:14;144010:9;144023:17;144042:22;144068:16;;;;;;;;;;;:32;;;:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;143993:109;;;;;;;;;144130:1;144121:5;:10;;:27;;;;-1:-1:-1;144135:13:0;;;144121:27;:57;;;;;144171:7;144152:26;;:15;:26;;;;144121:57;144113:93;;;;;;;7879:2:1;144113:93:0;;;7861:21:1;7918:2;7898:18;;;7891:30;7957:25;7937:18;;;7930:53;8000:18;;144113:93:0;7677:347:1;144113:93:0;144260:27;;144254:33;;:2;:33;:::i;:::-;144228:21;144236:5;144245:4;144228:21;:::i;:::-;144227:61;;;;:::i;:::-;144219:70;;;;;;143925:372;:::o;158203:477::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;158320:16:::1;:54:::0;;::::1;::::0;;::::1;::::0;;;::::1;::::0;::::1;::::0;;;158385:15:::1;:52:::0;;;;::::1;::::0;;::::1;;::::0;;158448:10:::1;:42:::0;;;;::::1;::::0;;;::::1;;::::0;;158579:27:::1;::::0;;;;;;;:25:::1;::::0;:27:::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;;158320:54;158579:27:::1;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;158549:57;;:27;:57:::0;158646:15:::1;::::0;:26:::1;::::0;;;;;;;:15:::1;::::0;;::::1;::::0;:24:::1;::::0;:26:::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;;:15;:26:::1;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;158617:55;;:26;:55:::0;-1:-1:-1;;;158203:477:0:o;35902:141::-;36382:5;;;;36368:10;:19;36360:79;;;;;;;10704:2:1;36360:79:0;;;10686:21:1;10743:2;10723:18;;;10716:30;10782:34;10762:18;;;10755:62;10853:17;10833:18;;;10826:45;10888:19;;36360:79:0;10502:411:1;36360:79:0;35974:14:::1;:23:::0;;;::::1;;::::0;::::1;::::0;;::::1;::::0;;;36013:22:::1;::::0;2721:74:1;;;36013:22:0::1;::::0;2709:2:1;2694:18;36013:22:0::1;;;;;;;;35902:141:::0;:::o;150145:800::-;150265:14;;150227:16;;150265:14;;;;;150264:15;150256:42;;;;;;;11120:2:1;150256:42:0;;;11102:21:1;11159:2;11139:18;;;11132:30;11198:16;11178:18;;;11171:44;11232:18;;150256:42:0;10918:338:1;150256:42:0;150359:21;150384:17;150405;:15;:17::i;:::-;150358:64;;;;;150556:12;150548:42;;;;;;;11463:2:1;150548:42:0;;;11445:21:1;11502:2;11482:18;;;11475:30;11541:19;11521:18;;;11514:47;11578:18;;150548:42:0;11261:341:1;150548:42:0;141754:4;150690:22;150699:13;150690:6;:22;:::i;:::-;150689:42;;;;:::i;:::-;150678:53;;141805:3;150794:12;:10;:12::i;:::-;150783:23;;:8;:23;:::i;:::-;150782:41;;;;:::i;:::-;150770:53;;;;:::i;:::-;;;150903:12;150891:8;:24;;150883:54;;;;;;;11939:2:1;150883:54:0;;;11921:21:1;11978:2;11958:18;;;11951:30;12017:19;11997:18;;;11990:47;12054:18;;150883:54:0;11737:341:1;150883:54:0;150245:700;;150145:800;;;;:::o;148757:898::-;148874:12;;148837:15;;148874:12;;148873:13;148865:38;;;;;;;12285:2:1;148865:38:0;;;12267:21:1;12324:2;12304:18;;;12297:30;12363:14;12343:18;;;12336:42;12395:18;;148865:38:0;12083:336:1;148865:38:0;148964:21;148989:17;149010;:15;:17::i;:::-;148963:64;;;;;149161:12;149153:40;;;;;;;12626:2:1;149153:40:0;;;12608:21:1;12665:2;12645:18;;;12638:30;12704:17;12684:18;;;12677:45;12739:18;;149153:40:0;12424:339:1;149153:40:0;149321:13;149292:25;141754:4;149292:7;:25;:::i;:::-;149291:43;;;;:::i;:::-;149281:53;;141805:3;149395:10;:8;:10::i;:::-;149385:20;;:7;:20;:::i;:::-;149384:38;;;;:::i;:::-;149373:49;;;;:::i;:::-;;;149500:11;149489:7;:22;;149481:50;;;;;;;12970:2:1;149481:50:0;;;12952:21:1;13009:2;12989:18;;;12982:30;13048:17;13028:18;;;13021:45;13083:18;;149481:50:0;12768:339:1;149481:50:0;149618:12;;149583:7;;:21;;;;;;;;149607:7;;149583;;;:19;;:21;;;;;;;;;;;;;;:7;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:31;;;;:::i;:::-;:47;;149575:72;;;;;;;13636:2:1;149575:72:0;;;13618:21:1;13675:2;13655:18;;;13648:30;13714:14;13694:18;;;13687:42;13746:18;;149575:72:0;13434:336:1;159476:112:0;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;159552:12:::1;:28:::0;159476:112::o;140393:27::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;140393:27:0;:::o;149690:409::-;149763:15;149801:33;149813:7;149822:11;149801;:33::i;:::-;149916:4;;149791:43;;-1:-1:-1;149876:82:0;;149916:4;;149923:10;149943:4;149950:7;149876:31;:82::i;:::-;150006:7;;:40;;;;;150026:10;150006:40;;;13949:74:1;14039:18;;;14032:34;;;150006:7:0;;;;;:19;;13922:18:1;;150006:40:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;150064:27:0;;;5431:25:1;;;5487:2;5472:18;;5465:34;;;150064:27:0;;-1:-1:-1;5404:18:1;;-1:-1:-1;150064:27:0;;;;;;;;149690:409;;;;:::o;159342:126::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;159426:15:::1;:34:::0;159342:126::o;156678:413::-;156745:10;142365:17;;;;:4;:17;;;;;;;;142357:41;;;;;;;14279:2:1;142357:41:0;;;14261:21:1;14318:2;14298:18;;;14291:30;14357:13;14337:18;;;14330:41;14388:18;;142357:41:0;14077:335:1;142357:41:0;156891:4:::1;::::0;156798:11;;156851:86:::1;::::0;156891:4:::1;;156898:10;156918:4;156798:11:::0;156851:31:::1;:86::i;:::-;157010:10;156987:34;::::0;;;:22:::1;:34;::::0;;;;:51;;157025:13;;156987:34;:51:::1;::::0;157025:13;;156987:51:::1;:::i;:::-;;;;;;;;157070:13;157049:17;;:34;;;;;;;:::i;:::-;::::0;;;-1:-1:-1;;;;;156678:413:0:o;156060:552::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;142365:17:::1;::::0;::::1;;::::0;;;:4:::1;:17;::::0;;;;;156160:15;;142365:17:::1;;142357:41;;;::::0;::::1;::::0;;14279:2:1;142357:41:0::1;::::0;::::1;14261:21:1::0;14318:2;14298:18;;;14291:30;14357:13;14337:18;;;14330:41;14388:18;;142357:41:0::1;14077:335:1::0;142357:41:0::1;156334:15:::2;::::0;156293:17:::2;::::0;156221:11;;156334:15;156293:36:::2;::::0;156221:11;;156293:36:::2;:::i;:::-;156292:57;;156284:80;;;::::0;::::2;::::0;;15365:2:1;156284:80:0::2;::::0;::::2;15347:21:1::0;15404:2;15384:18;;;15377:30;15443:12;15423:18;;;15416:40;15473:18;;156284:80:0::2;15163:334:1::0;156284:80:0::2;156375:39;::::0;::::2;;::::0;;;:22:::2;:39;::::0;;;;:59;;156418:16;;156375:39;:59:::2;::::0;156418:16;;156375:59:::2;:::i;:::-;;;;;;;;156466:16;156445:17;;:37;;;;;;;:::i;:::-;::::0;;;-1:-1:-1;;156568:4:0::2;::::0;156532:72:::2;::::0;156568:4:::2;;156575:15:::0;156592:11;156532:27:::2;:72::i;:::-;156177:435;142290:1:::1;156060:552:::0;;:::o;146861:322::-;146932:21;;146904:11;;146932:21;;146928:236;;;-1:-1:-1;146961:17:0;;146861:322;:::o;146928:236::-;147056:1;147050:7;;147149:3;147124:21;;147118:3;:27;;;;:::i;:::-;147117:35;;;;:::i;:::-;147111:41;;146861:322;:::o;36051:271::-;36120:14;;;;36106:10;:28;36098:94;;;;;;;15704:2:1;36098:94:0;;;15686:21:1;15743:2;15723:18;;;15716:30;15782:34;15762:18;;;15755:62;15853:23;15833:18;;;15826:51;15894:19;;36098:94:0;15502:417:1;36098:94:0;36221:5;;;36228:14;36208:35;;;36221:5;;;;16159:34:1;;36228:14:0;;;;16224:2:1;16209:18;;16202:43;36208:35:0;;16071:18:1;36208:35:0;;;;;;;36262:14;;;;36254:22;;;;;;36262:14;;;36254:22;;;;36287:27;;;36051:271::o;144815:675::-;144975:5;;:43;;;;;145002:15;144975:43;;;939:25:1;144864:19:0;;;;;;144975:5;;;:26;;912:18:1;;144975:43:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;145045:16;145063;145085:5;;;;;;;;;;;:17;;;:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;145123:14:0;;145044:60;;;;;-1:-1:-1;145044:60:0;;;-1:-1:-1;145123:14:0;;;;;145119:250;;;145171:8;145157:22;;145211:8;145198:21;;145119:250;;;145305:8;145291:22;;145345:8;145332:21;;145119:250;-1:-1:-1;145472:10:0;;-1:-1:-1;145450:18:0;:11;145464:4;145450:18;:::i;:::-;145449:33;;;;:::i;:::-;145437:45;;144815:675;;;:::o;155527:255::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;155614:8:::1;155610:164;;;155677:7;::::0;155690:32:::1;::::0;;;;155716:4:::1;155690:32;::::0;::::1;2721:74:1::0;155677:7:0::1;::::0;;::::1;::::0;:12:::1;::::0;:7;;155690:17:::1;::::0;2694:18:1;;155690:32:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;155677:46;;;;;;;;;;;;;939:25:1::0;;927:2;912:18;;793:177;155677:46:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;155527:255:::0;;:::o;155610:164::-:1;155750:7;::::0;:24:::1;::::0;;;;::::1;::::0;::::1;939:25:1::0;;;155750:7:0::1;::::0;;::::1;::::0;:12:::1;::::0;912:18:1;;155750:24:0::1;793:177:1::0;155610:164:0::1;155527:255:::0;;:::o;154969:458::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;155125:5:::1;::::0;155102:15:::1;::::0;155125:5:::1;;:38;155164:22:::0;;:64:::1;;155211:17;155164:64;;;155189:19;;155164:64;155125:104;;;;;;;;;;;;;939:25:1::0;;927:2;912:18;;793:177;155125:104:0::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;155101:128;;;;155352:10;:38;;;;-1:-1:-1::0;155367:22:0;;155352:38:::1;155348:71;;;155392:19;:27:::0;;;::::1;::::0;;155052:375:::1;154969:458:::0;:::o;160920:300::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;161136:5:::1;::::0;161094:61:::1;::::0;161122:12;;161136:5:::1;;161143:11:::0;161094:27:::1;:61::i;:::-;161171:41;::::0;;13979:42:1;13967:55;;13949:74;;14054:2;14039:18;;14032:34;;;161171:41:0::1;::::0;13922:18:1;161171:41:0::1;;;;;;;160920:300:::0;;:::o;157530:665::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;157609:25:::1;::::0;::::1;157601:59;;;::::0;::::1;::::0;;17551:2:1;157601:59:0::1;::::0;::::1;17533:21:1::0;17590:2;17570:18;;;17563:30;17629:23;17609:18;;;17602:51;17670:18;;157601:59:0::1;17349:345:1::0;157601:59:0::1;157679:17;::::0;::::1;;::::0;;;:4:::1;:17;::::0;;;;;::::1;;:25;;:17:::0;:25:::1;157671:57;;;::::0;::::1;::::0;;17901:2:1;157671:57:0::1;::::0;::::1;17883:21:1::0;17940:2;17920:18;;;17913:30;17979:21;17959:18;;;17952:49;18018:18;;157671:57:0::1;17699:343:1::0;157671:57:0::1;157792:17;::::0;::::1;;::::0;;;:4:::1;:17;::::0;;;;157785:24;;;::::1;::::0;;157888:259:::1;157909:10;:17:::0;157905:21;::::1;157888:259;;;157969:11;157952:28;;:10;157963:1;157952:13;;;;;;;;:::i;:::-;;::::0;;;::::1;::::0;;;::::1;::::0;::::1;;:28:::0;157948:188:::1;;158025:1;158001:10;158012:1;158001:13;;;;;;;;:::i;:::-;;;;;;;;;:26;;;;;;;;;;;;;;;;;;158115:5;;157948:188;157928:3:::0;::::1;::::0;::::1;:::i;:::-;;;;157888:259;;;-1:-1:-1::0;158164:23:0::1;::::0;2751:42:1;2739:55;;2721:74;;158164:23:0::1;::::0;2709:2:1;2694:18;158164:23:0::1;2545:256:1::0;158688:440:0;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;158851:19:::1;::::0;;;::::1;;;158847:66;;;158872:41;158893:19;;158872:20;:41::i;:::-;158974:5;:36:::0;;;::::1;;::::0;::::1;::::0;;::::1;::::0;;;159021:11:::1;:26:::0;;;159095:25:::1;::::0;;;;;;;:23:::1;::::0;:25:::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;;-1:-1:-1;158974:36:0;159095:25:::1;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;159080:40;::::0;:12;:40:::1;:::i;:::-;159058:19;:62:::0;-1:-1:-1;;158688:440:0:o;147845:823::-;147898:23;147933:21;147965:17;147993:27;148051:16;:14;:16::i;:::-;148039:28;;148094:10;;;;;;;;;;;:23;;;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;148078:41;;148130:18;148151:7;;;;;;;;;;;:19;;;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;148130:42;;148201:13;148189:9;:25;148185:476;;;141754:4;148287:10;148258:25;148270:13;148258:9;:25;:::i;:::-;148257:40;;;;:::i;:::-;148256:60;;;;:::i;:::-;148230:87;-1:-1:-1;148407:9:0;141860:3;148356:25;148368:13;148407:9;148356:25;:::i;:::-;148355:48;;;;:::i;:::-;148354:62;;;;:::i;:::-;148332:84;;148185:476;;;141754:4;148520:10;148491:25;148507:9;148491:13;:25;:::i;:::-;148490:40;;;;:::i;:::-;148489:60;;;;:::i;:::-;148477:73;;:2;:73;:::i;:::-;148458:92;-1:-1:-1;148640:9:0;141860:3;148589:25;148640:9;148589:13;:25;:::i;:::-;148588:48;;;;:::i;:::-;148587:62;;;;:::i;:::-;148565:84;;148185:476;148028:640;147845:823;;;;:::o;159596:205::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;159706:20:::1;:44:::0;;;;159761:14:::1;:32:::0;159596:205::o;151479:3173::-;151604:19;142214:5;;151604:19;;142214:5;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;151731:1:::1;151715:13;:17;151714:41;;;;;151753:1;151738:12;:16;151714:41;151712:44;151704:87;;;::::0;::::1;::::0;;19298:2:1;151704:87:0::1;::::0;::::1;19280:21:1::0;19337:2;19317:18;;;19310:30;19376:32;19356:18;;;19349:60;19426:18;;151704:87:0::1;19096:354:1::0;151704:87:0::1;152072:19;::::0;;;::::1;;;152068:70;;;152093:5;::::0;152118:19:::1;::::0;152093:45:::1;::::0;;;;:5:::1;::::0;;::::1;::::0;:24:::1;::::0;:45:::1;::::0;::::1;;939:25:1::0;;;927:2;912:18;;793:177;152093:45:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;152068:70;152213:22;152239:12;:10;:12::i;:::-;152206:45;;;;;152386:14;;152368;:32;;152360:61;;;::::0;::::1;::::0;;19657:2:1;152360:61:0::1;::::0;::::1;19639:21:1::0;19696:2;19676:18;;;19669:30;19735:18;19715;;;19708:46;19771:18;;152360:61:0::1;19455:340:1::0;152360:61:0::1;152487:5;;;;;;;;;;;:20;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;152465:19;:44:::0;152540:16;;152536:1919:::1;;152690:12;152677:25;;152786:1;152773:10;:14;152765:51;;;::::0;::::1;::::0;;20002:2:1;152765:51:0::1;::::0;::::1;19984:21:1::0;20041:2;20021:18;;;20014:30;20080:26;20060:18;;;20053:54;20124:18;;152765:51:0::1;19800:348:1::0;152765:51:0::1;152892:19;;152878:10;:33;;152870:63;;;::::0;::::1;::::0;;20355:2:1;152870:63:0::1;::::0;::::1;20337:21:1::0;20394:2;20374:18;;;20367:30;20433:19;20413:18;;;20406:47;20470:18;;152870:63:0::1;20153:341:1::0;152870:63:0::1;152988:7;::::0;:46:::1;::::0;;;;153016:4:::1;152988:46;::::0;::::1;13949:74:1::0;14039:18;;;14032:34;;;152988:7:0::1;::::0;;::::1;::::0;:19:::1;::::0;13922:18:1;;152988:46:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;153093:7:0::1;::::0;153117:5:::1;::::0;153093:43:::1;::::0;;;;:7:::1;153117:5:::0;;::::1;153093:43;::::0;::::1;13949:74:1::0;14039:18;;;14032:34;;;153093:7:0;::::1;::::0;-1:-1:-1;153093:15:0::1;::::0;-1:-1:-1;13922:18:1;;153093:43:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;153199:14:0::1;::::0;;;::::1;;;153195:339;;;153238:5;::::0;::::1;;:26;153265:10:::0;153277:22;:65:::1;;153323:19;;153277:65;;;153302:18;153277:65;153238:105;::::0;;::::1;::::0;;;;;;::::1;::::0;::::1;5431:25:1::0;;;;5472:18;;;5465:34;5404:18;;153238:105:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;152536:1919;;153195:339;153409:5;::::0;::::1;;:26;153436:10:::0;153448:22;:65:::1;;153494:19;;153448:65;;152536:1919;153694:13;153680:27;;153788:1;153774:11;:15;153766:53;;;::::0;::::1;::::0;;20951:2:1;153766:53:0::1;::::0;::::1;20933:21:1::0;20990:2;20970:18;;;20963:30;21029:27;21009:18;;;21002:55;21074:18;;153766:53:0::1;20749:349:1::0;153766:53:0::1;153896:20;;153881:11;:35;;153873:66;;;::::0;::::1;::::0;;21305:2:1;153873:66:0::1;::::0;::::1;21287:21:1::0;21344:2;21324:18;;;21317:30;21383:20;21363:18;;;21356:48;21421:18;;153873:66:0::1;21103:342:1::0;153873:66:0::1;153999:4;::::0;;154020:5:::1;::::0;153999:41:::1;::::0;;;;:4:::1;154020:5:::0;;::::1;153999:41:::0;;::::1;13949:74:1::0;;;;14039:18;;;14032:34;;;153999:4:0::1;::::0;:12:::1;::::0;13922:18:1;;153999:41:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;154103:14:0::1;::::0;;;::::1;;;154099:341;;;154142:5;::::0;::::1;;:26;154169:11:::0;154182:22;:65:::1;;154228:19;;154182:65;;154099:341;154314:5;::::0;::::1;;:26;154341:11:::0;154354:22;:65:::1;;154400:19;;154354:65;;;154379:18;154354:65;154314:106;::::0;;::::1;::::0;;;;;;::::1;::::0;::::1;5431:25:1::0;;;;5472:18;;;5465:34;5404:18;;154314:106:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;154099:341;154525:19;:26:::0;;;::::1;::::0;::::1;::::0;;154582:19:::1;::::0;154569:75:::1;::::0;;5092:25:1;;;5148:2;5133:18;;5126:34;;;5176:18;;5169:34;;;154628:15:0::1;5234:2:1::0;5219:18;;5212:34;154569:75:0::1;::::0;5079:3:1;5064:19;154569:75:0::1;;;;;;;151645:3007;151479:3173:::0;;;;;;:::o;157176:323::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;157252:25:::1;::::0;::::1;157244:59;;;::::0;::::1;::::0;;17551:2:1;157244:59:0::1;::::0;::::1;17533:21:1::0;17590:2;17570:18;;;17563:30;17629:23;17609:18;;;17602:51;17670:18;;157244:59:0::1;17349:345:1::0;157244:59:0::1;157324:17;::::0;::::1;;::::0;;;:4:::1;:17;::::0;;;;;::::1;;:26;157316:61;;;::::0;::::1;::::0;;22048:2:1;157316:61:0::1;::::0;::::1;22030:21:1::0;22087:2;22067:18;;;22060:30;22126:24;22106:18;;;22099:52;22168:18;;157316:61:0::1;21846:346:1::0;157316:61:0::1;157388:17;::::0;::::1;;::::0;;;:4:::1;:17;::::0;;;;;;;:24;;;::::1;157408:4;157388:24:::0;;::::1;::::0;;;157424:10:::1;:28:::0;;;;::::1;::::0;;;;;;;;::::1;::::0;;;::::1;::::0;::::1;::::0;;157470:21;;2721:74:1;;;157470:21:0::1;::::0;2694:18:1;157470:21:0::1;2545:256:1::0;147234:554:0;147282:21;147305;147328:17;147358;147378:16;:14;:16::i;:::-;147358:36;;147421:10;;;;;;;;;;;:23;;;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;147405:41;;147475:13;147463:9;:25;147459:255;;;147573:9;141860:3;147522:25;147534:13;147573:9;147522:25;:::i;:::-;147521:48;;;;:::i;:::-;147520:62;;;;:::i;:::-;147504:78;;147459:255;;;147693:9;141860:3;147642:25;147693:9;147642:13;:25;:::i;:::-;147641:48;;;;:::i;:::-;147640:62;;;;:::i;:::-;147624:78;;147459:255;147759:20;;147742:13;:37;;147726:54;;147347:441;147234:554;;;:::o;160658:133::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;160743:16:::1;:40:::0;;;::::1;;::::0;;;::::1;::::0;;;::::1;::::0;;160658:133::o;154660:301::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;154777:5:::1;::::0;::::1;;:24;154802:22:::0;;:64:::1;;154849:17;154802:64;;;154827:19;;154802:64;154777:90;;;;;;;;;;;;;939:25:1::0;;927:2;912:18;;793:177;154777:90:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;154926:19:0::1;:27:::0;;;::::1;::::0;;-1:-1:-1;;;154660:301:0:o;150981:443::-;151056:16;151096:35;151110:6;151118:12;151096:13;:35::i;:::-;151212:7;;151085:46;;-1:-1:-1;151172:84:0;;151212:7;;151222:10;151242:4;151249:6;151172:31;:84::i;:::-;151341:4;;151305:64;;151341:4;;151348:10;151360:8;151305:27;:64::i;:::-;151387:29;;;5431:25:1;;;5487:2;5472:18;;5465:34;;;151387:29:0;;5404:18:1;151387:29:0;5257:248:1;159136:92:0;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;159208:12:::1;::::0;;159192:28;;::::1;159208:12;::::0;;::::1;159207:13;159192:28;::::0;;159136:92::o;144339:369::-;144386:7;144407:14;144423:9;144436:17;144455:22;144481:15;;;;;;;;;;;:31;;;:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;144406:108;;;;;;;;;144542:1;144533:5;:10;;:27;;;;-1:-1:-1;144547:13:0;;;144533:27;:57;;;;;144583:7;144564:26;;:15;:26;;;;144533:57;144525:93;;;;;;;7879:2:1;144525:93:0;;;7861:21:1;7918:2;7898:18;;;7891:30;7957:25;7937:18;;;7930:53;8000:18;;144525:93:0;7677:347:1;144525:93:0;144672:26;;144666:32;;:2;:32;:::i;146537:302::-;146606:19;;146578:11;;146606:19;;146602:230;;;-1:-1:-1;146633:15:0;;146861:322;:::o;146602:230::-;146726:1;146720:7;;146817:3;146794:19;;146788:3;:25;;;;:::i;160424:226::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;160545:20:::1;:44:::0;;;;160600:19:::1;:42:::0;160424:226::o;159236:98::-;142214:5;;;;142200:10;:19;;:53;;-1:-1:-1;142237:16:0;;;;142223:10;:30;142200:53;142192:87;;;;;;;6867:2:1;142192:87:0;;;6849:21:1;6906:2;6886:18;;;6879:30;6945:23;6925:18;;;6918:51;6986:18;;142192:87:0;6665:345:1;142192:87:0;159312:14:::1;::::0;;159294:32;;::::1;159312:14;::::0;;;::::1;;;159311:15;159294:32:::0;;::::1;;::::0;;159236:98::o;108690:402::-;108915:51;;;108904:10;22478:15:1;;;108915:51:0;;;22460:34:1;22530:15;;;22510:18;;;22503:43;22562:18;;;;22555:34;;;108915:51:0;;;;;;;;;;22372:18:1;;;;108915:51:0;;;;;;;;;;;;;108904:63;;-1:-1:-1;;;;108904:10:0;;;;:63;;108915:51;108904:63;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;108868:99;;;;108986:7;:57;;;;-1:-1:-1;108998:11:0;;:16;;:44;;;109029:4;109018:24;;;;;;;;;;;;:::i;:::-;108978:106;;;;;;;23233:2:1;108978:106:0;;;23215:21:1;23272:2;23252:18;;;23245:30;23311:34;23291:18;;;23284:62;23382:6;23362:18;;;23355:34;23406:19;;108978:106:0;23031:400:1;108321:361:0;108516:45;;;108505:10;13967:55:1;;;108516:45:0;;;13949:74:1;14039:18;;;;14032:34;;;108516:45:0;;;;;;;;;;13922:18:1;;;;108516:45:0;;;;;;;;;;;;;108505:57;;-1:-1:-1;;;;108505:10:0;;;;:57;;108516:45;108505:57;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;108469:93;;;;108581:7;:57;;;;-1:-1:-1;108593:11:0;;:16;;:44;;;108624:4;108613:24;;;;;;;;;;;;:::i;:::-;108573:101;;;;;;;23638:2:1;108573:101:0;;;23620:21:1;23677:2;23657:18;;;23650:30;23716:33;23696:18;;;23689:61;23767:18;;108573:101:0;23436:355:1;108573:101:0;108391:291;;108321:361;;;:::o;14:118:1:-;100:5;93:13;86:21;79:5;76:32;66:60;;122:1;119;112:12;66:60;14:118;:::o;137:651::-;235:6;243;251;259;267;275;328:3;316:9;307:7;303:23;299:33;296:53;;;345:1;342;335:12;296:53;384:9;371:23;403:28;425:5;403:28;:::i;:::-;450:5;-1:-1:-1;502:2:1;487:18;;474:32;;-1:-1:-1;553:2:1;538:18;;525:32;;-1:-1:-1;609:2:1;594:18;;581:32;622:30;581:32;622:30;:::i;:::-;137:651;;;;-1:-1:-1;137:651:1;;725:3;710:19;;697:33;;777:3;762:19;;;749:33;;-1:-1:-1;137:651:1;-1:-1:-1;;137:651:1:o;975:154::-;1061:42;1054:5;1050:54;1043:5;1040:65;1030:93;;1119:1;1116;1109:12;1134:529;1211:6;1219;1227;1280:2;1268:9;1259:7;1255:23;1251:32;1248:52;;;1296:1;1293;1286:12;1248:52;1335:9;1322:23;1354:31;1379:5;1354:31;:::i;:::-;1404:5;-1:-1:-1;1461:2:1;1446:18;;1433:32;1474:33;1433:32;1474:33;:::i;:::-;1526:7;-1:-1:-1;1585:2:1;1570:18;;1557:32;1598:33;1557:32;1598:33;:::i;:::-;1650:7;1640:17;;;1134:529;;;;;:::o;1860:247::-;1919:6;1972:2;1960:9;1951:7;1947:23;1943:32;1940:52;;;1988:1;1985;1978:12;1940:52;2027:9;2014:23;2046:31;2071:5;2046:31;:::i;:::-;2096:5;1860:247;-1:-1:-1;;;1860:247:1:o;2292:248::-;2360:6;2368;2421:2;2409:9;2400:7;2396:23;2392:32;2389:52;;;2437:1;2434;2427:12;2389:52;-1:-1:-1;;2460:23:1;;;2530:2;2515:18;;;2502:32;;-1:-1:-1;2292:248:1:o;2806:180::-;2865:6;2918:2;2906:9;2897:7;2893:23;2889:32;2886:52;;;2934:1;2931;2924:12;2886:52;-1:-1:-1;2957:23:1;;2806:180;-1:-1:-1;2806:180:1:o;3662:315::-;3730:6;3738;3791:2;3779:9;3770:7;3766:23;3762:32;3759:52;;;3807:1;3804;3797:12;3759:52;3846:9;3833:23;3865:31;3890:5;3865:31;:::i;:::-;3915:5;3967:2;3952:18;;;;3939:32;;-1:-1:-1;;;3662:315:1:o;4306:309::-;4371:6;4379;4432:2;4420:9;4411:7;4407:23;4403:32;4400:52;;;4448:1;4445;4438:12;4400:52;4487:9;4474:23;4506:28;4528:5;4506:28;:::i;5510:316::-;5587:6;5595;5603;5656:2;5644:9;5635:7;5631:23;5627:32;5624:52;;;5672:1;5669;5662:12;5624:52;-1:-1:-1;;5695:23:1;;;5765:2;5750:18;;5737:32;;-1:-1:-1;5816:2:1;5801:18;;;5788:32;;5510:316;-1:-1:-1;5510:316:1:o;7015:179::-;7093:13;;7146:22;7135:34;;7125:45;;7115:73;;7184:1;7181;7174:12;7115:73;7015:179;;;:::o;7199:473::-;7302:6;7310;7318;7326;7334;7387:3;7375:9;7366:7;7362:23;7358:33;7355:53;;;7404:1;7401;7394:12;7355:53;7427:39;7456:9;7427:39;:::i;:::-;7417:49;;7506:2;7495:9;7491:18;7485:25;7475:35;;7550:2;7539:9;7535:18;7529:25;7519:35;;7594:2;7583:9;7579:18;7573:25;7563:35;;7617:49;7661:3;7650:9;7646:19;7617:49;:::i;:::-;7607:59;;7199:473;;;;;;;;:::o;8029:184::-;8081:77;8078:1;8071:88;8178:4;8175:1;8168:15;8202:4;8199:1;8192:15;8218:482;8307:1;8350:5;8307:1;8364:330;8385:7;8375:8;8372:21;8364:330;;;8504:4;8436:66;8432:77;8426:4;8423:87;8420:113;;;8513:18;;:::i;:::-;8563:7;8553:8;8549:22;8546:55;;;8583:16;;;;8546:55;8662:22;;;;8622:15;;;;8364:330;;;8368:3;8218:482;;;;;:::o;8705:866::-;8754:5;8784:8;8774:80;;-1:-1:-1;8825:1:1;8839:5;;8774:80;8873:4;8863:76;;-1:-1:-1;8910:1:1;8924:5;;8863:76;8955:4;8973:1;8968:59;;;;9041:1;9036:130;;;;8948:218;;8968:59;8998:1;8989:10;;9012:5;;;9036:130;9073:3;9063:8;9060:17;9057:43;;;9080:18;;:::i;:::-;-1:-1:-1;;9136:1:1;9122:16;;9151:5;;8948:218;;9250:2;9240:8;9237:16;9231:3;9225:4;9222:13;9218:36;9212:2;9202:8;9199:16;9194:2;9188:4;9185:12;9181:35;9178:77;9175:159;;;-1:-1:-1;9287:19:1;;;9319:5;;9175:159;9366:34;9391:8;9385:4;9366:34;:::i;:::-;9496:6;9428:66;9424:79;9415:7;9412:92;9409:118;;;9507:18;;:::i;:::-;9545:20;;-1:-1:-1;8705:866:1;;;;;:::o;9576:131::-;9636:5;9665:36;9692:8;9686:4;9665:36;:::i;9712:228::-;9752:7;9878:1;9810:66;9806:74;9803:1;9800:81;9795:1;9788:9;9781:17;9777:105;9774:131;;;9885:18;;:::i;:::-;-1:-1:-1;9925:9:1;;9712:228::o;9945:274::-;9985:1;10011;10001:189;;10046:77;10043:1;10036:88;10147:4;10144:1;10137:15;10175:4;10172:1;10165:15;10001:189;-1:-1:-1;10204:9:1;;9945:274::o;10224:273::-;10292:6;10345:2;10333:9;10324:7;10320:23;10316:32;10313:52;;;10361:1;10358;10351:12;10313:52;10393:9;10387:16;10443:4;10436:5;10432:16;10425:5;10422:27;10412:55;;10463:1;10460;10453:12;11607:125;11647:4;11675:1;11672;11669:8;11666:34;;;11680:18;;:::i;:::-;-1:-1:-1;11717:9:1;;11607:125::o;13112:184::-;13182:6;13235:2;13223:9;13214:7;13210:23;13206:32;13203:52;;;13251:1;13248;13241:12;13203:52;-1:-1:-1;13274:16:1;;13112:184;-1:-1:-1;13112:184:1:o;13301:128::-;13341:3;13372:1;13368:6;13365:1;13362:13;13359:39;;;13378:18;;:::i;:::-;-1:-1:-1;13414:9:1;;13301:128::o;14417:369::-;14456:4;14492:1;14489;14485:9;14601:1;14533:66;14529:74;14526:1;14522:82;14517:2;14510:10;14506:99;14503:125;;;14608:18;;:::i;:::-;14727:1;14659:66;14655:74;14652:1;14648:82;14644:2;14640:91;14637:117;;;14734:18;;:::i;:::-;-1:-1:-1;;14771:9:1;;14417:369::o;14791:367::-;14830:3;14865:1;14862;14858:9;14974:1;14906:66;14902:74;14899:1;14895:82;14890:2;14883:10;14879:99;14876:125;;;14981:18;;:::i;:::-;15100:1;15032:66;15028:74;15025:1;15021:82;15017:2;15013:91;15010:117;;;15107:18;;:::i;:::-;-1:-1:-1;;15143:9:1;;14791:367::o;16256:188::-;16335:13;;16388:30;16377:42;;16367:53;;16357:81;;16434:1;16431;16424:12;16449:450;16536:6;16544;16552;16605:2;16593:9;16584:7;16580:23;16576:32;16573:52;;;16621:1;16618;16611:12;16573:52;16644:40;16674:9;16644:40;:::i;:::-;16634:50;;16703:49;16748:2;16737:9;16733:18;16703:49;:::i;:::-;16693:59;;16795:2;16784:9;16780:18;16774:25;16839:10;16832:5;16828:22;16821:5;16818:33;16808:61;;16865:1;16862;16855:12;16904:440;16989:6;16997;17005;17058:2;17046:9;17037:7;17033:23;17029:32;17026:52;;;17074:1;17071;17064:12;17026:52;17106:9;17100:16;17125:28;17147:5;17125:28;:::i;:::-;17222:2;17207:18;;17201:25;17172:5;;-1:-1:-1;17235:33:1;17201:25;17235:33;:::i;:::-;17287:7;17277:17;;;17334:2;17323:9;17319:18;17313:25;17303:35;;16904:440;;;;;:::o;18047:184::-;18099:77;18096:1;18089:88;18196:4;18193:1;18186:15;18220:4;18217:1;18210:15;18236:195;18275:3;18306:66;18299:5;18296:77;18293:103;;18376:18;;:::i;:::-;-1:-1:-1;18423:1:1;18412:13;;18236:195::o;18436:655::-;18475:7;18507:66;18599:1;18596;18592:9;18627:1;18624;18620:9;18672:1;18668:2;18664:10;18661:1;18658:17;18653:2;18649;18645:11;18641:35;18638:61;;;18679:18;;:::i;:::-;18718:66;18810:1;18807;18803:9;18857:1;18853:2;18848:11;18845:1;18841:19;18836:2;18832;18828:11;18824:37;18821:63;;;18864:18;;:::i;:::-;18910:1;18907;18903:9;18893:19;;18957:1;18953:2;18948:11;18945:1;18941:19;18936:2;18932;18928:11;18924:37;18921:63;;;18964:18;;:::i;:::-;19029:1;19025:2;19020:11;19017:1;19013:19;19008:2;19004;19000:11;18996:37;18993:63;;;19036:18;;:::i;:::-;-1:-1:-1;;;19076:9:1;;;;;18436:655;-1:-1:-1;;;18436:655:1:o;20499:245::-;20566:6;20619:2;20607:9;20598:7;20594:23;20590:32;20587:52;;;20635:1;20632;20625:12;20587:52;20667:9;20661:16;20686:28;20708:5;20686:28;:::i;22600:426::-;22729:3;22767:6;22761:13;22792:1;22802:129;22816:6;22813:1;22810:13;22802:129;;;22914:4;22898:14;;;22894:25;;22888:32;22875:11;;;22868:53;22831:12;22802:129;;;22949:6;22946:1;22943:13;22940:48;;;22984:1;22975:6;22970:3;22966:16;22959:27;22940:48;-1:-1:-1;23004:16:1;;;;;22600:426;-1:-1:-1;;22600:426:1:o

Swarm Source

ipfs://208fed16333a2046b1f9866232ee4988528031799d47c7654db9f03ac633a279

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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