creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.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 anymore. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby removing 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.7.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 * ```\n * contract MyToken is ERC20Upgradeable {\n *     function initialize() initializer public {\n *         __ERC20_init(\"MyToken\", \"MTK\");\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. Equivalent to `reinitializer(1)`.\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     * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original\n     * initialization step. This is essential to configure modules that are added through upgrades and that require\n     * initialization.\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    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    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"
      },
      "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract PausableUpgradeable is Initializable, ContextUpgradeable {\n    /**\n     * @dev Emitted when the pause is triggered by `account`.\n     */\n    event Paused(address account);\n\n    /**\n     * @dev Emitted when the pause is lifted by `account`.\n     */\n    event Unpaused(address account);\n\n    bool private _paused;\n\n    /**\n     * @dev Initializes the contract in unpaused state.\n     */\n    function __Pausable_init() internal onlyInitializing {\n        __Pausable_init_unchained();\n    }\n\n    function __Pausable_init_unchained() internal onlyInitializing {\n        _paused = false;\n    }\n\n    /**\n     * @dev Modifier to make a function callable only when the contract is not paused.\n     *\n     * Requirements:\n     *\n     * - The contract must not be paused.\n     */\n    modifier whenNotPaused() {\n        _requireNotPaused();\n        _;\n    }\n\n    /**\n     * @dev Modifier to make a function callable only when the contract is paused.\n     *\n     * Requirements:\n     *\n     * - The contract must be paused.\n     */\n    modifier whenPaused() {\n        _requirePaused();\n        _;\n    }\n\n    /**\n     * @dev Returns true if the contract is paused, and false otherwise.\n     */\n    function paused() public view virtual returns (bool) {\n        return _paused;\n    }\n\n    /**\n     * @dev Throws if the contract is paused.\n     */\n    function _requireNotPaused() internal view virtual {\n        require(!paused(), \"Pausable: paused\");\n    }\n\n    /**\n     * @dev Throws if the contract is not paused.\n     */\n    function _requirePaused() internal view virtual {\n        require(paused(), \"Pausable: not paused\");\n    }\n\n    /**\n     * @dev Triggers stopped state.\n     *\n     * Requirements:\n     *\n     * - The contract must not be paused.\n     */\n    function _pause() internal virtual whenNotPaused {\n        _paused = true;\n        emit Paused(_msgSender());\n    }\n\n    /**\n     * @dev Returns to normal state.\n     *\n     * Requirements:\n     *\n     * - The contract must be paused.\n     */\n    function _unpause() internal virtual whenPaused {\n        _paused = false;\n        emit Unpaused(_msgSender());\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/security/ReentrancyGuardUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuardUpgradeable is Initializable {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot's contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler's defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction's gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant _NOT_ENTERED = 1;\n    uint256 private constant _ENTERED = 2;\n\n    uint256 private _status;\n\n    function __ReentrancyGuard_init() internal onlyInitializing {\n        __ReentrancyGuard_init_unchained();\n    }\n\n    function __ReentrancyGuard_init_unchained() internal onlyInitializing {\n        _status = _NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and making it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        // On the first call to nonReentrant, _notEntered will be true\n        require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n        // Any calls to nonReentrant after this point will fail\n        _status = _ENTERED;\n\n        _;\n\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _status = _NOT_ENTERED;\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/utils/AddressUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.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     *\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://diligence.consensys.net/posts/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.5.11/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 functionCall(target, data, \"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(\n        address target,\n        bytes memory data,\n        uint256 value\n    ) 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        require(isContract(target), \"Address: call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResult(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        require(isContract(target), \"Address: static call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason 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            // 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}\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/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.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 Ownable is Context {\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    constructor() {\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 anymore. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby removing 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"
      },
      "@openzeppelin/contracts/token/ERC20/ERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.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.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\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 ERC20 is Context, IERC20, IERC20Metadata {\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     * The default value of {decimals} is 18. To select a different value for\n     * {decimals} you should overload it.\n     *\n     * All two of these values are immutable: they can only be set once during\n     * construction.\n     */\n    constructor(string memory name_, string memory symbol_) {\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 value {ERC20} uses, unless this function is\n     * 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(\n        address from,\n        address to,\n        uint256 amount\n    ) 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(\n        address from,\n        address to,\n        uint256 amount\n    ) 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        }\n        _balances[to] += amount;\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        _balances[account] += amount;\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        }\n        _totalSupply -= amount;\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(\n        address owner,\n        address spender,\n        uint256 amount\n    ) 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(\n        address owner,\n        address spender,\n        uint256 amount\n    ) 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(\n        address from,\n        address to,\n        uint256 amount\n    ) 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(\n        address from,\n        address to,\n        uint256 amount\n    ) internal virtual {}\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-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/extensions/IERC20Metadata.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 \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\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/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.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(\n        address from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/draft-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    function safeTransfer(\n        IERC20 token,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    function safeTransferFrom(\n        IERC20 token,\n        address from,\n        address to,\n        uint256 value\n    ) 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(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) 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    function safeIncreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        uint256 newAllowance = token.allowance(address(this), spender) + value;\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    function safeDecreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            uint256 newAllowance = oldAllowance - value;\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n        }\n    }\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        if (returndata.length > 0) {\n            // Return data is optional\n            require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.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     *\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://diligence.consensys.net/posts/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.5.11/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 functionCall(target, data, \"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(\n        address target,\n        bytes memory data,\n        uint256 value\n    ) 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        require(isContract(target), \"Address: call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResult(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        require(isContract(target), \"Address: static call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResult(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        require(isContract(target), \"Address: delegate call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason 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            // 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}\n"
      },
      "@openzeppelin/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\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 Context {\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"
      },
      "contracts/interfaces/IBaseRewardPool.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\ninterface IBaseRewardPool {\n    function stakingDecimals() external view returns (uint256);\n\n    function totalStaked() external view returns (uint256);\n\n    function balanceOf(address account) external view returns (uint256);\n\n    function rewardPerToken(address token) external view returns (uint256);\n\n    function rewardTokenInfos()\n        external\n        view\n        returns\n        (\n            address[] memory bonusTokenAddresses,\n            string[] memory bonusTokenSymbols\n        );\n\n    function earned(address account, address token)\n        external\n        view\n        returns (uint256);\n\n    function allEarned(address account)\n        external\n        view\n        returns (uint256[] memory pendingBonusRewards);\n\n    function queueNewRewards(uint256 _rewards, address token)\n        external\n        returns (bool);\n\n    function getReward(address _account, address _receiver) external returns (bool);\n\n    function getRewards(address _account, address _receiver, address[] memory _rewardTokens) external;\n\n    function updateFor(address account) external;\n\n    function updateRewardQueuer(address _rewardManager, bool _allowed) external;\n}"
      },
      "contracts/interfaces/IBribeRewardDistributor.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\ninterface IBribeRewardDistributor {\n    struct Claimable {\n        address token;\n        uint256 amount;\n    }\n\n    struct Claim {\n        address token;\n        address account;\n        uint256 amount;\n        bytes32[] merkleProof;\n    }\n\n    function getClaimable(Claim[] calldata _claims) external view returns(Claimable[] memory);\n\n    function claim(Claim[] calldata _claims) external;\n}"
      },
      "contracts/interfaces/IConvertor.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\ninterface IConvertor {\n    function convert(address _for, uint256 _amount, uint256 _mode) external;\n\n    function convertFor(\n        uint256 _amountIn,\n        uint256 _convertRatio,\n        uint256 _minRec,\n        address _for,\n        uint256 _mode\n    ) external;\n\n    function smartConvertFor(uint256 _amountIn, uint256 _mode, address _for) external returns (uint256 obtainedmWomAmount);\n\n    function mPendleSV() external returns (address);\n\n    function mPendleConvertor() external returns (address);\n}"
      },
      "contracts/interfaces/IETHZapper.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IETHZapper {\n    function swapExactTokensToETH(\n        address tokenIn,\n        uint tokenAmountIn,\n        uint256 _amountOutMin,\n        address amountReciever\n    ) external;\n}\n"
      },
      "contracts/interfaces/IMasterPenpie.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity =0.8.19;\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport \"./IBribeRewardDistributor.sol\";\n\ninterface IMasterPenpie {\n    function poolLength() external view returns (uint256);\n\n    function setPoolManagerStatus(address _address, bool _bool) external;\n\n    function add(uint256 _allocPoint, address _stakingTokenToken, address _receiptToken, address _rewarder) external;\n\n    function set(address _stakingToken, uint256 _allocPoint, address _helper,\n        address _rewarder, bool _helperNeedsHarvest) external;\n\n    function createRewarder(address _stakingTokenToken, address mainRewardToken) external\n        returns (address);\n\n    // View function to see pending GMPs on frontend.\n    function getPoolInfo(address token) external view\n        returns (\n            uint256 emission,\n            uint256 allocpoint,\n            uint256 sizeOfPool,\n            uint256 totalPoint\n        );\n\n    function pendingTokens(address _stakingToken, address _user, address token) external view\n        returns (\n            uint256 _pendingGMP,\n            address _bonusTokenAddress,\n            string memory _bonusTokenSymbol,\n            uint256 _pendingBonusToken\n        );\n    \n    function allPendingTokensWithBribe(\n        address _stakingToken,\n        address _user,\n        IBribeRewardDistributor.Claim[] calldata _proof\n    )\n        external\n        view\n        returns (\n            uint256 pendingPenpie,\n            address[] memory bonusTokenAddresses,\n            string[] memory bonusTokenSymbols,\n            uint256[] memory pendingBonusRewards\n        );\n\n    function allPendingTokens(address _stakingToken, address _user) external view\n        returns (\n            uint256 pendingPenpie,\n            address[] memory bonusTokenAddresses,\n            string[] memory bonusTokenSymbols,\n            uint256[] memory pendingBonusRewards\n        );\n\n    function massUpdatePools() external;\n\n    function updatePool(address _stakingToken) external;\n\n    function deposit(address _stakingToken, uint256 _amount) external;\n\n    function depositFor(address _stakingToken, address _for, uint256 _amount) external;\n\n    function withdraw(address _stakingToken, uint256 _amount) external;\n\n    function beforeReceiptTokenTransfer(address _from, address _to, uint256 _amount) external;\n\n    function afterReceiptTokenTransfer(address _from, address _to, uint256 _amount) external;\n\n    function depositVlPenpieFor(uint256 _amount, address sender) external;\n\n    function withdrawVlPenpieFor(uint256 _amount, address sender) external;\n\n    function depositMPendleSVFor(uint256 _amount, address sender) external;\n\n    function withdrawMPendleSVFor(uint256 _amount, address sender) external;    \n\n    function multiclaimFor(address[] calldata _stakingTokens, address[][] calldata _rewardTokens, address user_address) external;\n\n    function multiclaimOnBehalf(address[] memory _stakingTokens, address[][] calldata _rewardTokens, address user_address, bool _isClaimPNP) external;\n\n    function multiclaim(address[] calldata _stakingTokens) external;\n\n    function emergencyWithdraw(address _stakingToken, address sender) external;\n\n    function updateEmissionRate(uint256 _gmpPerSec) external;\n\n    function stakingInfo(address _stakingToken, address _user)\n        external\n        view\n        returns (uint256 depositAmount, uint256 availableAmount);\n    \n    function totalTokenStaked(address _stakingToken) external view returns (uint256);\n\n    function getRewarder(address _stakingToken) external view returns (address rewarder);\n\n}"
      },
      "contracts/interfaces/IMintableERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity =0.8.19;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IMintableERC20 {\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 `recipient`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address recipient, uint256 amount)\n        external\n        returns (bool);\n\n    function symbol() external view returns (string memory);\n\n    function decimals() external view returns (uint8);\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)\n        external\n        view\n        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 `sender` to `recipient` 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(\n        address sender,\n        address recipient,\n        uint256 amount\n    ) external returns (bool);\n\n    function mint(address, uint256) external;\n    function faucet(uint256) external;\n\n    function burn(address, uint256) external;\n\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(\n        address indexed owner,\n        address indexed spender,\n        uint256 value\n    );\n}"
      },
      "contracts/interfaces/IPendleStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport \"../libraries/MarketApproxLib.sol\";\nimport \"../libraries/ActionBaseMintRedeem.sol\";\n\ninterface IPendleStaking {\n\n    function WETH() external view returns (address);\n\n    function convertPendle(uint256 amount, uint256[] calldata chainid) external payable returns (uint256);\n\n    function vote(address[] calldata _pools, uint64[] calldata _weights) external;\n\n    function depositMarket(address _market, address _for, address _from, uint256 _amount) external;\n\n    function withdrawMarket(address _market,  address _for, uint256 _amount) external;\n\n    function harvestMarketReward(address _lpAddress, address _callerAddress, uint256 _minEthRecive) external;\n}\n"
      },
      "contracts/interfaces/IPenpieBribeManager.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\ninterface IPenpieBribeManager {\n    struct Pool {\n        address _market;\n        bool _active;\n        uint256 _chainId;\n    }\n\n    function pools(uint256) external view returns(Pool memory);\n    function marketToPid(address _market) external view returns(uint256);\n    function exactCurrentEpoch() external view returns(uint256);\n    function getEpochEndTime(uint256 _epoch) external view returns(uint256 endTime);\n    function addBribeERC20(uint256 _batch, uint256 _pid, address _token, uint256 _amount) external;\n    function addBribeNative(uint256 _batch, uint256 _pid) external payable;\n    function getPoolLength() external view returns(uint256);\n}"
      },
      "contracts/interfaces/ISmartPendleConvert.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\ninterface ISmartPendleConvert {\n    \n    function smartConvert(uint256 _amountIn, uint256 _mode) external returns (uint256);\n\n}\n"
      },
      "contracts/interfaces/IWETH.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IWETH is IERC20 {\n    event Deposit(address indexed dst, uint256 wad);\n    event Withdrawal(address indexed src, uint256 wad);\n\n    function deposit() external payable;\n\n    function withdraw(uint256 wad) external;\n}"
      },
      "contracts/interfaces/pendle/IPBulkSeller.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\nimport \"../../libraries/BulkSellerMathCore.sol\";\n\ninterface IPBulkSeller {\n    event SwapExactTokenForSy(address receiver, uint256 netTokenIn, uint256 netSyOut);\n    event SwapExactSyForToken(address receiver, uint256 netSyIn, uint256 netTokenOut);\n    event RateUpdated(\n        uint256 newRateTokenToSy,\n        uint256 newRateSyToToken,\n        uint256 oldRateTokenToSy,\n        uint256 oldRateSyToToken\n    );\n    event ReBalanceTokenToSy(\n        uint256 netTokenDeposit,\n        uint256 netSyFromToken,\n        uint256 newTokenProp,\n        uint256 oldTokenProp\n    );\n    event ReBalanceSyToToken(\n        uint256 netSyRedeem,\n        uint256 netTokenFromSy,\n        uint256 newTokenProp,\n        uint256 oldTokenProp\n    );\n    event ReserveUpdated(uint256 totalToken, uint256 totalSy);\n    event FeeRateUpdated(uint256 newFeeRate, uint256 oldFeeRate);\n\n    function swapExactTokenForSy(\n        address receiver,\n        uint256 netTokenIn,\n        uint256 minSyOut\n    ) external payable returns (uint256 netSyOut);\n\n    function swapExactSyForToken(\n        address receiver,\n        uint256 exactSyIn,\n        uint256 minTokenOut,\n        bool swapFromInternalBalance\n    ) external returns (uint256 netTokenOut);\n\n    function SY() external view returns (address);\n\n    function token() external view returns (address);\n\n    function readState() external view returns (BulkSellerState memory);\n}\n"
      },
      "contracts/interfaces/pendle/IPendleMarket.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\nimport \"./IPPrincipalToken.sol\";\nimport \"./IStandardizedYield.sol\";\nimport \"./IPYieldToken.sol\";\n\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n\ninterface IPendleMarket is IERC20Metadata {\n\n    function readTokens() external view returns (\n        IStandardizedYield _SY,\n        IPPrincipalToken _PT,\n        IPYieldToken _YT\n    );\n\n    function rewardState(address _rewardToken) external view returns (\n        uint128 index,\n        uint128 lastBalance\n    );\n\n    function userReward(address token, address user) external view returns (\n        uint128 index, uint128 accrued\n    );\n\n    function redeemRewards(address user) external returns (uint256[] memory);\n\n    function getRewardTokens() external view returns (address[] memory);\n}"
      },
      "contracts/interfaces/pendle/IPendleMarketDepositHelper.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\nimport \"../../libraries/MarketApproxLib.sol\";\nimport \"../../libraries/ActionBaseMintRedeem.sol\";\n\ninterface IPendleMarketDepositHelper {\n    function totalStaked(address _market) external view returns (uint256);\n    function balance(address _market, address _address) external view returns (uint256);\n    function depositMarket(address _market, uint256 _amount) external;\n    function depositMarketFor(address _market, address _for, uint256 _amount) external;\n    function withdrawMarket(address _market, uint256 _amount) external;\n    function withdrawMarketWithClaim(address _market, uint256 _amount, bool _doClaim) external;\n    function harvest(address _market, uint256 _minEthToRecieve) external;\n    function setPoolInfo(address poolAddress, address rewarder, bool isActive) external;\n    function setOperator(address _address, bool _value) external;\n    function setmasterPenpie(address _masterPenpie) external;\n}\n"
      },
      "contracts/interfaces/pendle/IPendleRouter.sol": {
        "content": "// SPDX-License-Identifier:MIT\npragma solidity =0.8.19;\n\ninterface IPendleRouter {\n    struct SwapData {\n        SwapType swapType;\n        address extRouter;\n        bytes extCalldata;\n        bool needScale;\n    }\n\n    enum SwapType {\n        NONE,\n        KYBERSWAP,\n        ONE_INCH,\n        // ETH_WETH not used in Aggregator\n        ETH_WETH\n    }\n\n    struct ApproxParams {\n        uint256 guessMin;\n        uint256 guessMax;\n        uint256 guessOffchain;\n        uint256 maxIteration;\n        uint256 eps;\n    }\n\n    struct TokenInput {\n        // Token/Sy data\n        address tokenIn;\n        uint256 netTokenIn;\n        address tokenMintSy;\n        address bulk;\n        // aggregator data\n        address pendleSwap;\n        SwapData swapData;\n    }\n\n    struct TokenOutput {\n        // Token/Sy data\n        address tokenOut;\n        uint256 minTokenOut;\n        address tokenRedeemSy;\n        address bulk;\n        // aggregator data\n        address pendleSwap;\n        SwapData swapData;\n    }\n\n    function addLiquiditySingleToken(\n        address receiver,\n        address market,\n        uint256 minLpOut,\n        ApproxParams calldata guessPtReceivedFromSy,\n        TokenInput calldata input\n    ) external payable returns (uint256 netLpOut, uint256 netSyFee);\n\n    function redeemDueInterestAndRewards(\n        address user,\n        address[] calldata sys,\n        address[] calldata yts,\n        address[] calldata markets\n    ) external;\n}\n"
      },
      "contracts/interfaces/pendle/IPFeeDistributorV2.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\ninterface IPFeeDistributorV2 {\n    event SetMerkleRootAndFund(bytes32 indexed merkleRoot, uint256 amountFunded);\n\n    event Claimed(address indexed user, uint256 amountOut);\n\n    event UpdateProtocolClaimable(address indexed user, uint256 sumTopUp);\n\n    struct UpdateProtocolStruct {\n        address user;\n        bytes32[] proof;\n        address[] pools;\n        uint256[] topUps;\n    }\n\n    /**\n     * @notice submit total ETH accrued & proof to claim the outstanding amount. Intended to be\n     used by retail users\n     */\n    function claimRetail(\n        address receiver,\n        uint256 totalAccrued,\n        bytes32[] calldata proof\n    ) external returns (uint256 amountOut);\n\n    /**\n     * @notice Protocols that require the use of this function & feeData should contact the Pendle team.\n     * @notice Protocols should NOT EVER use claimRetail. Using it will make getProtocolFeeData unreliable.\n     */\n    function claimProtocol(address receiver, address[] calldata pools)\n        external\n        returns (uint256 totalAmountOut, uint256[] memory amountsOut);\n\n    /**\n    * @notice returns the claimable fees per pool. Only available if the Pendle team has specifically\n    set up the data\n     */\n    function getProtocolClaimables(address user, address[] calldata pools)\n        external\n        view\n        returns (uint256[] memory claimables);\n\n    function getProtocolTotalAccrued(address user) external view returns (uint256);\n}"
      },
      "contracts/interfaces/pendle/IPInterestManagerYT.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\ninterface IPInterestManagerYT {\n    function userInterest(\n        address user\n    ) external view returns (uint128 lastPYIndex, uint128 accruedInterest);\n}\n"
      },
      "contracts/interfaces/pendle/IPPrincipalToken.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\ninterface IPPrincipalToken is IERC20Metadata {\n    function burnByYT(address user, uint256 amount) external;\n\n    function mintByYT(address user, uint256 amount) external;\n\n    function initialize(address _YT) external;\n\n    function SY() external view returns (address);\n\n    function YT() external view returns (address);\n\n    function factory() external view returns (address);\n\n    function expiry() external view returns (uint256);\n\n    function isExpired() external view returns (bool);\n}\n"
      },
      "contracts/interfaces/pendle/IPSwapAggregator.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\nstruct SwapData {\n    SwapType swapType;\n    address extRouter;\n    bytes extCalldata;\n    bool needScale;\n}\n\nenum SwapType {\n    NONE,\n    KYBERSWAP,\n    ONE_INCH,\n    // ETH_WETH not used in Aggregator\n    ETH_WETH\n}\n\ninterface IPSwapAggregator {\n    function swap(address tokenIn, uint256 amountIn, SwapData calldata swapData) external payable;\n}\n"
      },
      "contracts/interfaces/pendle/IPVeToken.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity =0.8.19;\n\ninterface IPVeToken {\n    // ============= USER INFO =============\n\n    function balanceOf(address user) external view returns (uint128);\n\n    function positionData(address user) external view returns (uint128 amount, uint128 expiry);\n\n    // ============= META DATA =============\n\n    function totalSupplyStored() external view returns (uint128);\n\n    function totalSupplyCurrent() external returns (uint128);\n\n    function totalSupplyAndBalanceCurrent(address user) external returns (uint128, uint128);\n}"
      },
      "contracts/interfaces/pendle/IPVoteController.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity =0.8.19;\n\nimport \"../../libraries/VeBalanceLib.sol\";\n\ninterface IPVoteController {\n    struct UserPoolData {\n        uint64 weight;\n        VeBalance vote;\n    }\n\n    struct UserData {\n        uint64 totalVotedWeight;\n        mapping(address => UserPoolData) voteForPools;\n    }\n\n    function getUserData(\n        address user,\n        address[] calldata pools\n    )\n        external\n        view\n        returns (uint64 totalVotedWeight, UserPoolData[] memory voteForPools);\n\n    function getUserPoolVote(\n        address user,\n        address pool\n    ) external view returns (UserPoolData memory);\n\n    function getAllActivePools() external view returns (address[] memory);\n\n    function vote(address[] calldata pools, uint64[] calldata weights) external;\n\n    function broadcastResults(uint64 chainId) external payable;\n}\n"
      },
      "contracts/interfaces/pendle/IPVotingEscrowMainchain.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity =0.8.19;\n\nimport \"./IPVeToken.sol\";\nimport \"../../libraries/VeBalanceLib.sol\";\nimport \"../../libraries/VeHistoryLib.sol\";\n\ninterface IPVotingEscrowMainchain is IPVeToken {\n    event NewLockPosition(address indexed user, uint128 amount, uint128 expiry);\n\n    event Withdraw(address indexed user, uint128 amount);\n\n    event BroadcastTotalSupply(VeBalance newTotalSupply, uint256[] chainIds);\n\n    event BroadcastUserPosition(address indexed user, uint256[] chainIds);\n\n    // ============= ACTIONS =============\n\n    function increaseLockPosition(\n        uint128 additionalAmountToLock,\n        uint128 expiry\n    ) external returns (uint128);\n\n    function increaseLockPositionAndBroadcast(\n        uint128 additionalAmountToLock,\n        uint128 newExpiry,\n        uint256[] calldata chainIds\n    ) external payable returns (uint128 newVeBalance);\n\n    function withdraw() external returns (uint128);\n\n    function totalSupplyAt(uint128 timestamp) external view returns (uint128);\n\n    function getUserHistoryLength(address user) external view returns (uint256);\n\n    function getUserHistoryAt(\n        address user,\n        uint256 index\n    ) external view returns (Checkpoint memory);\n\n    function broadcastUserPosition(address user, uint256[] calldata chainIds) external payable;\n    \n    function getBroadcastPositionFee(uint256[] calldata chainIds) external view returns (uint256 fee);\n\n}\n"
      },
      "contracts/interfaces/pendle/IPYieldToken.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport \"./IRewardManager.sol\";\nimport \"./IPInterestManagerYT.sol\";\n\ninterface IPYieldToken is IERC20Metadata, IRewardManager, IPInterestManagerYT {\n    event NewInterestIndex(uint256 indexed newIndex);\n\n    event Mint(\n        address indexed caller,\n        address indexed receiverPT,\n        address indexed receiverYT,\n        uint256 amountSyToMint,\n        uint256 amountPYOut\n    );\n\n    event Burn(\n        address indexed caller,\n        address indexed receiver,\n        uint256 amountPYToRedeem,\n        uint256 amountSyOut\n    );\n\n    event RedeemRewards(address indexed user, uint256[] amountRewardsOut);\n\n    event RedeemInterest(address indexed user, uint256 interestOut);\n\n    event WithdrawFeeToTreasury(uint256[] amountRewardsOut, uint256 syOut);\n\n    function mintPY(address receiverPT, address receiverYT) external returns (uint256 amountPYOut);\n\n    function redeemPY(address receiver) external returns (uint256 amountSyOut);\n\n    function redeemPYMulti(\n        address[] calldata receivers,\n        uint256[] calldata amountPYToRedeems\n    ) external returns (uint256[] memory amountSyOuts);\n\n    function redeemDueInterestAndRewards(\n        address user,\n        bool redeemInterest,\n        bool redeemRewards\n    ) external returns (uint256 interestOut, uint256[] memory rewardsOut);\n\n    function rewardIndexesCurrent() external returns (uint256[] memory);\n\n    function pyIndexCurrent() external returns (uint256);\n\n    function pyIndexStored() external view returns (uint256);\n\n    function getRewardTokens() external view returns (address[] memory);\n\n    function SY() external view returns (address);\n\n    function PT() external view returns (address);\n\n    function factory() external view returns (address);\n\n    function expiry() external view returns (uint256);\n\n    function isExpired() external view returns (bool);\n\n    function doCacheIndexSameBlock() external view returns (bool);\n}\n"
      },
      "contracts/interfaces/pendle/IRewardManager.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\n\ninterface IRewardManager {\n    function userReward(\n        address token,\n        address user\n    ) external view returns (uint128 index, uint128 accrued);\n}\n"
      },
      "contracts/interfaces/pendle/IStandardizedYield.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity =0.8.19;\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\ninterface IStandardizedYield is IERC20Metadata {\n    /// @dev Emitted when any base tokens is deposited to mint shares\n    event Deposit(\n        address indexed caller,\n        address indexed receiver,\n        address indexed tokenIn,\n        uint256 amountDeposited,\n        uint256 amountSyOut\n    );\n\n    /// @dev Emitted when any shares are redeemed for base tokens\n    event Redeem(\n        address indexed caller,\n        address indexed receiver,\n        address indexed tokenOut,\n        uint256 amountSyToRedeem,\n        uint256 amountTokenOut\n    );\n\n    /// @dev check `assetInfo()` for more information\n    enum AssetType {\n        TOKEN,\n        LIQUIDITY\n    }\n\n    /// @dev Emitted when (`user`) claims their rewards\n    event ClaimRewards(address indexed user, address[] rewardTokens, uint256[] rewardAmounts);\n\n    /**\n     * @notice mints an amount of shares by depositing a base token.\n     * @param receiver shares recipient address\n     * @param tokenIn address of the base tokens to mint shares\n     * @param amountTokenToDeposit amount of base tokens to be transferred from (`msg.sender`)\n     * @param minSharesOut reverts if amount of shares minted is lower than this\n     * @return amountSharesOut amount of shares minted\n     * @dev Emits a {Deposit} event\n     *\n     * Requirements:\n     * - (`tokenIn`) must be a valid base token.\n     */\n    function deposit(\n        address receiver,\n        address tokenIn,\n        uint256 amountTokenToDeposit,\n        uint256 minSharesOut\n    ) external payable returns (uint256 amountSharesOut);\n\n    /**\n     * @notice redeems an amount of base tokens by burning some shares\n     * @param receiver recipient address\n     * @param amountSharesToRedeem amount of shares to be burned\n     * @param tokenOut address of the base token to be redeemed\n     * @param minTokenOut reverts if amount of base token redeemed is lower than this\n     * @param burnFromInternalBalance if true, burns from balance of `address(this)`, otherwise burns from `msg.sender`\n     * @return amountTokenOut amount of base tokens redeemed\n     * @dev Emits a {Redeem} event\n     *\n     * Requirements:\n     * - (`tokenOut`) must be a valid base token.\n     */\n    function redeem(\n        address receiver,\n        uint256 amountSharesToRedeem,\n        address tokenOut,\n        uint256 minTokenOut,\n        bool burnFromInternalBalance\n    ) external returns (uint256 amountTokenOut);\n\n    /**\n     * @notice exchangeRate * syBalance / 1e18 must return the asset balance of the account\n     * @notice vice-versa, if a user uses some amount of tokens equivalent to X asset, the amount of sy\n     he can mint must be X * exchangeRate / 1e18\n     * @dev SYUtils's assetToSy & syToAsset should be used instead of raw multiplication\n     & division\n     */\n    function exchangeRate() external view returns (uint256 res);\n\n    /**\n     * @notice claims reward for (`user`)\n     * @param user the user receiving their rewards\n     * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`\n     * @dev\n     * Emits a `ClaimRewards` event\n     * See {getRewardTokens} for list of reward tokens\n     */\n    function claimRewards(address user) external returns (uint256[] memory rewardAmounts);\n\n    /**\n     * @notice get the amount of unclaimed rewards for (`user`)\n     * @param user the user to check for\n     * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`\n     */\n    function accruedRewards(address user) external view returns (uint256[] memory rewardAmounts);\n\n    function rewardIndexesCurrent() external returns (uint256[] memory indexes);\n\n    function rewardIndexesStored() external view returns (uint256[] memory indexes);\n\n    /**\n     * @notice returns the list of reward token addresses\n     */\n    function getRewardTokens() external view returns (address[] memory);\n\n    /**\n     * @notice returns the address of the underlying yield token\n     */\n    function yieldToken() external view returns (address);\n\n    /**\n     * @notice returns all tokens that can mint this SY\n     */\n    function getTokensIn() external view returns (address[] memory res);\n\n    /**\n     * @notice returns all tokens that can be redeemed by this SY\n     */\n    function getTokensOut() external view returns (address[] memory res);\n\n    function isValidTokenIn(address token) external view returns (bool);\n\n    function isValidTokenOut(address token) external view returns (bool);\n\n    function previewDeposit(\n        address tokenIn,\n        uint256 amountTokenToDeposit\n    ) external view returns (uint256 amountSharesOut);\n\n    function previewRedeem(\n        address tokenOut,\n        uint256 amountSharesToRedeem\n    ) external view returns (uint256 amountTokenOut);\n\n    /**\n     * @notice This function contains information to interpret what the asset is\n     * @return assetType the type of the asset (0 for ERC20 tokens, 1 for AMM liquidity tokens)\n     * @return assetAddress the address of the asset\n     * @return assetDecimals the decimals of the asset\n     */\n    function assetInfo()\n        external\n        view\n        returns (AssetType assetType, address assetAddress, uint8 assetDecimals);\n}\n"
      },
      "contracts/libraries/ActionBaseMintRedeem.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport \"./TokenHelper.sol\";\nimport \"../interfaces/pendle/IStandardizedYield.sol\";\nimport \"../interfaces/pendle/IPYieldToken.sol\";\nimport \"../interfaces/pendle/IPBulkSeller.sol\";\n\nimport \"./Errors.sol\";\nimport \"../interfaces/pendle/IPSwapAggregator.sol\";\n\nstruct TokenInput {\n    // Token/Sy data\n    address tokenIn;\n    uint256 netTokenIn;\n    address tokenMintSy;\n    address bulk;\n    // aggregator data\n    address pendleSwap;\n    SwapData swapData;\n}\n\nstruct TokenOutput {\n    // Token/Sy data\n    address tokenOut;\n    uint256 minTokenOut;\n    address tokenRedeemSy;\n    address bulk;\n    // aggregator data\n    address pendleSwap;\n    SwapData swapData;\n}\n\n// solhint-disable no-empty-blocks\nabstract contract ActionBaseMintRedeem is TokenHelper {\n    bytes internal constant EMPTY_BYTES = abi.encode();\n\n    function _mintSyFromToken(\n        address receiver,\n        address SY,\n        uint256 minSyOut,\n        TokenInput calldata inp\n    ) internal returns (uint256 netSyOut) {\n        SwapType swapType = inp.swapData.swapType;\n\n        uint256 netTokenMintSy;\n\n        if (swapType == SwapType.NONE) {\n            _transferIn(inp.tokenIn, msg.sender, inp.netTokenIn);\n            netTokenMintSy = inp.netTokenIn;\n        } else if (swapType == SwapType.ETH_WETH) {\n            _transferIn(inp.tokenIn, msg.sender, inp.netTokenIn);\n            _wrap_unwrap_ETH(inp.tokenIn, inp.tokenMintSy, inp.netTokenIn);\n            netTokenMintSy = inp.netTokenIn;\n        } else {\n            if (inp.tokenIn == NATIVE) _transferIn(NATIVE, msg.sender, inp.netTokenIn);\n            else _transferFrom(IERC20(inp.tokenIn), msg.sender, inp.pendleSwap, inp.netTokenIn);\n\n            IPSwapAggregator(inp.pendleSwap).swap{\n                value: inp.tokenIn == NATIVE ? inp.netTokenIn : 0\n            }(inp.tokenIn, inp.netTokenIn, inp.swapData);\n            netTokenMintSy = _selfBalance(inp.tokenMintSy);\n        }\n\n        // outcome of all branches: satisfy pre-condition of __mintSy\n\n        netSyOut = __mintSy(receiver, SY, netTokenMintSy, minSyOut, inp);\n    }\n\n    /// @dev pre-condition: having netTokenMintSy of tokens in this contract\n    function __mintSy(\n        address receiver,\n        address SY,\n        uint256 netTokenMintSy,\n        uint256 minSyOut,\n        TokenInput calldata inp\n    ) private returns (uint256 netSyOut) {\n        uint256 netNative = inp.tokenMintSy == NATIVE ? netTokenMintSy : 0;\n\n        if (inp.bulk != address(0)) {\n            netSyOut = IPBulkSeller(inp.bulk).swapExactTokenForSy{ value: netNative }(\n                receiver,\n                netTokenMintSy,\n                minSyOut\n            );\n        } else {\n            netSyOut = IStandardizedYield(SY).deposit{ value: netNative }(\n                receiver,\n                inp.tokenMintSy,\n                netTokenMintSy,\n                minSyOut\n            );\n        }\n    }\n\n    function _redeemSyToToken(\n        address receiver,\n        address SY,\n        uint256 netSyIn,\n        TokenOutput calldata out,\n        bool doPull\n    ) internal returns (uint256 netTokenOut) {\n        SwapType swapType = out.swapData.swapType;\n\n        if (swapType == SwapType.NONE) {\n            netTokenOut = __redeemSy(receiver, SY, netSyIn, out, doPull);\n        } else if (swapType == SwapType.ETH_WETH) {\n            netTokenOut = __redeemSy(address(this), SY, netSyIn, out, doPull); // ETH:WETH is 1:1\n\n            _wrap_unwrap_ETH(out.tokenRedeemSy, out.tokenOut, netTokenOut);\n\n            _transferOut(out.tokenOut, receiver, netTokenOut);\n        } else {\n            uint256 netTokenRedeemed = __redeemSy(out.pendleSwap, SY, netSyIn, out, doPull);\n\n            IPSwapAggregator(out.pendleSwap).swap(\n                out.tokenRedeemSy,\n                netTokenRedeemed,\n                out.swapData\n            );\n\n            netTokenOut = _selfBalance(out.tokenOut);\n\n            _transferOut(out.tokenOut, receiver, netTokenOut);\n        }\n\n        // outcome of all branches: netTokenOut of tokens goes back to receiver\n\n        if (netTokenOut < out.minTokenOut) {\n            revert Errors.RouterInsufficientTokenOut(netTokenOut, out.minTokenOut);\n        }\n    }\n\n    function __redeemSy(\n        address receiver,\n        address SY,\n        uint256 netSyIn,\n        TokenOutput calldata out,\n        bool doPull\n    ) private returns (uint256 netTokenRedeemed) {\n        if (doPull) {\n            _transferFrom(IERC20(SY), msg.sender, _syOrBulk(SY, out), netSyIn);\n        }\n\n        if (out.bulk != address(0)) {\n            netTokenRedeemed = IPBulkSeller(out.bulk).swapExactSyForToken(\n                receiver,\n                netSyIn,\n                0,\n                true\n            );\n        } else {\n            netTokenRedeemed = IStandardizedYield(SY).redeem(\n                receiver,\n                netSyIn,\n                out.tokenRedeemSy,\n                0,\n                true\n            );\n        }\n    }\n\n    function _mintPyFromSy(\n        address receiver,\n        address SY,\n        address YT,\n        uint256 netSyIn,\n        uint256 minPyOut,\n        bool doPull\n    ) internal returns (uint256 netPyOut) {\n        if (doPull) {\n            _transferFrom(IERC20(SY), msg.sender, YT, netSyIn);\n        }\n\n        netPyOut = IPYieldToken(YT).mintPY(receiver, receiver);\n        if (netPyOut < minPyOut) revert Errors.RouterInsufficientPYOut(netPyOut, minPyOut);\n    }\n\n    function _redeemPyToSy(\n        address receiver,\n        address YT,\n        uint256 netPyIn,\n        uint256 minSyOut\n    ) internal returns (uint256 netSyOut) {\n        address PT = IPYieldToken(YT).PT();\n\n        _transferFrom(IERC20(PT), msg.sender, YT, netPyIn);\n\n        bool needToBurnYt = (!IPYieldToken(YT).isExpired());\n        if (needToBurnYt) _transferFrom(IERC20(YT), msg.sender, YT, netPyIn);\n\n        netSyOut = IPYieldToken(YT).redeemPY(receiver);\n        if (netSyOut < minSyOut) revert Errors.RouterInsufficientSyOut(netSyOut, minSyOut);\n    }\n\n    function _syOrBulk(address SY, TokenOutput calldata output)\n        internal\n        pure\n        returns (address addr)\n    {\n        return output.bulk != address(0) ? output.bulk : SY;\n    }\n}\n"
      },
      "contracts/libraries/BulkSellerMathCore.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport \"./TokenHelper.sol\";\nimport \"./math/Math.sol\";\nimport \"./Errors.sol\";\n\nstruct BulkSellerState {\n    uint256 rateTokenToSy;\n    uint256 rateSyToToken;\n    uint256 totalToken;\n    uint256 totalSy;\n    uint256 feeRate;\n}\n\nlibrary BulkSellerMathCore {\n    using Math for uint256;\n\n    function swapExactTokenForSy(\n        BulkSellerState memory state,\n        uint256 netTokenIn\n    ) internal pure returns (uint256 netSyOut) {\n        netSyOut = calcSwapExactTokenForSy(state, netTokenIn);\n        state.totalToken += netTokenIn;\n        state.totalSy -= netSyOut;\n    }\n\n    function swapExactSyForToken(\n        BulkSellerState memory state,\n        uint256 netSyIn\n    ) internal pure returns (uint256 netTokenOut) {\n        netTokenOut = calcSwapExactSyForToken(state, netSyIn);\n        state.totalSy += netSyIn;\n        state.totalToken -= netTokenOut;\n    }\n\n    function calcSwapExactTokenForSy(\n        BulkSellerState memory state,\n        uint256 netTokenIn\n    ) internal pure returns (uint256 netSyOut) {\n        uint256 postFeeRate = state.rateTokenToSy.mulDown(Math.ONE - state.feeRate);\n        assert(postFeeRate != 0);\n\n        netSyOut = netTokenIn.mulDown(postFeeRate);\n        if (netSyOut > state.totalSy)\n            revert Errors.BulkInsufficientSyForTrade(state.totalSy, netSyOut);\n    }\n\n    function calcSwapExactSyForToken(\n        BulkSellerState memory state,\n        uint256 netSyIn\n    ) internal pure returns (uint256 netTokenOut) {\n        uint256 postFeeRate = state.rateSyToToken.mulDown(Math.ONE - state.feeRate);\n        assert(postFeeRate != 0);\n\n        netTokenOut = netSyIn.mulDown(postFeeRate);\n        if (netTokenOut > state.totalToken)\n            revert Errors.BulkInsufficientTokenForTrade(state.totalToken, netTokenOut);\n    }\n\n    function getTokenProp(BulkSellerState memory state) internal pure returns (uint256) {\n        uint256 totalToken = state.totalToken;\n        uint256 totalTokenFromSy = state.totalSy.mulDown(state.rateSyToToken);\n        return totalToken.divDown(totalToken + totalTokenFromSy);\n    }\n\n    function getReBalanceParams(\n        BulkSellerState memory state,\n        uint256 targetTokenProp\n    ) internal pure returns (uint256 netTokenToDeposit, uint256 netSyToRedeem) {\n        uint256 currentTokenProp = getTokenProp(state);\n\n        if (currentTokenProp > targetTokenProp) {\n            netTokenToDeposit = state\n                .totalToken\n                .mulDown(currentTokenProp - targetTokenProp)\n                .divDown(currentTokenProp);\n        } else {\n            uint256 currentSyProp = Math.ONE - currentTokenProp;\n            netSyToRedeem = state.totalSy.mulDown(targetTokenProp - currentTokenProp).divDown(\n                currentSyProp\n            );\n        }\n    }\n\n    function reBalanceTokenToSy(\n        BulkSellerState memory state,\n        uint256 netTokenToDeposit,\n        uint256 netSyFromToken,\n        uint256 maxDiff\n    ) internal pure {\n        uint256 rate = netSyFromToken.divDown(netTokenToDeposit);\n\n        if (!Math.isAApproxB(rate, state.rateTokenToSy, maxDiff))\n            revert Errors.BulkBadRateTokenToSy(rate, state.rateTokenToSy, maxDiff);\n\n        state.totalToken -= netTokenToDeposit;\n        state.totalSy += netSyFromToken;\n    }\n\n    function reBalanceSyToToken(\n        BulkSellerState memory state,\n        uint256 netSyToRedeem,\n        uint256 netTokenFromSy,\n        uint256 maxDiff\n    ) internal pure {\n        uint256 rate = netTokenFromSy.divDown(netSyToRedeem);\n\n        if (!Math.isAApproxB(rate, state.rateSyToToken, maxDiff))\n            revert Errors.BulkBadRateSyToToken(rate, state.rateSyToToken, maxDiff);\n\n        state.totalToken += netTokenFromSy;\n        state.totalSy -= netSyToRedeem;\n    }\n\n    function setRate(\n        BulkSellerState memory state,\n        uint256 rateSyToToken,\n        uint256 rateTokenToSy,\n        uint256 maxDiff\n    ) internal pure {\n        if (\n            state.rateTokenToSy != 0 &&\n            !Math.isAApproxB(rateTokenToSy, state.rateTokenToSy, maxDiff)\n        ) {\n            revert Errors.BulkBadRateTokenToSy(rateTokenToSy, state.rateTokenToSy, maxDiff);\n        }\n\n        if (\n            state.rateSyToToken != 0 &&\n            !Math.isAApproxB(rateSyToToken, state.rateSyToToken, maxDiff)\n        ) {\n            revert Errors.BulkBadRateSyToToken(rateSyToToken, state.rateSyToToken, maxDiff);\n        }\n\n        state.rateTokenToSy = rateTokenToSy;\n        state.rateSyToToken = rateSyToToken;\n    }\n}\n"
      },
      "contracts/libraries/ERC20FactoryLib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\npragma experimental ABIEncoderV2;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { PenpieReceiptToken } from \"../rewards/PenpieReceiptToken.sol\";\nimport { BaseRewardPoolV2 } from \"../rewards/BaseRewardPoolV2.sol\";\n\nlibrary ERC20FactoryLib {\n    function createERC20(string memory name_, string memory symbol_) public returns(address) \n    {\n        ERC20 token = new MintableERC20(name_, symbol_);\n        return address(token);\n    }\n\n    function createReceipt(address _stakeToken, address _masterPenpie, string memory _name, string memory _symbol) public returns(address)\n    {\n        ERC20 token = new PenpieReceiptToken(_stakeToken, _masterPenpie, _name, _symbol);\n        return address(token);\n    }\n\n    function createRewarder(\n        address _receiptToken,\n        address mainRewardToken,\n        address _masterRadpie,\n        address _rewardQueuer\n    ) external returns (address) {\n        BaseRewardPoolV2 _rewarder = new BaseRewardPoolV2(\n            _receiptToken,\n            mainRewardToken,\n            _masterRadpie,\n            _rewardQueuer\n        );\n        return address(_rewarder);\n    }    \n}"
      },
      "contracts/libraries/Errors.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nlibrary Errors {\n    // BulkSeller\n    error BulkInsufficientSyForTrade(uint256 currentAmount, uint256 requiredAmount);\n    error BulkInsufficientTokenForTrade(uint256 currentAmount, uint256 requiredAmount);\n    error BulkInSufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);\n    error BulkInSufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);\n    error BulkInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);\n    error BulkNotMaintainer();\n    error BulkNotAdmin();\n    error BulkSellerAlreadyExisted(address token, address SY, address bulk);\n    error BulkSellerInvalidToken(address token, address SY);\n    error BulkBadRateTokenToSy(uint256 actualRate, uint256 currentRate, uint256 eps);\n    error BulkBadRateSyToToken(uint256 actualRate, uint256 currentRate, uint256 eps);\n\n    // APPROX\n    error ApproxFail();\n    error ApproxParamsInvalid(uint256 guessMin, uint256 guessMax, uint256 eps);\n    error ApproxBinarySearchInputInvalid(\n        uint256 approxGuessMin,\n        uint256 approxGuessMax,\n        uint256 minGuessMin,\n        uint256 maxGuessMax\n    );\n\n    // MARKET + MARKET MATH CORE\n    error MarketExpired();\n    error MarketZeroAmountsInput();\n    error MarketZeroAmountsOutput();\n    error MarketZeroLnImpliedRate();\n    error MarketInsufficientPtForTrade(int256 currentAmount, int256 requiredAmount);\n    error MarketInsufficientPtReceived(uint256 actualBalance, uint256 requiredBalance);\n    error MarketInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);\n    error MarketZeroTotalPtOrTotalAsset(int256 totalPt, int256 totalAsset);\n    error MarketExchangeRateBelowOne(int256 exchangeRate);\n    error MarketProportionMustNotEqualOne();\n    error MarketRateScalarBelowZero(int256 rateScalar);\n    error MarketScalarRootBelowZero(int256 scalarRoot);\n    error MarketProportionTooHigh(int256 proportion, int256 maxProportion);\n\n    error OracleUninitialized();\n    error OracleTargetTooOld(uint32 target, uint32 oldest);\n    error OracleZeroCardinality();\n\n    error MarketFactoryExpiredPt();\n    error MarketFactoryInvalidPt();\n    error MarketFactoryMarketExists();\n\n    error MarketFactoryLnFeeRateRootTooHigh(uint80 lnFeeRateRoot, uint256 maxLnFeeRateRoot);\n    error MarketFactoryReserveFeePercentTooHigh(\n        uint8 reserveFeePercent,\n        uint8 maxReserveFeePercent\n    );\n    error MarketFactoryZeroTreasury();\n    error MarketFactoryInitialAnchorTooLow(int256 initialAnchor, int256 minInitialAnchor);\n\n    // ROUTER\n    error RouterInsufficientLpOut(uint256 actualLpOut, uint256 requiredLpOut);\n    error RouterInsufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);\n    error RouterInsufficientPtOut(uint256 actualPtOut, uint256 requiredPtOut);\n    error RouterInsufficientYtOut(uint256 actualYtOut, uint256 requiredYtOut);\n    error RouterInsufficientPYOut(uint256 actualPYOut, uint256 requiredPYOut);\n    error RouterInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);\n    error RouterExceededLimitSyIn(uint256 actualSyIn, uint256 limitSyIn);\n    error RouterExceededLimitPtIn(uint256 actualPtIn, uint256 limitPtIn);\n    error RouterExceededLimitYtIn(uint256 actualYtIn, uint256 limitYtIn);\n    error RouterInsufficientSyRepay(uint256 actualSyRepay, uint256 requiredSyRepay);\n    error RouterInsufficientPtRepay(uint256 actualPtRepay, uint256 requiredPtRepay);\n    error RouterNotAllSyUsed(uint256 netSyDesired, uint256 netSyUsed);\n\n    error RouterTimeRangeZero();\n    error RouterCallbackNotPendleMarket(address caller);\n    error RouterInvalidAction(bytes4 selector);\n    error RouterInvalidFacet(address facet);\n\n    error RouterKyberSwapDataZero();\n\n    // YIELD CONTRACT\n    error YCExpired();\n    error YCNotExpired();\n    error YieldContractInsufficientSy(uint256 actualSy, uint256 requiredSy);\n    error YCNothingToRedeem();\n    error YCPostExpiryDataNotSet();\n    error YCNoFloatingSy();\n\n    // YieldFactory\n    error YCFactoryInvalidExpiry();\n    error YCFactoryYieldContractExisted();\n    error YCFactoryZeroExpiryDivisor();\n    error YCFactoryZeroTreasury();\n    error YCFactoryInterestFeeRateTooHigh(uint256 interestFeeRate, uint256 maxInterestFeeRate);\n    error YCFactoryRewardFeeRateTooHigh(uint256 newRewardFeeRate, uint256 maxRewardFeeRate);\n\n    // SY\n    error SYInvalidTokenIn(address token);\n    error SYInvalidTokenOut(address token);\n    error SYZeroDeposit();\n    error SYZeroRedeem();\n    error SYInsufficientSharesOut(uint256 actualSharesOut, uint256 requiredSharesOut);\n    error SYInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);\n\n    // SY-specific\n    error SYQiTokenMintFailed(uint256 errCode);\n    error SYQiTokenRedeemFailed(uint256 errCode);\n    error SYQiTokenRedeemRewardsFailed(uint256 rewardAccruedType0, uint256 rewardAccruedType1);\n    error SYQiTokenBorrowRateTooHigh(uint256 borrowRate, uint256 borrowRateMax);\n\n    error SYCurveInvalidPid();\n    error SYCurve3crvPoolNotFound();\n\n    error SYApeDepositAmountTooSmall(uint256 amountDeposited);\n    error SYBalancerInvalidPid();\n    error SYInvalidRewardToken(address token);\n\n    error SYStargateRedeemCapExceeded(uint256 amountLpDesired, uint256 amountLpRedeemable);\n\n    error SYBalancerReentrancy();\n\n    // Liquidity Mining\n    error VCInactivePool(address pool);\n    error VCPoolAlreadyActive(address pool);\n    error VCZeroVePendle(address user);\n    error VCExceededMaxWeight(uint256 totalWeight, uint256 maxWeight);\n    error VCEpochNotFinalized(uint256 wTime);\n    error VCPoolAlreadyAddAndRemoved(address pool);\n\n    error VEInvalidNewExpiry(uint256 newExpiry);\n    error VEExceededMaxLockTime();\n    error VEInsufficientLockTime();\n    error VENotAllowedReduceExpiry();\n    error VEZeroAmountLocked();\n    error VEPositionNotExpired();\n    error VEZeroPosition();\n    error VEZeroSlope(uint128 bias, uint128 slope);\n    error VEReceiveOldSupply(uint256 msgTime);\n\n    error GCNotPendleMarket(address caller);\n    error GCNotVotingController(address caller);\n\n    error InvalidWTime(uint256 wTime);\n    error ExpiryInThePast(uint256 expiry);\n    error ChainNotSupported(uint256 chainId);\n\n    error FDTotalAmountFundedNotMatch(uint256 actualTotalAmount, uint256 expectedTotalAmount);\n    error FDEpochLengthMismatch();\n    error FDInvalidPool(address pool);\n    error FDPoolAlreadyExists(address pool);\n    error FDInvalidNewFinishedEpoch(uint256 oldFinishedEpoch, uint256 newFinishedEpoch);\n    error FDInvalidStartEpoch(uint256 startEpoch);\n    error FDInvalidWTimeFund(uint256 lastFunded, uint256 wTime);\n    error FDFutureFunding(uint256 lastFunded, uint256 currentWTime);\n\n    error BDInvalidEpoch(uint256 epoch, uint256 startTime);\n\n    // Cross-Chain\n    error MsgNotFromSendEndpoint(uint16 srcChainId, bytes path);\n    error MsgNotFromReceiveEndpoint(address sender);\n    error InsufficientFeeToSendMsg(uint256 currentFee, uint256 requiredFee);\n    error ApproxDstExecutionGasNotSet();\n    error InvalidRetryData();\n\n    // GENERIC MSG\n    error ArrayLengthMismatch();\n    error ArrayEmpty();\n    error ArrayOutOfBounds();\n    error ZeroAddress();\n    error FailedToSendEther();\n    error InvalidMerkleProof();\n\n    error OnlyLayerZeroEndpoint();\n    error OnlyYT();\n    error OnlyYCFactory();\n    error OnlyWhitelisted();\n\n    // Swap Aggregator\n    error SAInsufficientTokenIn(address tokenIn, uint256 amountExpected, uint256 amountActual);\n    error UnsupportedSelector(uint256 aggregatorType, bytes4 selector);\n}"
      },
      "contracts/libraries/MarketApproxLib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport \"./math/Math.sol\";\nimport \"./math/MarketMathCore.sol\";\n\nstruct ApproxParams {\n    uint256 guessMin;\n    uint256 guessMax;\n    uint256 guessOffchain; // pass 0 in to skip this variable\n    uint256 maxIteration; // every iteration, the diff between guessMin and guessMax will be divided by 2\n    uint256 eps; // the max eps between the returned result & the correct result, base 1e18. Normally this number will be set\n    // to 1e15 (1e18/1000 = 0.1%)\n\n    /// Further explanation of the eps. Take swapExactSyForPt for example. To calc the corresponding amount of Pt to swap out,\n    /// it's necessary to run an approximation algorithm, because by default there only exists the Pt to Sy formula\n    /// To approx, the 5 values above will have to be provided, and the approx process will run as follows:\n    /// mid = (guessMin + guessMax) / 2 // mid here is the current guess of the amount of Pt out\n    /// netSyNeed = calcSwapSyForExactPt(mid)\n    /// if (netSyNeed > exactSyIn) guessMax = mid - 1 // since the maximum Sy in can't exceed the exactSyIn\n    /// else guessMin = mid (1)\n    /// For the (1), since netSyNeed <= exactSyIn, the result might be usable. If the netSyNeed is within eps of\n    /// exactSyIn (ex eps=0.1% => we have used 99.9% the amount of Sy specified), mid will be chosen as the final guess result\n\n    /// for guessOffchain, this is to provide a shortcut to guessing. The offchain SDK can precalculate the exact result\n    /// before the tx is sent. When the tx reaches the contract, the guessOffchain will be checked first, and if it satisfies the\n    /// approximation, it will be used (and save all the guessing). It's expected that this shortcut will be used in most cases\n    /// except in cases that there is a trade in the same market right before the tx\n}\n\nlibrary MarketApproxPtInLib {\n    using MarketMathCore for MarketState;\n    using PYIndexLib for PYIndex;\n    using Math for uint256;\n    using Math for int256;\n    using LogExpMath for int256;\n\n    /**\n     * @dev algorithm:\n        - Bin search the amount of PT to swap in\n        - Try swapping & get netSyOut\n        - Stop when netSyOut greater & approx minSyOut\n        - guess & approx is for netPtIn\n     */\n    function approxSwapPtForExactSy(\n        MarketState memory market,\n        PYIndex index,\n        uint256 minSyOut,\n        uint256 blockTime,\n        ApproxParams memory approx\n    ) internal pure returns (uint256 /*netPtIn*/, uint256 /*netSyOut*/, uint256 /*netSyFee*/) {\n        MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime);\n        if (approx.guessOffchain == 0) {\n            // no limit on min\n            approx.guessMax = Math.min(approx.guessMax, calcMaxPtIn(market, comp));\n            validateApprox(approx);\n        }\n\n        for (uint256 iter = 0; iter < approx.maxIteration; ++iter) {\n            uint256 guess = nextGuess(approx, iter);\n            (uint256 netSyOut, uint256 netSyFee, ) = calcSyOut(market, comp, index, guess);\n\n            if (netSyOut >= minSyOut) {\n                if (Math.isAGreaterApproxB(netSyOut, minSyOut, approx.eps))\n                    return (guess, netSyOut, netSyFee);\n                approx.guessMax = guess;\n            } else {\n                approx.guessMin = guess;\n            }\n        }\n        revert Errors.ApproxFail();\n    }\n\n    /**\n     * @dev algorithm:\n        - Bin search the amount of PT to swap in\n        - Flashswap the corresponding amount of SY out\n        - Pair those amount with exactSyIn SY to tokenize into PT & YT\n        - PT to repay the flashswap, YT transferred to user\n        - Stop when the amount of SY to be pulled to tokenize PT to repay loan approx the exactSyIn\n        - guess & approx is for netYtOut (also netPtIn)\n     */\n    function approxSwapExactSyForYt(\n        MarketState memory market,\n        PYIndex index,\n        uint256 exactSyIn,\n        uint256 blockTime,\n        ApproxParams memory approx\n    ) internal pure returns (uint256 /*netYtOut*/, uint256 /*netSyFee*/) {\n        MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime);\n        if (approx.guessOffchain == 0) {\n            approx.guessMin = Math.max(approx.guessMin, index.syToAsset(exactSyIn));\n            approx.guessMax = Math.min(approx.guessMax, calcMaxPtIn(market, comp));\n            validateApprox(approx);\n        }\n\n        // at minimum we will flashswap exactSyIn since we have enough SY to payback the PT loan\n\n        for (uint256 iter = 0; iter < approx.maxIteration; ++iter) {\n            uint256 guess = nextGuess(approx, iter);\n\n            (uint256 netSyOut, uint256 netSyFee, ) = calcSyOut(market, comp, index, guess);\n\n            uint256 netSyToTokenizePt = index.assetToSyUp(guess);\n\n            // for sure netSyToTokenizePt >= netSyOut since we are swapping PT to SY\n            uint256 netSyToPull = netSyToTokenizePt - netSyOut;\n\n            if (netSyToPull <= exactSyIn) {\n                if (Math.isASmallerApproxB(netSyToPull, exactSyIn, approx.eps))\n                    return (guess, netSyFee);\n                approx.guessMin = guess;\n            } else {\n                approx.guessMax = guess - 1;\n            }\n        }\n        revert Errors.ApproxFail();\n    }\n\n    /**\n     * @dev algorithm:\n        - Bin search the amount of PT to swap to SY\n        - Swap PT to SY\n        - Pair the remaining PT with the SY to add liquidity\n        - Stop when the ratio of PT / totalPt & SY / totalSy is approx\n        - guess & approx is for netPtSwap\n     */\n    function approxSwapPtToAddLiquidity(\n        MarketState memory market,\n        PYIndex index,\n        uint256 totalPtIn,\n        uint256 blockTime,\n        ApproxParams memory approx\n    )\n        internal\n        pure\n        returns (uint256 /*netPtSwap*/, uint256 /*netSyFromSwap*/, uint256 /*netSyFee*/)\n    {\n        MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime);\n        if (approx.guessOffchain == 0) {\n            // no limit on min\n            approx.guessMax = Math.min(approx.guessMax, calcMaxPtIn(market, comp));\n            approx.guessMax = Math.min(approx.guessMax, totalPtIn);\n            validateApprox(approx);\n            require(market.totalLp != 0, \"no existing lp\");\n        }\n\n        for (uint256 iter = 0; iter < approx.maxIteration; ++iter) {\n            uint256 guess = nextGuess(approx, iter);\n\n            (\n                uint256 syNumerator,\n                uint256 ptNumerator,\n                uint256 netSyOut,\n                uint256 netSyFee,\n\n            ) = calcNumerators(market, index, totalPtIn, comp, guess);\n\n            if (Math.isAApproxB(syNumerator, ptNumerator, approx.eps))\n                return (guess, netSyOut, netSyFee);\n\n            if (syNumerator <= ptNumerator) {\n                // needs more SY --> swap more PT\n                approx.guessMin = guess + 1;\n            } else {\n                // needs less SY --> swap less PT\n                approx.guessMax = guess - 1;\n            }\n        }\n        revert Errors.ApproxFail();\n    }\n\n    function calcNumerators(\n        MarketState memory market,\n        PYIndex index,\n        uint256 totalPtIn,\n        MarketPreCompute memory comp,\n        uint256 guess\n    )\n        internal\n        pure\n        returns (\n            uint256 syNumerator,\n            uint256 ptNumerator,\n            uint256 netSyOut,\n            uint256 netSyFee,\n            uint256 netSyToReserve\n        )\n    {\n        (netSyOut, netSyFee, netSyToReserve) = calcSyOut(market, comp, index, guess);\n\n        uint256 newTotalPt = uint256(market.totalPt) + guess;\n        uint256 newTotalSy = (uint256(market.totalSy) - netSyOut - netSyToReserve);\n\n        // it is desired that\n        // netSyOut / newTotalSy = netPtRemaining / newTotalPt\n        // which is equivalent to\n        // netSyOut * newTotalPt = netPtRemaining * newTotalSy\n\n        syNumerator = netSyOut * newTotalPt;\n        ptNumerator = (totalPtIn - guess) * newTotalSy;\n    }\n\n    struct Args7 {\n        MarketState market;\n        PYIndex index;\n        uint256 exactPtIn;\n        uint256 blockTime;\n    }\n\n    /**\n     * @dev algorithm:\n        - Bin search the amount of PT to swap to SY\n        - Flashswap the corresponding amount of SY out\n        - Tokenize all the SY into PT + YT\n        - PT to repay the flashswap, YT transferred to user\n        - Stop when the additional amount of PT to pull to repay the loan approx the exactPtIn\n        - guess & approx is for totalPtToSwap\n     */\n    function approxSwapExactPtForYt(\n        MarketState memory market,\n        PYIndex index,\n        uint256 exactPtIn,\n        uint256 blockTime,\n        ApproxParams memory approx\n    )\n        internal\n        pure\n        returns (uint256 /*netYtOut*/, uint256 /*totalPtToSwap*/, uint256 /*netSyFee*/)\n    {\n        MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime);\n        if (approx.guessOffchain == 0) {\n            approx.guessMin = Math.max(approx.guessMin, exactPtIn);\n            approx.guessMax = Math.min(approx.guessMax, calcMaxPtIn(market, comp));\n            validateApprox(approx);\n        }\n\n        for (uint256 iter = 0; iter < approx.maxIteration; ++iter) {\n            uint256 guess = nextGuess(approx, iter);\n\n            (uint256 netSyOut, uint256 netSyFee, ) = calcSyOut(market, comp, index, guess);\n\n            uint256 netAssetOut = index.syToAsset(netSyOut);\n\n            // guess >= netAssetOut since we are swapping PT to SY\n            uint256 netPtToPull = guess - netAssetOut;\n\n            if (netPtToPull <= exactPtIn) {\n                if (Math.isASmallerApproxB(netPtToPull, exactPtIn, approx.eps))\n                    return (netAssetOut, guess, netSyFee);\n                approx.guessMin = guess;\n            } else {\n                approx.guessMax = guess - 1;\n            }\n        }\n        revert Errors.ApproxFail();\n    }\n\n    ////////////////////////////////////////////////////////////////////////////////\n\n    function calcSyOut(\n        MarketState memory market,\n        MarketPreCompute memory comp,\n        PYIndex index,\n        uint256 netPtIn\n    ) internal pure returns (uint256 netSyOut, uint256 netSyFee, uint256 netSyToReserve) {\n        (int256 _netSyOut, int256 _netSyFee, int256 _netSyToReserve) = market.calcTrade(\n            comp,\n            index,\n            -int256(netPtIn)\n        );\n        netSyOut = uint256(_netSyOut);\n        netSyFee = uint256(_netSyFee);\n        netSyToReserve = uint256(_netSyToReserve);\n    }\n\n    function nextGuess(ApproxParams memory approx, uint256 iter) internal pure returns (uint256) {\n        if (iter == 0 && approx.guessOffchain != 0) return approx.guessOffchain;\n        if (approx.guessMin <= approx.guessMax) return (approx.guessMin + approx.guessMax) / 2;\n        revert Errors.ApproxFail();\n    }\n\n    /// INTENDED TO BE CALLED BY WHEN GUESS.OFFCHAIN == 0 ONLY ///\n\n    function validateApprox(ApproxParams memory approx) internal pure {\n        if (approx.guessMin > approx.guessMax || approx.eps > Math.ONE)\n            revert Errors.ApproxParamsInvalid(approx.guessMin, approx.guessMax, approx.eps);\n    }\n\n    function calcMaxPtIn(\n        MarketState memory market,\n        MarketPreCompute memory comp\n    ) internal pure returns (uint256) {\n        uint256 low = 0;\n        uint256 hi = uint256(comp.totalAsset) - 1;\n\n        while (low != hi) {\n            uint256 mid = (low + hi + 1) / 2;\n            if (calcSlope(comp, market.totalPt, int256(mid)) < 0) hi = mid - 1;\n            else low = mid;\n        }\n        return low;\n    }\n\n    function calcSlope(\n        MarketPreCompute memory comp,\n        int256 totalPt,\n        int256 ptToMarket\n    ) internal pure returns (int256) {\n        int256 diffAssetPtToMarket = comp.totalAsset - ptToMarket;\n        int256 sumPt = ptToMarket + totalPt;\n\n        require(diffAssetPtToMarket > 0 && sumPt > 0, \"invalid ptToMarket\");\n\n        int256 part1 = (ptToMarket * (totalPt + comp.totalAsset)).divDown(\n            sumPt * diffAssetPtToMarket\n        );\n\n        int256 part2 = sumPt.divDown(diffAssetPtToMarket).ln();\n        int256 part3 = Math.IONE.divDown(comp.rateScalar);\n\n        return comp.rateAnchor - (part1 - part2).mulDown(part3);\n    }\n}\n\nlibrary MarketApproxPtOutLib {\n    using MarketMathCore for MarketState;\n    using PYIndexLib for PYIndex;\n    using Math for uint256;\n    using Math for int256;\n    using LogExpMath for int256;\n\n    /**\n     * @dev algorithm:\n        - Bin search the amount of PT to swapExactOut\n        - Calculate the amount of SY needed\n        - Stop when the netSyIn is smaller approx exactSyIn\n        - guess & approx is for netSyIn\n     */\n    function approxSwapExactSyForPt(\n        MarketState memory market,\n        PYIndex index,\n        uint256 exactSyIn,\n        uint256 blockTime,\n        ApproxParams memory approx\n    ) internal pure returns (uint256 /*netPtOut*/, uint256 /*netSyFee*/) {\n        MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime);\n        if (approx.guessOffchain == 0) {\n            // no limit on min\n            approx.guessMax = Math.min(approx.guessMax, calcMaxPtOut(comp, market.totalPt));\n            validateApprox(approx);\n        }\n\n        for (uint256 iter = 0; iter < approx.maxIteration; ++iter) {\n            uint256 guess = nextGuess(approx, iter);\n\n            (uint256 netSyIn, uint256 netSyFee, ) = calcSyIn(market, comp, index, guess);\n\n            if (netSyIn <= exactSyIn) {\n                if (Math.isASmallerApproxB(netSyIn, exactSyIn, approx.eps))\n                    return (guess, netSyFee);\n                approx.guessMin = guess;\n            } else {\n                approx.guessMax = guess - 1;\n            }\n        }\n\n        revert Errors.ApproxFail();\n    }\n\n    /**\n     * @dev algorithm:\n        - Bin search the amount of PT to swapExactOut\n        - Flashswap that amount of PT & pair with YT to redeem SY\n        - Use the SY to repay the flashswap debt and the remaining is transferred to user\n        - Stop when the netSyOut is greater approx the minSyOut\n        - guess & approx is for netSyOut\n     */\n    function approxSwapYtForExactSy(\n        MarketState memory market,\n        PYIndex index,\n        uint256 minSyOut,\n        uint256 blockTime,\n        ApproxParams memory approx\n    ) internal pure returns (uint256 /*netYtIn*/, uint256 /*netSyOut*/, uint256 /*netSyFee*/) {\n        MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime);\n        if (approx.guessOffchain == 0) {\n            // no limit on min\n            approx.guessMax = Math.min(approx.guessMax, calcMaxPtOut(comp, market.totalPt));\n            validateApprox(approx);\n        }\n\n        for (uint256 iter = 0; iter < approx.maxIteration; ++iter) {\n            uint256 guess = nextGuess(approx, iter);\n\n            (uint256 netSyOwed, uint256 netSyFee, ) = calcSyIn(market, comp, index, guess);\n\n            uint256 netAssetToRepay = index.syToAssetUp(netSyOwed);\n            uint256 netSyOut = index.assetToSy(guess - netAssetToRepay);\n\n            if (netSyOut >= minSyOut) {\n                if (Math.isAGreaterApproxB(netSyOut, minSyOut, approx.eps))\n                    return (guess, netSyOut, netSyFee);\n                approx.guessMax = guess;\n            } else {\n                approx.guessMin = guess + 1;\n            }\n        }\n        revert Errors.ApproxFail();\n    }\n\n    struct Args6 {\n        MarketState market;\n        PYIndex index;\n        uint256 totalSyIn;\n        uint256 blockTime;\n        ApproxParams approx;\n    }\n\n    /**\n     * @dev algorithm:\n        - Bin search the amount of PT to swapExactOut\n        - Swap that amount of PT out\n        - Pair the remaining PT with the SY to add liquidity\n        - Stop when the ratio of PT / totalPt & SY / totalSy is approx\n        - guess & approx is for netPtFromSwap\n     */\n    function approxSwapSyToAddLiquidity(\n        MarketState memory _market,\n        PYIndex _index,\n        uint256 _totalSyIn,\n        uint256 _blockTime,\n        ApproxParams memory _approx\n    )\n        internal\n        pure\n        returns (uint256 /*netPtFromSwap*/, uint256 /*netSySwap*/, uint256 /*netSyFee*/)\n    {\n        Args6 memory a = Args6(_market, _index, _totalSyIn, _blockTime, _approx);\n\n        MarketPreCompute memory comp = a.market.getMarketPreCompute(a.index, a.blockTime);\n        if (a.approx.guessOffchain == 0) {\n            // no limit on min\n            a.approx.guessMax = Math.min(a.approx.guessMax, calcMaxPtOut(comp, a.market.totalPt));\n            validateApprox(a.approx);\n            require(a.market.totalLp != 0, \"no existing lp\");\n        }\n\n        for (uint256 iter = 0; iter < a.approx.maxIteration; ++iter) {\n            uint256 guess = nextGuess(a.approx, iter);\n\n            (uint256 netSyIn, uint256 netSyFee, uint256 netSyToReserve) = calcSyIn(\n                a.market,\n                comp,\n                a.index,\n                guess\n            );\n\n            if (netSyIn > a.totalSyIn) {\n                a.approx.guessMax = guess - 1;\n                continue;\n            }\n\n            uint256 syNumerator;\n            uint256 ptNumerator;\n\n            {\n                uint256 newTotalPt = uint256(a.market.totalPt) - guess;\n                uint256 netTotalSy = uint256(a.market.totalSy) + netSyIn - netSyToReserve;\n\n                // it is desired that\n                // netPtFromSwap / newTotalPt = netSyRemaining / netTotalSy\n                // which is equivalent to\n                // netPtFromSwap * netTotalSy = netSyRemaining * newTotalPt\n\n                ptNumerator = guess * netTotalSy;\n                syNumerator = (a.totalSyIn - netSyIn) * newTotalPt;\n            }\n\n            if (Math.isAApproxB(ptNumerator, syNumerator, a.approx.eps))\n                return (guess, netSyIn, netSyFee);\n\n            if (ptNumerator <= syNumerator) {\n                // needs more PT\n                a.approx.guessMin = guess + 1;\n            } else {\n                // needs less PT\n                a.approx.guessMax = guess - 1;\n            }\n        }\n        revert Errors.ApproxFail();\n    }\n\n    /**\n     * @dev algorithm:\n        - Bin search the amount of PT to swapExactOut\n        - Flashswap that amount of PT out\n        - Pair all the PT with the YT to redeem SY\n        - Use the SY to repay the flashswap debt\n        - Stop when the amount of YT required to pair with PT is approx exactYtIn\n        - guess & approx is for netPtFromSwap\n     */\n    function approxSwapExactYtForPt(\n        MarketState memory market,\n        PYIndex index,\n        uint256 exactYtIn,\n        uint256 blockTime,\n        ApproxParams memory approx\n    )\n        internal\n        pure\n        returns (uint256 /*netPtOut*/, uint256 /*totalPtSwapped*/, uint256 /*netSyFee*/)\n    {\n        MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime);\n        if (approx.guessOffchain == 0) {\n            approx.guessMin = Math.max(approx.guessMin, exactYtIn);\n            approx.guessMax = Math.min(approx.guessMax, calcMaxPtOut(comp, market.totalPt));\n            validateApprox(approx);\n        }\n\n        for (uint256 iter = 0; iter < approx.maxIteration; ++iter) {\n            uint256 guess = nextGuess(approx, iter);\n\n            (uint256 netSyOwed, uint256 netSyFee, ) = calcSyIn(market, comp, index, guess);\n\n            uint256 netYtToPull = index.syToAssetUp(netSyOwed);\n\n            if (netYtToPull <= exactYtIn) {\n                if (Math.isASmallerApproxB(netYtToPull, exactYtIn, approx.eps))\n                    return (guess - netYtToPull, guess, netSyFee);\n                approx.guessMin = guess;\n            } else {\n                approx.guessMax = guess - 1;\n            }\n        }\n        revert Errors.ApproxFail();\n    }\n\n    ////////////////////////////////////////////////////////////////////////////////\n\n    function calcSyIn(\n        MarketState memory market,\n        MarketPreCompute memory comp,\n        PYIndex index,\n        uint256 netPtOut\n    ) internal pure returns (uint256 netSyIn, uint256 netSyFee, uint256 netSyToReserve) {\n        (int256 _netSyIn, int256 _netSyFee, int256 _netSyToReserve) = market.calcTrade(\n            comp,\n            index,\n            int256(netPtOut)\n        );\n\n        // all safe since totalPt and totalSy is int128\n        netSyIn = uint256(-_netSyIn);\n        netSyFee = uint256(_netSyFee);\n        netSyToReserve = uint256(_netSyToReserve);\n    }\n\n    function calcMaxPtOut(\n        MarketPreCompute memory comp,\n        int256 totalPt\n    ) internal pure returns (uint256) {\n        int256 logitP = (comp.feeRate - comp.rateAnchor).mulDown(comp.rateScalar).exp();\n        int256 proportion = logitP.divDown(logitP + Math.IONE);\n        int256 numerator = proportion.mulDown(totalPt + comp.totalAsset);\n        int256 maxPtOut = totalPt - numerator;\n        // only get 99.9% of the theoretical max to accommodate some precision issues\n        return (uint256(maxPtOut) * 999) / 1000;\n    }\n\n    function nextGuess(ApproxParams memory approx, uint256 iter) internal pure returns (uint256) {\n        if (iter == 0 && approx.guessOffchain != 0) return approx.guessOffchain;\n        if (approx.guessMin <= approx.guessMax) return (approx.guessMin + approx.guessMax) / 2;\n        revert Errors.ApproxFail();\n    }\n\n    function validateApprox(ApproxParams memory approx) internal pure {\n        if (approx.guessMin > approx.guessMax || approx.eps > Math.ONE)\n            revert Errors.ApproxParamsInvalid(approx.guessMin, approx.guessMax, approx.eps);\n    }\n}\n"
      },
      "contracts/libraries/math/LogExpMath.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the “Software”), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n\n// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npragma solidity 0.8.19;\n\n/* solhint-disable */\n\n/**\n * @dev Exponentiation and logarithm functions for 18 decimal fixed point numbers (both base and exponent/argument).\n *\n * Exponentiation and logarithm with arbitrary bases (x^y and log_x(y)) are implemented by conversion to natural\n * exponentiation and logarithm (where the base is Euler's number).\n *\n * @author Fernando Martinelli - @fernandomartinelli\n * @author Sergio Yuhjtman - @sergioyuhjtman\n * @author Daniel Fernandez - @dmf7z\n */\nlibrary LogExpMath {\n    // All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying\n    // two numbers, and multiply by ONE when dividing them.\n\n    // All arguments and return values are 18 decimal fixed point numbers.\n    int256 constant ONE_18 = 1e18;\n\n    // Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the\n    // case of ln36, 36 decimals.\n    int256 constant ONE_20 = 1e20;\n    int256 constant ONE_36 = 1e36;\n\n    // The domain of natural exponentiation is bound by the word size and number of decimals used.\n    //\n    // Because internally the result will be stored using 20 decimals, the largest possible result is\n    // (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221.\n    // The smallest possible result is 10^(-18), which makes largest negative argument\n    // ln(10^(-18)) = -41.446531673892822312.\n    // We use 130.0 and -41.0 to have some safety margin.\n    int256 constant MAX_NATURAL_EXPONENT = 130e18;\n    int256 constant MIN_NATURAL_EXPONENT = -41e18;\n\n    // Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point\n    // 256 bit integer.\n    int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17;\n    int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17;\n\n    uint256 constant MILD_EXPONENT_BOUND = 2 ** 254 / uint256(ONE_20);\n\n    // 18 decimal constants\n    int256 constant x0 = 128000000000000000000; // 2ˆ7\n    int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; // eˆ(x0) (no decimals)\n    int256 constant x1 = 64000000000000000000; // 2ˆ6\n    int256 constant a1 = 6235149080811616882910000000; // eˆ(x1) (no decimals)\n\n    // 20 decimal constants\n    int256 constant x2 = 3200000000000000000000; // 2ˆ5\n    int256 constant a2 = 7896296018268069516100000000000000; // eˆ(x2)\n    int256 constant x3 = 1600000000000000000000; // 2ˆ4\n    int256 constant a3 = 888611052050787263676000000; // eˆ(x3)\n    int256 constant x4 = 800000000000000000000; // 2ˆ3\n    int256 constant a4 = 298095798704172827474000; // eˆ(x4)\n    int256 constant x5 = 400000000000000000000; // 2ˆ2\n    int256 constant a5 = 5459815003314423907810; // eˆ(x5)\n    int256 constant x6 = 200000000000000000000; // 2ˆ1\n    int256 constant a6 = 738905609893065022723; // eˆ(x6)\n    int256 constant x7 = 100000000000000000000; // 2ˆ0\n    int256 constant a7 = 271828182845904523536; // eˆ(x7)\n    int256 constant x8 = 50000000000000000000; // 2ˆ-1\n    int256 constant a8 = 164872127070012814685; // eˆ(x8)\n    int256 constant x9 = 25000000000000000000; // 2ˆ-2\n    int256 constant a9 = 128402541668774148407; // eˆ(x9)\n    int256 constant x10 = 12500000000000000000; // 2ˆ-3\n    int256 constant a10 = 113314845306682631683; // eˆ(x10)\n    int256 constant x11 = 6250000000000000000; // 2ˆ-4\n    int256 constant a11 = 106449445891785942956; // eˆ(x11)\n\n    /**\n     * @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent.\n     *\n     * Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`.\n     */\n    function exp(int256 x) internal pure returns (int256) {\n        unchecked {\n            require(x >= MIN_NATURAL_EXPONENT && x <= MAX_NATURAL_EXPONENT, \"Invalid exponent\");\n\n            if (x < 0) {\n                // We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it\n                // fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT).\n                // Fixed point division requires multiplying by ONE_18.\n                return ((ONE_18 * ONE_18) / exp(-x));\n            }\n\n            // First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n,\n            // where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7\n            // because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the\n            // decomposition.\n            // At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this\n            // decomposition, which will be lower than the smallest x_n.\n            // exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1.\n            // We mutate x by subtracting x_n, making it the remainder of the decomposition.\n\n            // The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause\n            // intermediate overflows. Instead we store them as plain integers, with 0 decimals.\n            // Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the\n            // decomposition.\n\n            // For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct\n            // it and compute the accumulated product.\n\n            int256 firstAN;\n            if (x >= x0) {\n                x -= x0;\n                firstAN = a0;\n            } else if (x >= x1) {\n                x -= x1;\n                firstAN = a1;\n            } else {\n                firstAN = 1; // One with no decimal places\n            }\n\n            // We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the\n            // smaller terms.\n            x *= 100;\n\n            // `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point\n            // one. Recall that fixed point multiplication requires dividing by ONE_20.\n            int256 product = ONE_20;\n\n            if (x >= x2) {\n                x -= x2;\n                product = (product * a2) / ONE_20;\n            }\n            if (x >= x3) {\n                x -= x3;\n                product = (product * a3) / ONE_20;\n            }\n            if (x >= x4) {\n                x -= x4;\n                product = (product * a4) / ONE_20;\n            }\n            if (x >= x5) {\n                x -= x5;\n                product = (product * a5) / ONE_20;\n            }\n            if (x >= x6) {\n                x -= x6;\n                product = (product * a6) / ONE_20;\n            }\n            if (x >= x7) {\n                x -= x7;\n                product = (product * a7) / ONE_20;\n            }\n            if (x >= x8) {\n                x -= x8;\n                product = (product * a8) / ONE_20;\n            }\n            if (x >= x9) {\n                x -= x9;\n                product = (product * a9) / ONE_20;\n            }\n\n            // x10 and x11 are unnecessary here since we have high enough precision already.\n\n            // Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series\n            // expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!).\n\n            int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places.\n            int256 term; // Each term in the sum, where the nth term is (x^n / n!).\n\n            // The first term is simply x.\n            term = x;\n            seriesSum += term;\n\n            // Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number,\n            // multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not.\n\n            term = ((term * x) / ONE_20) / 2;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 3;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 4;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 5;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 6;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 7;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 8;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 9;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 10;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 11;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 12;\n            seriesSum += term;\n\n            // 12 Taylor terms are sufficient for 18 decimal precision.\n\n            // We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor\n            // approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply\n            // all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication),\n            // and then drop two digits to return an 18 decimal value.\n\n            return (((product * seriesSum) / ONE_20) * firstAN) / 100;\n        }\n    }\n\n    /**\n     * @dev Natural logarithm (ln(a)) with signed 18 decimal fixed point argument.\n     */\n    function ln(int256 a) internal pure returns (int256) {\n        unchecked {\n            // The real natural logarithm is not defined for negative numbers or zero.\n            require(a > 0, \"out of bounds\");\n            if (LN_36_LOWER_BOUND < a && a < LN_36_UPPER_BOUND) {\n                return _ln_36(a) / ONE_18;\n            } else {\n                return _ln(a);\n            }\n        }\n    }\n\n    /**\n     * @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent.\n     *\n     * Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`.\n     */\n    function pow(uint256 x, uint256 y) internal pure returns (uint256) {\n        unchecked {\n            if (y == 0) {\n                // We solve the 0^0 indetermination by making it equal one.\n                return uint256(ONE_18);\n            }\n\n            if (x == 0) {\n                return 0;\n            }\n\n            // Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to\n            // arrive at that r`esult. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means\n            // x^y = exp(y * ln(x)).\n\n            // The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range.\n            require(x < 2 ** 255, \"x out of bounds\");\n            int256 x_int256 = int256(x);\n\n            // We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In\n            // both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end.\n\n            // This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range.\n            require(y < MILD_EXPONENT_BOUND, \"y out of bounds\");\n            int256 y_int256 = int256(y);\n\n            int256 logx_times_y;\n            if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) {\n                int256 ln_36_x = _ln_36(x_int256);\n\n                // ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just\n                // bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal\n                // multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the\n                // (downscaled) last 18 decimals.\n                logx_times_y = ((ln_36_x / ONE_18) *\n                    y_int256 +\n                    ((ln_36_x % ONE_18) * y_int256) /\n                    ONE_18);\n            } else {\n                logx_times_y = _ln(x_int256) * y_int256;\n            }\n            logx_times_y /= ONE_18;\n\n            // Finally, we compute exp(y * ln(x)) to arrive at x^y\n            require(\n                MIN_NATURAL_EXPONENT <= logx_times_y && logx_times_y <= MAX_NATURAL_EXPONENT,\n                \"product out of bounds\"\n            );\n\n            return uint256(exp(logx_times_y));\n        }\n    }\n\n    /**\n     * @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument.\n     */\n    function _ln(int256 a) private pure returns (int256) {\n        unchecked {\n            if (a < ONE_18) {\n                // Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less\n                // than one, 1/a will be greater than one, and this if statement will not be entered in the recursive call.\n                // Fixed point division requires multiplying by ONE_18.\n                return (-_ln((ONE_18 * ONE_18) / a));\n            }\n\n            // First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which\n            // we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is,\n            // ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot\n            // be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a.\n            // At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this\n            // decomposition, which will be lower than the smallest a_n.\n            // ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1.\n            // We mutate a by subtracting a_n, making it the remainder of the decomposition.\n\n            // For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point\n            // numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by\n            // ONE_18 to convert them to fixed point.\n            // For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide\n            // by it and compute the accumulated sum.\n\n            int256 sum = 0;\n            if (a >= a0 * ONE_18) {\n                a /= a0; // Integer, not fixed point division\n                sum += x0;\n            }\n\n            if (a >= a1 * ONE_18) {\n                a /= a1; // Integer, not fixed point division\n                sum += x1;\n            }\n\n            // All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format.\n            sum *= 100;\n            a *= 100;\n\n            // Because further a_n are  20 digit fixed point numbers, we multiply by ONE_20 when dividing by them.\n\n            if (a >= a2) {\n                a = (a * ONE_20) / a2;\n                sum += x2;\n            }\n\n            if (a >= a3) {\n                a = (a * ONE_20) / a3;\n                sum += x3;\n            }\n\n            if (a >= a4) {\n                a = (a * ONE_20) / a4;\n                sum += x4;\n            }\n\n            if (a >= a5) {\n                a = (a * ONE_20) / a5;\n                sum += x5;\n            }\n\n            if (a >= a6) {\n                a = (a * ONE_20) / a6;\n                sum += x6;\n            }\n\n            if (a >= a7) {\n                a = (a * ONE_20) / a7;\n                sum += x7;\n            }\n\n            if (a >= a8) {\n                a = (a * ONE_20) / a8;\n                sum += x8;\n            }\n\n            if (a >= a9) {\n                a = (a * ONE_20) / a9;\n                sum += x9;\n            }\n\n            if (a >= a10) {\n                a = (a * ONE_20) / a10;\n                sum += x10;\n            }\n\n            if (a >= a11) {\n                a = (a * ONE_20) / a11;\n                sum += x11;\n            }\n\n            // a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series\n            // that converges rapidly for values of `a` close to one - the same one used in ln_36.\n            // Let z = (a - 1) / (a + 1).\n            // ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))\n\n            // Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires\n            // division by ONE_20.\n            int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20);\n            int256 z_squared = (z * z) / ONE_20;\n\n            // num is the numerator of the series: the z^(2 * n + 1) term\n            int256 num = z;\n\n            // seriesSum holds the accumulated sum of each term in the series, starting with the initial z\n            int256 seriesSum = num;\n\n            // In each step, the numerator is multiplied by z^2\n            num = (num * z_squared) / ONE_20;\n            seriesSum += num / 3;\n\n            num = (num * z_squared) / ONE_20;\n            seriesSum += num / 5;\n\n            num = (num * z_squared) / ONE_20;\n            seriesSum += num / 7;\n\n            num = (num * z_squared) / ONE_20;\n            seriesSum += num / 9;\n\n            num = (num * z_squared) / ONE_20;\n            seriesSum += num / 11;\n\n            // 6 Taylor terms are sufficient for 36 decimal precision.\n\n            // Finally, we multiply by 2 (non fixed point) to compute ln(remainder)\n            seriesSum *= 2;\n\n            // We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both\n            // with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal\n            // value.\n\n            return (sum + seriesSum) / 100;\n        }\n    }\n\n    /**\n     * @dev Intrnal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument,\n     * for x close to one.\n     *\n     * Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND.\n     */\n    function _ln_36(int256 x) private pure returns (int256) {\n        unchecked {\n            // Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits\n            // worthwhile.\n\n            // First, we transform x to a 36 digit fixed point value.\n            x *= ONE_18;\n\n            // We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1).\n            // ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))\n\n            // Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires\n            // division by ONE_36.\n            int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36);\n            int256 z_squared = (z * z) / ONE_36;\n\n            // num is the numerator of the series: the z^(2 * n + 1) term\n            int256 num = z;\n\n            // seriesSum holds the accumulated sum of each term in the series, starting with the initial z\n            int256 seriesSum = num;\n\n            // In each step, the numerator is multiplied by z^2\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 3;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 5;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 7;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 9;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 11;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 13;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 15;\n\n            // 8 Taylor terms are sufficient for 36 decimal precision.\n\n            // All that remains is multiplying by 2 (non fixed point).\n            return seriesSum * 2;\n        }\n    }\n}\n"
      },
      "contracts/libraries/math/MarketMathCore.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport \"./Math.sol\";\nimport \"./LogExpMath.sol\";\n\nimport \"../PYIndex.sol\";\nimport \"../MiniHelpers.sol\";\nimport \"../Errors.sol\";\n\nstruct MarketState {\n    int256 totalPt;\n    int256 totalSy;\n    int256 totalLp;\n    address treasury;\n    /// immutable variables ///\n    int256 scalarRoot;\n    uint256 expiry;\n    /// fee data ///\n    uint256 lnFeeRateRoot;\n    uint256 reserveFeePercent; // base 100\n    /// last trade data ///\n    uint256 lastLnImpliedRate;\n}\n\n// params that are expensive to compute, therefore we pre-compute them\nstruct MarketPreCompute {\n    int256 rateScalar;\n    int256 totalAsset;\n    int256 rateAnchor;\n    int256 feeRate;\n}\n\n// solhint-disable ordering\nlibrary MarketMathCore {\n    using Math for uint256;\n    using Math for int256;\n    using LogExpMath for int256;\n    using PYIndexLib for PYIndex;\n\n    int256 internal constant MINIMUM_LIQUIDITY = 10 ** 3;\n    int256 internal constant PERCENTAGE_DECIMALS = 100;\n    uint256 internal constant DAY = 86400;\n    uint256 internal constant IMPLIED_RATE_TIME = 365 * DAY;\n\n    int256 internal constant MAX_MARKET_PROPORTION = (1e18 * 96) / 100;\n\n    using Math for uint256;\n    using Math for int256;\n\n    /*///////////////////////////////////////////////////////////////\n                UINT FUNCTIONS TO PROXY TO CORE FUNCTIONS\n    //////////////////////////////////////////////////////////////*/\n\n    function addLiquidity(\n        MarketState memory market,\n        uint256 syDesired,\n        uint256 ptDesired,\n        uint256 blockTime\n    )\n        internal\n        pure\n        returns (uint256 lpToReserve, uint256 lpToAccount, uint256 syUsed, uint256 ptUsed)\n    {\n        (\n            int256 _lpToReserve,\n            int256 _lpToAccount,\n            int256 _syUsed,\n            int256 _ptUsed\n        ) = addLiquidityCore(market, syDesired.Int(), ptDesired.Int(), blockTime);\n\n        lpToReserve = _lpToReserve.Uint();\n        lpToAccount = _lpToAccount.Uint();\n        syUsed = _syUsed.Uint();\n        ptUsed = _ptUsed.Uint();\n    }\n\n    function removeLiquidity(\n        MarketState memory market,\n        uint256 lpToRemove\n    ) internal pure returns (uint256 netSyToAccount, uint256 netPtToAccount) {\n        (int256 _syToAccount, int256 _ptToAccount) = removeLiquidityCore(market, lpToRemove.Int());\n\n        netSyToAccount = _syToAccount.Uint();\n        netPtToAccount = _ptToAccount.Uint();\n    }\n\n    function swapExactPtForSy(\n        MarketState memory market,\n        PYIndex index,\n        uint256 exactPtToMarket,\n        uint256 blockTime\n    ) internal pure returns (uint256 netSyToAccount, uint256 netSyFee, uint256 netSyToReserve) {\n        (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore(\n            market,\n            index,\n            exactPtToMarket.neg(),\n            blockTime\n        );\n\n        netSyToAccount = _netSyToAccount.Uint();\n        netSyFee = _netSyFee.Uint();\n        netSyToReserve = _netSyToReserve.Uint();\n    }\n\n    function swapSyForExactPt(\n        MarketState memory market,\n        PYIndex index,\n        uint256 exactPtToAccount,\n        uint256 blockTime\n    ) internal pure returns (uint256 netSyToMarket, uint256 netSyFee, uint256 netSyToReserve) {\n        (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore(\n            market,\n            index,\n            exactPtToAccount.Int(),\n            blockTime\n        );\n\n        netSyToMarket = _netSyToAccount.neg().Uint();\n        netSyFee = _netSyFee.Uint();\n        netSyToReserve = _netSyToReserve.Uint();\n    }\n\n    /*///////////////////////////////////////////////////////////////\n                    CORE FUNCTIONS\n    //////////////////////////////////////////////////////////////*/\n\n    function addLiquidityCore(\n        MarketState memory market,\n        int256 syDesired,\n        int256 ptDesired,\n        uint256 blockTime\n    )\n        internal\n        pure\n        returns (int256 lpToReserve, int256 lpToAccount, int256 syUsed, int256 ptUsed)\n    {\n        /// ------------------------------------------------------------\n        /// CHECKS\n        /// ------------------------------------------------------------\n        if (syDesired == 0 || ptDesired == 0) revert Errors.MarketZeroAmountsInput();\n        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();\n\n        /// ------------------------------------------------------------\n        /// MATH\n        /// ------------------------------------------------------------\n        if (market.totalLp == 0) {\n            lpToAccount = Math.sqrt((syDesired * ptDesired).Uint()).Int() - MINIMUM_LIQUIDITY;\n            lpToReserve = MINIMUM_LIQUIDITY;\n            syUsed = syDesired;\n            ptUsed = ptDesired;\n        } else {\n            int256 netLpByPt = (ptDesired * market.totalLp) / market.totalPt;\n            int256 netLpBySy = (syDesired * market.totalLp) / market.totalSy;\n            if (netLpByPt < netLpBySy) {\n                lpToAccount = netLpByPt;\n                ptUsed = ptDesired;\n                syUsed = (market.totalSy * lpToAccount) / market.totalLp;\n            } else {\n                lpToAccount = netLpBySy;\n                syUsed = syDesired;\n                ptUsed = (market.totalPt * lpToAccount) / market.totalLp;\n            }\n        }\n\n        if (lpToAccount <= 0) revert Errors.MarketZeroAmountsOutput();\n\n        /// ------------------------------------------------------------\n        /// WRITE\n        /// ------------------------------------------------------------\n        market.totalSy += syUsed;\n        market.totalPt += ptUsed;\n        market.totalLp += lpToAccount + lpToReserve;\n    }\n\n    function removeLiquidityCore(\n        MarketState memory market,\n        int256 lpToRemove\n    ) internal pure returns (int256 netSyToAccount, int256 netPtToAccount) {\n        /// ------------------------------------------------------------\n        /// CHECKS\n        /// ------------------------------------------------------------\n        if (lpToRemove == 0) revert Errors.MarketZeroAmountsInput();\n\n        /// ------------------------------------------------------------\n        /// MATH\n        /// ------------------------------------------------------------\n        netSyToAccount = (lpToRemove * market.totalSy) / market.totalLp;\n        netPtToAccount = (lpToRemove * market.totalPt) / market.totalLp;\n\n        if (netSyToAccount == 0 && netPtToAccount == 0) revert Errors.MarketZeroAmountsOutput();\n\n        /// ------------------------------------------------------------\n        /// WRITE\n        /// ------------------------------------------------------------\n        market.totalLp = market.totalLp.subNoNeg(lpToRemove);\n        market.totalPt = market.totalPt.subNoNeg(netPtToAccount);\n        market.totalSy = market.totalSy.subNoNeg(netSyToAccount);\n    }\n\n    function executeTradeCore(\n        MarketState memory market,\n        PYIndex index,\n        int256 netPtToAccount,\n        uint256 blockTime\n    ) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) {\n        /// ------------------------------------------------------------\n        /// CHECKS\n        /// ------------------------------------------------------------\n        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();\n        if (market.totalPt <= netPtToAccount)\n            revert Errors.MarketInsufficientPtForTrade(market.totalPt, netPtToAccount);\n\n        /// ------------------------------------------------------------\n        /// MATH\n        /// ------------------------------------------------------------\n        MarketPreCompute memory comp = getMarketPreCompute(market, index, blockTime);\n\n        (netSyToAccount, netSyFee, netSyToReserve) = calcTrade(\n            market,\n            comp,\n            index,\n            netPtToAccount\n        );\n\n        /// ------------------------------------------------------------\n        /// WRITE\n        /// ------------------------------------------------------------\n        _setNewMarketStateTrade(\n            market,\n            comp,\n            index,\n            netPtToAccount,\n            netSyToAccount,\n            netSyToReserve,\n            blockTime\n        );\n    }\n\n    function getMarketPreCompute(\n        MarketState memory market,\n        PYIndex index,\n        uint256 blockTime\n    ) internal pure returns (MarketPreCompute memory res) {\n        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();\n\n        uint256 timeToExpiry = market.expiry - blockTime;\n\n        res.rateScalar = _getRateScalar(market, timeToExpiry);\n        res.totalAsset = index.syToAsset(market.totalSy);\n\n        if (market.totalPt == 0 || res.totalAsset == 0)\n            revert Errors.MarketZeroTotalPtOrTotalAsset(market.totalPt, res.totalAsset);\n\n        res.rateAnchor = _getRateAnchor(\n            market.totalPt,\n            market.lastLnImpliedRate,\n            res.totalAsset,\n            res.rateScalar,\n            timeToExpiry\n        );\n        res.feeRate = _getExchangeRateFromImpliedRate(market.lnFeeRateRoot, timeToExpiry);\n    }\n\n    function calcTrade(\n        MarketState memory market,\n        MarketPreCompute memory comp,\n        PYIndex index,\n        int256 netPtToAccount\n    ) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) {\n        int256 preFeeExchangeRate = _getExchangeRate(\n            market.totalPt,\n            comp.totalAsset,\n            comp.rateScalar,\n            comp.rateAnchor,\n            netPtToAccount\n        );\n\n        int256 preFeeAssetToAccount = netPtToAccount.divDown(preFeeExchangeRate).neg();\n        int256 fee = comp.feeRate;\n\n        if (netPtToAccount > 0) {\n            int256 postFeeExchangeRate = preFeeExchangeRate.divDown(fee);\n            if (postFeeExchangeRate < Math.IONE)\n                revert Errors.MarketExchangeRateBelowOne(postFeeExchangeRate);\n\n            fee = preFeeAssetToAccount.mulDown(Math.IONE - fee);\n        } else {\n            fee = ((preFeeAssetToAccount * (Math.IONE - fee)) / fee).neg();\n        }\n\n        int256 netAssetToReserve = (fee * market.reserveFeePercent.Int()) / PERCENTAGE_DECIMALS;\n        int256 netAssetToAccount = preFeeAssetToAccount - fee;\n\n        netSyToAccount = netAssetToAccount < 0\n            ? index.assetToSyUp(netAssetToAccount)\n            : index.assetToSy(netAssetToAccount);\n        netSyFee = index.assetToSy(fee);\n        netSyToReserve = index.assetToSy(netAssetToReserve);\n    }\n\n    function _setNewMarketStateTrade(\n        MarketState memory market,\n        MarketPreCompute memory comp,\n        PYIndex index,\n        int256 netPtToAccount,\n        int256 netSyToAccount,\n        int256 netSyToReserve,\n        uint256 blockTime\n    ) internal pure {\n        uint256 timeToExpiry = market.expiry - blockTime;\n\n        market.totalPt = market.totalPt.subNoNeg(netPtToAccount);\n        market.totalSy = market.totalSy.subNoNeg(netSyToAccount + netSyToReserve);\n\n        market.lastLnImpliedRate = _getLnImpliedRate(\n            market.totalPt,\n            index.syToAsset(market.totalSy),\n            comp.rateScalar,\n            comp.rateAnchor,\n            timeToExpiry\n        );\n\n        if (market.lastLnImpliedRate == 0) revert Errors.MarketZeroLnImpliedRate();\n    }\n\n    function _getRateAnchor(\n        int256 totalPt,\n        uint256 lastLnImpliedRate,\n        int256 totalAsset,\n        int256 rateScalar,\n        uint256 timeToExpiry\n    ) internal pure returns (int256 rateAnchor) {\n        int256 newExchangeRate = _getExchangeRateFromImpliedRate(lastLnImpliedRate, timeToExpiry);\n\n        if (newExchangeRate < Math.IONE) revert Errors.MarketExchangeRateBelowOne(newExchangeRate);\n\n        {\n            int256 proportion = totalPt.divDown(totalPt + totalAsset);\n\n            int256 lnProportion = _logProportion(proportion);\n\n            rateAnchor = newExchangeRate - lnProportion.divDown(rateScalar);\n        }\n    }\n\n    /// @notice Calculates the current market implied rate.\n    /// @return lnImpliedRate the implied rate\n    function _getLnImpliedRate(\n        int256 totalPt,\n        int256 totalAsset,\n        int256 rateScalar,\n        int256 rateAnchor,\n        uint256 timeToExpiry\n    ) internal pure returns (uint256 lnImpliedRate) {\n        // This will check for exchange rates < Math.IONE\n        int256 exchangeRate = _getExchangeRate(totalPt, totalAsset, rateScalar, rateAnchor, 0);\n\n        // exchangeRate >= 1 so its ln >= 0\n        uint256 lnRate = exchangeRate.ln().Uint();\n\n        lnImpliedRate = (lnRate * IMPLIED_RATE_TIME) / timeToExpiry;\n    }\n\n    /// @notice Converts an implied rate to an exchange rate given a time to expiry. The\n    /// formula is E = e^rt\n    function _getExchangeRateFromImpliedRate(\n        uint256 lnImpliedRate,\n        uint256 timeToExpiry\n    ) internal pure returns (int256 exchangeRate) {\n        uint256 rt = (lnImpliedRate * timeToExpiry) / IMPLIED_RATE_TIME;\n\n        exchangeRate = LogExpMath.exp(rt.Int());\n    }\n\n    function _getExchangeRate(\n        int256 totalPt,\n        int256 totalAsset,\n        int256 rateScalar,\n        int256 rateAnchor,\n        int256 netPtToAccount\n    ) internal pure returns (int256 exchangeRate) {\n        int256 numerator = totalPt.subNoNeg(netPtToAccount);\n\n        int256 proportion = (numerator.divDown(totalPt + totalAsset));\n\n        if (proportion > MAX_MARKET_PROPORTION)\n            revert Errors.MarketProportionTooHigh(proportion, MAX_MARKET_PROPORTION);\n\n        int256 lnProportion = _logProportion(proportion);\n\n        exchangeRate = lnProportion.divDown(rateScalar) + rateAnchor;\n\n        if (exchangeRate < Math.IONE) revert Errors.MarketExchangeRateBelowOne(exchangeRate);\n    }\n\n    function _logProportion(int256 proportion) internal pure returns (int256 res) {\n        if (proportion == Math.IONE) revert Errors.MarketProportionMustNotEqualOne();\n\n        int256 logitP = proportion.divDown(Math.IONE - proportion);\n\n        res = logitP.ln();\n    }\n\n    function _getRateScalar(\n        MarketState memory market,\n        uint256 timeToExpiry\n    ) internal pure returns (int256 rateScalar) {\n        rateScalar = (market.scalarRoot * IMPLIED_RATE_TIME.Int()) / timeToExpiry.Int();\n        if (rateScalar <= 0) revert Errors.MarketRateScalarBelowZero(rateScalar);\n    }\n\n    function setInitialLnImpliedRate(\n        MarketState memory market,\n        PYIndex index,\n        int256 initialAnchor,\n        uint256 blockTime\n    ) internal pure {\n        /// ------------------------------------------------------------\n        /// CHECKS\n        /// ------------------------------------------------------------\n        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();\n\n        /// ------------------------------------------------------------\n        /// MATH\n        /// ------------------------------------------------------------\n        int256 totalAsset = index.syToAsset(market.totalSy);\n        uint256 timeToExpiry = market.expiry - blockTime;\n        int256 rateScalar = _getRateScalar(market, timeToExpiry);\n\n        /// ------------------------------------------------------------\n        /// WRITE\n        /// ------------------------------------------------------------\n        market.lastLnImpliedRate = _getLnImpliedRate(\n            market.totalPt,\n            totalAsset,\n            rateScalar,\n            initialAnchor,\n            timeToExpiry\n        );\n    }\n}\n"
      },
      "contracts/libraries/math/Math.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\npragma solidity 0.8.19;\n\n/* solhint-disable private-vars-leading-underscore, reason-string */\n\nlibrary Math {\n    uint256 internal constant ONE = 1e18; // 18 decimal places\n    int256 internal constant IONE = 1e18; // 18 decimal places\n\n    function subMax0(uint256 a, uint256 b) internal pure returns (uint256) {\n        unchecked {\n            return (a >= b ? a - b : 0);\n        }\n    }\n\n    function subNoNeg(int256 a, int256 b) internal pure returns (int256) {\n        require(a >= b, \"negative\");\n        return a - b; // no unchecked since if b is very negative, a - b might overflow\n    }\n\n    function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {\n        uint256 product = a * b;\n        unchecked {\n            return product / ONE;\n        }\n    }\n\n    function mulDown(int256 a, int256 b) internal pure returns (int256) {\n        int256 product = a * b;\n        unchecked {\n            return product / IONE;\n        }\n    }\n\n    function divDown(uint256 a, uint256 b) internal pure returns (uint256) {\n        uint256 aInflated = a * ONE;\n        unchecked {\n            return aInflated / b;\n        }\n    }\n\n    function divDown(int256 a, int256 b) internal pure returns (int256) {\n        int256 aInflated = a * IONE;\n        unchecked {\n            return aInflated / b;\n        }\n    }\n\n    function rawDivUp(uint256 a, uint256 b) internal pure returns (uint256) {\n        return (a + b - 1) / b;\n    }\n\n    // @author Uniswap\n    function sqrt(uint256 y) internal pure returns (uint256 z) {\n        if (y > 3) {\n            z = y;\n            uint256 x = y / 2 + 1;\n            while (x < z) {\n                z = x;\n                x = (y / x + x) / 2;\n            }\n        } else if (y != 0) {\n            z = 1;\n        }\n    }\n\n    function abs(int256 x) internal pure returns (uint256) {\n        return uint256(x > 0 ? x : -x);\n    }\n\n    function neg(int256 x) internal pure returns (int256) {\n        return x * (-1);\n    }\n\n    function neg(uint256 x) internal pure returns (int256) {\n        return Int(x) * (-1);\n    }\n\n    function max(uint256 x, uint256 y) internal pure returns (uint256) {\n        return (x > y ? x : y);\n    }\n\n    function max(int256 x, int256 y) internal pure returns (int256) {\n        return (x > y ? x : y);\n    }\n\n    function min(uint256 x, uint256 y) internal pure returns (uint256) {\n        return (x < y ? x : y);\n    }\n\n    function min(int256 x, int256 y) internal pure returns (int256) {\n        return (x < y ? x : y);\n    }\n\n    /*///////////////////////////////////////////////////////////////\n                               SIGNED CASTS\n    //////////////////////////////////////////////////////////////*/\n\n    function Int(uint256 x) internal pure returns (int256) {\n        require(x <= uint256(type(int256).max));\n        return int256(x);\n    }\n\n    function Int128(int256 x) internal pure returns (int128) {\n        require(type(int128).min <= x && x <= type(int128).max);\n        return int128(x);\n    }\n\n    function Int128(uint256 x) internal pure returns (int128) {\n        return Int128(Int(x));\n    }\n\n    /*///////////////////////////////////////////////////////////////\n                               UNSIGNED CASTS\n    //////////////////////////////////////////////////////////////*/\n\n    function Uint(int256 x) internal pure returns (uint256) {\n        require(x >= 0);\n        return uint256(x);\n    }\n\n    function Uint32(uint256 x) internal pure returns (uint32) {\n        require(x <= type(uint32).max);\n        return uint32(x);\n    }\n\n    function Uint112(uint256 x) internal pure returns (uint112) {\n        require(x <= type(uint112).max);\n        return uint112(x);\n    }\n\n    function Uint96(uint256 x) internal pure returns (uint96) {\n        require(x <= type(uint96).max);\n        return uint96(x);\n    }\n\n    function Uint128(uint256 x) internal pure returns (uint128) {\n        require(x <= type(uint128).max);\n        return uint128(x);\n    }\n\n    function isAApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {\n        return mulDown(b, ONE - eps) <= a && a <= mulDown(b, ONE + eps);\n    }\n\n    function isAGreaterApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {\n        return a >= b && a <= mulDown(b, ONE + eps);\n    }\n\n    function isASmallerApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {\n        return a <= b && a >= mulDown(b, ONE - eps);\n    }\n}\n"
      },
      "contracts/libraries/MiniHelpers.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nlibrary MiniHelpers {\n    function isCurrentlyExpired(uint256 expiry) internal view returns (bool) {\n        return (expiry <= block.timestamp);\n    }\n\n    function isExpired(uint256 expiry, uint256 blockTime) internal pure returns (bool) {\n        return (expiry <= blockTime);\n    }\n\n    function isTimeInThePast(uint256 timestamp) internal view returns (bool) {\n        return (timestamp <= block.timestamp); // same definition as isCurrentlyExpired\n    }\n}\n"
      },
      "contracts/libraries/MintableERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract MintableERC20 is ERC20, Ownable {\n    /*\n    The ERC20 deployed will be owned by the others contracts of the protocol, specifically by\n    MasterMagpie and WombatStaking, forbidding the misuse of these functions for nefarious purposes\n    */\n    constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {} \n\n    function mint(address account, uint256 amount) external virtual onlyOwner {\n        _mint(account, amount);\n    }\n\n    function burn(address account, uint256 amount) external virtual onlyOwner {\n        _burn(account, amount);\n    }\n}"
      },
      "contracts/libraries/PYIndex.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport \"../interfaces/pendle/IPYieldToken.sol\";\nimport \"../interfaces/pendle/IPPrincipalToken.sol\";\n\nimport \"./SYUtils.sol\";\nimport \"./math/Math.sol\";\n\ntype PYIndex is uint256;\n\nlibrary PYIndexLib {\n    using Math for uint256;\n    using Math for int256;\n\n    function newIndex(IPYieldToken YT) internal returns (PYIndex) {\n        return PYIndex.wrap(YT.pyIndexCurrent());\n    }\n\n    function syToAsset(PYIndex index, uint256 syAmount) internal pure returns (uint256) {\n        return SYUtils.syToAsset(PYIndex.unwrap(index), syAmount);\n    }\n\n    function assetToSy(PYIndex index, uint256 assetAmount) internal pure returns (uint256) {\n        return SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount);\n    }\n\n    function assetToSyUp(PYIndex index, uint256 assetAmount) internal pure returns (uint256) {\n        return SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount);\n    }\n\n    function syToAssetUp(PYIndex index, uint256 syAmount) internal pure returns (uint256) {\n        uint256 _index = PYIndex.unwrap(index);\n        return SYUtils.syToAssetUp(_index, syAmount);\n    }\n\n    function syToAsset(PYIndex index, int256 syAmount) internal pure returns (int256) {\n        int256 sign = syAmount < 0 ? int256(-1) : int256(1);\n        return sign * (SYUtils.syToAsset(PYIndex.unwrap(index), syAmount.abs())).Int();\n    }\n\n    function assetToSy(PYIndex index, int256 assetAmount) internal pure returns (int256) {\n        int256 sign = assetAmount < 0 ? int256(-1) : int256(1);\n        return sign * (SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount.abs())).Int();\n    }\n\n    function assetToSyUp(PYIndex index, int256 assetAmount) internal pure returns (int256) {\n        int256 sign = assetAmount < 0 ? int256(-1) : int256(1);\n        return sign * (SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount.abs())).Int();\n    }\n}\n"
      },
      "contracts/libraries/SYUtils.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nlibrary SYUtils {\n    uint256 internal constant ONE = 1e18;\n\n    function syToAsset(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {\n        return (syAmount * exchangeRate) / ONE;\n    }\n\n    function syToAssetUp(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {\n        return (syAmount * exchangeRate + ONE - 1) / ONE;\n    }\n\n    function assetToSy(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) {\n        return (assetAmount * ONE) / exchangeRate;\n    }\n\n    function assetToSyUp(\n        uint256 exchangeRate,\n        uint256 assetAmount\n    ) internal pure returns (uint256) {\n        return (assetAmount * ONE + exchangeRate - 1) / exchangeRate;\n    }\n}\n"
      },
      "contracts/libraries/TokenHelper.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"../interfaces/IWETH.sol\";\n\nabstract contract TokenHelper {\n    using SafeERC20 for IERC20;\n    address internal constant NATIVE = address(0);\n    uint256 internal constant LOWER_BOUND_APPROVAL = type(uint96).max / 2; // some tokens use 96 bits for approval\n\n    function _transferIn(address token, address from, uint256 amount) internal {\n        if (token == NATIVE) require(msg.value == amount, \"eth mismatch\");\n        else if (amount != 0) IERC20(token).safeTransferFrom(from, address(this), amount);\n    }\n\n    function _transferFrom(IERC20 token, address from, address to, uint256 amount) internal {\n        if (amount != 0) token.safeTransferFrom(from, to, amount);\n    }\n\n    function _transferOut(address token, address to, uint256 amount) internal {\n        if (amount == 0) return;\n        if (token == NATIVE) {\n            (bool success, ) = to.call{ value: amount }(\"\");\n            require(success, \"eth send failed\");\n        } else {\n            IERC20(token).safeTransfer(to, amount);\n        }\n    }\n\n    function _transferOut(address[] memory tokens, address to, uint256[] memory amounts) internal {\n        uint256 numTokens = tokens.length;\n        require(numTokens == amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < numTokens; ) {\n            _transferOut(tokens[i], to, amounts[i]);\n            unchecked {\n                i++;\n            }\n        }\n    }\n\n    function _selfBalance(address token) internal view returns (uint256) {\n        return (token == NATIVE) ? address(this).balance : IERC20(token).balanceOf(address(this));\n    }\n\n    function _selfBalance(IERC20 token) internal view returns (uint256) {\n        return token.balanceOf(address(this));\n    }\n\n    /// @notice Approves the stipulated contract to spend the given allowance in the given token\n    /// @dev PLS PAY ATTENTION to tokens that requires the approval to be set to 0 before changing it\n    function _safeApprove(address token, address to, uint256 value) internal {\n        (bool success, bytes memory data) = token.call(\n            abi.encodeWithSelector(IERC20.approve.selector, to, value)\n        );\n        require(success && (data.length == 0 || abi.decode(data, (bool))), \"Safe Approve\");\n    }\n\n    function _safeApproveInf(address token, address to) internal {\n        if (token == NATIVE) return;\n        if (IERC20(token).allowance(address(this), to) < LOWER_BOUND_APPROVAL) {\n            _safeApprove(token, to, 0);\n            _safeApprove(token, to, type(uint256).max);\n        }\n    }\n\n    function _wrap_unwrap_ETH(address tokenIn, address tokenOut, uint256 netTokenIn) internal {\n        if (tokenIn == NATIVE) IWETH(tokenOut).deposit{ value: netTokenIn }();\n        else IWETH(tokenIn).withdraw(netTokenIn);\n    }\n}\n"
      },
      "contracts/libraries/VeBalanceLib.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.19;\n\nimport \"./math/Math.sol\";\nimport \"./Errors.sol\";\n\nstruct VeBalance {\n    uint128 bias;\n    uint128 slope;\n}\n\nstruct LockedPosition {\n    uint128 amount;\n    uint128 expiry;\n}\n\nlibrary VeBalanceLib {\n    using Math for uint256;\n    uint128 internal constant MAX_LOCK_TIME = 104 weeks;\n    uint256 internal constant USER_VOTE_MAX_WEIGHT = 10 ** 18;\n\n    function add(\n        VeBalance memory a,\n        VeBalance memory b\n    ) internal pure returns (VeBalance memory res) {\n        res.bias = a.bias + b.bias;\n        res.slope = a.slope + b.slope;\n    }\n\n    function sub(\n        VeBalance memory a,\n        VeBalance memory b\n    ) internal pure returns (VeBalance memory res) {\n        res.bias = a.bias - b.bias;\n        res.slope = a.slope - b.slope;\n    }\n\n    function sub(\n        VeBalance memory a,\n        uint128 slope,\n        uint128 expiry\n    ) internal pure returns (VeBalance memory res) {\n        res.slope = a.slope - slope;\n        res.bias = a.bias - slope * expiry;\n    }\n\n    function isExpired(VeBalance memory a) internal view returns (bool) {\n        return a.slope * uint128(block.timestamp) >= a.bias;\n    }\n\n    function getCurrentValue(VeBalance memory a) internal view returns (uint128) {\n        if (isExpired(a)) return 0;\n        return getValueAt(a, uint128(block.timestamp));\n    }\n\n    function getValueAt(VeBalance memory a, uint128 t) internal pure returns (uint128) {\n        if (a.slope * t > a.bias) {\n            return 0;\n        }\n        return a.bias - a.slope * t;\n    }\n\n    function getExpiry(VeBalance memory a) internal pure returns (uint128) {\n        if (a.slope == 0) revert Errors.VEZeroSlope(a.bias, a.slope);\n        return a.bias / a.slope;\n    }\n\n    function convertToVeBalance(\n        LockedPosition memory position\n    ) internal pure returns (VeBalance memory res) {\n        res.slope = position.amount / MAX_LOCK_TIME;\n        res.bias = res.slope * position.expiry;\n    }\n\n    function convertToVeBalance(\n        LockedPosition memory position,\n        uint256 weight\n    ) internal pure returns (VeBalance memory res) {\n        res.slope = ((position.amount * weight) / MAX_LOCK_TIME / USER_VOTE_MAX_WEIGHT).Uint128();\n        res.bias = res.slope * position.expiry;\n    }\n\n    function convertToVeBalance(\n        uint128 amount,\n        uint128 expiry\n    ) internal pure returns (uint128, uint128) {\n        VeBalance memory balance = convertToVeBalance(LockedPosition(amount, expiry));\n        return (balance.bias, balance.slope);\n    }\n}\n"
      },
      "contracts/libraries/VeHistoryLib.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// Forked from OpenZeppelin (v4.5.0) (utils/Checkpoints.sol)\npragma solidity ^0.8.19;\n\nimport \"./math/Math.sol\";\nimport \"./VeBalanceLib.sol\";\nimport \"./WeekMath.sol\";\n\nstruct Checkpoint {\n    uint128 timestamp;\n    VeBalance value;\n}\n\nlibrary CheckpointHelper {\n    function assignWith(Checkpoint memory a, Checkpoint memory b) internal pure {\n        a.timestamp = b.timestamp;\n        a.value = b.value;\n    }\n}\n\nlibrary Checkpoints {\n    struct History {\n        Checkpoint[] _checkpoints;\n    }\n\n    function length(History storage self) internal view returns (uint256) {\n        return self._checkpoints.length;\n    }\n\n    function get(History storage self, uint256 index) internal view returns (Checkpoint memory) {\n        return self._checkpoints[index];\n    }\n\n    function push(History storage self, VeBalance memory value) internal {\n        uint256 pos = self._checkpoints.length;\n        if (pos > 0 && self._checkpoints[pos - 1].timestamp == WeekMath.getCurrentWeekStart()) {\n            self._checkpoints[pos - 1].value = value;\n        } else {\n            self._checkpoints.push(\n                Checkpoint({ timestamp: WeekMath.getCurrentWeekStart(), value: value })\n            );\n        }\n    }\n}\n"
      },
      "contracts/libraries/WeekMath.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.19;\n\nlibrary WeekMath {\n    uint128 internal constant WEEK = 7 days;\n\n    function getWeekStartTimestamp(uint128 timestamp) internal pure returns (uint128) {\n        return (timestamp / WEEK) * WEEK;\n    }\n\n    function getCurrentWeekStart() internal view returns (uint128) {\n        return getWeekStartTimestamp(uint128(block.timestamp));\n    }\n\n    function isValidWTime(uint256 time) internal pure returns (bool) {\n        return time % WEEK == 0;\n    }\n}\n"
      },
      "contracts/pendle/PendleStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\npragma abicoder v2;\n\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { PendleStakingBaseUpg } from \"./PendleStakingBaseUpg.sol\";\nimport { IPVotingEscrowMainchain } from \"../interfaces/pendle/IPVotingEscrowMainchain.sol\";\nimport { IPFeeDistributorV2 } from \"../interfaces/pendle/IPFeeDistributorV2.sol\";\nimport { IPVoteController } from \"../interfaces/pendle/IPVoteController.sol\";\n\nimport \"../interfaces/IConvertor.sol\";\nimport \"../libraries/ERC20FactoryLib.sol\";\nimport \"../libraries/WeekMath.sol\";\n\n/// @title PendleStaking\n/// @notice PendleStaking is the main contract that holds vePendle position on behalf on user to get boosted yield and vote.\n///         PendleStaking is the main contract interacting with Pendle Finance side\n/// @author Magpie Team\n\ncontract PendleStaking is PendleStakingBaseUpg {\n    using SafeERC20 for IERC20;\n\n    uint256 public lockPeriod;\n\n    /* ============ Events ============ */\n    event SetLockDays(uint256 _oldLockDays, uint256 _newLockDays);\n\n    constructor() {_disableInitializers();}\n\n    function __PendleStaking_init(\n        address _pendle,\n        address _WETH,\n        address _vePendle,\n        address _distributorETH,\n        address _pendleRouter,\n        address _masterPenpie\n    ) public initializer {\n        __PendleStakingBaseUpg_init(\n            _pendle,\n            _WETH,\n            _vePendle,\n            _distributorETH,\n            _pendleRouter,\n            _masterPenpie\n        );\n        lockPeriod = 720 * 86400;\n    }\n\n    /// @notice get the penpie claimable revenue share in ETH\n    function totalUnclaimedETH() external view returns (uint256) {\n        return distributorETH.getProtocolTotalAccrued(address(this));\n    }\n\n    /* ============ VePendle Related Functions ============ */\n\n    function vote(\n        address[] calldata _pools,\n        uint64[] calldata _weights\n    ) external override {\n        if (msg.sender != voteManager) revert OnlyVoteManager();\n        if (_pools.length != _weights.length) revert LengthMismatch();\n\n        IPVoteController(pendleVote).vote(_pools, _weights);\n    }\n\n    function bootstrapVePendle(uint256[] calldata chainId) payable external onlyOwner returns( uint256 ) {\n        uint256 amount = IERC20(PENDLE).balanceOf(address(this));\n        IERC20(PENDLE).safeApprove(address(vePendle), amount);\n        uint128 lockTime = _getIncreaseLockTime();\n        return IPVotingEscrowMainchain(vePendle).increaseLockPositionAndBroadcast{value:msg.value}(uint128(amount), lockTime, chainId);\n    }\n\n    /// @notice convert PENDLE to mPendle\n    /// @param _amount the number of Pendle to convert\n    /// @dev the Pendle must already be in the contract\n    function convertPendle(\n        uint256 _amount,\n        uint256[] calldata chainId\n    ) public payable override whenNotPaused returns (uint256) {\n        uint256 preVePendleAmount = accumulatedVePendle();\n        if (_amount == 0) revert ZeroNotAllowed();\n\n        IERC20(PENDLE).safeTransferFrom(msg.sender, address(this), _amount);\n        IERC20(PENDLE).safeApprove(address(vePendle), _amount);\n\n        uint128 unlockTime = _getIncreaseLockTime();\n        IPVotingEscrowMainchain(vePendle).increaseLockPositionAndBroadcast{value:msg.value}(uint128(_amount), unlockTime, chainId);\n\n        uint256 mintedVePendleAmount = accumulatedVePendle() -\n            preVePendleAmount;\n        emit PendleLocked(_amount, lockPeriod, mintedVePendleAmount);\n\n        return mintedVePendleAmount;\n    }\n\n    function increaseLockTime(uint256 _unlockTime) external {\n        uint128 unlockTime = WeekMath.getWeekStartTimestamp(\n            uint128(block.timestamp + _unlockTime)\n        );\n        IPVotingEscrowMainchain(vePendle).increaseLockPosition(0, unlockTime);\n    }\n\n    function harvestVePendleReward(address[] calldata _pools) external {\n        if (this.totalUnclaimedETH() == 0) {\n            revert NoVePendleReward();\n        }\n\n        if (\n            (protocolFee != 0 && feeCollector == address(0)) ||\n            bribeManagerEOA == address(0)\n        ) revert InvalidFeeDestination();\n\n        (uint256 totalAmountOut, uint256[] memory amountsOut) = distributorETH\n            .claimProtocol(address(this), _pools);\n        // for protocol\n        uint256 fee = (totalAmountOut * protocolFee) / DENOMINATOR;\n        IERC20(WETH).safeTransfer(feeCollector, fee);\n\n        // for caller\n        uint256 callerFeeAmount = (totalAmountOut * vePendleHarvestCallerFee) /\n            DENOMINATOR;\n        IERC20(WETH).safeTransfer(msg.sender, callerFeeAmount);\n\n        uint256 left = totalAmountOut - fee - callerFeeAmount;\n        IERC20(WETH).safeTransfer(bribeManagerEOA, left);\n\n        emit VePendleHarvested(\n            totalAmountOut,\n            _pools,\n            amountsOut,\n            fee,\n            callerFeeAmount,\n            left\n        );\n    }\n\n    /* ============ Admin Functions ============ */\n\n    function setLockDays(uint256 _newLockPeriod) external onlyOwner {\n        uint256 oldLockPeriod = lockPeriod;\n        lockPeriod = _newLockPeriod;\n\n        emit SetLockDays(oldLockPeriod, lockPeriod);\n    }\n\n    /* ============ Internal Functions ============ */\n\n    function _getIncreaseLockTime() internal view returns (uint128) {\n        return\n            WeekMath.getWeekStartTimestamp(\n                uint128(block.timestamp + lockPeriod)\n            );\n    }\n}\n"
      },
      "contracts/pendle/PendleStakingBaseUpg.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.19;\npragma abicoder v2;\n\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { OwnableUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport { Initializable } from \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport { ReentrancyGuardUpgradeable } from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport { PausableUpgradeable } from \"@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol\";\n\nimport { IPendleMarketDepositHelper } from \"../interfaces/pendle/IPendleMarketDepositHelper.sol\";\nimport { IPVotingEscrowMainchain } from \"../interfaces/pendle/IPVotingEscrowMainchain.sol\";\nimport { IPFeeDistributorV2 } from \"../interfaces/pendle/IPFeeDistributorV2.sol\";\nimport { IPVoteController } from \"../interfaces/pendle/IPVoteController.sol\";\nimport { IPendleRouter } from \"../interfaces/pendle/IPendleRouter.sol\";\nimport { IMasterPenpie } from \"../interfaces/IMasterPenpie.sol\";\nimport { IETHZapper } from \"../interfaces/IETHZapper.sol\";\n\nimport \"../interfaces/ISmartPendleConvert.sol\";\nimport \"../interfaces/IBaseRewardPool.sol\";\nimport \"../interfaces/IMintableERC20.sol\";\nimport \"../interfaces/IWETH.sol\";\nimport \"../interfaces/IPendleStaking.sol\";\nimport \"../interfaces/pendle/IPendleMarket.sol\";\nimport \"../interfaces/IPenpieBribeManager.sol\";\n\nimport \"../interfaces/IConvertor.sol\";\nimport \"../libraries/ERC20FactoryLib.sol\";\nimport \"../libraries/WeekMath.sol\";\n\n/// @title PendleStakingBaseUpg\n/// @notice PendleStaking is the main contract that holds vePendle position on behalf on user to get boosted yield and vote.\n///         PendleStaking is the main contract interacting with Pendle Finance side\n/// @author Magpie Team\n\nabstract contract PendleStakingBaseUpg is\n    Initializable,\n    OwnableUpgradeable,\n    ReentrancyGuardUpgradeable,\n    PausableUpgradeable,\n    IPendleStaking\n{\n    using SafeERC20 for IERC20;\n\n    /* ============ Structs ============ */\n\n    struct Pool {\n        address market;\n        address rewarder;\n        address helper;\n        address receiptToken;\n        uint256 lastHarvestTime;\n        bool isActive;\n    }\n\n    struct Fees {\n        uint256 value; // allocation denominated by DENOMINATOR\n        address to;\n        bool isMPENDLE;\n        bool isAddress;\n        bool isActive;\n    }\n\n    /* ============ State Variables ============ */\n    // Addresses\n    address public PENDLE;\n    address public WETH;\n    address public mPendleConvertor;\n    address public mPendleOFT;\n    address public marketDepositHelper;\n    address public masterPenpie;\n    address public voteManager;\n    uint256 public harvestTimeGap;\n\n    address internal constant NATIVE = address(0);\n\n    //Pendle Finance addresses\n    IPVotingEscrowMainchain public vePendle;\n    IPFeeDistributorV2 public distributorETH;\n    IPVoteController public pendleVote;\n    IPendleRouter public pendleRouter;\n\n    mapping(address => Pool) public pools;\n    address[] public poolTokenList;\n\n    // Lp Fees\n    uint256 constant DENOMINATOR = 10000;\n    uint256 public totalPendleFee; // total fee percentage for PENDLE reward\n    Fees[] public pendleFeeInfos; // infor of fee and destination\n    uint256 public autoBribeFee; // fee for any reward other than PENDLE\n\n    // vePendle Fees\n    uint256 public vePendleHarvestCallerFee;\n    uint256 public protocolFee; // fee charged by penpie team for operation\n    address public feeCollector; // penpie team fee destination\n    address public bribeManagerEOA; // An EOA address to later user vePendle harvested reward as bribe\n\n    /* ===== 1st upgrade ===== */\n    address public bribeManager;\n    address public smartPendleConvert;\n    address public ETHZapper;\n    uint256 public harvestCallerPendleFee;\n\n    uint256[46] private __gap;\n\n\n    /* ============ Events ============ */\n\n    // Admin\n    event PoolAdded(address _market, address _rewarder, address _receiptToken);\n    event PoolRemoved(uint256 _pid, address _lpToken);\n\n    event SetMPendleConvertor(address _oldmPendleConvertor, address _newmPendleConvertor);\n\n    // Fee\n    event AddPendleFee(address _to, uint256 _value, bool _isMPENDLE, bool _isAddress);\n    event SetPendleFee(address _to, uint256 _value);\n    event RemovePendleFee(uint256 value, address to, bool _isMPENDLE, bool _isAddress);\n    event RewardPaidTo(address _market, address _to, address _rewardToken, uint256 _feeAmount);\n    event VePendleHarvested(\n        uint256 _total,\n        address[] _pool,\n        uint256[] _totalAmounts,\n        uint256 _protocolFee,\n        uint256 _callerFee,\n        uint256 _rest\n    );\n\n    event NewMarketDeposit(\n        address indexed _user,\n        address indexed _market,\n        uint256 _lpAmount,\n        address indexed _receptToken,\n        uint256 _receptAmount\n    );\n    event NewMarketWithdraw(\n        address indexed _user,\n        address indexed _market,\n        uint256 _lpAmount,\n        address indexed _receptToken,\n        uint256 _receptAmount\n    );\n    event PendleLocked(uint256 _amount, uint256 _lockDays, uint256 _vePendleAccumulated);\n\n    // Vote Manager\n    event VoteSet(\n        address _voter,\n        uint256 _vePendleHarvestCallerFee,\n        uint256 _harvestCallerPendleFee,\n        uint256 _voteProtocolFee,\n        address _voteFeeCollector\n    );\n    event VoteManagerUpdated(address _oldVoteManager, address _voteManager);\n    event BribeManagerUpdated(address _oldBribeManager, address _bribeManager);\n    event BribeManagerEOAUpdated(address _oldBribeManagerEOA, address _bribeManagerEOA);\n\n    event SmartPendleConvertUpdated(address _OldSmartPendleConvert, address _smartPendleConvert);\n\n    event PoolHelperUpdated(address _market);\n\n    /* ============ Errors ============ */\n\n    error OnlyPoolHelper();\n    error OnlyActivePool();\n    error PoolOccupied();\n    error InvalidFee();\n    error LengthMismatch();\n    error OnlyVoteManager();\n    error TimeGapTooMuch();\n    error NoVePendleReward();\n    error InvalidFeeDestination();\n    error ZeroNotAllowed();\n    error InvalidAddress();\n\n    /* ============ Constructor ============ */\n\n    function __PendleStakingBaseUpg_init(\n        address _pendle,\n        address _WETH,\n        address _vePendle,\n        address _distributorETH,\n        address _pendleRouter,\n        address _masterPenpie\n    ) public initializer {\n        __Ownable_init();\n        __ReentrancyGuard_init();\n        __Pausable_init();\n        PENDLE = _pendle;\n        WETH = _WETH;\n        masterPenpie = _masterPenpie;\n        vePendle = IPVotingEscrowMainchain(_vePendle);\n        distributorETH = IPFeeDistributorV2(_distributorETH);\n        pendleRouter = IPendleRouter(_pendleRouter);\n    }\n\n    /* ============ Modifiers ============ */\n\n    modifier _onlyPoolHelper(address _market) {\n        Pool storage poolInfo = pools[_market];\n\n        if (msg.sender != poolInfo.helper) revert OnlyPoolHelper();\n        _;\n    }\n\n    modifier _onlyActivePool(address _market) {\n        Pool storage poolInfo = pools[_market];\n\n        if (!poolInfo.isActive) revert OnlyActivePool();\n        _;\n    }\n\n    modifier _onlyActivePoolHelper(address _market) {\n        Pool storage poolInfo = pools[_market];\n\n        if (msg.sender != poolInfo.helper) revert OnlyPoolHelper();\n        if (!poolInfo.isActive) revert OnlyActivePool();\n        _;\n    }\n\n    /* ============ External Getters ============ */\n\n    receive() external payable {\n        // Deposit ETH to WETH\n        IWETH(WETH).deposit{ value: msg.value }();\n    }\n\n    /// @notice get the number of vePendle of this contract\n    function accumulatedVePendle() public view returns (uint256) {\n        return IPVotingEscrowMainchain(vePendle).balanceOf(address(this));\n    }\n\n    function getPoolLength() external view returns (uint256) {\n        return poolTokenList.length;\n    }\n\n    /* ============ External Functions ============ */\n\n    function depositMarket(\n        address _market,\n        address _for,\n        address _from,\n        uint256 _amount\n    ) external override nonReentrant whenNotPaused _onlyActivePoolHelper(_market){\n        Pool storage poolInfo = pools[_market];\n        _harvestMarketRewards(poolInfo.market, false);\n\n        IERC20(poolInfo.market).safeTransferFrom(_from, address(this), _amount);\n\n        // mint the receipt to the user driectly\n        IMintableERC20(poolInfo.receiptToken).mint(_for, _amount);\n\n        emit NewMarketDeposit(_for, _market, _amount, poolInfo.receiptToken, _amount);\n    }\n\n    function withdrawMarket(\n        address _market,\n        address _for,\n        uint256 _amount\n    ) external override nonReentrant whenNotPaused _onlyPoolHelper(_market) {\n        Pool storage poolInfo = pools[_market];\n        _harvestMarketRewards(poolInfo.market, false);\n\n        IMintableERC20(poolInfo.receiptToken).burn(_for, _amount);\n\n        IERC20(poolInfo.market).safeTransfer(_for, _amount);\n        // emit New withdraw\n        emit NewMarketWithdraw(_for, _market, _amount, poolInfo.receiptToken, _amount);\n    }\n\n    /// @notice harvest a Rewards from Pendle Liquidity Pool\n    /// @param _market Pendle Pool lp as helper identifier\n    function harvestMarketReward(\n        address _market,\n        address _caller,\n        uint256 _minEthRecive\n    ) external whenNotPaused _onlyActivePool(_market) {\n        address[] memory _markets = new address[](1);\n        _markets[0] = _market;\n        _harvestBatchMarketRewards(_markets, _caller, _minEthRecive); // triggers harvest from Pendle finance\n    }\n\n    function batchHarvestMarketRewards(\n        address[] calldata _markets,\n        uint256 minEthToRecieve\n    ) external whenNotPaused {\n        _harvestBatchMarketRewards(_markets, msg.sender, minEthToRecieve);\n    }\n\n    /* ============ Admin Functions ============ */\n\n    function registerPool(\n        address _market,\n        uint256 _allocPoints,\n        string memory name,\n        string memory symbol\n    ) external onlyOwner {\n        if (pools[_market].isActive != false) {\n            revert PoolOccupied();\n        }\n\n        IERC20 newToken = IERC20(\n            ERC20FactoryLib.createReceipt(_market, masterPenpie, name, symbol)\n        );\n\n        address rewarder = IMasterPenpie(masterPenpie).createRewarder(\n            address(newToken),\n            address(PENDLE)\n        );\n\n        IPendleMarketDepositHelper(marketDepositHelper).setPoolInfo(_market, rewarder, true);\n\n        IMasterPenpie(masterPenpie).add(\n            _allocPoints,\n            address(_market),\n            address(newToken),\n            address(rewarder)\n        );\n\n        pools[_market] = Pool({\n            isActive: true,\n            market: _market,\n            receiptToken: address(newToken),\n            rewarder: address(rewarder),\n            helper: marketDepositHelper,\n            lastHarvestTime: block.timestamp\n        });\n        poolTokenList.push(_market);\n\n        emit PoolAdded(_market, address(rewarder), address(newToken));\n    }\n\n    /// @notice set the mPendleConvertor address\n    /// @param _mPendleConvertor the mPendleConvertor address\n    function setMPendleConvertor(address _mPendleConvertor) external onlyOwner {\n        address oldMPendleConvertor = mPendleConvertor;\n        mPendleConvertor = _mPendleConvertor;\n\n        emit SetMPendleConvertor(oldMPendleConvertor, mPendleConvertor);\n    }\n\n    function setVoteManager(address _voteManager) external onlyOwner {\n        address oldVoteManager = voteManager;\n        voteManager = _voteManager;\n\n        emit VoteManagerUpdated(oldVoteManager, voteManager);\n    }\n\n    function setBribeManager(address _bribeManager, address _bribeManagerEOA) external onlyOwner {\n        address oldBribeManager = bribeManager;\n        bribeManager = _bribeManager;\n\n        address oldBribeManagerEOA = bribeManagerEOA;\n        bribeManagerEOA = _bribeManagerEOA;\n\n        emit BribeManagerUpdated(oldBribeManager, bribeManager);\n        emit BribeManagerEOAUpdated(oldBribeManagerEOA, bribeManagerEOA);\n    }\n\n    function setmasterPenpie(address _masterPenpie) external onlyOwner {\n        masterPenpie = _masterPenpie;\n    }\n\n    function setMPendleOFT(address _setMPendleOFT) external onlyOwner {\n        mPendleOFT = _setMPendleOFT;\n    }\n\n    function setETHZapper(address _ETHZapper) external onlyOwner {\n        ETHZapper = _ETHZapper;\n    }\n\n    /**\n     * @notice pause Pendle staking, restricting certain operations\n     */\n    function pause() external nonReentrant onlyOwner {\n        _pause();\n    }\n\n    /**\n     * @notice unpause Pendle staking, enabling certain operations\n     */\n    function unpause() external nonReentrant onlyOwner {\n        _unpause();\n    }\n\n    /// @notice This function adds a fee to the magpie protocol\n    /// @param _value the initial value for that fee\n    /// @param _to the address or contract that receives the fee\n    /// @param _isMPENDLE true if the fee is sent as MPENDLE, otherwise it will be PENDLE\n    /// @param _isAddress true if the receiver is an address, otherwise it's a BaseRewarder\n    function addPendleFee(\n        uint256 _value,\n        address _to,\n        bool _isMPENDLE,\n        bool _isAddress\n    ) external onlyOwner {\n        if (_value >= DENOMINATOR) revert InvalidFee();\n\n        pendleFeeInfos.push(\n            Fees({\n                value: _value,\n                to: _to,\n                isMPENDLE: _isMPENDLE,\n                isAddress: _isAddress,\n                isActive: true\n            })\n        );\n        totalPendleFee += _value;\n\n        emit AddPendleFee(_to, _value, _isMPENDLE, _isAddress);\n    }\n\n    /**\n     * @dev Set the Pendle fee.\n     * @param _index The index of the fee.\n     * @param _value The value of the fee.\n     * @param _to The address to which the fee is sent.\n     * @param _isMPENDLE Boolean indicating if the fee is in MPENDLE.\n     * @param _isAddress Boolean indicating if the fee is in an external token.\n     * @param _isActive Boolean indicating if the fee is active.\n     */\n    function setPendleFee(\n        uint256 _index,\n        uint256 _value,\n        address _to,\n        bool _isMPENDLE,\n        bool _isAddress,\n        bool _isActive\n    ) external onlyOwner {\n        if (_value >= DENOMINATOR) revert InvalidFee();\n\n        Fees storage fee = pendleFeeInfos[_index];\n        fee.to = _to;\n        fee.isMPENDLE = _isMPENDLE;\n        fee.isAddress = _isAddress;\n        fee.isActive = _isActive;\n\n        totalPendleFee = totalPendleFee - fee.value + _value;\n        fee.value = _value;\n\n        emit SetPendleFee(fee.to, _value);\n    }\n\n    /// @notice remove some fee\n    /// @param _index the index of the fee in the fee list\n    function removePendleFee(uint256 _index) external onlyOwner {\n        Fees memory feeToRemove = pendleFeeInfos[_index];\n\n        for (uint i = _index; i < pendleFeeInfos.length - 1; i++) {\n            pendleFeeInfos[i] = pendleFeeInfos[i + 1];\n        }\n        pendleFeeInfos.pop();\n        totalPendleFee -= feeToRemove.value;\n\n        emit RemovePendleFee(\n            feeToRemove.value,\n            feeToRemove.to,\n            feeToRemove.isMPENDLE,\n            feeToRemove.isAddress\n        );\n    }\n\n    function setVote(\n        address _pendleVote,\n        uint256 _vePendleHarvestCallerFee,\n        uint256 _harvestCallerPendleFee,\n        uint256 _protocolFee,\n        address _feeCollector\n    ) external onlyOwner {\n        if ((_vePendleHarvestCallerFee + _protocolFee) > DENOMINATOR) revert InvalidFee();\n\n        if ((_harvestCallerPendleFee + _protocolFee) > DENOMINATOR) revert InvalidFee();\n\n        pendleVote = IPVoteController(_pendleVote);\n        vePendleHarvestCallerFee = _vePendleHarvestCallerFee;\n        harvestCallerPendleFee = _harvestCallerPendleFee;\n        protocolFee = _protocolFee;\n        feeCollector = _feeCollector;\n\n        emit VoteSet(\n            _pendleVote,\n            vePendleHarvestCallerFee,\n            harvestCallerPendleFee,\n            protocolFee,\n            feeCollector\n        );\n    }\n\n    function setMarketDepositHelper(address _helper) external onlyOwner {\n        marketDepositHelper = _helper;\n    }\n\n    function setHarvestTimeGap(uint256 _period) external onlyOwner {\n        if (_period > 4 hours) revert TimeGapTooMuch();\n\n        harvestTimeGap = _period;\n    }\n\n    function setSmartConvert(address _smartPendleConvert) external onlyOwner {\n        if (_smartPendleConvert == address(0)) revert InvalidAddress();\n        address oldSmartPendleConvert = smartPendleConvert;\n        smartPendleConvert = _smartPendleConvert;\n\n        emit SmartPendleConvertUpdated(oldSmartPendleConvert, smartPendleConvert);\n    }\n\n    function setAutoBribeFee(uint256 _autoBribeFee) external onlyOwner {\n        if (_autoBribeFee > DENOMINATOR) revert InvalidFee();\n        autoBribeFee = _autoBribeFee;\n    }\n\n    function updateMarketRewards(address _market, uint256[] memory amounts) external onlyOwner {\n        Pool storage poolInfo = pools[_market];\n        address[] memory bonusTokens = IPendleMarket(_market).getRewardTokens();\n        require(bonusTokens.length == amounts.length, \"...\");\n\n        uint256 pendleBefore = IERC20(PENDLE).balanceOf(address(this));\n        for (uint256 i; i < bonusTokens.length; i++) {\n            if (bonusTokens[i] == NATIVE) bonusTokens[i] = address(WETH);\n            uint256 leftAmounts = amounts[i];\n            _sendRewards(_market, bonusTokens[i], poolInfo.rewarder, amounts[i], leftAmounts);\n        }\n\n        uint256 pendleForMPendleFee = IERC20(PENDLE).balanceOf(address(this)) - pendleBefore;\n        _sendMPendleFees(pendleForMPendleFee);\n    }\n\n    function updatePoolHelper(\n        address _market,\n        address _helper\n    ) external onlyOwner _onlyActivePool(_market) {\n        if (_helper == address(0) || _market == address(0)) revert InvalidAddress();\n        \n        Pool storage poolInfo = pools[_market];\n        poolInfo.helper = _helper;\n\n        IPendleMarketDepositHelper(_helper).setPoolInfo(\n            _market,\n            poolInfo.rewarder,\n            poolInfo.isActive\n        );\n\n        emit PoolHelperUpdated(_helper);\n    }\n\n    /* ============ Internal Functions ============ */\n\n    function _harvestMarketRewards(address _market, bool _force) internal {\n        Pool storage poolInfo = pools[_market];\n        if (!_force && (block.timestamp - poolInfo.lastHarvestTime) < harvestTimeGap) return;\n        uint256 pendleBefore = IERC20(PENDLE).balanceOf(address(this));\n\n        poolInfo.lastHarvestTime = block.timestamp;\n\n        address[] memory bonusTokens = IPendleMarket(_market).getRewardTokens();\n        uint256[] memory amountsBefore = new uint256[](bonusTokens.length);\n\n        for (uint256 i; i < bonusTokens.length; i++) {\n            if (bonusTokens[i] == NATIVE) bonusTokens[i] = address(WETH);\n            amountsBefore[i] = IERC20(bonusTokens[i]).balanceOf(address(this));\n        }\n\n        IPendleMarket(_market).redeemRewards(address(this));\n\n        for (uint256 i; i < bonusTokens.length; i++) {\n            if (bonusTokens[i] == NATIVE) bonusTokens[i] = address(WETH);\n            uint256 amountAfter = IERC20(bonusTokens[i]).balanceOf(address(this));\n            uint256 bonusBalance = amountAfter - amountsBefore[i];\n            uint256 leftBonusBalance = bonusBalance;\n            if (bonusBalance > 0) {\n                _sendRewards(\n                    _market,\n                    bonusTokens[i],\n                    poolInfo.rewarder,\n                    bonusBalance,\n                    leftBonusBalance\n                );\n            }\n        }\n\n        uint256 pendleForMPendleFee = IERC20(PENDLE).balanceOf(address(this)) - pendleBefore;\n        _sendMPendleFees(pendleForMPendleFee);\n    }\n\n    function _harvestBatchMarketRewards(\n        address[] memory _markets,\n        address _caller,\n        uint256 _minEthToRecieve\n    ) internal {\n        uint256 harvestCallerTotalPendleReward;\n        uint256 pendleBefore = IERC20(PENDLE).balanceOf(address(this));\n\n        for (uint256 i = 0; i < _markets.length; i++) {\n            if (!pools[_markets[i]].isActive) revert OnlyActivePool();\n            Pool storage poolInfo = pools[_markets[i]];\n\n            poolInfo.lastHarvestTime = block.timestamp;\n\n            address[] memory bonusTokens = IPendleMarket(_markets[i]).getRewardTokens();\n            uint256[] memory amountsBefore = new uint256[](bonusTokens.length);\n\n            for (uint256 j; j < bonusTokens.length; j++) {\n                if (bonusTokens[j] == NATIVE) bonusTokens[j] = address(WETH);\n\n                amountsBefore[j] = IERC20(bonusTokens[j]).balanceOf(address(this));\n            }\n\n            IPendleMarket(_markets[i]).redeemRewards(address(this));\n\n            for (uint256 j; j < bonusTokens.length; j++) {\n                uint256 amountAfter = IERC20(bonusTokens[j]).balanceOf(address(this));\n\n                uint256 originalBonusBalance = amountAfter - amountsBefore[j];\n                uint256 leftBonusBalance = originalBonusBalance;\n                uint256 currentMarketHarvestPendleReward;\n\n                if (originalBonusBalance == 0) continue;\n\n                if (bonusTokens[j] == PENDLE) {\n                    currentMarketHarvestPendleReward =\n                        (originalBonusBalance * harvestCallerPendleFee) /\n                        DENOMINATOR;\n                    leftBonusBalance = originalBonusBalance - currentMarketHarvestPendleReward;\n                }\n                harvestCallerTotalPendleReward += currentMarketHarvestPendleReward;\n\n                _sendRewards(\n                    _markets[i],\n                    bonusTokens[j],\n                    poolInfo.rewarder,\n                    originalBonusBalance,\n                    leftBonusBalance\n                );\n            }\n        }\n\n        uint256 pendleForMPendleFee = IERC20(PENDLE).balanceOf(address(this)) - pendleBefore - harvestCallerTotalPendleReward;\n        _sendMPendleFees(pendleForMPendleFee);\n        \n        if (harvestCallerTotalPendleReward > 0) {\n            IERC20(PENDLE).approve(ETHZapper, harvestCallerTotalPendleReward);\n\n            IETHZapper(ETHZapper).swapExactTokensToETH(\n                PENDLE,\n                harvestCallerTotalPendleReward,\n                _minEthToRecieve,\n                _caller\n            );\n        }\n    }\n\n    function _sendMPendleFees(uint256 _pendleAmount) internal {\n        uint256 totalmPendleFees;\n        uint256 mPendleFeesToSend;\n\n        if (_pendleAmount > 0) {\n            mPendleFeesToSend = _convertPendleTomPendle(_pendleAmount);\n        } else {\n            return; // no need to send mPendle\n        }\n\n        for (uint256 i = 0; i < pendleFeeInfos.length; i++) {\n            Fees storage feeInfo = pendleFeeInfos[i];\n            if (feeInfo.isActive && feeInfo.isMPENDLE){\n                totalmPendleFees+=feeInfo.value;\n            }\n        }\n\n        for (uint256 i = 0; i < pendleFeeInfos.length; i++) {\n            Fees storage feeInfo = pendleFeeInfos[i];\n            if (feeInfo.isActive && feeInfo.isMPENDLE) {\n                uint256 amount = mPendleFeesToSend * (feeInfo.value * DENOMINATOR / totalmPendleFees)/ DENOMINATOR;\n                if(amount > 0){\n                    if (!feeInfo.isAddress) {\n                        IERC20(mPendleOFT).safeApprove(feeInfo.to, amount);\n                        IBaseRewardPool(feeInfo.to).queueNewRewards(amount, mPendleOFT);\n                    } else {\n                        IERC20(mPendleOFT).safeTransfer(feeInfo.to, amount);\n                    }\n                }\n            }\n        }\n    }\n\n    function _convertPendleTomPendle(uint256 _pendleAmount) internal returns(uint256 mPendleToSend) {\n        uint256 mPendleBefore = IERC20(mPendleOFT).balanceOf(address(this));\n        \n        if (smartPendleConvert != address(0)) {\n            IERC20(PENDLE).safeApprove(smartPendleConvert, _pendleAmount);\n            ISmartPendleConvert(smartPendleConvert).smartConvert(_pendleAmount, 0);\n            mPendleToSend = IERC20(mPendleOFT).balanceOf(address(this)) - mPendleBefore;\n        } else {\n            IERC20(PENDLE).safeApprove(mPendleConvertor, _pendleAmount);\n            IConvertor(mPendleConvertor).convert(address(this), _pendleAmount, 0);\n            mPendleToSend = IERC20(mPendleOFT).balanceOf(address(this)) - mPendleBefore;\n        }\n    }\n\n    /// @notice Send rewards to the rewarders\n    /// @param _market the PENDLE market\n    /// @param _rewardToken the address of the reward token to send\n    /// @param _rewarder the rewarder for PENDLE lp that will get the rewards\n    /// @param _originalRewardAmount  the initial amount of rewards after harvest\n    /// @param _leftRewardAmount the intial amount - harvest caller rewardfee amount after harvest\n    function _sendRewards(\n        address _market,\n        address _rewardToken,\n        address _rewarder,\n        uint256 _originalRewardAmount,\n        uint256 _leftRewardAmount\n    ) internal {\n        if (_leftRewardAmount == 0) return;\n\n        if (_rewardToken == address(PENDLE)) {\n            for (uint256 i = 0; i < pendleFeeInfos.length; i++) {\n                Fees storage feeInfo = pendleFeeInfos[i];\n\n                if (feeInfo.isActive) {\n                    uint256 feeAmount = (_originalRewardAmount * feeInfo.value) / DENOMINATOR;\n                    _leftRewardAmount -= feeAmount;\n                    uint256 feeTosend = feeAmount;\n\n                    if (!feeInfo.isMPENDLE) {\n                        if (!feeInfo.isAddress) {\n                            IERC20(_rewardToken).safeApprove(feeInfo.to, feeTosend);\n                            IBaseRewardPool(feeInfo.to).queueNewRewards(feeTosend, _rewardToken);\n                        } else {\n                            IERC20(_rewardToken).safeTransfer(feeInfo.to, feeTosend);\n                        }\n                    }\n                    emit RewardPaidTo(_market, feeInfo.to, _rewardToken, feeTosend);\n                }\n            }\n        } else {\n            // other than PENDLE reward token.\n            // if auto Bribe fee is 0, then all go to LP rewarder\n            if (autoBribeFee > 0 && bribeManager != address(0)) {\n                uint256 bribePid = IPenpieBribeManager(bribeManager).marketToPid(_market);\n                if (IPenpieBribeManager(bribeManager).pools(bribePid)._active) {\n                    uint256 autoBribeAmount = (_originalRewardAmount * autoBribeFee) / DENOMINATOR;\n                    _leftRewardAmount -= autoBribeAmount;\n                    IERC20(_rewardToken).safeApprove(bribeManager, autoBribeAmount);\n                    IPenpieBribeManager(bribeManager).addBribeERC20(\n                        1,\n                        bribePid,\n                        _rewardToken,\n                        autoBribeAmount\n                    );\n\n                    emit RewardPaidTo(_market, bribeManager, _rewardToken, autoBribeAmount);\n                }\n            }\n        }\n\n        IERC20(_rewardToken).safeApprove(_rewarder, 0);\n        IERC20(_rewardToken).safeApprove(_rewarder, _leftRewardAmount);\n        IBaseRewardPool(_rewarder).queueNewRewards(_leftRewardAmount, _rewardToken);\n        emit RewardPaidTo(_market, _rewarder, _rewardToken, _leftRewardAmount);\n    }\n}"
      },
      "contracts/rewards/BaseRewardPoolV2.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport  { IMasterPenpie } from \"../interfaces/IMasterPenpie.sol\";\n\nimport \"../interfaces/IBaseRewardPool.sol\";\n\n/// @title A contract for managing rewards for a pool\n/// @author Magpie Team\n/// @notice You can use this contract for getting informations about rewards for a specific pools\ncontract BaseRewardPoolV2 is Ownable, IBaseRewardPool {\n    using SafeERC20 for IERC20Metadata;\n    using SafeERC20 for IERC20;\n\n    /* ============ State Variables ============ */\n\n    address public immutable receiptToken;\n    address public immutable operator;          // master Penpie\n    uint256 public immutable receiptTokenDecimals;\n\n    address[] public rewardTokens;\n\n    struct Reward {\n        address rewardToken;\n        uint256 rewardPerTokenStored;\n        uint256 queuedRewards;\n    }\n\n    struct UserInfo {\n        uint256 userRewardPerTokenPaid;\n        uint256 userRewards;\n    }\n\n    mapping(address => Reward) public rewards;                           // [rewardToken]\n    // amount by [rewardToken][account], \n    mapping(address => mapping(address => UserInfo)) public userInfos;\n    mapping(address => bool) public isRewardToken;\n    mapping(address => bool) public rewardQueuers;\n\n    /* ============ Events ============ */\n\n    event RewardAdded(uint256 _reward, address indexed _token);\n    event Staked(address indexed _user, uint256 _amount);\n    event Withdrawn(address indexed _user, uint256 _amount);\n    event RewardPaid(address indexed _user, address indexed _receiver, uint256 _reward, address indexed _token);\n    event RewardQueuerUpdated(address indexed _manager, bool _allowed);\n\n    /* ============ Errors ============ */\n\n    error OnlyRewardQueuer();\n    error OnlyMasterPenpie();\n    error NotAllowZeroAddress();\n    error MustBeRewardToken();\n\n    /* ============ Constructor ============ */\n\n    constructor(\n        address _receiptToken,\n        address _rewardToken,\n        address _masterPenpie,\n        address _rewardQueuer\n    ) {\n        if(\n            _receiptToken == address(0) ||\n            _masterPenpie  == address(0) ||\n            _rewardQueuer  == address(0)\n        ) revert NotAllowZeroAddress();\n\n        receiptToken = _receiptToken;\n        receiptTokenDecimals = IERC20Metadata(receiptToken).decimals();\n        operator = _masterPenpie;\n\n        if (_rewardToken != address(0)) {\n            rewards[_rewardToken] = Reward({\n                rewardToken: _rewardToken,\n                rewardPerTokenStored: 0,\n                queuedRewards: 0\n            });\n            rewardTokens.push(_rewardToken);\n        }\n\n        isRewardToken[_rewardToken] = true;\n        rewardQueuers[_rewardQueuer] = true;\n    }\n\n    /* ============ Modifiers ============ */\n\n    modifier onlyRewardQueuer() {\n        if (!rewardQueuers[msg.sender])\n            revert OnlyRewardQueuer();\n        _;\n    }\n\n    modifier onlyMasterPenpie() {\n        if (msg.sender != operator)\n            revert OnlyMasterPenpie();\n        _;\n    }\n\n    modifier updateReward(address _account) {\n        _updateFor(_account);\n        _;\n    }\n\n    modifier updateRewards(address _account, address[] memory _rewards) {\n        uint256 length = _rewards.length;\n        uint256 userShare = balanceOf(_account);\n        \n        for (uint256 index = 0; index < length; ++index) {\n            address rewardToken = _rewards[index];\n            UserInfo storage userInfo = userInfos[rewardToken][_account];\n            // if a reward stopped queuing, no need to recalculate to save gas fee\n            if (userInfo.userRewardPerTokenPaid == rewardPerToken(rewardToken))\n                continue;\n            userInfo.userRewards = _earned(_account, rewardToken, userShare);\n            userInfo.userRewardPerTokenPaid = rewardPerToken(rewardToken);\n        }\n        _;\n    }    \n\n    /* ============ External Getters ============ */\n\n    /// @notice Returns current amount of staked tokens\n    /// @return Returns current amount of staked tokens\n    function totalStaked() public override virtual view returns (uint256) {\n        return IERC20(receiptToken).totalSupply();\n    }\n\n    /// @notice Returns amount of staked tokens in master Penpie by account\n    /// @param _account Address account\n    /// @return Returns amount of staked tokens by account\n    function balanceOf(address _account) public override virtual view returns (uint256) {\n        return IERC20(receiptToken).balanceOf(_account);\n    }\n\n    function stakingDecimals() external override virtual view returns (uint256) {\n        return receiptTokenDecimals;\n    }\n\n    /// @notice Returns amount of reward token per staking tokens in pool\n    /// @param _rewardToken Address reward token\n    /// @return Returns amount of reward token per staking tokens in pool\n    function rewardPerToken(address _rewardToken)\n        public\n        override\n        view\n        returns (uint256)\n    {\n        return rewards[_rewardToken].rewardPerTokenStored;\n    }\n\n    function rewardTokenInfos()\n        override\n        external\n        view\n        returns\n        (\n            address[] memory bonusTokenAddresses,\n            string[] memory bonusTokenSymbols\n        )\n    {\n        uint256 rewardTokensLength = rewardTokens.length;\n        bonusTokenAddresses = new address[](rewardTokensLength);\n        bonusTokenSymbols = new string[](rewardTokensLength);\n        for (uint256 i; i < rewardTokensLength; i++) {\n            bonusTokenAddresses[i] = rewardTokens[i];\n            bonusTokenSymbols[i] = IERC20Metadata(address(bonusTokenAddresses[i])).symbol();\n        }\n    }\n\n    /// @notice Returns amount of reward token earned by a user\n    /// @param _account Address account\n    /// @param _rewardToken Address reward token\n    /// @return Returns amount of reward token earned by a user\n    function earned(address _account, address _rewardToken)\n        public\n        override\n        view\n        returns (uint256)\n    {\n        return _earned(_account, _rewardToken, balanceOf(_account));\n    }\n\n    /// @notice Returns amount of all reward tokens\n    /// @param _account Address account\n    /// @return pendingBonusRewards as amounts of all rewards.\n    function allEarned(address _account)\n        external\n        override\n        view\n        returns (\n            uint256[] memory pendingBonusRewards\n        )\n    {\n        uint256 length = rewardTokens.length;\n        pendingBonusRewards = new uint256[](length);\n        for (uint256 i = 0; i < length; i++) {\n            pendingBonusRewards[i] = earned(_account, rewardTokens[i]);\n        }\n\n        return pendingBonusRewards;\n    }\n\n    function getRewardLength() external view returns(uint256) {\n        return rewardTokens.length;\n    }    \n\n    /* ============ External Functions ============ */\n\n    /// @notice Updates the reward information for one account\n    /// @param _account Address account\n    function updateFor(address _account) override external {\n        _updateFor(_account);\n    }\n\n    function getReward(address _account, address _receiver)\n        public\n        onlyMasterPenpie\n        updateReward(_account)\n        returns (bool)\n    {\n        uint256 length = rewardTokens.length;\n\n        for (uint256 index = 0; index < length; ++index) {\n                address rewardToken = rewardTokens[index];\n                _sendReward(rewardToken, _account, _receiver);\n            }\n        return true;\n    }\n\n    function getRewards(address _account, address _receiver, address[] memory _rewardTokens) override\n        external\n        onlyMasterPenpie\n        updateRewards(_account, _rewardTokens)\n    {\n       uint256 length = _rewardTokens.length;\n        \n        for (uint256 index = 0; index < length; ++index) {\n            address rewardToken = _rewardTokens[index];\n            _sendReward(rewardToken, _account, _receiver);\n        }\n    }\n\n    /// @notice Sends new rewards to be distributed to the users staking. Only possible to donate already registered token\n    /// @param _amountReward Amount of reward token to be distributed\n    /// @param _rewardToken Address reward token\n    function donateRewards(uint256 _amountReward, address _rewardToken) external {\n        if (!isRewardToken[_rewardToken])\n            revert MustBeRewardToken();\n\n        _provisionReward(_amountReward, _rewardToken);\n    }\n\n    /* ============ Admin Functions ============ */\n\n    function updateRewardQueuer(address _rewardManager, bool _allowed) external onlyOwner {\n        rewardQueuers[_rewardManager] = _allowed;\n\n        emit RewardQueuerUpdated(_rewardManager, rewardQueuers[_rewardManager]);\n    }\n\n    /// @notice Sends new rewards to be distributed to the users staking. Only callable by manager\n    /// @param _amountReward Amount of reward token to be distributed\n    /// @param _rewardToken Address reward token\n    function queueNewRewards(uint256 _amountReward, address _rewardToken)\n        override\n        external\n        onlyRewardQueuer\n        returns (bool)\n    {\n        if (!isRewardToken[_rewardToken]) {\n            rewardTokens.push(_rewardToken);\n            isRewardToken[_rewardToken] = true;\n        }\n\n        _provisionReward(_amountReward, _rewardToken);\n        return true;\n    }\n\n    /* ============ Internal Functions ============ */\n\n    function _provisionReward(uint256 _amountReward, address _rewardToken) internal {\n        IERC20(_rewardToken).safeTransferFrom(\n            msg.sender,\n            address(this),\n            _amountReward\n        );\n        Reward storage rewardInfo = rewards[_rewardToken];\n\n        uint256 totalStake = totalStaked();\n        if (totalStake == 0) {\n            rewardInfo.queuedRewards += _amountReward;\n        } else {\n            if (rewardInfo.queuedRewards > 0) {\n                _amountReward += rewardInfo.queuedRewards;\n                rewardInfo.queuedRewards = 0;\n            }\n            rewardInfo.rewardPerTokenStored =\n                rewardInfo.rewardPerTokenStored +\n                (_amountReward * 10**receiptTokenDecimals) /\n                totalStake;\n        }\n        emit RewardAdded(_amountReward, _rewardToken);\n    }\n\n    function _earned(address _account, address _rewardToken, uint256 _userShare) internal view returns (uint256) {\n        UserInfo storage userInfo = userInfos[_rewardToken][_account];\n        return ((_userShare *\n                (rewardPerToken(_rewardToken) -\n                    userInfo.userRewardPerTokenPaid)) /\n                10**receiptTokenDecimals) + userInfo.userRewards;\n    }\n\n    function _sendReward(address _rewardToken, address _account, address _receiver) internal {\n        uint256 _amount = userInfos[_rewardToken][_account].userRewards;\n        if (_amount != 0) {\n            userInfos[_rewardToken][_account].userRewards = 0;\n            IERC20(_rewardToken).safeTransfer(_receiver, _amount);\n            emit RewardPaid(_account, _receiver, _amount, _rewardToken);\n        }\n    }\n\n    function _updateFor(address _account) internal {\n        uint256 length = rewardTokens.length;\n        for (uint256 index = 0; index < length; ++index) {\n            address rewardToken = rewardTokens[index];\n            UserInfo storage userInfo = userInfos[rewardToken][_account];\n            // if a reward stopped queuing, no need to recalculate to save gas fee\n            if (userInfo.userRewardPerTokenPaid == rewardPerToken(rewardToken))\n                continue;\n\n            userInfo.userRewards = earned(_account, rewardToken);\n            userInfo.userRewardPerTokenPaid = rewardPerToken(rewardToken);\n        }\n    }\n}"
      },
      "contracts/rewards/PenpieReceiptToken.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\npragma solidity ^0.8.19;\n\nimport { ERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IMasterPenpie } from \"../interfaces/IMasterPenpie.sol\";\n\n/// @title PenpieReceiptToken is to represent a Pendle Market deposited to penpie posistion. PenpieReceiptToken is minted to user who deposited Market token\n///        on pendle staking to increase defi lego\n///         \n///         Reward from Magpie and on BaseReward should be updated upon every transfer.\n///\n/// @author Magpie Team\n/// @notice Mater penpie emit `PNP` reward token based on Time. For a pool, \n\ncontract PenpieReceiptToken is ERC20, Ownable {\n    using SafeERC20 for IERC20Metadata;\n    using SafeERC20 for IERC20;\n\n    address public underlying;\n    address public immutable masterPenpie;\n\n\n    /* ============ Errors ============ */\n\n    /* ============ Events ============ */\n\n    constructor(address _underlying, address _masterPenpie, string memory name, string memory symbol) ERC20(name, symbol) {\n        underlying = _underlying;\n        masterPenpie = _masterPenpie;\n    } \n\n    // should only be called by 1. pendleStaking for Pendle Market deposits 2. masterPenpie for other general staking token such as mPendleOFT or PNP-ETH Lp tokens\n    function mint(address account, uint256 amount) external virtual onlyOwner {\n        _mint(account, amount);\n    }\n\n    // should only be called by 1. pendleStaking for Pendle Market deposits 2. masterPenpie for other general staking token such as mPendleOFT or PNP-ETH Lp tokens\n    function burn(address account, uint256 amount) external virtual onlyOwner {\n        _burn(account, amount);\n    }\n\n    // rewards are calculated based on user's receipt token balance, so reward should be updated on master penpie before transfer\n    function _beforeTokenTransfer(\n        address from,\n        address to,\n        uint256 amount\n    ) internal override {\n        IMasterPenpie(masterPenpie).beforeReceiptTokenTransfer(from, to, amount);\n    }\n\n    // rewards are calculated based on user's receipt token balance, so balance should be updated on master penpie before transfer\n    function _afterTokenTransfer(\n        address from,\n        address to,\n        uint256 amount\n    ) internal override {\n        IMasterPenpie(masterPenpie).afterReceiptTokenTransfer(from, to, amount);\n    }\n\n}"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 100
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {
        "contracts/libraries/ERC20FactoryLib.sol": {
          "ERC20FactoryLib": "0xae8e4bc88f792297a808cb5f32d4950fbbd8aeba"
        }
      }
    }
  }}