creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    function __Ownable_init() internal onlyInitializing {\n        __Ownable_init_unchained();\n    }\n\n    function __Ownable_init_unchained() internal onlyInitializing {\n        _transferOwnership(_msgSender());\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n\n    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[49] private __gap;\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/AddressUpgradeable.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n *     function initialize() initializer public {\n *         __ERC20_init(\"MyToken\", \"MTK\");\n *     }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n *     function initializeV2() reinitializer(2) public {\n *         __ERC20Permit_init(\"MyToken\");\n *     }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n *     _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n    /**\n     * @dev Indicates that the contract has been initialized.\n     * @custom:oz-retyped-from bool\n     */\n    uint8 private _initialized;\n\n    /**\n     * @dev Indicates that the contract is in the process of being initialized.\n     */\n    bool private _initializing;\n\n    /**\n     * @dev Triggered when the contract has been initialized or reinitialized.\n     */\n    event Initialized(uint8 version);\n\n    /**\n     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n     * `onlyInitializing` functions can be used to initialize parent contracts.\n     *\n     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\n     * constructor.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier initializer() {\n        bool isTopLevelCall = !_initializing;\n        require(\n            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\n            \"Initializable: contract is already initialized\"\n        );\n        _initialized = 1;\n        if (isTopLevelCall) {\n            _initializing = true;\n        }\n        _;\n        if (isTopLevelCall) {\n            _initializing = false;\n            emit Initialized(1);\n        }\n    }\n\n    /**\n     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n     * used to initialize parent contracts.\n     *\n     * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n     * are added through upgrades and that require initialization.\n     *\n     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n     * cannot be nested. If one is invoked in the context of another, execution will revert.\n     *\n     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n     * a contract, executing them in the right order is up to the developer or operator.\n     *\n     * WARNING: setting the version to 255 will prevent any future reinitialization.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier reinitializer(uint8 version) {\n        require(!_initializing && _initialized < version, \"Initializable: contract is already initialized\");\n        _initialized = version;\n        _initializing = true;\n        _;\n        _initializing = false;\n        emit Initialized(version);\n    }\n\n    /**\n     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n     * {initializer} and {reinitializer} modifiers, directly or indirectly.\n     */\n    modifier onlyInitializing() {\n        require(_initializing, \"Initializable: contract is not initializing\");\n        _;\n    }\n\n    /**\n     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n     * through proxies.\n     *\n     * Emits an {Initialized} event the first time it is successfully executed.\n     */\n    function _disableInitializers() internal virtual {\n        require(!_initializing, \"Initializable: contract is initializing\");\n        if (_initialized != type(uint8).max) {\n            _initialized = type(uint8).max;\n            emit Initialized(type(uint8).max);\n        }\n    }\n\n    /**\n     * @dev Returns the highest version that has been initialized. See {reinitializer}.\n     */\n    function _getInitializedVersion() internal view returns (uint8) {\n        return _initialized;\n    }\n\n    /**\n     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n     */\n    function _isInitializing() internal view returns (bool) {\n        return _initializing;\n    }\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20Upgradeable.sol\";\nimport \"./extensions/IERC20MetadataUpgradeable.sol\";\nimport \"../../utils/ContextUpgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * The default value of {decimals} is 18. To change this, you should override\n * this function so it returns a different value.\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {\n    mapping(address => uint256) private _balances;\n\n    mapping(address => mapping(address => uint256)) private _allowances;\n\n    uint256 private _totalSupply;\n\n    string private _name;\n    string private _symbol;\n\n    /**\n     * @dev Sets the values for {name} and {symbol}.\n     *\n     * All two of these values are immutable: they can only be set once during\n     * construction.\n     */\n    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {\n        __ERC20_init_unchained(name_, symbol_);\n    }\n\n    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {\n        _name = name_;\n        _symbol = symbol_;\n    }\n\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() public view virtual override returns (string memory) {\n        return _name;\n    }\n\n    /**\n     * @dev Returns the symbol of the token, usually a shorter version of the\n     * name.\n     */\n    function symbol() public view virtual override returns (string memory) {\n        return _symbol;\n    }\n\n    /**\n     * @dev Returns the number of decimals used to get its user representation.\n     * For example, if `decimals` equals `2`, a balance of `505` tokens should\n     * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n     *\n     * Tokens usually opt for a value of 18, imitating the relationship between\n     * Ether and Wei. This is the default value returned by this function, unless\n     * it's overridden.\n     *\n     * NOTE: This information is only used for _display_ purposes: it in\n     * no way affects any of the arithmetic of the contract, including\n     * {IERC20-balanceOf} and {IERC20-transfer}.\n     */\n    function decimals() public view virtual override returns (uint8) {\n        return 18;\n    }\n\n    /**\n     * @dev See {IERC20-totalSupply}.\n     */\n    function totalSupply() public view virtual override returns (uint256) {\n        return _totalSupply;\n    }\n\n    /**\n     * @dev See {IERC20-balanceOf}.\n     */\n    function balanceOf(address account) public view virtual override returns (uint256) {\n        return _balances[account];\n    }\n\n    /**\n     * @dev See {IERC20-transfer}.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     * - the caller must have a balance of at least `amount`.\n     */\n    function transfer(address to, uint256 amount) public virtual override returns (bool) {\n        address owner = _msgSender();\n        _transfer(owner, to, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-allowance}.\n     */\n    function allowance(address owner, address spender) public view virtual override returns (uint256) {\n        return _allowances[owner][spender];\n    }\n\n    /**\n     * @dev See {IERC20-approve}.\n     *\n     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\n     * `transferFrom`. This is semantically equivalent to an infinite approval.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function approve(address spender, uint256 amount) public virtual override returns (bool) {\n        address owner = _msgSender();\n        _approve(owner, spender, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-transferFrom}.\n     *\n     * Emits an {Approval} event indicating the updated allowance. This is not\n     * required by the EIP. See the note at the beginning of {ERC20}.\n     *\n     * NOTE: Does not update the allowance if the current allowance\n     * is the maximum `uint256`.\n     *\n     * Requirements:\n     *\n     * - `from` and `to` cannot be the zero address.\n     * - `from` must have a balance of at least `amount`.\n     * - the caller must have allowance for ``from``'s tokens of at least\n     * `amount`.\n     */\n    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {\n        address spender = _msgSender();\n        _spendAllowance(from, spender, amount);\n        _transfer(from, to, amount);\n        return true;\n    }\n\n    /**\n     * @dev Atomically increases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n        address owner = _msgSender();\n        _approve(owner, spender, allowance(owner, spender) + addedValue);\n        return true;\n    }\n\n    /**\n     * @dev Atomically decreases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `spender` must have allowance for the caller of at least\n     * `subtractedValue`.\n     */\n    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n        address owner = _msgSender();\n        uint256 currentAllowance = allowance(owner, spender);\n        require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n        unchecked {\n            _approve(owner, spender, currentAllowance - subtractedValue);\n        }\n\n        return true;\n    }\n\n    /**\n     * @dev Moves `amount` of tokens from `from` to `to`.\n     *\n     * This internal function is equivalent to {transfer}, and can be used to\n     * e.g. implement automatic token fees, slashing mechanisms, etc.\n     *\n     * Emits a {Transfer} event.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `from` must have a balance of at least `amount`.\n     */\n    function _transfer(address from, address to, uint256 amount) internal virtual {\n        require(from != address(0), \"ERC20: transfer from the zero address\");\n        require(to != address(0), \"ERC20: transfer to the zero address\");\n\n        _beforeTokenTransfer(from, to, amount);\n\n        uint256 fromBalance = _balances[from];\n        require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n        unchecked {\n            _balances[from] = fromBalance - amount;\n            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by\n            // decrementing then incrementing.\n            _balances[to] += amount;\n        }\n\n        emit Transfer(from, to, amount);\n\n        _afterTokenTransfer(from, to, amount);\n    }\n\n    /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n     * the total supply.\n     *\n     * Emits a {Transfer} event with `from` set to the zero address.\n     *\n     * Requirements:\n     *\n     * - `account` cannot be the zero address.\n     */\n    function _mint(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: mint to the zero address\");\n\n        _beforeTokenTransfer(address(0), account, amount);\n\n        _totalSupply += amount;\n        unchecked {\n            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\n            _balances[account] += amount;\n        }\n        emit Transfer(address(0), account, amount);\n\n        _afterTokenTransfer(address(0), account, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, reducing the\n     * total supply.\n     *\n     * Emits a {Transfer} event with `to` set to the zero address.\n     *\n     * Requirements:\n     *\n     * - `account` cannot be the zero address.\n     * - `account` must have at least `amount` tokens.\n     */\n    function _burn(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: burn from the zero address\");\n\n        _beforeTokenTransfer(account, address(0), amount);\n\n        uint256 accountBalance = _balances[account];\n        require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n        unchecked {\n            _balances[account] = accountBalance - amount;\n            // Overflow not possible: amount <= accountBalance <= totalSupply.\n            _totalSupply -= amount;\n        }\n\n        emit Transfer(account, address(0), amount);\n\n        _afterTokenTransfer(account, address(0), amount);\n    }\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n     *\n     * This internal function is equivalent to `approve`, and can be used to\n     * e.g. set automatic allowances for certain subsystems, etc.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `owner` cannot be the zero address.\n     * - `spender` cannot be the zero address.\n     */\n    function _approve(address owner, address spender, uint256 amount) internal virtual {\n        require(owner != address(0), \"ERC20: approve from the zero address\");\n        require(spender != address(0), \"ERC20: approve to the zero address\");\n\n        _allowances[owner][spender] = amount;\n        emit Approval(owner, spender, amount);\n    }\n\n    /**\n     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.\n     *\n     * Does not update the allowance amount in case of infinite allowance.\n     * Revert if not enough allowance is available.\n     *\n     * Might emit an {Approval} event.\n     */\n    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {\n        uint256 currentAllowance = allowance(owner, spender);\n        if (currentAllowance != type(uint256).max) {\n            require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n            unchecked {\n                _approve(owner, spender, currentAllowance - amount);\n            }\n        }\n    }\n\n    /**\n     * @dev Hook that is called before any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n     * will be transferred to `to`.\n     * - when `from` is zero, `amount` tokens will be minted for `to`.\n     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}\n\n    /**\n     * @dev Hook that is called after any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n     * has been transferred to `to`.\n     * - when `from` is zero, `amount` tokens have been minted for `to`.\n     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}\n\n    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[45] private __gap;\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20Upgradeable.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20MetadataUpgradeable is IERC20Upgradeable {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20Upgradeable {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC721Upgradeable.sol\";\nimport \"./IERC721ReceiverUpgradeable.sol\";\nimport \"./extensions/IERC721MetadataUpgradeable.sol\";\nimport \"../../utils/AddressUpgradeable.sol\";\nimport \"../../utils/ContextUpgradeable.sol\";\nimport \"../../utils/StringsUpgradeable.sol\";\nimport \"../../utils/introspection/ERC165Upgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including\n * the Metadata extension, but not including the Enumerable extension, which is available separately as\n * {ERC721Enumerable}.\n */\ncontract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {\n    using AddressUpgradeable for address;\n    using StringsUpgradeable for uint256;\n\n    // Token name\n    string private _name;\n\n    // Token symbol\n    string private _symbol;\n\n    // Mapping from token ID to owner address\n    mapping(uint256 => address) private _owners;\n\n    // Mapping owner address to token count\n    mapping(address => uint256) private _balances;\n\n    // Mapping from token ID to approved address\n    mapping(uint256 => address) private _tokenApprovals;\n\n    // Mapping from owner to operator approvals\n    mapping(address => mapping(address => bool)) private _operatorApprovals;\n\n    /**\n     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.\n     */\n    function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {\n        __ERC721_init_unchained(name_, symbol_);\n    }\n\n    function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {\n        _name = name_;\n        _symbol = symbol_;\n    }\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {\n        return\n            interfaceId == type(IERC721Upgradeable).interfaceId ||\n            interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||\n            super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev See {IERC721-balanceOf}.\n     */\n    function balanceOf(address owner) public view virtual override returns (uint256) {\n        require(owner != address(0), \"ERC721: address zero is not a valid owner\");\n        return _balances[owner];\n    }\n\n    /**\n     * @dev See {IERC721-ownerOf}.\n     */\n    function ownerOf(uint256 tokenId) public view virtual override returns (address) {\n        address owner = _ownerOf(tokenId);\n        require(owner != address(0), \"ERC721: invalid token ID\");\n        return owner;\n    }\n\n    /**\n     * @dev See {IERC721Metadata-name}.\n     */\n    function name() public view virtual override returns (string memory) {\n        return _name;\n    }\n\n    /**\n     * @dev See {IERC721Metadata-symbol}.\n     */\n    function symbol() public view virtual override returns (string memory) {\n        return _symbol;\n    }\n\n    /**\n     * @dev See {IERC721Metadata-tokenURI}.\n     */\n    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {\n        _requireMinted(tokenId);\n\n        string memory baseURI = _baseURI();\n        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : \"\";\n    }\n\n    /**\n     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each\n     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty\n     * by default, can be overridden in child contracts.\n     */\n    function _baseURI() internal view virtual returns (string memory) {\n        return \"\";\n    }\n\n    /**\n     * @dev See {IERC721-approve}.\n     */\n    function approve(address to, uint256 tokenId) public virtual override {\n        address owner = ERC721Upgradeable.ownerOf(tokenId);\n        require(to != owner, \"ERC721: approval to current owner\");\n\n        require(\n            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),\n            \"ERC721: approve caller is not token owner or approved for all\"\n        );\n\n        _approve(to, tokenId);\n    }\n\n    /**\n     * @dev See {IERC721-getApproved}.\n     */\n    function getApproved(uint256 tokenId) public view virtual override returns (address) {\n        _requireMinted(tokenId);\n\n        return _tokenApprovals[tokenId];\n    }\n\n    /**\n     * @dev See {IERC721-setApprovalForAll}.\n     */\n    function setApprovalForAll(address operator, bool approved) public virtual override {\n        _setApprovalForAll(_msgSender(), operator, approved);\n    }\n\n    /**\n     * @dev See {IERC721-isApprovedForAll}.\n     */\n    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {\n        return _operatorApprovals[owner][operator];\n    }\n\n    /**\n     * @dev See {IERC721-transferFrom}.\n     */\n    function transferFrom(address from, address to, uint256 tokenId) public virtual override {\n        //solhint-disable-next-line max-line-length\n        require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: caller is not token owner or approved\");\n\n        _transfer(from, to, tokenId);\n    }\n\n    /**\n     * @dev See {IERC721-safeTransferFrom}.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {\n        safeTransferFrom(from, to, tokenId, \"\");\n    }\n\n    /**\n     * @dev See {IERC721-safeTransferFrom}.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {\n        require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: caller is not token owner or approved\");\n        _safeTransfer(from, to, tokenId, data);\n    }\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n     * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n     *\n     * `data` is additional data, it has no specified format and it is sent in call to `to`.\n     *\n     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.\n     * implement alternative mechanisms to perform token transfer, such as signature-based.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {\n        _transfer(from, to, tokenId);\n        require(_checkOnERC721Received(from, to, tokenId, data), \"ERC721: transfer to non ERC721Receiver implementer\");\n    }\n\n    /**\n     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist\n     */\n    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {\n        return _owners[tokenId];\n    }\n\n    /**\n     * @dev Returns whether `tokenId` exists.\n     *\n     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.\n     *\n     * Tokens start existing when they are minted (`_mint`),\n     * and stop existing when they are burned (`_burn`).\n     */\n    function _exists(uint256 tokenId) internal view virtual returns (bool) {\n        return _ownerOf(tokenId) != address(0);\n    }\n\n    /**\n     * @dev Returns whether `spender` is allowed to manage `tokenId`.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {\n        address owner = ERC721Upgradeable.ownerOf(tokenId);\n        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);\n    }\n\n    /**\n     * @dev Safely mints `tokenId` and transfers it to `to`.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must not exist.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _safeMint(address to, uint256 tokenId) internal virtual {\n        _safeMint(to, tokenId, \"\");\n    }\n\n    /**\n     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is\n     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.\n     */\n    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {\n        _mint(to, tokenId);\n        require(\n            _checkOnERC721Received(address(0), to, tokenId, data),\n            \"ERC721: transfer to non ERC721Receiver implementer\"\n        );\n    }\n\n    /**\n     * @dev Mints `tokenId` and transfers it to `to`.\n     *\n     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible\n     *\n     * Requirements:\n     *\n     * - `tokenId` must not exist.\n     * - `to` cannot be the zero address.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _mint(address to, uint256 tokenId) internal virtual {\n        require(to != address(0), \"ERC721: mint to the zero address\");\n        require(!_exists(tokenId), \"ERC721: token already minted\");\n\n        _beforeTokenTransfer(address(0), to, tokenId, 1);\n\n        // Check that tokenId was not minted by `_beforeTokenTransfer` hook\n        require(!_exists(tokenId), \"ERC721: token already minted\");\n\n        unchecked {\n            // Will not overflow unless all 2**256 token ids are minted to the same owner.\n            // Given that tokens are minted one by one, it is impossible in practice that\n            // this ever happens. Might change if we allow batch minting.\n            // The ERC fails to describe this case.\n            _balances[to] += 1;\n        }\n\n        _owners[tokenId] = to;\n\n        emit Transfer(address(0), to, tokenId);\n\n        _afterTokenTransfer(address(0), to, tokenId, 1);\n    }\n\n    /**\n     * @dev Destroys `tokenId`.\n     * The approval is cleared when the token is burned.\n     * This is an internal function that does not check if the sender is authorized to operate on the token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _burn(uint256 tokenId) internal virtual {\n        address owner = ERC721Upgradeable.ownerOf(tokenId);\n\n        _beforeTokenTransfer(owner, address(0), tokenId, 1);\n\n        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook\n        owner = ERC721Upgradeable.ownerOf(tokenId);\n\n        // Clear approvals\n        delete _tokenApprovals[tokenId];\n\n        unchecked {\n            // Cannot overflow, as that would require more tokens to be burned/transferred\n            // out than the owner initially received through minting and transferring in.\n            _balances[owner] -= 1;\n        }\n        delete _owners[tokenId];\n\n        emit Transfer(owner, address(0), tokenId);\n\n        _afterTokenTransfer(owner, address(0), tokenId, 1);\n    }\n\n    /**\n     * @dev Transfers `tokenId` from `from` to `to`.\n     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must be owned by `from`.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _transfer(address from, address to, uint256 tokenId) internal virtual {\n        require(ERC721Upgradeable.ownerOf(tokenId) == from, \"ERC721: transfer from incorrect owner\");\n        require(to != address(0), \"ERC721: transfer to the zero address\");\n\n        _beforeTokenTransfer(from, to, tokenId, 1);\n\n        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook\n        require(ERC721Upgradeable.ownerOf(tokenId) == from, \"ERC721: transfer from incorrect owner\");\n\n        // Clear approvals from the previous owner\n        delete _tokenApprovals[tokenId];\n\n        unchecked {\n            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:\n            // `from`'s balance is the number of token held, which is at least one before the current\n            // transfer.\n            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require\n            // all 2**256 token ids to be minted, which in practice is impossible.\n            _balances[from] -= 1;\n            _balances[to] += 1;\n        }\n        _owners[tokenId] = to;\n\n        emit Transfer(from, to, tokenId);\n\n        _afterTokenTransfer(from, to, tokenId, 1);\n    }\n\n    /**\n     * @dev Approve `to` to operate on `tokenId`\n     *\n     * Emits an {Approval} event.\n     */\n    function _approve(address to, uint256 tokenId) internal virtual {\n        _tokenApprovals[tokenId] = to;\n        emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);\n    }\n\n    /**\n     * @dev Approve `operator` to operate on all of `owner` tokens\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {\n        require(owner != operator, \"ERC721: approve to caller\");\n        _operatorApprovals[owner][operator] = approved;\n        emit ApprovalForAll(owner, operator, approved);\n    }\n\n    /**\n     * @dev Reverts if the `tokenId` has not been minted yet.\n     */\n    function _requireMinted(uint256 tokenId) internal view virtual {\n        require(_exists(tokenId), \"ERC721: invalid token ID\");\n    }\n\n    /**\n     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.\n     * The call is not executed if the target address is not a contract.\n     *\n     * @param from address representing the previous owner of the given token ID\n     * @param to target address that will receive the tokens\n     * @param tokenId uint256 ID of the token to be transferred\n     * @param data bytes optional data to send along with the call\n     * @return bool whether the call correctly returned the expected magic value\n     */\n    function _checkOnERC721Received(\n        address from,\n        address to,\n        uint256 tokenId,\n        bytes memory data\n    ) private returns (bool) {\n        if (to.isContract()) {\n            try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {\n                return retval == IERC721ReceiverUpgradeable.onERC721Received.selector;\n            } catch (bytes memory reason) {\n                if (reason.length == 0) {\n                    revert(\"ERC721: transfer to non ERC721Receiver implementer\");\n                } else {\n                    /// @solidity memory-safe-assembly\n                    assembly {\n                        revert(add(32, reason), mload(reason))\n                    }\n                }\n            }\n        } else {\n            return true;\n        }\n    }\n\n    /**\n     * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is\n     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.\n     *\n     * Calling conditions:\n     *\n     * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.\n     * - When `from` is zero, the tokens will be minted for `to`.\n     * - When `to` is zero, ``from``'s tokens will be burned.\n     * - `from` and `to` are never both zero.\n     * - `batchSize` is non-zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}\n\n    /**\n     * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is\n     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.\n     *\n     * Calling conditions:\n     *\n     * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.\n     * - When `from` is zero, the tokens were minted for `to`.\n     * - When `to` is zero, ``from``'s tokens were burned.\n     * - `from` and `to` are never both zero.\n     * - `batchSize` is non-zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}\n\n    /**\n     * @dev Unsafe write access to the balances, used by extensions that \"mint\" tokens using an {ownerOf} override.\n     *\n     * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant\n     * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such\n     * that `ownerOf(tokenId)` is `a`.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function __unsafe_increaseBalance(address account, uint256 amount) internal {\n        _balances[account] += amount;\n    }\n\n    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[44] private __gap;\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Enumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../ERC721Upgradeable.sol\";\nimport \"./IERC721EnumerableUpgradeable.sol\";\nimport \"../../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev This implements an optional extension of {ERC721} defined in the EIP that adds\n * enumerability of all the token ids in the contract as well as all token ids owned by each\n * account.\n */\nabstract contract ERC721EnumerableUpgradeable is Initializable, ERC721Upgradeable, IERC721EnumerableUpgradeable {\n    function __ERC721Enumerable_init() internal onlyInitializing {\n    }\n\n    function __ERC721Enumerable_init_unchained() internal onlyInitializing {\n    }\n    // Mapping from owner to list of owned token IDs\n    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;\n\n    // Mapping from token ID to index of the owner tokens list\n    mapping(uint256 => uint256) private _ownedTokensIndex;\n\n    // Array with all token ids, used for enumeration\n    uint256[] private _allTokens;\n\n    // Mapping from token id to position in the allTokens array\n    mapping(uint256 => uint256) private _allTokensIndex;\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC721Upgradeable) returns (bool) {\n        return interfaceId == type(IERC721EnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.\n     */\n    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {\n        require(index < ERC721Upgradeable.balanceOf(owner), \"ERC721Enumerable: owner index out of bounds\");\n        return _ownedTokens[owner][index];\n    }\n\n    /**\n     * @dev See {IERC721Enumerable-totalSupply}.\n     */\n    function totalSupply() public view virtual override returns (uint256) {\n        return _allTokens.length;\n    }\n\n    /**\n     * @dev See {IERC721Enumerable-tokenByIndex}.\n     */\n    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {\n        require(index < ERC721EnumerableUpgradeable.totalSupply(), \"ERC721Enumerable: global index out of bounds\");\n        return _allTokens[index];\n    }\n\n    /**\n     * @dev See {ERC721-_beforeTokenTransfer}.\n     */\n    function _beforeTokenTransfer(\n        address from,\n        address to,\n        uint256 firstTokenId,\n        uint256 batchSize\n    ) internal virtual override {\n        super._beforeTokenTransfer(from, to, firstTokenId, batchSize);\n\n        if (batchSize > 1) {\n            // Will only trigger during construction. Batch transferring (minting) is not available afterwards.\n            revert(\"ERC721Enumerable: consecutive transfers not supported\");\n        }\n\n        uint256 tokenId = firstTokenId;\n\n        if (from == address(0)) {\n            _addTokenToAllTokensEnumeration(tokenId);\n        } else if (from != to) {\n            _removeTokenFromOwnerEnumeration(from, tokenId);\n        }\n        if (to == address(0)) {\n            _removeTokenFromAllTokensEnumeration(tokenId);\n        } else if (to != from) {\n            _addTokenToOwnerEnumeration(to, tokenId);\n        }\n    }\n\n    /**\n     * @dev Private function to add a token to this extension's ownership-tracking data structures.\n     * @param to address representing the new owner of the given token ID\n     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address\n     */\n    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {\n        uint256 length = ERC721Upgradeable.balanceOf(to);\n        _ownedTokens[to][length] = tokenId;\n        _ownedTokensIndex[tokenId] = length;\n    }\n\n    /**\n     * @dev Private function to add a token to this extension's token tracking data structures.\n     * @param tokenId uint256 ID of the token to be added to the tokens list\n     */\n    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {\n        _allTokensIndex[tokenId] = _allTokens.length;\n        _allTokens.push(tokenId);\n    }\n\n    /**\n     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that\n     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for\n     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).\n     * This has O(1) time complexity, but alters the order of the _ownedTokens array.\n     * @param from address representing the previous owner of the given token ID\n     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address\n     */\n    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {\n        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and\n        // then delete the last slot (swap and pop).\n\n        uint256 lastTokenIndex = ERC721Upgradeable.balanceOf(from) - 1;\n        uint256 tokenIndex = _ownedTokensIndex[tokenId];\n\n        // When the token to delete is the last token, the swap operation is unnecessary\n        if (tokenIndex != lastTokenIndex) {\n            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];\n\n            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token\n            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index\n        }\n\n        // This also deletes the contents at the last position of the array\n        delete _ownedTokensIndex[tokenId];\n        delete _ownedTokens[from][lastTokenIndex];\n    }\n\n    /**\n     * @dev Private function to remove a token from this extension's token tracking data structures.\n     * This has O(1) time complexity, but alters the order of the _allTokens array.\n     * @param tokenId uint256 ID of the token to be removed from the tokens list\n     */\n    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {\n        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and\n        // then delete the last slot (swap and pop).\n\n        uint256 lastTokenIndex = _allTokens.length - 1;\n        uint256 tokenIndex = _allTokensIndex[tokenId];\n\n        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so\n        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding\n        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)\n        uint256 lastTokenId = _allTokens[lastTokenIndex];\n\n        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token\n        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index\n\n        // This also deletes the contents at the last position of the array\n        delete _allTokensIndex[tokenId];\n        _allTokens.pop();\n    }\n\n    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[46] private __gap;\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC721Upgradeable.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721EnumerableUpgradeable is IERC721Upgradeable {\n    /**\n     * @dev Returns the total amount of tokens stored by the contract.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.\n     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.\n     */\n    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);\n\n    /**\n     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.\n     * Use along with {totalSupply} to enumerate all tokens.\n     */\n    function tokenByIndex(uint256 index) external view returns (uint256);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC721Upgradeable.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional metadata extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721MetadataUpgradeable is IERC721Upgradeable {\n    /**\n     * @dev Returns the token collection name.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the token collection symbol.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.\n     */\n    function tokenURI(uint256 tokenId) external view returns (string memory);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721ReceiverUpgradeable {\n    /**\n     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n     * by `operator` from `from`, this function is called.\n     *\n     * It must return its Solidity selector to confirm the token transfer.\n     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n     *\n     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.\n     */\n    function onERC721Received(\n        address operator,\n        address from,\n        uint256 tokenId,\n        bytes calldata data\n    ) external returns (bytes4);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165Upgradeable.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721Upgradeable is IERC165Upgradeable {\n    /**\n     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n     */\n    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n     */\n    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n    /**\n     * @dev Returns the number of tokens in ``owner``'s account.\n     */\n    function balanceOf(address owner) external view returns (uint256 balance);\n\n    /**\n     * @dev Returns the owner of the `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function ownerOf(uint256 tokenId) external view returns (address owner);\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n     * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Transfers `tokenId` token from `from` to `to`.\n     *\n     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721\n     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must\n     * understand this adds an external call which potentially creates a reentrancy vulnerability.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n     * The approval is cleared when the token is transferred.\n     *\n     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n     *\n     * Requirements:\n     *\n     * - The caller must own the token or be an approved operator.\n     * - `tokenId` must exist.\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address to, uint256 tokenId) external;\n\n    /**\n     * @dev Approve or remove `operator` as an operator for the caller.\n     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n     *\n     * Requirements:\n     *\n     * - The `operator` cannot be the caller.\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function setApprovalForAll(address operator, bool approved) external;\n\n    /**\n     * @dev Returns the account approved for `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function getApproved(uint256 tokenId) external view returns (address operator);\n\n    /**\n     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n     *\n     * See {setApprovalForAll}\n     */\n    function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     *\n     * Furthermore, `isContract` will also return true if the target contract within\n     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n     * which only has an effect at the end of a transaction.\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n    function __Context_init() internal onlyInitializing {\n    }\n\n    function __Context_init_unchained() internal onlyInitializing {\n    }\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[50] private __gap;\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165Upgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {\n    function __ERC165_init() internal onlyInitializing {\n    }\n\n    function __ERC165_init_unchained() internal onlyInitializing {\n    }\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IERC165Upgradeable).interfaceId;\n    }\n\n    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[50] private __gap;\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165Upgradeable {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary MathUpgradeable {\n    enum Rounding {\n        Down, // Toward negative infinity\n        Up, // Toward infinity\n        Zero // Toward zero\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds up instead\n     * of rounding down.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n     * with further edits by Uniswap Labs also under MIT license.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod0 := mul(x, y)\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n                // The surrounding unchecked block does not change this fact.\n                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            require(denominator > prod1, \"Math: mulDiv overflow\");\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n            // See https://cs.stackexchange.com/q/138556/92363.\n\n            // Does not overflow because the denominator cannot be zero at this stage in the function.\n            uint256 twos = denominator & (~denominator + 1);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n            // in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10 ** 64) {\n                value /= 10 ** 64;\n                result += 64;\n            }\n            if (value >= 10 ** 32) {\n                value /= 10 ** 32;\n                result += 32;\n            }\n            if (value >= 10 ** 16) {\n                value /= 10 ** 16;\n                result += 16;\n            }\n            if (value >= 10 ** 8) {\n                value /= 10 ** 8;\n                result += 8;\n            }\n            if (value >= 10 ** 4) {\n                value /= 10 ** 4;\n                result += 4;\n            }\n            if (value >= 10 ** 2) {\n                value /= 10 ** 2;\n                result += 2;\n            }\n            if (value >= 10 ** 1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/utils/math/SignedMathUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard signed math utilities missing in the Solidity language.\n */\nlibrary SignedMathUpgradeable {\n    /**\n     * @dev Returns the largest of two signed numbers.\n     */\n    function max(int256 a, int256 b) internal pure returns (int256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two signed numbers.\n     */\n    function min(int256 a, int256 b) internal pure returns (int256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two signed numbers without overflow.\n     * The result is rounded towards zero.\n     */\n    function average(int256 a, int256 b) internal pure returns (int256) {\n        // Formula from the book \"Hacker's Delight\"\n        int256 x = (a & b) + ((a ^ b) >> 1);\n        return x + (int256(uint256(x) >> 255) & (a ^ b));\n    }\n\n    /**\n     * @dev Returns the absolute unsigned value of a signed value.\n     */\n    function abs(int256 n) internal pure returns (uint256) {\n        unchecked {\n            // must be unchecked in order to support `n = type(int256).min`\n            return uint256(n >= 0 ? n : -n);\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/MathUpgradeable.sol\";\nimport \"./math/SignedMathUpgradeable.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary StringsUpgradeable {\n    bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n    uint8 private constant _ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            uint256 length = MathUpgradeable.log10(value) + 1;\n            string memory buffer = new string(length);\n            uint256 ptr;\n            /// @solidity memory-safe-assembly\n            assembly {\n                ptr := add(buffer, add(32, length))\n            }\n            while (true) {\n                ptr--;\n                /// @solidity memory-safe-assembly\n                assembly {\n                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n                }\n                value /= 10;\n                if (value == 0) break;\n            }\n            return buffer;\n        }\n    }\n\n    /**\n     * @dev Converts a `int256` to its ASCII `string` decimal representation.\n     */\n    function toString(int256 value) internal pure returns (string memory) {\n        return string(abi.encodePacked(value < 0 ? \"-\" : \"\", toString(SignedMathUpgradeable.abs(value))));\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            return toHexString(value, MathUpgradeable.log256(value) + 1);\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = _SYMBOLS[value & 0xf];\n            value >>= 4;\n        }\n        require(value == 0, \"Strings: hex length insufficient\");\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n    }\n\n    /**\n     * @dev Returns true if the two strings are equal.\n     */\n    function equal(string memory a, string memory b) internal pure returns (bool) {\n        return keccak256(bytes(a)) == keccak256(bytes(b));\n    }\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(IERC20 token, address spender, uint256 value) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n        require(\n            (value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n     * to be set to zero before setting it to a non-zero value, such as USDT.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\n            _callOptionalReturn(token, approvalCall);\n        }\n    }\n\n    /**\n     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\n     * Revert on invalid signature.\n     */\n    function safePermit(\n        IERC20Permit token,\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal {\n        uint256 nonceBefore = token.nonces(owner);\n        token.permit(owner, spender, value, deadline, v, r, s);\n        uint256 nonceAfter = token.nonces(owner);\n        require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        require(returndata.length == 0 || abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n        // and not revert is the subcall reverts.\n\n        (bool success, bytes memory returndata) = address(token).call(data);\n        return\n            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     *\n     * Furthermore, `isContract` will also return true if the target contract within\n     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n     * which only has an effect at the end of a transaction.\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/math/Math.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    enum Rounding {\n        Down, // Toward negative infinity\n        Up, // Toward infinity\n        Zero // Toward zero\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds up instead\n     * of rounding down.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n     * with further edits by Uniswap Labs also under MIT license.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod0 := mul(x, y)\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n                // The surrounding unchecked block does not change this fact.\n                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            require(denominator > prod1, \"Math: mulDiv overflow\");\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n            // See https://cs.stackexchange.com/q/138556/92363.\n\n            // Does not overflow because the denominator cannot be zero at this stage in the function.\n            uint256 twos = denominator & (~denominator + 1);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n            // in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10 ** 64) {\n                value /= 10 ** 64;\n                result += 64;\n            }\n            if (value >= 10 ** 32) {\n                value /= 10 ** 32;\n                result += 32;\n            }\n            if (value >= 10 ** 16) {\n                value /= 10 ** 16;\n                result += 16;\n            }\n            if (value >= 10 ** 8) {\n                value /= 10 ** 8;\n                result += 8;\n            }\n            if (value >= 10 ** 4) {\n                value /= 10 ** 4;\n                result += 4;\n            }\n            if (value >= 10 ** 2) {\n                value /= 10 ** 2;\n                result += 2;\n            }\n            if (value >= 10 ** 1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);\n        }\n    }\n}\n"
      },
      "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol": {
        "content": "pragma solidity >=0.5.0;\n\ninterface IUniswapV2Pair {\n    event Approval(address indexed owner, address indexed spender, uint value);\n    event Transfer(address indexed from, address indexed to, uint value);\n\n    function name() external pure returns (string memory);\n    function symbol() external pure returns (string memory);\n    function decimals() external pure returns (uint8);\n    function totalSupply() external view returns (uint);\n    function balanceOf(address owner) external view returns (uint);\n    function allowance(address owner, address spender) external view returns (uint);\n\n    function approve(address spender, uint value) external returns (bool);\n    function transfer(address to, uint value) external returns (bool);\n    function transferFrom(address from, address to, uint value) external returns (bool);\n\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n    function PERMIT_TYPEHASH() external pure returns (bytes32);\n    function nonces(address owner) external view returns (uint);\n\n    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;\n\n    event Mint(address indexed sender, uint amount0, uint amount1);\n    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);\n    event Swap(\n        address indexed sender,\n        uint amount0In,\n        uint amount1In,\n        uint amount0Out,\n        uint amount1Out,\n        address indexed to\n    );\n    event Sync(uint112 reserve0, uint112 reserve1);\n\n    function MINIMUM_LIQUIDITY() external pure returns (uint);\n    function factory() external view returns (address);\n    function token0() external view returns (address);\n    function token1() external view returns (address);\n    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);\n    function price0CumulativeLast() external view returns (uint);\n    function price1CumulativeLast() external view returns (uint);\n    function kLast() external view returns (uint);\n\n    function mint(address to) external returns (uint liquidity);\n    function burn(address to) external returns (uint amount0, uint amount1);\n    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;\n    function skim(address to) external;\n    function sync() external;\n\n    function initialize(address, address) external;\n}\n"
      },
      "contracts/dso/OCHI.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"../interfaces/IOCHI.sol\";\nimport \"../interfaces/IPriceFeedAggregator.sol\";\nimport \"../interfaces/IMintableBurnable.sol\";\nimport \"../interfaces/ILPRewards.sol\";\nimport \"../library/ExternalContractAddresses.sol\";\nimport \"./PoolHelper.sol\";\n\n/// @title Contract for creating and executing options\n/// @notice Each LP token has its own LPRewards contract\n/// @dev This contract is upgradeable\n/// @dev This contract handles creation and execution of options but does not hold LP tokens\ncontract OCHI is IOCHI, ERC721EnumerableUpgradeable, OwnableUpgradeable {\n  using SafeERC20 for IERC20;\n  using SafeERC20 for IMintableBurnable;\n  using SafeERC20 for IUniswapV2Pair;\n\n  uint256 public constant MAX_LOCK_PERIOD_EPOCHS = 52;\n  uint256 public constant EPOCH_DURATION = 1 weeks;\n  uint256 public constant BASE_PRICE = 1e8;\n  uint256 public constant MULTIPLIER = 1e18;\n  uint256 public constant TARGET_RATIO = 80_00;\n  uint256 public constant DENOMINATOR = 100_00;\n  address public constant WETH = ExternalContractAddresses.WETH;\n\n  IERC20 public usc;\n  IMintableBurnable public chi;\n  IUniswapV2Pair public uscEthPair;\n  IUniswapV2Pair public chiEthPair;\n  IPriceFeedAggregator public priceFeedAggregator;\n\n  uint64 public currentEpoch;\n  uint256 public firstEpochTimestamp;\n  uint256 public mintedOCHI;\n  uint256 public totalOCHIlocked;\n\n  mapping(IUniswapV2Pair token => ILPRewards) public lpRewards;\n  mapping(uint256 id => ChiOption) public options;\n\n  function initialize(\n    IERC20 _usc,\n    IMintableBurnable _chi,\n    IPriceFeedAggregator _priceFeedAggregator,\n    IUniswapV2Pair _uscEthPair,\n    IUniswapV2Pair _chiEthPair,\n    ILPRewards _uscEthPairRewards,\n    ILPRewards _chiEthPairRewards,\n    uint256 _firstEpochTimestamp\n  ) external initializer {\n    __ERC721_init(\"Option CHI\", \"oCHI\");\n    __Ownable_init();\n    usc = _usc;\n    chi = _chi;\n    priceFeedAggregator = _priceFeedAggregator;\n    uscEthPair = _uscEthPair;\n    chiEthPair = _chiEthPair;\n\n    lpRewards[_uscEthPair] = _uscEthPairRewards;\n    lpRewards[_chiEthPair] = _chiEthPairRewards;\n\n    currentEpoch = 1;\n    firstEpochTimestamp = _firstEpochTimestamp;\n  }\n\n  /// @inheritdoc IOCHI\n  function mint(\n    uint256 chiAmount,\n    uint256 uscEthPairAmount,\n    uint256 chiEthPairAmount,\n    uint64 lockPeriodInEpochs\n  ) external {\n    if (lockPeriodInEpochs > MAX_LOCK_PERIOD_EPOCHS || lockPeriodInEpochs == 0) {\n      revert InvalidLockPeriod(lockPeriodInEpochs);\n    }\n\n    (uint256 strikePrice, uint256 oChiAmount) = calculateOptionData(\n      chiAmount,\n      uscEthPairAmount,\n      chiEthPairAmount,\n      lockPeriodInEpochs\n    );\n\n    uint64 nextEpoch = currentEpoch + 1;\n    uint256 tokenId = ++mintedOCHI;\n    options[tokenId] = ChiOption({\n      amount: oChiAmount,\n      strikePrice: strikePrice,\n      uscEthPairAmount: uscEthPairAmount,\n      chiEthPairAmount: chiEthPairAmount,\n      lockedUntil: nextEpoch + lockPeriodInEpochs,\n      validUntil: nextEpoch + 2 * lockPeriodInEpochs\n    });\n\n    totalOCHIlocked += oChiAmount;\n\n    ILPRewards uscEthLPRewards = lpRewards[uscEthPair];\n    ILPRewards chiEthLPRewards = lpRewards[chiEthPair];\n\n    if (chiAmount > 0) {\n      chi.burnFrom(msg.sender, chiAmount);\n    }\n    IERC20(address(uscEthPair)).safeTransferFrom(msg.sender, address(uscEthLPRewards), uscEthPairAmount);\n    IERC20(address(chiEthPair)).safeTransferFrom(msg.sender, address(chiEthLPRewards), chiEthPairAmount);\n\n    uscEthLPRewards.lockLP(tokenId, uscEthPairAmount, uint64(lockPeriodInEpochs));\n    chiEthLPRewards.lockLP(tokenId, chiEthPairAmount, uint64(lockPeriodInEpochs));\n\n    _safeMint(msg.sender, tokenId);\n\n    emit Mint(tokenId, chiAmount, uscEthPairAmount, chiEthPairAmount, lockPeriodInEpochs, strikePrice, oChiAmount);\n  }\n\n  /// @inheritdoc IOCHI\n  function burn(uint256 tokenId) external {\n    if (!_isApprovedOrOwner(msg.sender, tokenId)) {\n      revert NotAllowed(tokenId);\n    }\n\n    ChiOption storage option = options[tokenId];\n\n    if (currentEpoch < option.lockedUntil) {\n      revert OptionLocked(tokenId);\n    }\n    if (currentEpoch > option.validUntil) {\n      revert OptionExpired(tokenId);\n    }\n\n    lpRewards[uscEthPair].claimRewards(tokenId, msg.sender);\n    lpRewards[chiEthPair].claimRewards(tokenId, msg.sender);\n\n    uint256 chiAmount = option.amount;\n    uint256 chiBalance = chi.balanceOf(address(this));\n    uint256 amountToTransfer = Math.min(chiAmount, chiBalance);\n    uint256 amountToMint = chiAmount - amountToTransfer;\n\n    if (amountToMint > 0) {\n      chi.mint(msg.sender, amountToMint);\n    }\n    if (amountToTransfer > 0) {\n      chi.transfer(msg.sender, amountToTransfer);\n    }\n\n    _burn(tokenId);\n\n    totalOCHIlocked -= options[tokenId].amount;\n\n    emit Burn(msg.sender, tokenId, chiAmount);\n  }\n\n  /// @inheritdoc IOCHI\n  function updateEpoch() public {\n    if (block.timestamp < firstEpochTimestamp + (currentEpoch - 1) * EPOCH_DURATION) {\n      revert EpochNotFinished();\n    }\n\n    lpRewards[uscEthPair].updateEpoch();\n    lpRewards[chiEthPair].updateEpoch();\n\n    currentEpoch++;\n    emit UpdateEpoch(currentEpoch - 1, block.timestamp);\n  }\n\n  /// @inheritdoc IOCHI\n  function claimRewards(uint256 tokenId) public {\n    if (!_isApprovedOrOwner(msg.sender, tokenId)) {\n      revert NotAllowed(tokenId);\n    }\n\n    lpRewards[uscEthPair].claimRewards(tokenId, msg.sender);\n    lpRewards[chiEthPair].claimRewards(tokenId, msg.sender);\n\n    emit ClaimRewardsOCHI(tokenId);\n  }\n\n  /// @notice Claims rewards for all users tokens\n  function claimAllRewards() public {\n    uint256 balance = balanceOf(msg.sender);\n    for (uint256 i = 0; i < balance; i++) {\n      claimRewards(tokenOfOwnerByIndex(msg.sender, i));\n    }\n  }\n\n  /// @inheritdoc IOCHI\n  function recoverLPTokens() external onlyOwner {\n    lpRewards[uscEthPair].recoverLPTokens(msg.sender);\n    lpRewards[chiEthPair].recoverLPTokens(msg.sender);\n\n    emit RecoverLPTokens();\n  }\n\n  /// @inheritdoc IOCHI\n  function getUnclaimedRewardsValue(uint256 tokenId) external view returns (int256) {\n    return\n      lpRewards[uscEthPair].calculateUnclaimedReward(tokenId) + lpRewards[chiEthPair].calculateUnclaimedReward(tokenId);\n  }\n\n  /// @notice Calulates strike price and ochi amount for supplied asets\n  /// @param chiAmount amount of chi token to supply\n  /// @param uscEthPairAmount amount of USC/ETH LP token to supply\n  /// @param chiEthPairAmount amount of CHI/ETH LP token to supply\n  /// @param lockPeriodInEpochs locking duration\n  /// @return strikePrice strike price given for supplied assets\n  /// @return oChiAmount ochi amount given for supplied assets\n  function calculateOptionData(\n    uint256 chiAmount,\n    uint256 uscEthPairAmount,\n    uint256 chiEthPairAmount,\n    uint256 lockPeriodInEpochs\n  ) public view returns (uint256 strikePrice, uint256 oChiAmount) {\n    uint256 timeMultiplier = Math.mulDiv(lockPeriodInEpochs, MULTIPLIER, 4 * MAX_LOCK_PERIOD_EPOCHS);\n    uint256 chiMultiplier = Math.mulDiv(chiAmount, MULTIPLIER, chi.totalSupply());\n    (uint256 poolMultiplier, uint256 positionsValue) = getAndValidatePositionsData(uscEthPairAmount, chiEthPairAmount);\n\n    uint256 discount = timeMultiplier + Math.min(poolMultiplier + chiMultiplier, MULTIPLIER / 4);\n    uint256 chiPrice = _peek(address(chi));\n    strikePrice = Math.mulDiv(chiPrice, MULTIPLIER - discount, MULTIPLIER);\n\n    uint256 chiValue = Math.mulDiv(chiAmount, chiPrice, MULTIPLIER);\n    oChiAmount = Math.mulDiv(positionsValue + chiValue, MULTIPLIER, strikePrice);\n  }\n\n  /// @notice Calculates multiplier for supplied LP assets\n  /// @param uscEthPairAmount amount of USC/ETH LP token to supply\n  /// @param chiEthPairAmount amount of CHI/ETH LP token to supply\n  /// @return multiplier multiplier used for caluclating discount\n  /// @param value parameter used for calculating ochi amount\n  function getAndValidatePositionsData(\n    uint256 uscEthPairAmount,\n    uint256 chiEthPairAmount\n  ) public view returns (uint256 multiplier, uint256 value) {\n    uint256 uscEthPairTotalSupply = uscEthPair.totalSupply();\n    uint256 chiEthPairTotalSupply = chiEthPair.totalSupply();\n\n    if (\n      uscEthPair.balanceOf(address(this)) + uscEthPairAmount >\n      Math.mulDiv(uscEthPairTotalSupply, TARGET_RATIO, DENOMINATOR) ||\n      chiEthPair.balanceOf(address(this)) + chiEthPairAmount >\n      Math.mulDiv(chiEthPairTotalSupply, TARGET_RATIO, DENOMINATOR)\n    ) {\n      revert PolTargetRatioExceeded();\n    }\n\n    multiplier =\n      Math.mulDiv(uscEthPairAmount, MULTIPLIER, uscEthPairTotalSupply) +\n      Math.mulDiv(chiEthPairAmount, MULTIPLIER, chiEthPairTotalSupply);\n\n    value = (PoolHelper.getUSDValueForLP(uscEthPairAmount, uscEthPair, priceFeedAggregator) +\n      PoolHelper.getUSDValueForLP(chiEthPairAmount, chiEthPair, priceFeedAggregator));\n  }\n\n  /// @notice Calculates the total LP reward for the last epoch\n  /// @return totalReward total reward in usd value\n  function getLastEpochTotalReward() external view returns (int256 totalReward) {\n    if (currentEpoch < 2) return 0;\n    return lpRewards[uscEthPair].getLastEpochProfit() + lpRewards[chiEthPair].getLastEpochProfit();\n  }\n\n  function _peek(address asset) internal view returns (uint256 price) {\n    price = priceFeedAggregator.peek(asset);\n  }\n}\n"
      },
      "contracts/dso/PoolHelper.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"../interfaces/IPriceFeedAggregator.sol\";\n\nlibrary PoolHelper {\n  function getTotalPoolUSDValue(\n    IUniswapV2Pair pair,\n    IPriceFeedAggregator priceFeedAggregator\n  ) internal view returns (uint256) {\n    (uint112 token0amount, uint112 token1amount, ) = pair.getReserves();\n    uint256 price0 = priceFeedAggregator.peek(pair.token0());\n    uint256 price1 = priceFeedAggregator.peek(pair.token1());\n\n    // assuming both tokens have 18 decimals!\n    uint256 totalValue = Math.mulDiv(token0amount, price0, 1e18) + Math.mulDiv(token1amount, price1, 1e18);\n    return totalValue;\n  }\n\n  function getUSDValueForLP(\n    uint256 lpAmount,\n    IUniswapV2Pair pair,\n    IPriceFeedAggregator priceFeedAggregator\n  ) internal view returns (uint256) {\n    return Math.mulDiv(getTotalPoolUSDValue(pair, priceFeedAggregator), lpAmount, pair.totalSupply());\n  }\n}\n"
      },
      "contracts/interfaces/IArbitrage.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IArbitrage {\n  event SetPriceTolerance(uint16 priceTolerance);\n  event SetMaxMintPriceDiff(uint256 maxMintPriceDiff);\n  event Mint(address indexed account, address token, uint256 amount, uint256 uscAmount);\n  event ExecuteArbitrage(\n    address indexed account,\n    uint256 indexed arbNum,\n    uint256 deltaUsd,\n    uint256 reserveDiff,\n    uint256 ethPrice,\n    uint256 rewardValue\n  );\n\n  error DeltaBiggerThanAmountReceivedETH(uint256 deltaETH, uint256 receivedETH);\n  error ToleranceTooBig(uint16 _tolerance);\n  error PriceSlippageTooBig();\n\n  /// @notice Sets spot price tolerance from TWAP price\n  /// @dev 100% = 10000\n  /// @param _priceTolerance Price tolerance in percents\n  /// @custom:usage This function should be called from owner in purpose of setting price tolerance\n  function setPriceTolerance(uint16 _priceTolerance) external;\n\n  /// @notice Sets max mint price diff\n  /// @param _maxMintPriceDiff Max mint price diff\n  /// @custom:usage This function should be called from owner in purpose of setting max mint price diff\n  function setMaxMintPriceDiff(uint256 _maxMintPriceDiff) external;\n\n  /// @notice Mint USC tokens for ETH\n  /// @dev If USC price is different from target price for less then max mint price diff, then minting is allowed without performing arbitrage\n  /// @return uscAmount Amount of USC tokens minted\n  function mint() external payable returns (uint256 uscAmount);\n\n  /// @notice Mint USC tokens for WETH\n  /// @dev If USC price is different from target price for less then max mint price diff, then minting is allowed without performing arbitrage\n  /// @param wethAmount Amount of WETH to mint with\n  /// @return uscAmount Amount of USC tokens minted\n  function mintWithWETH(uint256 wethAmount) external returns (uint256 uscAmount);\n\n  /// @notice Mint USC tokens for stETH\n  /// @dev If USC price is different from target price for less then max mint price diff, then minting is allowed without performing arbitrage\n  /// @param stETHAmount Amount of stETH to mint with\n  /// @return uscAmount Amount of USC tokens minted\n  function mintWithStETH(uint256 stETHAmount) external returns (uint256 uscAmount);\n\n  /// @notice Executes arbitrage, profit sent to caller\n  /// @notice Returns reward value in USD\n  /// @return rewardValue Reward value in USD\n  /// @custom:usage This function should be called from external keeper in purpose of pegging USC price and getting reward\n  /// @custom:usage This function has no restrictions, anyone can be arbitrager\n  function executeArbitrage() external returns (uint256 rewardValue);\n\n  /// @notice Gets information for perfoming arbitrage such as price diff, reserve diff, discount\n  /// @return isPriceAboveTarget True if USC price is above target price\n  /// @return isExcessOfReserves True if there is excess of reserves\n  /// @return reserveDiff Reserve diff, excess or deficit of reserves\n  /// @return discount Discount in percents, only if price is equal to target price\n  function getArbitrageData()\n    external\n    view\n    returns (bool isPriceAboveTarget, bool isExcessOfReserves, uint256 reserveDiff, uint256 discount);\n}\n"
      },
      "contracts/interfaces/IBurnableERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBurnableERC20 {\n  function burnFrom(address account, uint256 amount) external;\n}\n"
      },
      "contracts/interfaces/IChiLocking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IChiLocking {\n  struct LockedPosition {\n    uint256 amount;\n    uint256 startEpoch;\n    uint256 duration; // in epochs\n    uint256 shares;\n    uint256 withdrawnChiAmount;\n  }\n\n  struct LockingData {\n    uint256 lastUpdatedEpoch;\n    uint256 unclaimedStETH;\n    LockedPosition[] positions;\n  }\n\n  struct AllLockedPositionsOutput {\n    LockedPosition position;\n    uint256 votingPower;\n    uint256 stETHreward;\n    uint256 totalAccumulatedChi;\n    uint256 totalChiRewards;\n  }\n\n  struct EpochData {\n    uint256 lockedSharesInEpoch;\n    uint256 totalLockedChiInEpoch;\n    uint256 sharesToUnlock;\n    uint256 cumulativeStETHPerLockedShare;\n    uint256 cumulativeStETHPerUnlocked;\n    uint256 numberOfEndingPositions;\n  }\n\n  event SetUscStaking(address indexed uscStaking);\n  event SetRewardController(address indexed rewardController);\n  event SetChiLocker(address indexed chiLocker, bool indexed status);\n  event LockChi(address indexed account, uint256 amount, uint256 shares, uint256 startEpoch, uint256 endEpoch);\n  event UpdateEpoch(\n    uint256 indexed epoch,\n    uint256 totalLockedChi,\n    uint256 chiEmissions,\n    uint256 stETHrewards,\n    uint256 stEthPerLockedShare\n  );\n  event ClaimStETH(address indexed account, uint256 amount);\n  event WithdrawChiFromAccount(address indexed account, address indexed toAddress, uint256 amount);\n\n  error ZeroAmount();\n  error NotRewardController();\n  error NotChiLocker();\n  error UnavailableWithdrawAmount(uint256 amount);\n\n  /// @notice Sets address of uscStaking contract\n  /// @param _uscStaking Address of uscStaking contract\n  function setUscStaking(address _uscStaking) external;\n\n  /// @notice Sets address of rewardController contract\n  /// @param _rewardController Address of rewardController contract\n  function setRewardController(address _rewardController) external;\n\n  /// @notice Sets address of contract who can call lock function\n  /// @param contractAddress Address of contract who calles lock function, chiStaking currently\n  /// @param toSet true if contract can call lock function, false otherwise\n  function setChiLocker(address contractAddress, bool toSet) external;\n\n  /// @notice Gets locked position for given account and position index\n  /// @param account Account to get locked position for\n  /// @param pos Index of locked position\n  /// @return position Locked position\n  function getLockedPosition(address account, uint256 pos) external view returns (LockedPosition memory position);\n\n  /// @notice Gets all locked position for given account\n  /// @param account Account to get locked positions\n  /// @return out Array of locked positions\n  function getAllLockedPositions(address account) external view returns (AllLockedPositionsOutput[] memory out);\n\n  /// @notice Gets total staked chi amount, locked amount is also considered staked\n  /// @return stakedChi Total staked chi amount\n  function getStakedChi() external view returns (uint256 stakedChi);\n\n  /// @notice Gets total locked chi amount\n  /// @return lockedChi Total locked chi amount\n  function getLockedChi() external view returns (uint256 lockedChi);\n\n  /// @notice Gets total voting power\n  /// @return totalVotingPower Total voting power\n  function getTotalVotingPower() external view returns (uint256 totalVotingPower);\n\n  /// @notice Gets total chi amount that is available to withdraw for given account\n  /// @param account Account to get available chi amount for\n  /// @return availableTotal Total amount of chi that is available to withdraw\n  function availableChiWithdraw(address account) external view returns (uint256 availableTotal);\n\n  /// @notice Locks given amount of chi for given account for given duration\n  /// @param account Account to lock chi for\n  /// @param amount Amount of chi to lock\n  /// @param duration Duration of locking in epochs\n  /// @custom:usage This function should be called from chiStaking and uscStaking contracts in purpose of locking chi\n  function lockChi(address account, uint256 amount, uint256 duration) external;\n\n  /// @notice Updates epoch data\n  /// @param chiEmissions Amount of chi incentives for chi lockers that is emitted in current epoch\n  /// @param stETHrewards Amount of stETH rewards for chi lockers that is emitted in current epoch\n  /// @custom:usage This function should be called from rewardController contract in purpose of updating epoch data\n  function updateEpoch(uint256 chiEmissions, uint256 stETHrewards) external;\n\n  /// @notice Claims stETH rewards for given account\n  /// @notice This contract does not send stETH rewards nor holds them, reserveHolder does that\n  /// @notice This contract only calculates and updates unclaimed stETH amount for given account\n  /// @param account Account to claim stETH rewards for\n  /// @return amount Amount of stETH rewards that user can claim\n  /// @custom:usage This function should be called from rewardController contract in purpose of claiming stETH rewards\n  function claimStETH(address account) external returns (uint256 amount);\n\n  /// @notice Withdraws given amount of unlocked chi tokens for given account, sends to account by default\n  /// @notice This contract hold CHI tokens and inside this function sends them back to user\n  /// @param account Account to withdraw CHI for\n  /// @param amount Amount of CHI tokens to withdraw\n  /// @custom:usage This function should be called from chiStaking contract in purpose of withdrawing CHI tokens\n  function withdrawChiFromAccount(address account, uint256 amount) external;\n\n  /// @notice Withdraws given amount of unlocked chi tokens for given account, sends to account by default\n  /// @notice This contract hold CHI tokens and inside this function sends them back to user\n  /// @param account Account to withdraw CHI for\n  /// @param toAddress Address to which to send tokens\n  /// @param amount Amount of CHI tokens to withdraw\n  /// @custom:usage This function should be called from chiStaking contract in purpose of withdrawing CHI tokens\n  function withdrawChiFromAccountToAddress(address account, address toAddress, uint256 amount) external;\n\n  /// @notice Calculates and returns unclaimed stETH amount for given account\n  /// @param account Account to calculate unclaimed stETH amount for\n  /// @return totalAmount Total amount of unclaimed stETH for given account\n  function unclaimedStETHAmount(address account) external view returns (uint256 totalAmount);\n\n  /// @notice Calculates and returns voting power for given account\n  /// @param account Account to calculate voting power for\n  /// @return votingPower Voting power for given account\n  function getVotingPower(address account) external view returns (uint256 votingPower);\n}\n"
      },
      "contracts/interfaces/IChiStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {IChiLocking} from \"./IChiLocking.sol\";\nimport {IStaking} from \"./IStaking.sol\";\n\ninterface IChiStaking is IStaking {\n  event Lock(address indexed account, uint256 amount, uint256 duration, bool useStakedTokens);\n  event ClaimStETH(address indexed account, uint256 amount);\n  event SetChiLocking(address indexed chiLocking);\n  event SetRewardController(address indexed rewardController);\n\n  error InvalidDuration(uint256 duration);\n\n  /// @notice Sets address of chiLocking contract\n  /// @param _chiLocking Address of chiLocking contract\n  function setChiLocking(IChiLocking _chiLocking) external;\n\n  /// @notice Updates epoch data\n  /// @param stETHrewards Amount of stETH rewards for chi stakers that is emitted in current epoch\n  /// @custom:usage This function should be called from rewardController contract in purpose of updating epoch data\n  function updateEpoch(uint256 stETHrewards) external;\n\n  /// @notice Locks given amount of chi tokens for given duration for caller\n  /// @dev If caller want to use staked tokens for locking, function will unstake them first\n  /// @param amount Amount of chi tokens to lock\n  /// @param duration Locking duration in epochs\n  /// @param useStakedTokens If true, then staked tokens will be used for locking\n  function lock(uint256 amount, uint256 duration, bool useStakedTokens) external;\n}\n"
      },
      "contracts/interfaces/IChiVesting.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IChiVesting {\n  struct VestingData {\n    uint256 startAmount;\n    uint256 shares;\n    uint256 unlockedChi;\n    uint256 lastWithdrawnEpoch;\n    uint256 unclaimedStETH;\n    uint256 lastClaimedEpoch;\n  }\n\n  struct EpochData {\n    uint256 cumulativeStETHRewardPerShare;\n    uint256 cumulativeUnlockedPerShare;\n  }\n\n  event AddVesting(address indexed account, uint256 amount, uint256 shares);\n  event UpdateEpoch(uint256 indexed epoch, uint256 stETHrewards, uint256 totalLockedChi);\n  event WithdrawChi(address indexed account, uint256 amount);\n  event ClaimStETH(address indexed account, uint256 amount);\n  event SetRewardController(address indexed rewardController);\n  event SetChiVester(address indexed chiVester, bool indexed toSet);\n\n  error NotRewardController();\n  error NotChiVester();\n  error CliffPassed();\n  error UnavailableWithdrawAmount(uint256 amount);\n\n  /// @notice Gets cliff duration\n  /// @return duration Cliff duration\n  function cliffDuration() external view returns (uint256 duration);\n\n  /// @notice Sets address of rewardController contract\n  /// @param rewardController Address of rewardController contract\n  function setRewardController(address rewardController) external;\n\n  /// @notice Updates status of contract that can add vesting, TimeWeightedBonding contract in this case\n  /// @param contractAddress Address of contract\n  /// @param toSet Status to set\n  function setChiVester(address contractAddress, bool toSet) external;\n\n  /// @notice Gets total locked chi amount\n  /// @return lockedChi Total locked chi amount\n  function getLockedChi() external view returns (uint256 lockedChi);\n\n  /// @notice Vests given amount of chi tokens for given account\n  /// @param account Account to vest tokens for\n  /// @param chiAmount Amount of chi tokens to vest\n  /// @custom:usage This function should be called from TimeWeightedBonding contract in purpose of vesting chi tokens\n  function addVesting(address account, uint256 chiAmount) external;\n\n  /// @notice Updates epoch data\n  /// @param chiEmissions Amount of chi incentives for vesters in current epoch\n  /// @param stETHrewards Amount of stETH rewards for vesters that is emitted in current epoch\n  /// @custom:usage This function should be called from rewardController contract in purpose of updating epoch data\n  function updateEpoch(uint256 chiEmissions, uint256 stETHrewards) external;\n\n  /// @notice Withdraws vested chi tokens for caller\n  /// @dev Contract hold vested chi tokens and inside this function it transfers them to caller\n  /// @param amount Amount of chi tokens to withdraw\n  function withdrawChi(uint256 amount) external;\n\n  /// @notice Claims stETH rewards for given account\n  /// @notice This contract does not send stETH rewards nor holds them, reserveHolder does that\n  /// @notice This contract only calculates and updates unclaimed stETH amount for given account\n  /// @param account Account to claim stETH rewards for\n  /// @return amount Amount of stETH rewards that user can claim\n  /// @custom:usage This function should be called from rewardController contract in purpose of claiming stETH rewards\n  function claimStETH(address account) external returns (uint256 amount);\n\n  /// @notice Calculates and returns unclaimed stETH rewards for given account\n  /// @param account Account to calculate unclaimed stETH rewards for\n  /// @return amount Amount of unclaimed stETH rewards\n  function unclaimedStETHAmount(address account) external view returns (uint256 amount);\n\n  /// @notice Calculates and returns voting power earned from vesting for given account\n  /// @param account Account to calculate voting power for\n  /// @return votingPower Voting power earned from vesting\n  function getVotingPower(address account) external view returns (uint256 votingPower);\n\n  /// @notice Gets total voting power\n  /// @return totalVotingPower Total voting power\n  function getTotalVotingPower() external view returns (uint256 totalVotingPower);\n\n  /// @notice Calculates and returns chi amount that is available for withdrawing for given account\n  /// @param account Account to calculate available chi amount for\n  /// @return availableChi Total amount of chi that is available for withdrawing\n  function availableChiWithdraw(address account) external view returns (uint256 availableChi);\n}\n"
      },
      "contracts/interfaces/ICurvePool.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ICurvePool {\n  /// @notice Swaps tokens\n  /// @param i Index of token to swap from\n  /// @param j Index of token to swap to\n  /// @param dx Amount of tokens to swap\n  /// @param min_dy Minimum amount of tokens to receive\n  /// @return dy Amount of tokens received\n  function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external payable returns (uint256 dy);\n}\n"
      },
      "contracts/interfaces/ILPRewards.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ILPRewards {\n  struct LockingTokenData {\n    uint256 amountLocked;\n    uint64 lastClaimedEpoch;\n    uint64 endingEpoch;\n  }\n\n  struct EpochData {\n    int256 totalDeltaAmountLocked;\n    int256 cumulativeProfit;\n  }\n\n  event LockLP(uint256 indexed lockingTokenId, uint256 amount, uint64 currentEpoch, uint64 endingEpoch);\n  event ClaimRewards(uint256 indexed lockingTokenId, address indexed account, uint256 amount, int256 rewardUSD);\n  event UpdateEpoch(uint64 indexed epoch, uint256 lpValue, int256 totalAmountLocked, int256 profitPerToken);\n  event RecoverLPTokens(address indexed account, uint256 amount);\n  event SetOCHI(address indexed ochi);\n\n  error LockingTokenIdAlreadyUsed(uint256 lockingTokenId);\n  error ClaimngRewardLessThanZero();\n  error NotOCHI();\n\n  /// @notice Gets current epoch\n  /// @return currentEpoch Current epoch\n  function currentEpoch() external view returns (uint64 currentEpoch);\n\n  /// @notice Sets address of OCHI contract\n  /// @param _ochi Address of OCHI contract\n  /// @custom:usage This function should be called only once during deployment\n  /// @custom:usage Caller must be owner\n  function setOCHI(address _ochi) external;\n\n  /// @notice Locks LP tokens for given period, user gets rewards until the end of the period\n  /// @param lockingTokenId Unique OCHI id\n  /// @param amount Amount of LP tokens to lock\n  /// @param epochDuration Locking duration in epochs\n  /// @custom:usage This function should be called from OCHI contract in purpose of locking LP tokens\n  function lockLP(uint256 lockingTokenId, uint256 amount, uint64 epochDuration) external;\n\n  /// @notice Claims rewards for given token id\n  /// @param lockingTokenId Unique OCHI id\n  /// @param account Account to send rewards to\n  /// @custom:usage This function should be called from OCHI contract in purpose of claiming rewards\n  function claimRewards(uint256 lockingTokenId, address account) external;\n\n  /// @notice End current epoch and start new one\n  /// @custom:usage This function should be called from OCHI contract when epoch is updated\n  function updateEpoch() external;\n\n  /// @notice Takes LP tokens from contract and sends them to receiver\n  /// @param receiver Account to send LP tokens to\n  /// @custom:usage This function should be called only in case of moving liquidity to another pool\n  function recoverLPTokens(address receiver) external;\n\n  /// @notice Calculates unclaimed rewards for given token id\n  /// @param lockingTokenId Unique OCHI id\n  /// @return rewardUSD Amount of unclaimed rewards in USD\n  function calculateUnclaimedReward(uint256 lockingTokenId) external view returns (int256 rewardUSD);\n  \n  /// @notice returns profit in USD for the last epoch\n  /// @return profit profit in USD\n  function getLastEpochProfit() external view returns (int256 profit);\n}\n"
      },
      "contracts/interfaces/ILPStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport {IStakingWithEpochs} from \"./IStakingWithEpochs.sol\";\nimport {IStaking} from \"./IStaking.sol\";\n\ninterface ILPStaking is IStakingWithEpochs, IStaking {\n  event UpdateEpoch(uint256 indexed epoch, uint256 chiEmissions);\n  event LockChi(address indexed account, uint256 amount, uint256 duration);\n  event ClaimStETH(address indexed account, uint256 amount);\n\n  error InvalidDuration(uint256 duration);\n\n  function updateEpoch(uint256, uint256) external;\n\n  function lockChi(uint256) external;\n}\n"
      },
      "contracts/interfaces/IMintableBurnable.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IMintableBurnable is IERC20 {\n  function mint(address to, uint256 amount) external;\n\n  function burnFrom(address from, uint256 amount) external;\n}\n"
      },
      "contracts/interfaces/IMintableERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IMintableERC20 is IERC20 {\n  function mint(address account, uint256 amount) external;\n}\n"
      },
      "contracts/interfaces/IOCHI.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IOCHI {\n  struct ChiOption {\n    uint256 amount;\n    uint256 strikePrice;\n    uint256 uscEthPairAmount;\n    uint256 chiEthPairAmount;\n    uint64 lockedUntil;\n    uint64 validUntil;\n  }\n\n  event Mint(\n    uint256 indexed tokenId,\n    uint256 chiAmount,\n    uint256 uscEthPairAmount,\n    uint256 chiEthPairAmount,\n    uint64 lockPeriodInEpochs,\n    uint256 strikePrice,\n    uint256 oChiAmount\n  );\n  event Burn(address indexed account, uint256 indexed tokenId, uint256 chiAmount);\n  event UpdateEpoch(uint64 indexed epoch, uint256 timestamp);\n  event ClaimRewardsOCHI(uint256 indexed tokenId);\n  event RecoverLPTokens();\n\n  error PolTargetRatioExceeded();\n  error InvalidLockPeriod(uint256 lockPeriod);\n  error NotAllowed(uint256 tokenId);\n  error OptionLocked(uint256 tokenId);\n  error OptionExpired(uint256 tokenId);\n  error EpochNotFinished();\n\n  /// @notice Mint OCHI tokens for given period, user gets rewards until the end of the period\n  /// @param chiAmount Amount of CHI tokens user wants to burn in order to boost his discount in option\n  /// @param uscEthPairAmount Amount of USC/ETH LP tokens user wants to lock and sell to protocol\n  /// @param chiEthPairAmount Amount of CHI/ETH LP tokens user wants to lock and sell to protocol\n  /// @param lockPeriodInEpochs Locking duration in epochs\n  function mint(\n    uint256 chiAmount,\n    uint256 uscEthPairAmount,\n    uint256 chiEthPairAmount,\n    uint64 lockPeriodInEpochs\n  ) external;\n\n  /// @notice Burn OCHI token in order to execute option and get his discounted CHI\n  /// @notice When lock period expires user has lock period window to execute option, after that option can not be executed\n  /// @param tokenId Unique OCHI id\n  function burn(uint256 tokenId) external;\n\n  /// @notice Ends current epoch and start new one\n  function updateEpoch() external;\n\n  /// @notice Claims rewards for given token id\n  /// @param tokenId Unique OCHI id\n  function claimRewards(uint256 tokenId) external;\n\n  /// @notice Returns unclaimed rewards value for given token id\n  /// @param tokenId Unique OCHI id\n  /// @return rewardsUSD rewards value in USD\n  function getUnclaimedRewardsValue(uint256 tokenId) external view returns (int256 rewardsUSD);\n\n  /// @notice Recovers LP tokens from rewards contract\n  /// @custom:usage This function should be called only in case of moving liquidity to another pool\n  function recoverLPTokens() external;\n\n  function calculateOptionData(\n    uint256 chiAmount,\n    uint256 uscEthPairAmount,\n    uint256 chiEthPairAmount,\n    uint256 lockPeriodInEpochs\n  ) external view returns (uint256 strikePrice, uint256 oChiAmount);\n\n  function getAndValidatePositionsData(\n    uint256 uscEthPairAmount,\n    uint256 chiEthPairAmount\n  ) external view returns (uint256 multiplier, uint256 value);\n\n  function getLastEpochTotalReward() external view returns (int256 totalReward);\n\n  function claimAllRewards() external;\n}\n"
      },
      "contracts/interfaces/IPriceFeedAggregator.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPriceFeedAggregator {\n  event SetPriceFeed(address indexed base, address indexed feed);\n\n  error ZeroAddress();\n\n  /// @notice Sets price feed adapter for given token\n  /// @param base Token address\n  /// @param feed Price feed adapter address\n  function setPriceFeed(address base, address feed) external;\n\n  /// @notice Gets price for given token\n  /// @param base Token address\n  /// @return price Price for given token\n  function peek(address base) external view returns (uint256 price);\n}\n"
      },
      "contracts/interfaces/IReserveHolder.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IReserveHolder {\n  event SetArbitrager(address indexed arbitrager, bool enabled);\n  event SetClaimer(address indexed claimer);\n  event SetEthThreshold(uint256 threshold);\n  event SetSwapEthTolerance(uint256 tolerance);\n  event SetCurveStEthSafeGuardPercentage(uint256 percentage);\n  event Deposit(address indexed account, uint256 amount);\n  event Rebalance(uint256 ethAmount, uint256 stEthAmount);\n  event Redeem(address indexed account, uint256 amount);\n  event RedeemSwap(uint256 ethAmount, uint256 stEthAmount);\n  event ClaimRewards(address indexed account, uint256 amount);\n  event Receive(address indexed account, uint256 amount);\n\n  error NotArbitrager(address _account);\n  error NotClaimer(address _account);\n  error ThresholdTooHigh(uint256 _threshold);\n  error SafeGuardTooHigh(uint256 _safeGuard);\n  error EtherSendFailed(address _account, uint256 _amount);\n\n  /// @notice Updates arbitrager status\n  /// @param arbitrager Arbitrager address\n  /// @param status Arbitrager status\n  function setArbitrager(address arbitrager, bool status) external;\n\n  /// @notice Sets claimer address\n  /// @param claimer Claimer address\n  /// @custom:usage Claimer should be rewardController contract\n  function setClaimer(address claimer) external;\n\n  /// @notice Sets eth threshold\n  /// @param ethThreshold Eth threshold\n  /// @custom:usage Eth threshold should be set in percentage\n  /// @custom:usage Part of reserves is in WETH so arbitrage contract can use them without swapping stETH for ETH\n  function setEthThreshold(uint256 ethThreshold) external;\n\n  /// @notice Sets swap eth tolerance\n  /// @param swapEthTolerance Swap eth tolerance\n  /// @custom:usage Swap eth tolerance should be set in wei\n  /// @custom:usage Absolute tolerance for swapping stETH for ETH\n  function setSwapEthTolerance(uint256 swapEthTolerance) external;\n\n  /// @notice Sets curve stETH safe guard percentage\n  /// @param curveStEthSafeGuardPercentage Curve stETH safe guard percentage\n  function setCurveStEthSafeGuardPercentage(uint256 curveStEthSafeGuardPercentage) external;\n\n  /// @notice Gets reserve value in USD\n  /// @return reserveValue Reserve value in USD\n  function getReserveValue() external view returns (uint256 reserveValue);\n\n  /// @notice Gets current rewards generated by stETH\n  /// @return currentRewards Current rewards generated by stETH\n  function getCurrentRewards() external view returns (uint256 currentRewards);\n\n  /// @notice Gets cumulative rewards generated by stETH\n  /// @return cumulativeRewards Cumulative rewards generated by stETH\n  function getCumulativeRewards() external view returns (uint256 cumulativeRewards);\n\n  /// @notice Deposits stETH to reseves\n  /// @param amount Amount of stETH to deposit\n  function deposit(uint256 amount) external;\n\n  /// @notice Rebalance reserve in order to achieve balace/ethThreshold ratio\n  /// @dev If there is more WETH than ethThreshold then unwrap WETH and get stETH from Lido\n  /// @dev If there is less WETH than ethThreshold then swap stETH for WETH on UniV2\n  /// @custom:usage This function should be called by external keeper\n  function rebalance() external;\n\n  /// @notice Redeems stETH from reserves\n  /// @param amount Amount of stETH to redeem\n  /// @return wethAmount Amount of WETH received\n  /// @custom:usage This function should be called by arbitrage contract\n  function redeem(uint256 amount) external returns (uint256 wethAmount);\n\n  /// @notice Claims stETH rewards in given amount for given account\n  /// @notice Contract does not perform any check and is relying on rewardController contract to perform them\n  /// @param account Account to claim stETH rewards for\n  /// @param amount Amount of stETH to claim\n  /// @custom:usage This function should be called by rewardController contract\n  function claimRewards(address account, uint256 amount) external;\n\n  /// @notice Wrapps ETH to WETH\n  /// @dev Users can buy USC with ETH which is transfered to this contract. This function should be called to wrapp than ETH to WETH\n  /// @custom:usage This function should be called by external keeper\n  function wrapETH() external;\n}\n"
      },
      "contracts/interfaces/IRewardController.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {IArbitrage} from \"./IArbitrage.sol\";\n\ninterface IRewardController {\n  struct EpochData {\n    uint256 totalUscReward;\n    uint256 reserveHolderTotalRewards;\n  }\n\n  struct StETHRewards {\n    uint256 uscStakingStEthReward;\n    uint256 chiStakingStEthReward;\n    uint256 chiLockingStEthReward;\n    uint256 chiVestingStEthReward;\n    uint256 uscEthLPStakingStEthReward;\n    uint256 chiEthLPStakingStEthReward;\n  }\n\n  struct ChiIncentives {\n    uint256 uscStakingChiIncentives;\n    uint256 chiLockingChiIncentives;\n    uint256 chiVestingChiIncentives;\n  }\n\n  event RewardUSC(address indexed account, uint256 amount);\n  event UpdateEpoch(uint256 indexed epoch, uint256 totalStEthReward, uint256 totalChiIncentives);\n  event ClaimStEth(address indexed account, uint256 amount);\n  event SetChiIncentivesPerEpoch(uint256 indexed chiIncentivesPerEpoch);\n  event SetArbitrager(address indexed arbitrager);\n\n  error ZeroAmount();\n  error NotArbitrager();\n  error EpochNotFinished();\n\n  /// @notice Set amount of chi incentives per epoch for chi lockers\n  /// @param _chiIncentivesForChiLocking Amount of chi incentives per epoch\n  function setChiIncentivesForChiLocking(uint256 _chiIncentivesForChiLocking) external;\n\n  /// @notice Set amount of chi incentives per epoch for USC staking\n  /// @param _chiIncentivesForUscStaking Amount of chi incentives per epoch\n  function setChiIncentivesForUscStaking(uint256 _chiIncentivesForUscStaking) external;\n\n  /// @notice Set amount of chi incentives per epoch for USC-ETH LP staking contracts\n  /// @param _chiIncentivesForUscEthLPStaking Amount of chi incentives per epoch\n  function setChiIncentivesForUscEthLPStaking(uint256 _chiIncentivesForUscEthLPStaking) external;\n\n  /// @notice Set amount of chi incentives per epoch for CHI-ETH LP staking contracts\n  /// @param _chiIncentivesForChiEthLPStaking Amount of chi incentives per epoch\n  function setChiIncentivesForChiEthLPStaking(uint256 _chiIncentivesForChiEthLPStaking) external;\n\n  /// @notice Sets arbitrager contract\n  /// @param _arbitrager Arbitrager contract\n  function setArbitrager(IArbitrage _arbitrager) external;\n\n  /// @notice Freezes given amount of USC token\n  /// @dev Frozen tokens are not transfered they are burned and later minted again when conditions are met\n  /// @param amount Amount of USC tokens to freeze\n  /// @custom:usage This function should be called from Arbitrager contract in purpose of freezing USC tokens\n  function rewardUSC(uint256 amount) external;\n\n  /// @notice Updates epoch data\n  /// @dev This functio will update epochs in all subcontracts and will distribute chi incentives and stETH rewards\n  /// @custom:usage This function should be called once a week in order to end current epoch and start new one\n  /// @custom:usage Thsi function ends current epoch and distributes chi incentives and stETH rewards to all contracts in this epoch\n  function updateEpoch() external;\n\n  /// @notice Claims stETH rewards for caller\n  /// @dev This function will claim stETH rewards from all subcontracts and will send them to caller\n  /// @dev Thsi contract does not hold stETH, instead it sends it through reserveHolder contract\n  function claimStEth() external;\n\n  /// @notice Calculates and returns unclaimed stETH amount for given account in all subcontracts\n  /// @param account Account to calculate unclaimed stETH amount for\n  /// @return totalAmount Total amount of unclaimed stETH for given account\n  function unclaimedStETHAmount(address account) external view returns (uint256 totalAmount);\n}\n"
      },
      "contracts/interfaces/IStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {IStakingWithEpochs} from \"./IStakingWithEpochs.sol\";\n\ninterface IStaking is IStakingWithEpochs {\n  /// @notice Sets address of rewardController contract\n  /// @param rewardController Address of rewardController contract\n  function setRewardController(address rewardController) external;\n\n  /// @notice Gets total staked chi amount\n  /// @return stakedChi Total staked chi amount\n  function getStakedChi() external view returns (uint256 stakedChi);\n\n  /// @notice Claims stETH rewards for given account\n  /// @notice This contract does not send stETH rewards nor holds them, reserveHolder does that\n  /// @notice This contract only calculates and updates unclaimed stETH amount for given account\n  /// @param account Account to claim stETH rewards for\n  /// @return amount Amount of stETH rewards that user can claim\n  /// @custom:usage This function should be called from rewardController contract in purpose of claiming stETH rewards\n  function claimStETH(address account) external returns (uint256 amount);\n\n  /// @notice Calculates and returns unclaimed stETH rewards for given account\n  /// @param account Account to calculate unclaimed stETH rewards for\n  /// @return amount Amount of unclaimed stETH rewards\n  function unclaimedStETHAmount(address account) external view returns (uint256 amount);\n}\n"
      },
      "contracts/interfaces/IStakingWithEpochs.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IStakingWithEpochs {\n  enum RewardToken {\n    USC,\n    CHI,\n    STETH\n  }\n\n  struct EpochData {\n    uint256 shares;\n    mapping(RewardToken => uint256) cumulativeRewardsPerShare;\n  }\n\n  struct StakeData {\n    uint256 lastUpdatedEpoch;\n    uint256 shares;\n    uint256 addSharesNextEpoch;\n    mapping(RewardToken => uint256) unclaimedRewards;\n  }\n\n  event Stake(address indexed account, uint256 amount);\n  event Unstake(address indexed account, address indexed toAddress, uint256 amount);\n\n  error ZeroAmount();\n  error NotRewardController();\n  error AmountBelowStakedBalance(uint256 stakedBalance, uint256 amount);\n\n  /// @notice Gets current reward for given account and token\n  /// @param account Account to get reward for\n  /// @param token Token to get reward for\n  /// @return amount Current reward for given account and token\n  /// @custom:usage This function should be used in inheriting contracts to get current reward for given account and token\n  function getUnclaimedRewards(address account, RewardToken token) external view returns (uint256 amount);\n\n  /// @notice Gets cumulative reward per share for given account and token\n  /// @param epoch Epoch to get cumulative reward per share for\n  /// @param token Token to get cumulative reward per share for\n  /// @return amount Cumulative reward per share for given epoch and token\n  /// @custom:usage This function should be used in inheriting contracts to get cumulative reward per share for given epoch and token\n  function getCumulativeRewardsPerShare(uint256 epoch, RewardToken token) external view returns (uint256 amount);\n\n  /// @notice Stakes given amount of tokens\n  /// @param amount Amount of tokens to stake\n  /// @custom:usage This function should be called from inheriting contracts to stake tokens\n  /// @custom:usage Logic should be the same for both uscStaking and chiStaking contracts\n  function stake(uint256 amount) external;\n\n  /// @notice Unstakes given amount of tokens, sends tokens to msg.sender by default\n  /// @param amount Amount of tokens to unstake\n  /// @custom:usage This function should be called from inheriting contracts to unstake tokens\n  /// @custom:usage Logic should be the same for both uscStaking and chiStaking contracts\n  function unstake(uint256 amount) external;\n\n  /// @notice Unstakes given amount of tokens\n  /// @param amount Amount of tokens to unstake\n  /// @param toAddress Address to send tokens\n  /// @custom:usage This function should be called from inheriting contracts to unstake tokens\n  /// @custom:usage Logic should be the same for both uscStaking and chiStaking contracts\n  function unstake(uint256 amount, address toAddress) external;\n}\n"
      },
      "contracts/interfaces/ISTETH.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface ISTETH is IERC20 {\n  function submit(address referral) external payable returns (uint256);\n}\n"
      },
      "contracts/interfaces/IUSCStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {IStaking} from \"./IStaking.sol\";\nimport {IArbitrage} from \"./IArbitrage.sol\";\n\ninterface IUSCStaking is IStaking {\n  event UpdateEpoch(uint256 indexed epoch, uint256 chiEmissions, uint256 uscRewards, uint256 stETHrewards);\n  event LockChi(address indexed account, uint256 amount, uint256 duration);\n  event ClaimUSCRewards(address indexed account, uint256 amount);\n  event ClaimStETH(address indexed account, uint256 amount);\n\n  error NotClaimable();\n  error InvalidDuration(uint256 duration);\n\n  /// @notice Updates epoch data\n  /// @param chiEmissions Amount of CHI token incentives emitted in current epoch for USC stakers\n  /// @param uscRewards Amount of USC token frozen in current epoch for USC stakers\n  /// @param stETHrewards Amount of stETH token rewards in current epoch for USC stakers\n  /// @custom:usage This function should be called from rewardController contract in purpose of updating epoch data\n  function updateEpoch(uint256 chiEmissions, uint256 uscRewards, uint256 stETHrewards) external;\n\n  /// @notice Locks CHI tokens that user earned from incentives for given duration\n  /// @param duration Locking duration in epochs\n  function lockChi(uint256 duration) external;\n\n  /// @notice Claims USC rewards for caller\n  /// @dev This function can be called only when price is above target and there is excess of reserves\n  function claimUSCRewards() external;\n}\n"
      },
      "contracts/interfaces/IWETH.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IWETH is IERC20 {\n  function deposit() external payable;\n\n  function withdraw(uint256 amount) external;\n}\n"
      },
      "contracts/library/ExternalContractAddresses.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/// @notice external contract addresses on Ethereum Mainnet\nlibrary ExternalContractAddresses {\n  address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;\n  address public constant stETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;\n  address public constant UNI_V2_SWAP_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;\n  address public constant UNI_V2_POOL_FACTORY = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;\n  address public constant ETH_USD_CHAINLINK_FEED = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419;\n  address public constant STETH_USD_CHAINLINK_FEED = 0xCfE54B5cD566aB89272946F602D76Ea879CAb4a8;\n  address public constant CURVE_ETH_STETH_POOL = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022;\n}\n"
      },
      "contracts/periphery/DataProvider.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol\";\nimport \"../library/ExternalContractAddresses.sol\";\nimport \"../interfaces/IPriceFeedAggregator.sol\";\nimport \"../interfaces/IStakingWithEpochs.sol\";\nimport \"../interfaces/IOCHI.sol\";\nimport \"../staking/RewardController.sol\";\nimport \"../staking/ChiStaking.sol\";\nimport \"../staking/ChiLocking.sol\";\nimport \"../staking/LPStaking.sol\";\nimport \"../staking/ChiVesting.sol\";\nimport \"../dso/PoolHelper.sol\";\nimport \"../dso/OCHI.sol\";\nimport \"../interfaces/IOCHI.sol\";\nimport \"../staking/USCStaking.sol\";\nimport \"../ReserveHolder.sol\";\n\n/// @title Data provider\n/// @notice Data provider containing view functions used by frontend\ncontract DataProvider {\n  function POLdata(\n    OCHI dso,\n    IUniswapV2Pair uniPair,\n    IPriceFeedAggregator priceFeed\n  ) external view returns (uint256 polUsdValue, uint256 polPercent) {\n    uint256 lpAmount = uniPair.balanceOf(address(dso.lpRewards(uniPair)));\n    polUsdValue = PoolHelper.getUSDValueForLP(lpAmount, uniPair, priceFeed);\n    polPercent = Math.mulDiv(lpAmount, 10 ** 18, uniPair.totalSupply());\n  }\n\n  function getLPTokenPrice(\n    IUniswapV2Pair pair,\n    IPriceFeedAggregator priceFeedAggregator\n  ) public view returns (uint256) {\n    return PoolHelper.getUSDValueForLP(1 ether, pair, priceFeedAggregator);\n  }\n\n  function getTotalPoolValue(\n    IUniswapV2Pair pair,\n    IPriceFeedAggregator priceFeedAggregator\n  ) public view returns (uint256) {\n    return PoolHelper.getTotalPoolUSDValue(pair, priceFeedAggregator);\n  }\n\n  function chiStakingAPR(\n    address chi,\n    ChiStaking chiStaking,\n    ChiLocking chiLocking,\n    USCStaking uscStaking,\n    LPStaking uscEthLpStaking,\n    LPStaking chiEthLpStaking,\n    RewardController rewardController,\n    IPriceFeedAggregator priceFeedAggregator,\n    ReserveHolder reserveHolder\n  ) public view returns (uint256) {\n    uint256 stEthPrice = priceFeedAggregator.peek(ExternalContractAddresses.stETH);\n    uint256 chiPrice = priceFeedAggregator.peek(chi);\n    uint256 totalStakedChi = chiStaking.getStakedChi() +\n      chiLocking.getStakedChi() +\n      uscStaking.getStakedChi() +\n      uscEthLpStaking.getStakedChi() +\n      chiEthLpStaking.getStakedChi();\n    uint256 chiStakedValue = Math.mulDiv(totalStakedChi, chiPrice, 1e8);\n\n    uint256 currentEpoch = chiStaking.currentEpoch();\n\n    (, uint256 totalRewardsTwoEpochsAgo) = currentEpoch >= 2 ? rewardController.epochs(currentEpoch - 2) : (0, 0);\n    (, uint256 totalRewardsLastEpoch) = rewardController.epochs(currentEpoch - 1);\n\n    uint256 totalEthReward;\n    if (currentEpoch == 0 || currentEpoch == 1) {\n      totalEthReward = (reserveHolder.totalStEthDeposited() * 4) / 100 / 52;\n    } else {\n      totalEthReward = totalRewardsLastEpoch - totalRewardsTwoEpochsAgo;\n    }\n\n    uint256 totalEthRewardValue = Math.mulDiv(totalEthReward, stEthPrice, 1e8);\n\n    return Math.mulDiv(totalEthRewardValue * 52, 1e18, chiStakedValue);\n  }\n\n  function uscStakingAPR(\n    address chi,\n    ChiStaking chiStaking,\n    ChiLocking chiLocking,\n    USCStaking uscStaking,\n    LPStaking uscEthLpStaking,\n    LPStaking chiEthLpStaking,\n    RewardController rewardController,\n    IPriceFeedAggregator priceFeedAggregator,\n    ReserveHolder reserveHolder\n  ) external view returns (uint256 totalApr, uint256 uscApr, uint256 chiApr, uint256 boostedStChiApr) {\n    uint256 currentEpoch = rewardController.currentEpoch();\n    uint256 stChiApr = chiStakingAPR(\n      chi,\n      chiStaking,\n      chiLocking,\n      uscStaking,\n      uscEthLpStaking,\n      chiEthLpStaking,\n      rewardController,\n      priceFeedAggregator,\n      reserveHolder\n    );\n    uint256 chiEmissions = rewardController.chiIncentivesForUscStaking();\n    uint256 totalUscStaked = uscStaking.totalSupply();\n    boostedStChiApr = (stChiApr * chiEmissions * 52) / totalUscStaked;\n\n    uint256 chiPrice = priceFeedAggregator.peek(chi);\n    uint256 chiEmissionsValue = Math.mulDiv(chiEmissions, chiPrice, 1e8);\n    chiApr = Math.mulDiv(chiEmissionsValue * 52, 1e18, totalUscStaked);\n\n    (uint256 uscRewardAmount, ) = rewardController.epochs(currentEpoch - 1);\n    uscApr = Math.mulDiv(uscRewardAmount * 52, 1e18, totalUscStaked);\n\n    return (boostedStChiApr + chiApr + uscApr, uscApr, chiApr, boostedStChiApr);\n  }\n\n  function chiLockingAPR(\n    address chi,\n    ChiStaking chiStaking,\n    ChiLocking chiLocking,\n    USCStaking uscStaking,\n    LPStaking uscEthLpStaking,\n    LPStaking chiEthLpStaking,\n    RewardController rewardController,\n    IPriceFeedAggregator priceFeedAggregator,\n    ReserveHolder reserveHolder\n  ) public view returns (uint256 totalApr, uint256 chiApr, uint256 stChiApr, uint256 boostedStChiApr) {\n    uint256 chiPrice = priceFeedAggregator.peek(chi);\n    uint256 totalLockedChiValue = Math.mulDiv(chiLocking.getLockedChi(), chiPrice, 1e8);\n    uint256 chiEmissions = rewardController.chiIncentivesForChiLocking();\n    uint256 chiEmissionsValue = Math.mulDiv(chiEmissions, chiPrice, 1e8);\n    chiApr = Math.mulDiv(chiEmissionsValue * 52, 1e18, totalLockedChiValue);\n    stChiApr = chiStakingAPR(\n      chi,\n      chiStaking,\n      chiLocking,\n      uscStaking,\n      uscEthLpStaking,\n      chiEthLpStaking,\n      rewardController,\n      priceFeedAggregator,\n      reserveHolder\n    );\n\n    boostedStChiApr = Math.mulDiv(chiEmissionsValue, stChiApr * 52, totalLockedChiValue);\n\n    return (chiApr + stChiApr + boostedStChiApr, chiApr, stChiApr, boostedStChiApr);\n  }\n\n  function dsoAPR(address chi, OCHI ochi, IPriceFeedAggregator priceFeedAggregator) external view returns (uint256) {\n    int256 totalProfitPrevWeek = ochi.getLastEpochTotalReward();\n    if (totalProfitPrevWeek < 0) {\n      return 0;\n    }\n\n    uint256 totalChiLocked = ochi.totalOCHIlocked();\n    uint256 chiPrice = priceFeedAggregator.peek(chi);\n    uint256 totalChiLockedValue = Math.mulDiv(totalChiLocked, chiPrice, 1e18);\n    return Math.mulDiv(uint256(totalProfitPrevWeek) * 52, 1e18, totalChiLockedValue);\n  }\n\n  function uscEthLPAPR(\n    address chi,\n    ChiStaking chiStaking,\n    ChiLocking chiLocking,\n    USCStaking uscStaking,\n    LPStaking uscEthLpStaking,\n    LPStaking chiEthLpStaking,\n    RewardController rewardController,\n    IPriceFeedAggregator priceFeedAggregator,\n    IUniswapV2Pair uscEthPair,\n    ReserveHolder reserveHolder\n  ) external view returns (uint256 totalApr, uint256 chiApr, uint256 stChiApr) {\n    uint256 chiPrice = priceFeedAggregator.peek(chi);\n    uint256 chiEmissions = rewardController.chiIncentivesForUscEthLPStaking();\n    uint256 chiEmissionsValue = Math.mulDiv(chiEmissions, chiPrice, 1e8);\n\n    uint256 totalStaked = uscEthLpStaking.totalSupply();\n    uint256 uscEthPairPrice = getLPTokenPrice(uscEthPair, priceFeedAggregator);\n    uint256 totalStakedValue = Math.mulDiv(totalStaked, uscEthPairPrice, 1e8);\n    chiApr = Math.mulDiv(chiEmissionsValue * 52, 1e18, totalStakedValue);\n\n    uint256 chiStakingAPR = chiStakingAPR(\n      chi,\n      chiStaking,\n      chiLocking,\n      uscStaking,\n      uscEthLpStaking,\n      chiEthLpStaking,\n      rewardController,\n      priceFeedAggregator,\n      reserveHolder\n    );\n    stChiApr = Math.mulDiv(chiEmissionsValue, chiStakingAPR * 52, totalStakedValue);\n\n    return (chiApr + stChiApr, chiApr, stChiApr);\n  }\n\n  function chiEthLPAPR(\n    address chi,\n    ChiStaking chiStaking,\n    ChiLocking chiLocking,\n    USCStaking uscStaking,\n    LPStaking uscEthLpStaking,\n    LPStaking chiEthLpStaking,\n    RewardController rewardController,\n    IPriceFeedAggregator priceFeedAggregator,\n    IUniswapV2Pair chiEthPair,\n    ReserveHolder reserveHolder\n  ) external view returns (uint256 totalApr, uint256 chiApr, uint256 stChiApr) {\n    uint256 chiPrice = priceFeedAggregator.peek(chi);\n    uint256 chiEmissions = rewardController.chiIncentivesForChiEthLPStaking();\n    uint256 chiEmissionsValue = Math.mulDiv(chiEmissions, chiPrice, 1e8);\n\n    uint256 totalStaked = chiEthLpStaking.totalSupply();\n    uint256 chiEthPairPrice = getLPTokenPrice(chiEthPair, priceFeedAggregator);\n    uint256 totalStakedValue = Math.mulDiv(totalStaked, chiEthPairPrice, 1e8);\n    chiApr = Math.mulDiv(chiEmissionsValue * 52, 1e18, totalStakedValue);\n\n    uint256 chiStakingAPR = chiStakingAPR(\n      chi,\n      chiStaking,\n      chiLocking,\n      uscStaking,\n      uscEthLpStaking,\n      chiEthLpStaking,\n      rewardController,\n      priceFeedAggregator,\n      reserveHolder\n    );\n    stChiApr = Math.mulDiv(chiEmissionsValue, chiStakingAPR * 52, totalStakedValue);\n\n    return (chiApr + stChiApr, chiApr, stChiApr);\n  }\n\n  function estimatedYieldPerWeek(\n    address chi,\n    ChiStaking chiStaking,\n    ChiLocking chiLocking,\n    USCStaking uscStaking,\n    LPStaking uscEthLpStaking,\n    LPStaking chiEthLpStaking,\n    RewardController rewardController,\n    IPriceFeedAggregator priceFeedAggregator,\n    ReserveHolder reserveHolder\n  ) external view returns (uint256) {\n    (uint256 chiLockingApr, , , ) = chiLockingAPR(\n      chi,\n      chiStaking,\n      chiLocking,\n      uscStaking,\n      uscEthLpStaking,\n      chiEthLpStaking,\n      rewardController,\n      priceFeedAggregator,\n      reserveHolder\n    );\n    uint256 totalLockedChi = chiLocking.getLockedChi();\n    uint256 chiPrice = priceFeedAggregator.peek(chi);\n    uint256 totalLockedChiValue = Math.mulDiv(totalLockedChi, chiPrice, 1e18);\n    uint256 chiLockingWeeklyAPR = chiLockingApr / 52;\n\n    return Math.mulDiv(totalLockedChiValue, chiLockingWeeklyAPR, 1e18);\n  }\n\n  function totalValueOfLockedChi(\n    IPriceFeedAggregator priceFeedAggregator,\n    ChiLocking chiLocking,\n    ChiVesting chiVesting,\n    address chi,\n    address account\n  ) external view returns (uint256) {\n    IChiLocking.AllLockedPositionsOutput[] memory lockedPositions = chiLocking.getAllLockedPositions(account);\n\n    uint256 totalChi;\n    for (uint256 i = 0; i < lockedPositions.length; i++) {\n      totalChi += lockedPositions[i].totalAccumulatedChi;\n    }\n\n    (uint256 totalVestedChi, , , , , ) = chiVesting.vestingData(account);\n    totalChi += totalVestedChi;\n\n    uint256 chiPrice = priceFeedAggregator.peek(address(chi));\n\n    return Math.mulDiv(totalChi, chiPrice, 1e18);\n  }\n}\n"
      },
      "contracts/ReserveHolder.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"./interfaces/IReserveHolder.sol\";\nimport \"./interfaces/IPriceFeedAggregator.sol\";\nimport \"./interfaces/ISTETH.sol\";\nimport \"./interfaces/IWETH.sol\";\nimport \"./interfaces/ICurvePool.sol\";\nimport \"./library/ExternalContractAddresses.sol\";\n\n/// @title Contract for holding stETH reserves\n/// @notice This contract holds stETH reserves and rebalances them\n/// @notice Part of reserves are is WETH so arbitrage can take them and perform aribtrage without swapping stETH for WETH\n/// @dev This contract is upgradeable\ncontract ReserveHolder is IReserveHolder, OwnableUpgradeable {\n  using SafeERC20 for ISTETH;\n  using SafeERC20 for IWETH;\n\n  uint256 public constant BASE_PRICE = 1e8;\n  uint256 public constant MAX_PERCENTAGE = 100_00;\n  IWETH public constant WETH = IWETH(ExternalContractAddresses.WETH);\n  ISTETH public constant stETH = ISTETH(ExternalContractAddresses.stETH);\n  ICurvePool public constant curvePool = ICurvePool(ExternalContractAddresses.CURVE_ETH_STETH_POOL);\n\n  IPriceFeedAggregator public priceFeedAggregator;\n  address public claimer;\n  uint256 public totalClaimed;\n  uint256 public swapEthTolerance;\n  uint256 public ethThreshold;\n  uint256 public totalStEthDeposited;\n  uint256 public curveStEthSafeGuardPercentage;\n\n  mapping(address account => bool status) public isArbitrager;\n\n  modifier onlyArbitrager() {\n    if (isArbitrager[msg.sender] != true) {\n      revert NotArbitrager(msg.sender);\n    }\n    _;\n  }\n\n  modifier onlyClaimer() {\n    if (msg.sender != claimer) {\n      revert NotClaimer(msg.sender);\n    }\n    _;\n  }\n\n  receive() external payable {\n    emit Receive(msg.sender, msg.value);\n  }\n\n  function initialize(\n    IPriceFeedAggregator _priceFeedAggregator,\n    address _claimer,\n    uint256 _ethThreshold,\n    uint256 _curveStEthSafeGuardPercentage\n  ) external initializer {\n    if (_ethThreshold > MAX_PERCENTAGE) {\n      revert ThresholdTooHigh(_ethThreshold);\n    }\n    __Ownable_init();\n    claimer = _claimer;\n    priceFeedAggregator = _priceFeedAggregator;\n    ethThreshold = _ethThreshold;\n    curveStEthSafeGuardPercentage = _curveStEthSafeGuardPercentage;\n    swapEthTolerance = 0.1 ether;\n  }\n\n  /// @inheritdoc IReserveHolder\n  function setArbitrager(address arbitrager, bool status) external onlyOwner {\n    isArbitrager[arbitrager] = status;\n    emit SetArbitrager(arbitrager, status);\n  }\n\n  /// @inheritdoc IReserveHolder\n  function setClaimer(address _claimer) external onlyOwner {\n    claimer = _claimer;\n    emit SetClaimer(_claimer);\n  }\n\n  /// @inheritdoc IReserveHolder\n  function setEthThreshold(uint256 _ethThreshold) external onlyOwner {\n    if (_ethThreshold > MAX_PERCENTAGE) {\n      revert ThresholdTooHigh(_ethThreshold);\n    }\n    ethThreshold = _ethThreshold;\n    emit SetEthThreshold(_ethThreshold);\n  }\n\n  /// @inheritdoc IReserveHolder\n  function setSwapEthTolerance(uint256 _swapEthTolerance) external onlyOwner {\n    swapEthTolerance = _swapEthTolerance;\n    emit SetSwapEthTolerance(_swapEthTolerance);\n  }\n\n  /// @inheritdoc IReserveHolder\n  function setCurveStEthSafeGuardPercentage(uint256 _curveStEthSafeGuardPercentage) external onlyOwner {\n    if (_curveStEthSafeGuardPercentage > MAX_PERCENTAGE) {\n      revert SafeGuardTooHigh(_curveStEthSafeGuardPercentage);\n    }\n    curveStEthSafeGuardPercentage = _curveStEthSafeGuardPercentage;\n    emit SetCurveStEthSafeGuardPercentage(_curveStEthSafeGuardPercentage);\n  }\n\n  /// @inheritdoc IReserveHolder\n  function getReserveValue() external view returns (uint256) {\n    uint256 stEthPrice = priceFeedAggregator.peek(address(stETH));\n    uint256 ethPrice = priceFeedAggregator.peek(address(WETH));\n    uint256 ethBalance = address(this).balance + WETH.balanceOf(address(this));\n    return Math.mulDiv(totalStEthDeposited, stEthPrice, 1e18) + Math.mulDiv(ethBalance, ethPrice, 1e18);\n  }\n\n  /// @inheritdoc IReserveHolder\n  function getCurrentRewards() external view returns (uint256) {\n    return stETH.balanceOf(address(this)) - totalStEthDeposited;\n  }\n\n  /// @inheritdoc IReserveHolder\n  function getCumulativeRewards() external view returns (uint256) {\n    return stETH.balanceOf(address(this)) - totalStEthDeposited + totalClaimed;\n  }\n\n  /// @inheritdoc IReserveHolder\n  function deposit(uint256 amount) external {\n    uint256 balanceBefore = stETH.balanceOf(address(this));\n    stETH.safeTransferFrom(msg.sender, address(this), amount);\n    totalStEthDeposited += stETH.balanceOf(address(this)) - balanceBefore;\n\n    emit Deposit(msg.sender, amount);\n  }\n\n  /// @inheritdoc IReserveHolder\n  function rebalance() external {\n    uint256 ethPrice = _peek(address(WETH));\n    uint256 stEthPrice = _peek(address(stETH));\n    uint256 ethValue = Math.mulDiv(WETH.balanceOf(address(this)), ethPrice, BASE_PRICE);\n    uint256 stEthValue = Math.mulDiv(stETH.balanceOf(address(this)), stEthPrice, BASE_PRICE);\n    uint256 ethThresholdValue = Math.mulDiv((ethValue + stEthValue), ethThreshold, MAX_PERCENTAGE);\n\n    if (ethThresholdValue > ethValue) {\n      uint256 stEthAmountToSwap = Math.mulDiv((ethThresholdValue - ethValue), BASE_PRICE, stEthPrice);\n      uint256 stEthBalanceBefore = stETH.balanceOf(address(this));\n      _swap(stEthAmountToSwap);\n      uint256 stEthBalanceAfter = stETH.balanceOf(address(this));\n      totalStEthDeposited -= stEthBalanceBefore - stEthBalanceAfter;\n\n      emit Rebalance(0, stEthAmountToSwap);\n    } else if (ethThresholdValue < ethValue) {\n      uint256 stEthBalanceBefore = stETH.balanceOf(address(this));\n      uint256 ethAmountToSwap = Math.mulDiv((ethValue - ethThresholdValue), BASE_PRICE, ethPrice);\n\n      WETH.withdraw(ethAmountToSwap);\n      stETH.submit{value: ethAmountToSwap}(address(this));\n\n      uint256 stEthBalanceAfter = stETH.balanceOf(address(this));\n      totalStEthDeposited += stEthBalanceAfter - stEthBalanceBefore;\n\n      emit Rebalance(ethAmountToSwap, 0);\n    }\n  }\n\n  /// @inheritdoc IReserveHolder\n  function redeem(uint256 amount) external onlyArbitrager returns (uint256) {\n    uint256 ethBalance = WETH.balanceOf(address(this));\n    if (amount > ethBalance) {\n      uint256 stEthBalanceBefore = stETH.balanceOf(address(this));\n      uint256 stEthAmountToSwap = amount - ethBalance;\n      uint256 safeStEthAmountToSwap = stEthAmountToSwap +\n        Math.mulDiv(stEthAmountToSwap, curveStEthSafeGuardPercentage, MAX_PERCENTAGE);\n      _swap(safeStEthAmountToSwap);\n      uint256 stEthBalanceAfter = stETH.balanceOf(address(this));\n      totalStEthDeposited -= stEthBalanceBefore - stEthBalanceAfter;\n\n      emit RedeemSwap(amount - ethBalance, safeStEthAmountToSwap);\n    }\n    WETH.safeTransfer(msg.sender, amount);\n\n    emit Redeem(msg.sender, amount);\n    return amount;\n  }\n\n  /// @inheritdoc IReserveHolder\n  function claimRewards(address account, uint256 amount) external onlyClaimer {\n    totalClaimed += amount;\n    stETH.safeTransfer(account, amount);\n    emit ClaimRewards(account, amount);\n  }\n\n  /// @inheritdoc IReserveHolder\n  function wrapETH() external {\n    WETH.deposit{value: address(this).balance}();\n  }\n\n  function _swap(uint256 amountIn) private {\n    stETH.approve(address(curvePool), amountIn);\n    uint256 ethReceived = curvePool.exchange(1, 0, amountIn, 0);\n    WETH.deposit{value: ethReceived}();\n  }\n\n  function _peek(address asset) private view returns (uint256) {\n    uint256 price = priceFeedAggregator.peek(asset);\n    return price;\n  }\n\n  function _safeTransferETH(address to, uint256 value) private {\n    (bool success, ) = to.call{value: value}(new bytes(0));\n    if (!success) {\n      revert EtherSendFailed(to, value);\n    }\n  }\n}\n"
      },
      "contracts/staking/ChiLocking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"../interfaces/IChiLocking.sol\";\nimport \"../library/ExternalContractAddresses.sol\";\n\n/// @title Contract for locking CHI tokens\n/// @notice This contract holds CHI tokens that are locked\n/// @notice Locking of reward distribution of CHI tokens is done per epoch\ncontract ChiLocking is IChiLocking, OwnableUpgradeable {\n  using SafeERC20 for IERC20;\n\n  uint256 public constant MAX_LOCK_DURATION = 208; // 4 years in weeks\n  IERC20 public constant stETH = IERC20(ExternalContractAddresses.stETH);\n\n  IERC20 public chi;\n  address public rewardController;\n  uint256 public currentEpoch;\n  uint256 public totalLockedChi;\n  uint256 public totalUnlockedChi;\n  uint256 public totalLockedShares;\n  uint256 public totalVotingPower;\n  uint256 public sumOfLockedDurations;\n  uint256 public numberOfLockedPositions;\n  uint256 public addVotingPowerInEpoch;\n  uint256 public sumShareDurationProduct;\n  uint256 public addedAmountInEpoch;\n\n  mapping(address account => bool status) public chiLockers;\n  mapping(address account => LockingData) public locks;\n  mapping(uint256 id => EpochData) public epochs;\n\n  modifier onlyRewardController() {\n    if (msg.sender != rewardController) {\n      revert NotRewardController();\n    }\n    _;\n  }\n\n  modifier onlyChiLockers() {\n    if (!chiLockers[msg.sender]) {\n      revert NotChiLocker();\n    }\n    _;\n  }\n\n  function initialize(IERC20 _chi, address _chiStaking) external initializer {\n    __Ownable_init();\n    chiLockers[_chiStaking] = true;\n    chi = _chi;\n    currentEpoch = 1;\n  }\n\n  /// @inheritdoc IChiLocking\n  function setUscStaking(address _uscStaking) external onlyOwner {\n    chiLockers[_uscStaking] = true;\n    emit SetUscStaking(_uscStaking);\n  }\n\n  /// @inheritdoc IChiLocking\n  function setRewardController(address _rewardController) external onlyOwner {\n    rewardController = _rewardController;\n    emit SetRewardController(_rewardController);\n  }\n\n  /// @inheritdoc IChiLocking\n  function setChiLocker(address contractAddress, bool toSet) external onlyOwner {\n    chiLockers[contractAddress] = toSet;\n    emit SetChiLocker(contractAddress, toSet);\n  }\n\n  /// @inheritdoc IChiLocking\n  function getLockedPosition(address account, uint256 pos) external view returns (LockedPosition memory) {\n    return locks[account].positions[pos];\n  }\n\n  function getAllLockedPositions(address account) external view returns (AllLockedPositionsOutput[] memory out) {\n    LockingData storage lockData = locks[account];\n\n    out = new AllLockedPositionsOutput[](lockData.positions.length);\n\n    for (uint256 i = 0; i < lockData.positions.length; i++) {\n      LockedPosition storage position = lockData.positions[i];\n      out[i].position = position;\n      out[i].votingPower = _getCurrentVotingPowerForPosition(position);\n      out[i].stETHreward = _getUnclaimedStETHPositionAmount(lockData.positions[i], lockData.lastUpdatedEpoch);\n      out[i].totalAccumulatedChi = _totalAccumulatedAtEpoch(currentEpoch, lockData.positions[i]);\n      out[i].totalChiRewards = (out[i].totalAccumulatedChi > position.amount)\n        ? out[i].totalAccumulatedChi - position.amount\n        : 0;\n    }\n  }\n\n  /// @inheritdoc IChiLocking\n  function getStakedChi() public view returns (uint256) {\n    return totalLockedChi + totalUnlockedChi;\n  }\n\n  /// @inheritdoc IChiLocking\n  function getLockedChi() public view returns (uint256) {\n    return totalLockedChi;\n  }\n\n  /// @inheritdoc IChiLocking\n  function getTotalVotingPower() external view returns (uint256) {\n    return totalVotingPower;\n  }\n\n  /// @inheritdoc IChiLocking\n  function availableChiWithdraw(address account) public view returns (uint256 availableTotal) {\n    LockingData storage lockData = locks[account];\n    for (uint256 i = 0; i < lockData.positions.length; i++) {\n      availableTotal += _availableToWithdrawFromPosition(lockData.positions[i]);\n    }\n  }\n\n  /// @inheritdoc IChiLocking\n  function lockChi(address account, uint256 amount, uint256 duration) external onlyChiLockers {\n    if (amount == 0) {\n      revert ZeroAmount();\n    }\n\n    uint256 shares = _getNumberOfShares(amount);\n    duration++;\n\n    EpochData storage currentEpochData = epochs[currentEpoch];\n    EpochData storage afterEndEpoch = epochs[currentEpoch + duration];\n\n    locks[account].positions.push(\n      LockedPosition({\n        amount: amount,\n        startEpoch: currentEpoch,\n        duration: duration,\n        shares: shares,\n        withdrawnChiAmount: 0\n      })\n    );\n\n    totalLockedChi += amount;\n    totalLockedShares += shares;\n    currentEpochData.lockedSharesInEpoch += shares;\n    currentEpochData.totalLockedChiInEpoch += amount;\n    afterEndEpoch.sharesToUnlock += shares;\n\n    totalVotingPower += Math.mulDiv(amount, duration, MAX_LOCK_DURATION);\n    sumShareDurationProduct += Math.mulDiv(shares, duration, MAX_LOCK_DURATION);\n    sumOfLockedDurations += duration + 1;\n    numberOfLockedPositions += 1;\n    afterEndEpoch.numberOfEndingPositions += 1;\n\n    emit LockChi(account, amount, shares, currentEpoch, currentEpoch + duration);\n  }\n\n  /// @inheritdoc IChiLocking\n  function updateEpoch(uint256 chiEmissions, uint256 stETHrewards) external onlyRewardController {\n    EpochData storage epoch = epochs[currentEpoch];\n\n    uint256 stETHrewardsForLocked;\n    uint256 stakedChi = getStakedChi();\n    if (stakedChi != 0) {\n      stETHrewardsForLocked = Math.mulDiv(stETHrewards, totalLockedChi, stakedChi);\n    }\n    uint256 stETHrewardsForUnlocked = stETHrewards - stETHrewardsForLocked;\n\n    uint256 stEthPerLockedShare;\n    if (epoch.lockedSharesInEpoch != 0) {\n      stEthPerLockedShare = Math.mulDiv(stETHrewardsForLocked, 1e18, epoch.lockedSharesInEpoch);\n    }\n    epoch.cumulativeStETHPerLockedShare = epochs[currentEpoch - 1].cumulativeStETHPerLockedShare + stEthPerLockedShare;\n\n    uint256 stEthPerUnlocked;\n    if (totalUnlockedChi != 0) {\n      stEthPerUnlocked = Math.mulDiv(stETHrewardsForUnlocked, 1e18, totalUnlockedChi);\n    }\n\n    epoch.cumulativeStETHPerUnlocked = epochs[currentEpoch - 1].cumulativeStETHPerUnlocked + stEthPerUnlocked;\n\n    totalVotingPower -= totalLockedChi / MAX_LOCK_DURATION;\n    sumShareDurationProduct -= totalLockedShares / MAX_LOCK_DURATION;\n\n    if (totalLockedShares != 0) {\n      totalVotingPower += Math.mulDiv(chiEmissions, sumShareDurationProduct, totalLockedShares);\n    }\n\n    totalLockedChi += chiEmissions;\n    epoch.totalLockedChiInEpoch += chiEmissions;\n\n    EpochData storage nextEpoch = epochs[currentEpoch + 1];\n\n    uint256 amountToUnlock;\n    if (totalLockedShares != 0) {\n      amountToUnlock = Math.mulDiv(epoch.totalLockedChiInEpoch, nextEpoch.sharesToUnlock, epoch.lockedSharesInEpoch);\n    }\n\n    totalLockedChi = totalLockedChi - amountToUnlock;\n    totalUnlockedChi = totalUnlockedChi + amountToUnlock;\n    totalLockedShares -= nextEpoch.sharesToUnlock;\n\n    nextEpoch.lockedSharesInEpoch = totalLockedShares;\n    nextEpoch.totalLockedChiInEpoch = totalLockedChi;\n\n    currentEpoch++;\n    emit UpdateEpoch(currentEpoch - 1, totalLockedChi, chiEmissions, stETHrewards, stEthPerLockedShare);\n  }\n\n  /// @inheritdoc IChiLocking\n  function claimStETH(address account) external onlyRewardController returns (uint256 amount) {\n    _updateUnclaimedStETH(account);\n    amount = locks[account].unclaimedStETH;\n    locks[account].unclaimedStETH = 0;\n\n    emit ClaimStETH(account, amount);\n  }\n\n  /// @inheritdoc IChiLocking\n  function withdrawChiFromAccount(address account, uint256 amount) public onlyChiLockers {\n    withdrawChiFromAccountToAddress(account, account, amount);\n  }\n\n  /// @inheritdoc IChiLocking\n  function withdrawChiFromAccountToAddress(address account, address toAddress, uint256 amount) public onlyChiLockers {\n    if (amount == 0) {\n      return;\n    }\n\n    _updateUnclaimedStETH(account);\n\n    uint256 toWithdraw = amount;\n\n    LockingData storage lockData = locks[account];\n\n    uint256 pos = 0;\n    while (toWithdraw > 0) {\n      if (pos >= lockData.positions.length) {\n        revert UnavailableWithdrawAmount(amount);\n      }\n\n      LockedPosition storage position = lockData.positions[pos];\n\n      if (currentEpoch < position.startEpoch + position.duration) {\n        pos++;\n      } else {\n        uint256 availableToWithdraw = _availableToWithdrawFromPosition(position);\n\n        if (availableToWithdraw > toWithdraw) {\n          position.withdrawnChiAmount += toWithdraw;\n          toWithdraw = 0;\n        } else {\n          position.withdrawnChiAmount += toWithdraw;\n          toWithdraw -= availableToWithdraw;\n          _removePosition(lockData, pos);\n        }\n      }\n    }\n\n    totalUnlockedChi -= amount;\n    chi.safeTransfer(toAddress, amount);\n\n    emit WithdrawChiFromAccount(account, toAddress, amount);\n  }\n\n  /// @inheritdoc IChiLocking\n  function unclaimedStETHAmount(address account) public view returns (uint256 totalAmount) {\n    LockingData storage lockData = locks[account];\n    totalAmount = lockData.unclaimedStETH;\n    if (lockData.lastUpdatedEpoch == currentEpoch) return totalAmount;\n\n    for (uint256 i = 0; i < lockData.positions.length; i++) {\n      totalAmount += _getUnclaimedStETHPositionAmount(lockData.positions[i], lockData.lastUpdatedEpoch);\n    }\n  }\n\n  /// @inheritdoc IChiLocking\n  function getVotingPower(address account) public view returns (uint256) {\n    uint256 votingPower = 0;\n    LockingData storage lockData = locks[account];\n    for (uint256 i = 0; i < lockData.positions.length; i++) {\n      votingPower += _getCurrentVotingPowerForPosition(lockData.positions[i]);\n    }\n    return votingPower;\n  }\n\n  function _getNumberOfShares(uint256 chiAmount) internal view returns (uint256) {\n    if (totalLockedChi == 0) return chiAmount;\n    return Math.mulDiv(chiAmount, totalLockedShares, totalLockedChi);\n  }\n\n  function _updateUnclaimedStETH(address account) internal {\n    locks[account].unclaimedStETH = unclaimedStETHAmount(account);\n    locks[account].lastUpdatedEpoch = currentEpoch;\n  }\n\n  function _getUnclaimedStETHPositionAmount(\n    LockedPosition storage position,\n    uint256 lastUpdated\n  ) internal view returns (uint256 unclaimedAmount) {\n    if (lastUpdated == currentEpoch) return 0;\n\n    uint256 fromEpoch = lastUpdated < position.startEpoch ? position.startEpoch : lastUpdated;\n    uint256 toEpoch = currentEpoch - 1;\n\n    uint256 lockEndsInEpoch = position.startEpoch + position.duration - 1;\n    if (fromEpoch <= lockEndsInEpoch) {\n      if (toEpoch <= lockEndsInEpoch) {\n        unclaimedAmount += _unclaiemdStETHDuringLocked(position, fromEpoch, toEpoch);\n        return unclaimedAmount;\n      } else {\n        unclaimedAmount += _unclaiemdStETHDuringLocked(position, fromEpoch, lockEndsInEpoch);\n      }\n\n      fromEpoch = lockEndsInEpoch + 1;\n    }\n\n    unclaimedAmount += _unclaiemdStETHAfterLocked(position, fromEpoch, toEpoch);\n  }\n\n  function _unclaiemdStETHDuringLocked(\n    LockedPosition storage position,\n    uint256 fromEpoch,\n    uint256 toEpoch\n  ) internal view returns (uint256) {\n    uint256 rewardPerShare = epochs[toEpoch].cumulativeStETHPerLockedShare -\n      epochs[fromEpoch - 1].cumulativeStETHPerLockedShare;\n\n    return Math.mulDiv(rewardPerShare, position.shares, 1e18);\n  }\n\n  function _unclaiemdStETHAfterLocked(\n    LockedPosition storage position,\n    uint256 fromEpoch,\n    uint256 toEpoch\n  ) internal view returns (uint256) {\n    uint256 unlockedChiAmount = _availableToWithdrawFromPosition(position);\n    uint256 rewardPerChi = epochs[toEpoch].cumulativeStETHPerUnlocked -\n      epochs[fromEpoch - 1].cumulativeStETHPerUnlocked;\n    return Math.mulDiv(rewardPerChi, unlockedChiAmount, 1e18);\n  }\n\n  function _getCurrentVotingPowerForPosition(LockedPosition storage position) internal view returns (uint256) {\n    if (currentEpoch >= position.startEpoch + position.duration || currentEpoch < position.startEpoch) return 0;\n    uint256 epochsUntilEnd = position.startEpoch + position.duration - currentEpoch;\n\n    if (currentEpoch == position.startEpoch) {\n      return Math.mulDiv(position.amount, epochsUntilEnd, MAX_LOCK_DURATION);\n    } else {\n      return Math.mulDiv(_totalAccumulatedAtEpoch(currentEpoch - 1, position), epochsUntilEnd, MAX_LOCK_DURATION);\n    }\n  }\n\n  function _availableToWithdrawFromPosition(LockedPosition storage position) internal view returns (uint256) {\n    uint256 endLockingEpoch = position.startEpoch + position.duration - 1;\n    if (currentEpoch <= endLockingEpoch) return 0;\n    return _totalAccumulatedAtEpoch(endLockingEpoch, position) - position.withdrawnChiAmount;\n  }\n\n  function _totalAccumulatedAtEpoch(uint256 epochNum, LockedPosition storage position) internal view returns (uint256) {\n    uint256 endEpoch = position.startEpoch + position.duration - 1;\n    if (endEpoch < epochNum) epochNum = endEpoch;\n\n    EpochData storage epoch = epochs[epochNum];\n    return Math.mulDiv(position.shares, epoch.totalLockedChiInEpoch, epoch.lockedSharesInEpoch);\n  }\n\n  function _removePosition(LockingData storage lockData, uint256 pos) internal {\n    lockData.positions[pos] = lockData.positions[lockData.positions.length - 1];\n    lockData.positions.pop();\n  }\n}\n"
      },
      "contracts/staking/ChiStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport \"../interfaces/IChiStaking.sol\";\nimport \"../interfaces/IChiLocking.sol\";\nimport \"./StakingWithEpochs.sol\";\n\n/// @title Contract for staking chi tokens\n/// @notice This contract holds staked CHI tokens, staking is done per epoch\n/// @dev This contract is upgradeable\ncontract ChiStaking is IChiStaking, OwnableUpgradeable, StakingWithEpochs {\n  using SafeERC20 for IERC20;\n\n  uint256 public constant MIN_LOCK_DURATION = 4; // 4 epochs = 4 weeks = 1 month\n  uint256 public constant MAX_LOCK_DURATION = 208; // 208 epochs = 208 weeks = 4 years\n\n  IChiLocking public chiLocking;\n\n  function initialize(IERC20 _chiAddress) external initializer {\n    __Ownable_init();\n    __StakingWithEpochs_init(\"Staked CHI\", \"stCHI\", _chiAddress);\n  }\n\n  /// @inheritdoc IChiStaking\n  function setChiLocking(IChiLocking _chiLocking) external onlyOwner {\n    chiLocking = _chiLocking;\n    emit SetChiLocking(address(_chiLocking));\n  }\n\n  /// @inheritdoc IStaking\n  function setRewardController(address _rewardController) external onlyOwner {\n    _setRewardController(_rewardController);\n    emit SetRewardController(_rewardController);\n  }\n\n  /// @inheritdoc IStaking\n  function getStakedChi() external view returns (uint256) {\n    return epochs[currentEpoch].shares;\n  }\n\n  /// @inheritdoc IChiStaking\n  function updateEpoch(uint256 stETHrewards) external onlyRewardController {\n    _updateCumulativeRewardsForToken(epochs[currentEpoch], epochs[currentEpoch - 1], RewardToken.STETH, stETHrewards);\n    _updateEpoch();\n  }\n\n  /// @inheritdoc IStakingWithEpochs\n  function unstake(uint256 amount) public override(IStakingWithEpochs, StakingWithEpochs) {\n    unstake(amount, msg.sender);\n  }\n\n  /// @inheritdoc IStakingWithEpochs\n  function unstake(uint256 amount, address toAddress) public override(IStakingWithEpochs, StakingWithEpochs) {\n    uint256 availableOnLocking = chiLocking.availableChiWithdraw(msg.sender);\n    if (amount <= availableOnLocking) {\n      chiLocking.withdrawChiFromAccountToAddress(msg.sender, toAddress, amount);\n    } else {\n      chiLocking.withdrawChiFromAccountToAddress(msg.sender, toAddress, availableOnLocking);\n      StakingWithEpochs.unstake(amount - availableOnLocking, toAddress);\n    }\n  }\n\n  /// @inheritdoc IChiStaking\n  function lock(uint256 amount, uint256 duration, bool useStakedTokens) external {\n    if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION) {\n      revert InvalidDuration(duration);\n    }\n\n    if (!useStakedTokens) {\n      stakeToken.safeTransferFrom(msg.sender, address(chiLocking), amount);\n      chiLocking.lockChi(msg.sender, amount, duration);\n    } else {\n      unstake(amount, address(chiLocking));\n      chiLocking.lockChi(msg.sender, amount, duration + 1);\n    }\n\n    emit Lock(msg.sender, amount, duration, useStakedTokens);\n  }\n\n  /// @inheritdoc IStaking\n  function claimStETH(address account) external onlyRewardController returns (uint256) {\n    uint256 amount = _claimAndUpdateReward(account, RewardToken.STETH);\n    emit ClaimStETH(account, amount);\n    return amount;\n  }\n\n  /// @inheritdoc IStaking\n  function unclaimedStETHAmount(address account) public view returns (uint256) {\n    return _getCurrentReward(account, RewardToken.STETH);\n  }\n\n  function _calculatingRewards(RewardToken token) internal pure override returns (bool) {\n    if (token == RewardToken.STETH) return true;\n    return false;\n  }\n}\n"
      },
      "contracts/staking/ChiVesting.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"../interfaces/IChiVesting.sol\";\n\n/// @title Contract for vesting chi tokens\n/// @notice This contract holds chi tokens that are vested\n/// @notice This contract hold vested chi tokens, vesting is done on epochs\n/// @dev This contract is upgradeable\ncontract ChiVesting is IChiVesting, OwnableUpgradeable {\n  using SafeERC20 for IERC20;\n\n  uint256 public constant MAX_LOCK_DURATION = 208;\n  IERC20 public chi;\n\n  uint256 public cliffDuration;\n  uint256 public vestingDuration;\n  uint256 public totalLockedChi;\n  uint256 public totalUnlockedChi;\n  uint256 public totalShares;\n  uint256 public currentEpoch;\n  uint256 public totalVotingPower;\n  address public rewardController;\n\n  mapping(address account => bool status) public chiVesters;\n  mapping(address account => VestingData) public vestingData;\n  mapping(uint256 id => EpochData) public epochs;\n\n  modifier onlyRewardController() {\n    if (msg.sender != rewardController) {\n      revert NotRewardController();\n    }\n    _;\n  }\n\n  modifier onlyChiVesters() {\n    if (!chiVesters[msg.sender]) {\n      revert NotChiVester();\n    }\n    _;\n  }\n\n  function initialize(IERC20 _chi, uint256 _cliffDuration, uint256 _vestingDuration) external initializer {\n    __Ownable_init();\n\n    chi = _chi;\n    cliffDuration = _cliffDuration;\n    vestingDuration = _vestingDuration;\n    currentEpoch = 1;\n  }\n\n  /// @inheritdoc IChiVesting\n  function setRewardController(address _rewardController) external onlyOwner {\n    rewardController = _rewardController;\n    emit SetRewardController(_rewardController);\n  }\n\n  /// @inheritdoc IChiVesting\n  function setChiVester(address contractAddress, bool toSet) external onlyOwner {\n    chiVesters[contractAddress] = toSet;\n    emit SetChiVester(contractAddress, toSet);\n  }\n\n  /// @inheritdoc IChiVesting\n  function getLockedChi() public view returns (uint256) {\n    return totalLockedChi;\n  }\n\n  /// @inheritdoc IChiVesting\n  function addVesting(address account, uint256 chiAmount) external onlyChiVesters {\n    if (currentEpoch > cliffDuration) {\n      revert CliffPassed();\n    }\n\n    _updateUnclaimedStETH(account);\n\n    uint256 shares = _getNumberOfShares(chiAmount);\n    VestingData storage vesting = vestingData[account];\n    vesting.shares += shares;\n    vesting.startAmount += chiAmount;\n\n    totalLockedChi += chiAmount;\n    totalShares += shares;\n\n    totalVotingPower += Math.mulDiv(chiAmount, _epochsUntilEnd(), MAX_LOCK_DURATION);\n\n    emit AddVesting(account, chiAmount, shares);\n  }\n\n  /// @inheritdoc IChiVesting\n  function updateEpoch(uint256 chiEmissions, uint256 stETHrewards) external onlyRewardController {\n    EpochData storage epoch = epochs[currentEpoch];\n\n    uint256 stETHRewardPerShare;\n    if (totalShares != 0) {\n      stETHRewardPerShare = Math.mulDiv(stETHrewards, 1e18, totalShares);\n    }\n    epoch.cumulativeStETHRewardPerShare = epochs[currentEpoch - 1].cumulativeStETHRewardPerShare + stETHRewardPerShare;\n\n    uint256 epochsUntilEnd = _epochsUntilEnd();\n\n    totalLockedChi += chiEmissions;\n\n    if (epochsUntilEnd > 0) {\n      totalVotingPower += Math.mulDiv(chiEmissions, _epochsUntilEnd(), MAX_LOCK_DURATION);\n    }\n\n    uint256 amountToUnlock;\n    if (totalLockedChi > 0 && currentEpoch > cliffDuration && epochsUntilEnd > 0) {\n      amountToUnlock = totalLockedChi / _epochsUntilEnd();\n    }\n\n    uint256 amountUnlockedPerShare;\n    if (totalShares > 0) {\n      amountUnlockedPerShare = Math.mulDiv(amountToUnlock, 1e18, totalShares);\n    }\n    epoch.cumulativeUnlockedPerShare = epochs[currentEpoch - 1].cumulativeUnlockedPerShare + amountUnlockedPerShare;\n\n    totalLockedChi -= amountToUnlock;\n    totalUnlockedChi += amountToUnlock;\n\n    if (epochsUntilEnd > 0) {\n      // decrease for unlocked tokens\n      uint256 decreaseVotingPower = Math.mulDiv(amountToUnlock, epochsUntilEnd, MAX_LOCK_DURATION);\n      if (decreaseVotingPower > totalVotingPower) {\n        totalVotingPower = 0;\n      } else {\n        totalVotingPower -= decreaseVotingPower;\n      }\n\n      // regular decrease in voting power because of less time until end of locking\n      totalVotingPower = Math.mulDiv(totalVotingPower, epochsUntilEnd - 1, epochsUntilEnd);\n    }\n\n    currentEpoch++;\n    emit UpdateEpoch(currentEpoch - 1, stETHrewards, totalLockedChi);\n  }\n\n  /// @inheritdoc IChiVesting\n  function withdrawChi(uint256 amount) external {\n    VestingData storage vesting = vestingData[msg.sender];\n\n    _updateAvailabeWithdraw(msg.sender);\n    if (amount > vesting.unlockedChi) {\n      revert UnavailableWithdrawAmount(amount);\n    }\n\n    vesting.unlockedChi -= amount;\n    totalUnlockedChi -= amount;\n\n    chi.safeTransfer(msg.sender, amount);\n\n    emit WithdrawChi(msg.sender, amount);\n  }\n\n  /// @inheritdoc IChiVesting\n  function claimStETH(address account) external onlyRewardController returns (uint256) {\n    _updateUnclaimedStETH(account);\n    uint256 amount = vestingData[account].unclaimedStETH;\n    vestingData[account].unclaimedStETH = 0;\n\n    emit ClaimStETH(account, amount);\n    return amount;\n  }\n\n  /// @inheritdoc IChiVesting\n  function unclaimedStETHAmount(address account) public view returns (uint256) {\n    VestingData storage vesting = vestingData[account];\n    uint256 totalAmount = vesting.unclaimedStETH;\n\n    uint256 rewardPerShare = epochs[currentEpoch - 1].cumulativeStETHRewardPerShare -\n      epochs[vesting.lastClaimedEpoch].cumulativeStETHRewardPerShare;\n    totalAmount += Math.mulDiv(rewardPerShare, vesting.shares, 1e18);\n\n    return totalAmount;\n  }\n\n  /// @inheritdoc IChiVesting\n  function getVotingPower(address account) external view returns (uint256) {\n    if (currentEpoch > cliffDuration + vestingDuration) return 0;\n    VestingData storage vesting = vestingData[account];\n    return vesting.shares != 0 ? Math.mulDiv(totalVotingPower, vesting.shares, totalShares) : 0;\n  }\n\n  /// @inheritdoc IChiVesting\n  function getTotalVotingPower() external view returns (uint256) {\n    return totalVotingPower;\n  }\n\n  /// @inheritdoc IChiVesting\n  function availableChiWithdraw(address account) public view returns (uint256) {\n    VestingData storage vesting = vestingData[account];\n    return vesting.unlockedChi + _availableUnlockFromTo(vesting.lastWithdrawnEpoch, currentEpoch - 1, vesting.shares);\n  }\n\n  // includes available amount in `toEpoch`\n  function _availableUnlockFromTo(uint256 fromEpoch, uint256 toEpoch, uint256 shares) internal view returns (uint256) {\n    uint256 availableWithdrawPerShare = epochs[toEpoch].cumulativeUnlockedPerShare -\n      epochs[fromEpoch].cumulativeUnlockedPerShare;\n    return Math.mulDiv(availableWithdrawPerShare, shares, 1e18);\n  }\n\n  function _getNumberOfShares(uint256 chiAmount) internal view returns (uint256) {\n    if (totalLockedChi == 0) return chiAmount;\n    return Math.mulDiv(chiAmount, totalShares, totalLockedChi);\n  }\n\n  function _updateAvailabeWithdraw(address account) internal {\n    vestingData[account].unlockedChi = availableChiWithdraw(account);\n    vestingData[account].lastWithdrawnEpoch = currentEpoch - 1;\n  }\n\n  function _updateUnclaimedStETH(address account) internal {\n    vestingData[account].unclaimedStETH = unclaimedStETHAmount(account);\n    vestingData[account].lastClaimedEpoch = currentEpoch - 1;\n  }\n\n  function _epochsUntilEnd() internal view returns (uint256) {\n    if (currentEpoch > cliffDuration + vestingDuration + 1) return 0;\n    return cliffDuration + vestingDuration + 1 - currentEpoch;\n  }\n}\n"
      },
      "contracts/staking/LPStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport \"../interfaces/ILPStaking.sol\";\nimport \"../interfaces/IChiLocking.sol\";\nimport \"./StakingWithEpochs.sol\";\n\n/// @title Contract for staking USC/ETH and CHI/ETH LP tokens\n/// @notice Staking is done in epochs\n/// @dev Contract is upgradeable\ncontract LPStaking is ILPStaking, OwnableUpgradeable, StakingWithEpochs {\n  using SafeERC20 for IERC20;\n\n  uint256 public constant MIN_LOCK_DURATION = 4;\n  uint256 public constant MAX_LOCK_DURATION = 208;\n\n  IERC20 public chi;\n  IChiLocking public chiLockingContract;\n\n  uint256 public chiAmountFromEmissions;\n\n  function initialize(\n    IERC20 _chi,\n    IChiLocking _chiLockingContract,\n    IERC20 _token,\n    string memory _name,\n    string memory _symbol\n  ) external initializer {\n    __Ownable_init();\n    __StakingWithEpochs_init(_name, _symbol, _token);\n\n    chi = _chi;\n    chiLockingContract = _chiLockingContract;\n  }\n\n  /// @notice Sets reward controller contract address\n  /// @param _rewardController Reward controller contract address\n  function setRewardController(address _rewardController) external onlyOwner {\n    _setRewardController(_rewardController);\n  }\n\n  /// @notice Updates epoch data\n  /// @param chiEmissions Amount of CHI token incentives emitted in current epoch for USC stakers\n  /// @custom:usage This function should be called from rewardController contract in purpose of updating epoch data\n  function updateEpoch(uint256 chiEmissions, uint256 stETHrewards) external onlyRewardController {\n    EpochData storage epoch = epochs[currentEpoch];\n    EpochData storage prevEpoch = epochs[currentEpoch - 1];\n\n    _updateCumulativeRewardsForToken(epoch, prevEpoch, RewardToken.CHI, chiEmissions);\n    _updateCumulativeRewardsForToken(epoch, prevEpoch, RewardToken.STETH, stETHrewards);\n    _updateEpoch();\n\n    chiAmountFromEmissions += chiEmissions;\n\n    emit UpdateEpoch(currentEpoch - 1, chiEmissions);\n  }\n\n  /// @inheritdoc IStaking\n  function claimStETH(address account) external onlyRewardController returns (uint256) {\n    uint256 amount = _claimAndUpdateReward(account, RewardToken.STETH);\n    emit ClaimStETH(account, amount);\n    return amount;\n  }\n\n  /// @inheritdoc IStaking\n  function getStakedChi() external view returns (uint256) {\n    return chiAmountFromEmissions;\n  }\n\n  /// @inheritdoc IStaking\n  function unclaimedStETHAmount(address account) public view returns (uint256) {\n    return _getCurrentReward(account, RewardToken.STETH);\n  }\n\n  /// @notice Locks CHI tokens that user earned from incentives for given duration\n  /// @param duration Locking duration in epochs\n  function lockChi(uint256 duration) external {\n    if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION) {\n      revert InvalidDuration(duration);\n    }\n\n    uint256 amount = _claimAndUpdateReward(msg.sender, RewardToken.CHI);\n    chiLockingContract.lockChi(msg.sender, amount, duration);\n\n    chiAmountFromEmissions -= amount;\n\n    chi.safeTransfer(address(chiLockingContract), amount);\n\n    emit LockChi(msg.sender, amount, duration);\n  }\n\n  function _calculatingRewards(RewardToken token) internal pure override returns (bool) {\n    if (token == RewardToken.CHI) return true;\n    if (token == RewardToken.STETH) return true;\n    return false;\n  }\n}\n"
      },
      "contracts/staking/RewardController.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"../interfaces/IRewardController.sol\";\nimport \"../interfaces/IMintableERC20.sol\";\nimport \"../interfaces/IBurnableERC20.sol\";\nimport \"../interfaces/IArbitrage.sol\";\nimport \"../interfaces/IReserveHolder.sol\";\nimport \"../interfaces/IStaking.sol\";\nimport \"../interfaces/IUSCStaking.sol\";\nimport \"../interfaces/IChiStaking.sol\";\nimport \"../interfaces/IChiLocking.sol\";\nimport \"../interfaces/IChiVesting.sol\";\nimport \"../interfaces/ILPStaking.sol\";\n\n/// @title Contract for managing rewards\n/// @notice This contract manages rewards for chi lockers, chi stakers, chi vesters and usc stakers\n/// @notice This contract holds chi incentives for all contracts and distributes then at the end of epoch\n/// @dev This contract is upgradeable\ncontract RewardController is IRewardController, OwnableUpgradeable {\n  using SafeERC20 for IERC20;\n\n  uint256 public constant EPOCH_DURATION = 1 weeks;\n\n  IERC20 public chi;\n  IERC20 public usc;\n  IReserveHolder public reserveHolder;\n  IArbitrage public arbitrager;\n  IUSCStaking public uscStaking;\n  IChiStaking public chiStaking;\n  IChiLocking public chiLocking;\n  IChiVesting public chiVesting;\n  ILPStaking public uscEthLPStaking;\n  ILPStaking public chiEthLPStaking;\n\n  uint256 public currentEpoch;\n  uint256 public firstEpochTimestamp;\n  uint256 public chiIncentivesForChiLocking;\n  uint256 public chiIncentivesForUscStaking;\n  uint256 public chiIncentivesForUscEthLPStaking;\n  uint256 public chiIncentivesForChiEthLPStaking;\n\n  mapping(uint256 id => EpochData) public epochs;\n\n  modifier onlyArbitrager() {\n    if (msg.sender != address(arbitrager)) {\n      revert NotArbitrager();\n    }\n    _;\n  }\n\n  function initialize(\n    IERC20 _chi,\n    IERC20 _usc,\n    IReserveHolder _reserveHolder,\n    IUSCStaking _uscStaking,\n    IChiStaking _chiStaking,\n    IChiLocking _chiLocking,\n    IChiVesting _chiVesting,\n    ILPStaking _uscEthLPStaking,\n    ILPStaking _chiEthLPStaking,\n    uint256 _firstEpochTimestamp\n  ) external initializer {\n    __Ownable_init();\n    chi = _chi;\n    usc = _usc;\n    reserveHolder = _reserveHolder;\n    uscStaking = _uscStaking;\n    chiStaking = _chiStaking;\n    chiLocking = _chiLocking;\n    chiVesting = _chiVesting;\n    uscEthLPStaking = _uscEthLPStaking;\n    chiEthLPStaking = _chiEthLPStaking;\n    firstEpochTimestamp = _firstEpochTimestamp;\n    currentEpoch = 1;\n  }\n\n  /// @inheritdoc IRewardController\n  function setChiIncentivesForChiLocking(uint256 _chiIncentivesForChiLocking) external onlyOwner {\n    chiIncentivesForChiLocking = _chiIncentivesForChiLocking;\n  }\n\n  /// @inheritdoc IRewardController\n  function setChiIncentivesForUscStaking(uint256 _chiIncentivesForUscStaking) external onlyOwner {\n    chiIncentivesForUscStaking = _chiIncentivesForUscStaking;\n  }\n\n  /// @inheritdoc IRewardController\n  function setChiIncentivesForUscEthLPStaking(uint256 _chiIncentivesForUscEthLPStaking) external onlyOwner {\n    chiIncentivesForUscEthLPStaking = _chiIncentivesForUscEthLPStaking;\n  }\n\n  /// @inheritdoc IRewardController\n  function setChiIncentivesForChiEthLPStaking(uint256 _chiIncentivesForChiEthLPStaking) external onlyOwner {\n    chiIncentivesForChiEthLPStaking = _chiIncentivesForChiEthLPStaking;\n  }\n\n  /// @inheritdoc IRewardController\n  function setArbitrager(IArbitrage _arbitrager) external onlyOwner {\n    arbitrager = _arbitrager;\n    emit SetArbitrager(address(_arbitrager));\n  }\n\n  /// @inheritdoc IRewardController\n  function rewardUSC(uint256 amount) external onlyArbitrager {\n    if (amount == 0) {\n      revert ZeroAmount();\n    }\n\n    usc.safeTransferFrom(msg.sender, address(this), amount);\n    epochs[currentEpoch].totalUscReward += amount;\n\n    emit RewardUSC(msg.sender, amount);\n  }\n\n  /// @inheritdoc IRewardController\n  function updateEpoch() public {\n    if (block.timestamp < firstEpochTimestamp + currentEpoch * EPOCH_DURATION) {\n      revert EpochNotFinished();\n    }\n\n    StETHRewards memory stEthRewards = _updateAndGetStETHRewards();\n    ChiIncentives memory chiIncentives = _updateAndGetChiIncentives();\n\n    _updateEpochsInSubcontracts(stEthRewards, chiIncentives, epochs[currentEpoch].totalUscReward);\n\n    usc.safeTransfer(address(uscStaking), epochs[currentEpoch].totalUscReward);\n    chi.safeTransfer(address(uscStaking), chiIncentives.uscStakingChiIncentives);\n    chi.safeTransfer(address(chiLocking), chiIncentives.chiLockingChiIncentives);\n    chi.safeTransfer(address(chiVesting), chiIncentives.chiVestingChiIncentives);\n    chi.safeTransfer(address(uscEthLPStaking), chiIncentivesForUscEthLPStaking);\n    chi.safeTransfer(address(chiEthLPStaking), chiIncentivesForChiEthLPStaking);\n\n    currentEpoch++;\n\n    uint256 totalStEthRewards = stEthRewards.uscStakingStEthReward +\n      stEthRewards.chiStakingStEthReward +\n      stEthRewards.chiLockingStEthReward +\n      stEthRewards.chiVestingStEthReward;\n    uint256 totalChiIncentives = chiIncentives.uscStakingChiIncentives +\n      chiIncentives.chiLockingChiIncentives +\n      chiIncentives.chiVestingChiIncentives +\n      chiIncentivesForUscEthLPStaking +\n      chiIncentivesForChiEthLPStaking;\n    emit UpdateEpoch(currentEpoch - 1, totalStEthRewards, totalChiIncentives);\n  }\n\n  /// @inheritdoc IRewardController\n  function claimStEth() external {\n    uint256 totalAmount;\n    totalAmount += IStaking(address(uscStaking)).claimStETH(msg.sender);\n    totalAmount += IStaking(address(chiStaking)).claimStETH(msg.sender);\n    totalAmount += IStaking(address(chiLocking)).claimStETH(msg.sender);\n    totalAmount += IStaking(address(chiVesting)).claimStETH(msg.sender);\n    totalAmount += IStaking(address(uscEthLPStaking)).claimStETH(msg.sender);\n    totalAmount += IStaking(address(chiEthLPStaking)).claimStETH(msg.sender);\n\n    reserveHolder.claimRewards(msg.sender, totalAmount);\n\n    emit ClaimStEth(msg.sender, totalAmount);\n  }\n\n  /// @inheritdoc IRewardController\n  function unclaimedStETHAmount(address account) external view returns (uint256) {\n    uint256 totalAmount;\n    totalAmount += IStaking(address(uscStaking)).unclaimedStETHAmount(account);\n    totalAmount += IStaking(address(chiStaking)).unclaimedStETHAmount(account);\n    totalAmount += IStaking(address(chiLocking)).unclaimedStETHAmount(account);\n    totalAmount += IStaking(address(chiVesting)).unclaimedStETHAmount(account);\n    totalAmount += IStaking(address(uscEthLPStaking)).unclaimedStETHAmount(account);\n    totalAmount += IStaking(address(chiEthLPStaking)).unclaimedStETHAmount(account);\n\n    return totalAmount;\n  }\n\n  function _updateAndGetChiIncentives() internal view returns (ChiIncentives memory) {\n    uint256 chiLockingLocked = chiLocking.getLockedChi();\n    uint256 chiVestingLocked = chiVesting.getLockedChi();\n    uint256 totalLockedChi = chiLockingLocked + chiVestingLocked;\n\n    uint256 chiLockingChiIncentives;\n    uint256 chiVestingChiIncentives;\n    if (totalLockedChi != 0) {\n      chiLockingChiIncentives = Math.mulDiv(chiLockingLocked, chiIncentivesForChiLocking, totalLockedChi);\n      chiVestingChiIncentives = Math.mulDiv(chiVestingLocked, chiIncentivesForChiLocking, totalLockedChi);\n    }\n\n    return\n      ChiIncentives({\n        uscStakingChiIncentives: chiIncentivesForUscStaking,\n        chiLockingChiIncentives: chiLockingChiIncentives,\n        chiVestingChiIncentives: chiVestingChiIncentives\n      });\n  }\n\n  function _updateEpochsInSubcontracts(\n    StETHRewards memory stEthRewards,\n    ChiIncentives memory chiIncentives,\n    uint256 uscReward\n  ) internal {\n    uscStaking.updateEpoch(chiIncentives.uscStakingChiIncentives, uscReward, stEthRewards.uscStakingStEthReward);\n    chiStaking.updateEpoch(stEthRewards.chiStakingStEthReward);\n    chiLocking.updateEpoch(chiIncentives.chiLockingChiIncentives, stEthRewards.chiLockingStEthReward);\n    chiVesting.updateEpoch(chiIncentives.chiVestingChiIncentives, stEthRewards.chiVestingStEthReward);\n    uscEthLPStaking.updateEpoch(chiIncentivesForUscEthLPStaking, stEthRewards.uscEthLPStakingStEthReward);\n    chiEthLPStaking.updateEpoch(chiIncentivesForChiEthLPStaking, stEthRewards.chiEthLPStakingStEthReward);\n  }\n\n  function _updateAndGetStETHRewards() internal returns (StETHRewards memory) {\n    epochs[currentEpoch].reserveHolderTotalRewards = reserveHolder.getCumulativeRewards();\n    uint256 stETHEpochrewards = epochs[currentEpoch].reserveHolderTotalRewards -\n      epochs[currentEpoch - 1].reserveHolderTotalRewards;\n\n    uint256 uscStakedChi = uscStaking.getStakedChi();\n    uint256 chiStakedChi = chiStaking.getStakedChi();\n    uint256 chiLockedChi = chiLocking.getStakedChi();\n    uint256 chiVestingChi = chiVesting.getLockedChi();\n    uint256 uscEthLPStakingChi = uscEthLPStaking.getStakedChi();\n    uint256 chiEthLPStakingChi = chiEthLPStaking.getStakedChi();\n    uint256 totalChi = uscStakedChi +\n      chiStakedChi +\n      chiLockedChi +\n      chiVestingChi +\n      uscEthLPStakingChi +\n      chiEthLPStakingChi;\n\n    uint256 uscStakingStEthReward;\n    uint256 chiStakingStEthReward;\n    uint256 chiLockingStEthReward;\n    uint256 chiVestingStEthReward;\n    uint256 uscEthLPStakingStEthReward;\n    uint256 chiEthLPStakingStEthReward;\n    if (totalChi != 0) {\n      uscStakingStEthReward = Math.mulDiv(uscStakedChi, stETHEpochrewards, totalChi);\n      chiStakingStEthReward = Math.mulDiv(chiStakedChi, stETHEpochrewards, totalChi);\n      chiLockingStEthReward = Math.mulDiv(chiLockedChi, stETHEpochrewards, totalChi);\n      chiVestingStEthReward = Math.mulDiv(chiVestingChi, stETHEpochrewards, totalChi);\n      uscEthLPStakingStEthReward = Math.mulDiv(uscEthLPStakingChi, stETHEpochrewards, totalChi);\n      chiEthLPStakingStEthReward = Math.mulDiv(chiEthLPStakingStEthReward, stETHEpochrewards, totalChi);\n    }\n\n    return\n      StETHRewards({\n        uscStakingStEthReward: uscStakingStEthReward,\n        chiStakingStEthReward: chiStakingStEthReward,\n        chiLockingStEthReward: chiLockingStEthReward,\n        chiVestingStEthReward: chiVestingStEthReward,\n        uscEthLPStakingStEthReward: uscEthLPStakingStEthReward,\n        chiEthLPStakingStEthReward: chiEthLPStakingStEthReward\n      });\n  }\n}\n"
      },
      "contracts/staking/StakingWithEpochs.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"../interfaces/IStakingWithEpochs.sol\";\nimport \"../interfaces/IMintableERC20.sol\";\nimport \"../interfaces/IBurnableERC20.sol\";\nimport \"../interfaces/IArbitrage.sol\";\n\n/// @title Contract for staking tokens\n/// @notice Staking logic is placed inside this contract\n/// @dev This contract is abstract and CHIStaking and USCStaking should inherit from him\nabstract contract StakingWithEpochs is IStakingWithEpochs, ERC20Upgradeable {\n  using SafeERC20 for IERC20;\n\n  IERC20 public stakeToken;\n\n  address public rewardController;\n  uint256 public currentEpoch;\n\n  mapping(address account => StakeData) public stakes;\n  mapping(uint256 id => EpochData) public epochs;\n\n  modifier onlyRewardController() {\n    if (msg.sender != rewardController) {\n      revert NotRewardController();\n    }\n    _;\n  }\n\n  function __StakingWithEpochs_init(\n    string memory _name,\n    string memory _symbol,\n    IERC20 _stakeToken\n  ) internal onlyInitializing {\n    __ERC20_init(_name, _symbol);\n    stakeToken = _stakeToken;\n    currentEpoch = 1;\n  }\n\n  /// @inheritdoc IStakingWithEpochs\n  function getUnclaimedRewards(address account, RewardToken token) external view returns (uint256) {\n    return stakes[account].unclaimedRewards[token];\n  }\n\n  /// @inheritdoc IStakingWithEpochs\n  function getCumulativeRewardsPerShare(uint256 epoch, RewardToken token) external view returns (uint256) {\n    return epochs[epoch].cumulativeRewardsPerShare[token];\n  }\n\n  function unclaimedStChiAmount(address account) public view returns (uint256) {\n    return _getCurrentReward(account, RewardToken.CHI);\n  }\n\n  /// @inheritdoc IStakingWithEpochs\n  function stake(uint256 amount) external {\n    if (amount == 0) {\n      revert ZeroAmount();\n    }\n\n    stakeToken.safeTransferFrom(msg.sender, address(this), amount);\n    _updateUnclaimedRewards(msg.sender);\n\n    stakes[msg.sender].addSharesNextEpoch += amount;\n    epochs[currentEpoch + 1].shares += amount;\n    _mint(msg.sender, amount);\n\n    emit Stake(msg.sender, amount);\n  }\n\n  /// @inheritdoc IStakingWithEpochs\n  function unstake(uint256 amount) public virtual {\n    unstake(amount, msg.sender);\n  }\n\n  /// @inheritdoc IStakingWithEpochs\n  function unstake(uint256 amount, address toAddress) public virtual {\n    if (amount == 0) {\n      revert ZeroAmount();\n    }\n\n    StakeData storage stakeData = stakes[msg.sender];\n\n    uint256 addSharesNextEpoch = stakeData.addSharesNextEpoch;\n    if (stakeData.shares + addSharesNextEpoch < amount) {\n      revert AmountBelowStakedBalance(stakeData.shares + addSharesNextEpoch, amount);\n    }\n\n    _updateUnclaimedRewards(msg.sender);\n    addSharesNextEpoch = stakeData.addSharesNextEpoch;\n\n    if (addSharesNextEpoch > amount) {\n      stakeData.addSharesNextEpoch -= amount;\n      epochs[currentEpoch + 1].shares -= amount;\n    } else {\n      uint256 fromCurrentShares = amount - addSharesNextEpoch;\n      stakeData.shares -= fromCurrentShares;\n      epochs[currentEpoch].shares -= fromCurrentShares;\n      epochs[currentEpoch + 1].shares -= addSharesNextEpoch;\n      stakeData.addSharesNextEpoch = 0;\n    }\n\n    stakeToken.safeTransfer(toAddress, amount);\n    _burn(msg.sender, amount);\n\n    emit Unstake(msg.sender, toAddress, amount);\n  }\n\n  function _updateUnclaimedRewards(address account) internal {\n    StakeData storage stakeData = stakes[account];\n\n    if (currentEpoch == stakeData.lastUpdatedEpoch) return;\n\n    uint256 fromEpoch = stakeData.lastUpdatedEpoch > 0 ? stakeData.lastUpdatedEpoch - 1 : 0;\n    _updateUnclaimedRewardsFromTo(stakeData, epochs[fromEpoch], epochs[currentEpoch - 1], stakeData.shares);\n\n    if (stakeData.addSharesNextEpoch > 0) {\n      _updateUnclaimedRewardsFromTo(\n        stakeData,\n        epochs[stakeData.lastUpdatedEpoch],\n        epochs[currentEpoch - 1],\n        stakeData.addSharesNextEpoch\n      );\n      stakeData.shares += stakeData.addSharesNextEpoch;\n      stakeData.addSharesNextEpoch = 0;\n    }\n\n    stakeData.lastUpdatedEpoch = currentEpoch;\n  }\n\n  function _updateUnclaimedRewardsFromTo(\n    StakeData storage stakeData,\n    EpochData storage fromEpoch,\n    EpochData storage toEpoch,\n    uint256 shares\n  ) internal {\n    for (uint8 i = 0; i <= uint8(type(RewardToken).max); i++) {\n      RewardToken token = RewardToken(i);\n      if (_calculatingRewards(token)) {\n        stakeData.unclaimedRewards[token] += Math.mulDiv(\n          toEpoch.cumulativeRewardsPerShare[token] - fromEpoch.cumulativeRewardsPerShare[token],\n          shares,\n          1e18\n        );\n      }\n    }\n  }\n\n  function _updateCumulativeRewardsForToken(\n    EpochData storage epoch,\n    EpochData storage prevEpoch,\n    RewardToken token,\n    uint256 amount\n  ) internal {\n    if (epoch.shares == 0) return;\n\n    epoch.cumulativeRewardsPerShare[token] =\n      prevEpoch.cumulativeRewardsPerShare[token] +\n      Math.mulDiv(amount, 1e18, epoch.shares);\n  }\n\n  function _updateEpoch() internal {\n    epochs[currentEpoch + 1].shares += epochs[currentEpoch].shares;\n    currentEpoch++;\n  }\n\n  function _claimAndUpdateReward(address account, RewardToken token) internal returns (uint256) {\n    _updateUnclaimedRewards(account);\n    uint256 amount = stakes[account].unclaimedRewards[token];\n    stakes[account].unclaimedRewards[token] = 0;\n    return amount;\n  }\n\n  function _getCurrentReward(address account, RewardToken token) public view returns (uint256) {\n    StakeData storage stakeData = stakes[account];\n    uint256 totalAmount = stakeData.unclaimedRewards[token];\n\n    if (currentEpoch == stakeData.lastUpdatedEpoch) return totalAmount;\n    totalAmount += Math.mulDiv(\n      epochs[currentEpoch - 1].cumulativeRewardsPerShare[token] -\n        epochs[stakeData.lastUpdatedEpoch - 1].cumulativeRewardsPerShare[token],\n      stakeData.shares,\n      1e18\n    );\n\n    if (stakeData.addSharesNextEpoch > 0 && currentEpoch > stakeData.lastUpdatedEpoch + 1) {\n      totalAmount += Math.mulDiv(\n        epochs[currentEpoch - 1].cumulativeRewardsPerShare[token] -\n          epochs[stakeData.lastUpdatedEpoch].cumulativeRewardsPerShare[token],\n        stakeData.addSharesNextEpoch,\n        1e18\n      );\n    }\n\n    return totalAmount;\n  }\n\n  function _setRewardController(address _rewardController) internal {\n    rewardController = _rewardController;\n  }\n\n  function _calculatingRewards(RewardToken token) internal virtual returns (bool);\n\n  uint256[50] private __gap;\n}\n"
      },
      "contracts/staking/USCStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport \"../interfaces/IUSCStaking.sol\";\nimport \"../interfaces/IArbitrage.sol\";\nimport \"../interfaces/IChiLocking.sol\";\nimport \"./StakingWithEpochs.sol\";\n\n/// @title Contract for staking USC tokens\n/// @notice Staking is done in epochs\n/// @dev Contract is upgradeable\ncontract USCStaking is IUSCStaking, OwnableUpgradeable, StakingWithEpochs {\n  using SafeERC20 for IERC20;\n\n  uint256 public constant MIN_LOCK_DURATION = 4; // 4 epochs = 4 weeks = 1 month\n  uint256 public constant MAX_LOCK_DURATION = 208; // 208 epochs = 208 weeks = 4 years\n\n  IERC20 public chi;\n  IChiLocking public chiLockingContract;\n\n  uint256 public chiAmountFromEmissions;\n\n  function initialize(IERC20 _usc, IERC20 _chi, IChiLocking _chiLockingContract) external initializer {\n    __Ownable_init();\n    __StakingWithEpochs_init(\"Staked USC\", \"stUSC\", _usc);\n    chi = _chi;\n    chiLockingContract = _chiLockingContract;\n  }\n\n  /// @inheritdoc IStaking\n  function setRewardController(address _rewardController) external onlyOwner {\n    _setRewardController(_rewardController);\n  }\n\n  /// @inheritdoc IUSCStaking\n  function updateEpoch(uint256 chiEmissions, uint256 uscRewards, uint256 stETHrewards) external onlyRewardController {\n    EpochData storage epoch = epochs[currentEpoch];\n    EpochData storage prevEpoch = epochs[currentEpoch - 1];\n\n    _updateCumulativeRewardsForToken(epoch, prevEpoch, RewardToken.CHI, chiEmissions);\n    _updateCumulativeRewardsForToken(epoch, prevEpoch, RewardToken.USC, uscRewards);\n    _updateCumulativeRewardsForToken(epoch, prevEpoch, RewardToken.STETH, stETHrewards);\n    _updateEpoch();\n\n    chiAmountFromEmissions += chiEmissions;\n\n    emit UpdateEpoch(currentEpoch - 1, chiEmissions, uscRewards, stETHrewards);\n  }\n\n  /// @inheritdoc IUSCStaking\n  function lockChi(uint256 duration) external {\n    if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION) {\n      revert InvalidDuration(duration);\n    }\n\n    uint256 amount = _claimAndUpdateReward(msg.sender, RewardToken.CHI);\n    chiLockingContract.lockChi(msg.sender, amount, duration);\n\n    chiAmountFromEmissions -= amount;\n    chi.safeTransfer(address(chiLockingContract), amount);\n\n    emit LockChi(msg.sender, amount, duration);\n  }\n\n  /// @inheritdoc IUSCStaking\n  function claimUSCRewards() external {\n    uint256 amount = _claimAndUpdateReward(msg.sender, RewardToken.USC);\n    stakeToken.safeTransfer(msg.sender, amount);\n\n    emit ClaimUSCRewards(msg.sender, amount);\n  }\n\n  /// @inheritdoc IStaking\n  function claimStETH(address account) external onlyRewardController returns (uint256) {\n    uint256 amount = _claimAndUpdateReward(account, RewardToken.STETH);\n    emit ClaimStETH(account, amount);\n    return amount;\n  }\n\n  /// @inheritdoc IStaking\n  function unclaimedStETHAmount(address account) public view returns (uint256) {\n    return _getCurrentReward(account, RewardToken.STETH);\n  }\n\n  /// @inheritdoc IStaking\n  function getStakedChi() external view returns (uint256) {\n    return chiAmountFromEmissions;\n  }\n\n  function _calculatingRewards(RewardToken token) internal pure override returns (bool) {\n    if (token == RewardToken.USC) return true;\n    if (token == RewardToken.CHI) return true;\n    if (token == RewardToken.STETH) return true;\n    return false;\n  }\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 1000
      },
      "viaIR": true,
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}