creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/dependencies/chainlink/AggregatorV3Interface.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// Chainlink Contracts v0.4.0 (v0.8/interfaces/AggregatorV3Interface.sol)\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n    function decimals() external view returns (uint8);\n\n    function description() external view returns (string memory);\n\n    function version() external view returns (uint256);\n\n    // getRoundData and latestRoundData should both raise \"No data present\"\n    // if they do not have data to report, instead of returning unset values\n    // which could be misinterpreted as actual reported values.\n    function getRoundData(\n        uint80 _roundId\n    )\n        external\n        view\n        returns (\n            uint80 roundId,\n            int256 answer,\n            uint256 startedAt,\n            uint256 updatedAt,\n            uint80 answeredInRound\n        );\n\n    function latestRoundData()\n        external\n        view\n        returns (\n            uint80 roundId,\n            int256 answer,\n            uint256 startedAt,\n            uint256 updatedAt,\n            uint80 answeredInRound\n        );\n}\n"
      },
      "contracts/dependencies/openzeppelin/proxy/utils/Initializable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/Address.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n *     function initialize() initializer public {\n *         __ERC20_init(\"MyToken\", \"MTK\");\n *     }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n *     function initializeV2() reinitializer(2) public {\n *         __ERC20Permit_init(\"MyToken\");\n *     }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n *     _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n    /**\n     * @dev Indicates that the contract has been initialized.\n     * @custom:oz-retyped-from bool\n     */\n    uint8 private _initialized;\n\n    /**\n     * @dev Indicates that the contract is in the process of being initialized.\n     */\n    bool private _initializing;\n\n    /**\n     * @dev Triggered when the contract has been initialized or reinitialized.\n     */\n    event Initialized(uint8 version);\n\n    /**\n     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n     * `onlyInitializing` functions can be used to initialize parent contracts.\n     *\n     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\n     * constructor.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier initializer() {\n        bool isTopLevelCall = !_initializing;\n        require(\n            (isTopLevelCall && _initialized < 1) ||\n                (!Address.isContract(address(this)) && _initialized == 1),\n            \"Initializable: contract is already initialized\"\n        );\n        _initialized = 1;\n        if (isTopLevelCall) {\n            _initializing = true;\n        }\n        _;\n        if (isTopLevelCall) {\n            _initializing = false;\n            emit Initialized(1);\n        }\n    }\n\n    /**\n     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n     * used to initialize parent contracts.\n     *\n     * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n     * are added through upgrades and that require initialization.\n     *\n     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n     * cannot be nested. If one is invoked in the context of another, execution will revert.\n     *\n     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n     * a contract, executing them in the right order is up to the developer or operator.\n     *\n     * WARNING: setting the version to 255 will prevent any future reinitialization.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier reinitializer(uint8 version) {\n        require(\n            !_initializing && _initialized < version,\n            \"Initializable: contract is already initialized\"\n        );\n        _initialized = version;\n        _initializing = true;\n        _;\n        _initializing = false;\n        emit Initialized(version);\n    }\n\n    /**\n     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n     * {initializer} and {reinitializer} modifiers, directly or indirectly.\n     */\n    modifier onlyInitializing() {\n        require(_initializing, \"Initializable: contract is not initializing\");\n        _;\n    }\n\n    /**\n     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n     * through proxies.\n     *\n     * Emits an {Initialized} event the first time it is successfully executed.\n     */\n    function _disableInitializers() internal virtual {\n        require(!_initializing, \"Initializable: contract is initializing\");\n        if (_initialized != type(uint8).max) {\n            _initialized = type(uint8).max;\n            emit Initialized(type(uint8).max);\n        }\n    }\n\n    /**\n     * @dev Returns the highest version that has been initialized. See {reinitializer}.\n     */\n    function _getInitializedVersion() internal view returns (uint8) {\n        return _initialized;\n    }\n\n    /**\n     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n     */\n    function _isInitializing() internal view returns (bool) {\n        return _initializing;\n    }\n}\n"
      },
      "contracts/dependencies/openzeppelin/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     *\n     * Furthermore, `isContract` will also return true if the target contract within\n     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n     * which only has an effect at the end of a transaction.\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value\n    ) internal returns (bytes memory) {\n        return\n            functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data\n    ) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data\n    ) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"
      },
      "contracts/dependencies/openzeppelin/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/dependencies/openzeppelin/utils/StorageSlot.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)\n// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC1967 implementation slot:\n * ```solidity\n * contract ERC1967 {\n *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n *     function _getImplementation() internal view returns (address) {\n *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n *     }\n *\n *     function _setImplementation(address newImplementation) internal {\n *         require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n *     }\n * }\n * ```\n *\n * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._\n * _Available since v4.9 for `string`, `bytes`._\n */\nlibrary StorageSlot {\n    struct AddressSlot {\n        address value;\n    }\n\n    struct BooleanSlot {\n        bool value;\n    }\n\n    struct Bytes32Slot {\n        bytes32 value;\n    }\n\n    struct Uint256Slot {\n        uint256 value;\n    }\n\n    struct StringSlot {\n        string value;\n    }\n\n    struct BytesSlot {\n        bytes value;\n    }\n\n    /**\n     * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n     */\n    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\n     */\n    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\n     */\n    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\n     */\n    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns an `StringSlot` with member `value` located at `slot`.\n     */\n    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.\n     */\n    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := store.slot\n        }\n    }\n\n    /**\n     * @dev Returns an `BytesSlot` with member `value` located at `slot`.\n     */\n    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.\n     */\n    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := store.slot\n        }\n    }\n}\n"
      },
      "contracts/dependencies/openzeppelin/utils/structs/EnumerableSet.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)\n// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```solidity\n * contract Example {\n *     // Add the library methods\n *     using EnumerableSet for EnumerableSet.AddressSet;\n *\n *     // Declare a set state variable\n *     EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n *\n * [WARNING]\n * ====\n * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure\n * unusable.\n * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.\n *\n * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an\n * array of EnumerableSet.\n * ====\n */\nlibrary EnumerableSet {\n    // To implement this library for multiple types with as little code\n    // repetition as possible, we write it in terms of a generic Set type with\n    // bytes32 values.\n    // The Set implementation uses private functions, and user-facing\n    // implementations (such as AddressSet) are just wrappers around the\n    // underlying Set.\n    // This means that we can only create new EnumerableSets for types that fit\n    // in bytes32.\n\n    struct Set {\n        // Storage of set values\n        bytes32[] _values;\n        // Position of the value in the `values` array, plus 1 because index 0\n        // means a value is not in the set.\n        mapping(bytes32 => uint256) _indexes;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function _add(Set storage set, bytes32 value) private returns (bool) {\n        if (!_contains(set, value)) {\n            set._values.push(value);\n            // The value is stored at length-1, but we add 1 to all indexes\n            // and use 0 as a sentinel value\n            set._indexes[value] = set._values.length;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function _remove(Set storage set, bytes32 value) private returns (bool) {\n        // We read and store the value's index to prevent multiple reads from the same storage slot\n        uint256 valueIndex = set._indexes[value];\n\n        if (valueIndex != 0) {\n            // Equivalent to contains(set, value)\n            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n            // the array, and then remove the last element (sometimes called as 'swap and pop').\n            // This modifies the order of the array, as noted in {at}.\n\n            uint256 toDeleteIndex = valueIndex - 1;\n            uint256 lastIndex = set._values.length - 1;\n\n            if (lastIndex != toDeleteIndex) {\n                bytes32 lastValue = set._values[lastIndex];\n\n                // Move the last value to the index where the value to delete is\n                set._values[toDeleteIndex] = lastValue;\n                // Update the index for the moved value\n                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex\n            }\n\n            // Delete the slot where the moved value was stored\n            set._values.pop();\n\n            // Delete the index for the deleted slot\n            delete set._indexes[value];\n\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function _contains(Set storage set, bytes32 value) private view returns (bool) {\n        return set._indexes[value] != 0;\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function _length(Set storage set) private view returns (uint256) {\n        return set._values.length;\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function _at(Set storage set, uint256 index) private view returns (bytes32) {\n        return set._values[index];\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function _values(Set storage set) private view returns (bytes32[] memory) {\n        return set._values;\n    }\n\n    // Bytes32Set\n\n    struct Bytes32Set {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _add(set._inner, value);\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _remove(set._inner, value);\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n        return _contains(set._inner, value);\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(Bytes32Set storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n        return _at(set._inner, index);\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        bytes32[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n\n    // AddressSet\n\n    struct AddressSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(AddressSet storage set, address value) internal returns (bool) {\n        return _add(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(AddressSet storage set, address value) internal returns (bool) {\n        return _remove(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(AddressSet storage set, address value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(AddressSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(AddressSet storage set, uint256 index) internal view returns (address) {\n        return address(uint160(uint256(_at(set._inner, index))));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(AddressSet storage set) internal view returns (address[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        address[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n\n    // UintSet\n\n    struct UintSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(UintSet storage set, uint256 value) internal returns (bool) {\n        return _add(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(UintSet storage set, uint256 value) internal returns (bool) {\n        return _remove(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(UintSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n        return uint256(_at(set._inner, index));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(UintSet storage set) internal view returns (uint256[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        uint256[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n}\n"
      },
      "contracts/protocol/interfaces/IAddressResolver.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\ninterface IAddressResolver {\n    error UnmatchedInputs();\n\n    event AddressImported(bytes32 name, address destination);\n\n    function getAddress(bytes32 name, string calldata reason) external view returns (address);\n\n    function getAddress(bytes32 name) external view returns (address);\n\n    function getAddresses() external view returns (address[] memory);\n\n    function getNames() external view returns (bytes32[] memory);\n}\n"
      },
      "contracts/protocol/interfaces/IBeaconProxyController.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\ninterface IBeaconProxyController {\n    error NoBeaconProxyContract();\n    error InvalidProxyContract();\n\n    event BeaconProxyUpdated(\n        bytes32 indexed id,\n        address indexed proxyAddress,\n        address indexed newImplementationAddress,\n        address oldImplementationAddress\n    );\n\n    function getBeaconProxyAddress(bytes32 beaconName) external view returns (address);\n\n    function setFutureValueVaultImpl(address newImpl) external;\n\n    function setLendingMarketImpl(address newImpl) external;\n\n    function setZCTokenImpl(address newImpl) external;\n\n    function deployFutureValueVault() external returns (address futureValueVault);\n\n    function deployLendingMarket(\n        bytes32 ccy,\n        uint256 orderFeeRate,\n        uint256 cbLimitRange\n    ) external returns (address market);\n\n    function deployZCToken(\n        string memory name,\n        string memory symbol,\n        uint8 decimals,\n        address asset,\n        uint256 maturity\n    ) external returns (address futureValueToken);\n}\n"
      },
      "contracts/protocol/interfaces/ICurrencyController.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {PriceFeed} from \"../storages/CurrencyControllerStorage.sol\";\n\n/**\n * @dev Currency Controller contract is responsible for managing supported\n * currencies in Secured Finance Protocol\n *\n * Contract links new currencies to ETH Chainlink price feeds, without existing price feed\n * contract owner is not able to add a new currency into the protocol\n */\ninterface ICurrencyController {\n    error InvalidCurrency();\n    error InvalidHaircut();\n    error InvalidPriceFeed();\n    error InvalidDecimals(address priceFeed, uint8 decimals);\n    error InvalidPriceFeedInputs();\n    error StalePriceFeed(\n        address priceFeed,\n        uint256 heartbeat,\n        uint256 updatedAt,\n        uint256 blockTimestamp\n    );\n\n    event CurrencyAdded(bytes32 indexed ccy, uint256 haircut);\n    event CurrencyRemoved(bytes32 indexed ccy);\n\n    event HaircutUpdated(bytes32 indexed ccy, uint256 haircut);\n\n    event PriceFeedUpdated(bytes32 ccy, uint256 decimals, address[] indexed priceFeeds);\n\n    function convert(\n        bytes32 _fromCcy,\n        bytes32 _toCcy,\n        uint256 _amount\n    ) external view returns (uint256 amount);\n\n    function convert(\n        bytes32 _fromCcy,\n        bytes32 _toCcy,\n        uint256[] calldata _amounts\n    ) external view returns (uint256[] memory amounts);\n\n    function convertToBaseCurrency(\n        bytes32 _ccy,\n        uint256 _amount\n    ) external view returns (uint256 amount);\n\n    function convertToBaseCurrency(\n        bytes32 _ccy,\n        int256 _amount\n    ) external view returns (int256 amount);\n\n    function convertToBaseCurrency(\n        bytes32 _ccy,\n        uint256[] calldata _amounts\n    ) external view returns (uint256[] memory amounts);\n\n    function convertFromBaseCurrency(\n        bytes32 _ccy,\n        uint256 _amountETH\n    ) external view returns (uint256 amount);\n\n    function convertFromBaseCurrency(\n        bytes32 _ccy,\n        uint256[] calldata _amounts\n    ) external view returns (uint256[] memory amounts);\n\n    function getDecimals(bytes32) external view returns (uint8);\n\n    function getCurrencies() external view returns (bytes32[] memory);\n\n    function getHaircut(bytes32 _ccy) external view returns (uint256);\n\n    function getPriceFeed(bytes32 _ccy) external view returns (PriceFeed memory);\n\n    function getLastPrice(bytes32 _ccy) external view returns (int256 price);\n\n    function getAggregatedLastPrice(bytes32 _ccy) external view returns (int256);\n\n    function currencyExists(bytes32 _ccy) external view returns (bool);\n\n    function updatePriceFeed(\n        bytes32 _ccy,\n        uint8 _decimals,\n        address[] calldata _priceFeeds,\n        uint256[] calldata _heartbeats\n    ) external;\n\n    function addCurrency(\n        bytes32 _ccy,\n        uint8 _decimals,\n        uint256 _haircut,\n        address[] calldata _priceFeeds,\n        uint256[] calldata _heartbeats\n    ) external;\n\n    function updateHaircut(bytes32 _ccy, uint256 _haircut) external;\n\n    function removeCurrency(bytes32 _ccy) external;\n}\n"
      },
      "contracts/protocol/interfaces/IGenesisValueVault.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {AutoRollLog} from \"../storages/GenesisValueVaultStorage.sol\";\n\ninterface IGenesisValueVault {\n    error NoCompoundFactorExists(uint256 maturity);\n    error CompoundFactorIsZero();\n    error ResidualAmountIsNotZero();\n    error UnitPriceIsZero();\n    error InvalidMaturity();\n    error InvalidAmount();\n    error InvalidOrderFeeRate();\n    error CurrencyAlreadyInitialized();\n    error InitialCompoundFactorAlreadyFinalized();\n    error AutoRollLogAlreadyUpdated(uint256 currentMaturity, uint256 nextMaturity);\n    error InsufficientBalance();\n    error InsufficientLockedBalance();\n\n    event Transfer(bytes32 indexed ccy, address indexed from, address indexed to, int256 value);\n    event AutoRollExecuted(\n        bytes32 indexed ccy,\n        uint256 lendingCompoundFactor,\n        uint256 borrowingCompoundFactor,\n        uint256 unitPrice,\n        uint256 currentMaturity,\n        uint256 previousMaturity\n    );\n    event BalanceLocked(bytes32 indexed ccy, address indexed user, uint256 value);\n    event BalanceUnlocked(bytes32 indexed ccy, address indexed user, uint256 value);\n\n    function isAutoRolled(bytes32 _ccy, uint256 _maturity) external view returns (bool);\n\n    function isInitialized(bytes32 ccy) external view returns (bool);\n\n    function decimals(bytes32 ccy) external view returns (uint8);\n\n    function getTotalLendingSupply(bytes32 ccy) external view returns (uint256);\n\n    function getTotalBorrowingSupply(bytes32 ccy) external view returns (uint256);\n\n    function getBalance(bytes32 ccy, address user) external view returns (int256);\n\n    function getMaturityGenesisValue(bytes32 ccy, uint256 maturity) external view returns (int256);\n\n    function getCurrentMaturity(bytes32 ccy) external view returns (uint256);\n\n    function getLendingCompoundFactor(bytes32 ccy) external view returns (uint256);\n\n    function getBorrowingCompoundFactor(bytes32 ccy) external view returns (uint256);\n\n    function getAutoRollLog(\n        bytes32 ccy,\n        uint256 maturity\n    ) external view returns (AutoRollLog memory);\n\n    function getLatestAutoRollLog(bytes32 ccy) external view returns (AutoRollLog memory);\n\n    function getTotalLockedBalance(bytes32 ccy) external view returns (uint256);\n\n    function calculateFVFromFV(\n        bytes32 ccy,\n        uint256 basisMaturity,\n        uint256 destinationMaturity,\n        int256 futureValue\n    ) external view returns (int256);\n\n    function calculateGVFromFV(\n        bytes32 ccy,\n        uint256 basisMaturity,\n        int256 futureValue\n    ) external view returns (int256);\n\n    function calculateFVFromGV(\n        bytes32 ccy,\n        uint256 basisMaturity,\n        int256 genesisValue\n    ) external view returns (int256);\n\n    function getBalanceFluctuationByAutoRolls(\n        bytes32 ccy,\n        address user,\n        uint256 maturity\n    ) external view returns (int256 fluctuation);\n\n    function calculateBalanceFluctuationByAutoRolls(\n        bytes32 ccy,\n        int256 balance,\n        uint256 fromMaturity,\n        uint256 toMaturity\n    ) external view returns (int256 fluctuation);\n\n    function initializeCurrencySetting(\n        bytes32 ccy,\n        uint8 decimals,\n        uint256 compoundFactor,\n        uint256 maturity\n    ) external;\n\n    function updateInitialCompoundFactor(bytes32 ccy, uint256 unitPrice) external;\n\n    function updateDecimals(bytes32 _ccy, uint8 _decimals) external;\n\n    function executeAutoRoll(\n        bytes32 ccy,\n        uint256 maturity,\n        uint256 nextMaturity,\n        uint256 unitPrice,\n        uint256 orderFeeRate\n    ) external;\n\n    function updateGenesisValueWithFutureValue(\n        bytes32 ccy,\n        address user,\n        uint256 basisMaturity,\n        int256 fvAmount\n    ) external;\n\n    function updateGenesisValueWithResidualAmount(\n        bytes32 ccy,\n        address user,\n        uint256 basisMaturity\n    ) external;\n\n    function lock(\n        bytes32 ccy,\n        address user,\n        uint256 amount\n    ) external returns (uint256 lockedAmount);\n\n    function unlock(bytes32 ccy, address user, uint256 amount) external;\n\n    function transferFrom(bytes32 ccy, address sender, address receiver, int256 amount) external;\n\n    function cleanUpBalance(bytes32 ccy, address user, uint256 maturity) external;\n\n    function executeForcedReset(bytes32 _ccy, address _user) external;\n\n    function executeForcedReset(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        int256 _amountInFV\n    ) external returns (int256 removedAmount, int256 balance);\n}\n"
      },
      "contracts/protocol/interfaces/ILendingMarket.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport \"../types/ProtocolTypes.sol\";\nimport {ItayoseLog} from \"../storages/LendingMarketStorage.sol\";\nimport {OrderBookLib, FilledOrder, PartiallyFilledOrder} from \"../libraries/OrderBookLib.sol\";\n\ninterface ILendingMarket {\n    error NoOrderExists();\n    error CallerNotMaker();\n    error MarketNotOpened();\n    error AlreadyItayosePeriod();\n    error NotItayosePeriod();\n    error NotPreOrderPeriod();\n\n    function getOrderBookDetail(\n        uint8 orderBookId\n    )\n        external\n        view\n        returns (bytes32 ccy, uint256 maturity, uint256 openingDate, uint256 preOpeningDate);\n\n    function getCircuitBreakerThresholds(\n        uint8 orderBookId\n    )\n        external\n        view\n        returns (uint256 lendCircuitBreakerThreshold, uint256 borrowCircuitBreakerThreshold);\n\n    function getBestLendUnitPrice(uint8 orderBookId) external view returns (uint256 unitPrice);\n\n    function getBestLendUnitPrices(\n        uint8[] calldata orderBookIds\n    ) external view returns (uint256[] memory);\n\n    function getBestBorrowUnitPrice(uint8 orderBookId) external view returns (uint256 unitPrice);\n\n    function getBestBorrowUnitPrices(\n        uint8[] calldata orderBookIds\n    ) external view returns (uint256[] memory);\n\n    function getMarketUnitPrice(uint8 orderBookId) external view returns (uint256);\n\n    function getLastOrderTimestamp(uint8 orderBookId) external view returns (uint48);\n\n    function getBlockUnitPriceHistory(\n        uint8 orderBookId\n    ) external view returns (uint256[] memory unitPrices, uint48 timestamp);\n\n    function getBlockUnitPriceAverage(\n        uint8 orderBookId,\n        uint256 count\n    ) external view returns (uint256);\n\n    function getBorrowOrderBook(\n        uint8 orderBookId,\n        uint256 start,\n        uint256 limit\n    )\n        external\n        view\n        returns (\n            uint256[] memory unitPrices,\n            uint256[] memory amounts,\n            uint256[] memory quantities,\n            uint256 next\n        );\n\n    function getLendOrderBook(\n        uint8 orderBookId,\n        uint256 start,\n        uint256 limit\n    )\n        external\n        view\n        returns (\n            uint256[] memory unitPrices,\n            uint256[] memory amounts,\n            uint256[] memory quantities,\n            uint256 next\n        );\n\n    function getItayoseEstimation(\n        uint8 orderBookId\n    )\n        external\n        view\n        returns (\n            uint256 openingUnitPrice,\n            uint256 lastLendUnitPrice,\n            uint256 lastBorrowUnitPrice,\n            uint256 totalOffsetAmount\n        );\n\n    function getMaturity(uint8 orderBookId) external view returns (uint256);\n\n    function getMaturities(\n        uint8[] calldata orderBookIds\n    ) external view returns (uint256[] memory maturities);\n\n    function getCurrency() external view returns (bytes32);\n\n    function getOrderFeeRate() external view returns (uint256);\n\n    function getCircuitBreakerLimitRange() external view returns (uint256);\n\n    function getOpeningDate(uint8 orderBookId) external view returns (uint256);\n\n    function isReady(uint8 orderBookId) external view returns (bool);\n\n    function isMatured(uint8 orderBookId) external view returns (bool);\n\n    function isOpened(uint8 orderBookId) external view returns (bool);\n\n    function isItayosePeriod(uint8 orderBookId) external view returns (bool);\n\n    function isPreOrderPeriod(uint8 orderBookId) external returns (bool);\n\n    function getItayoseLog(uint256 maturity) external view returns (ItayoseLog memory);\n\n    function getOrder(\n        uint8 orderBookId,\n        uint48 orderId\n    )\n        external\n        view\n        returns (\n            ProtocolTypes.Side,\n            uint256 unitPrice,\n            uint256 maturity,\n            address maker,\n            uint256 amount,\n            uint256 timestamp,\n            bool isPreOrder\n        );\n\n    function getTotalAmountFromLendOrders(\n        uint8 orderBookId,\n        address user\n    )\n        external\n        view\n        returns (\n            uint256 activeAmount,\n            uint256 inactiveAmount,\n            uint256 inactiveFutureValue,\n            uint256 maturity\n        );\n\n    function getTotalAmountFromBorrowOrders(\n        uint8 orderBookId,\n        address user,\n        uint256 _minUnitPrice\n    )\n        external\n        view\n        returns (\n            uint256 activeAmount,\n            uint256 inactiveAmount,\n            uint256 inactiveFutureValue,\n            uint256 maturity\n        );\n\n    function getLendOrderIds(\n        uint8 orderBookId,\n        address user\n    ) external view returns (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds);\n\n    function getBorrowOrderIds(\n        uint8 orderBookId,\n        address user\n    ) external view returns (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds);\n\n    function calculateFilledAmount(\n        uint8 orderBookId,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice\n    )\n        external\n        view\n        returns (\n            uint256 lastUnitPrice,\n            uint256 filledAmount,\n            uint256 filledAmountInFV,\n            uint256 feeInFV,\n            uint256 placedAmount\n        );\n\n    function createOrderBook(\n        uint256 maturity,\n        uint256 openingDate,\n        uint256 preOpeningDate\n    ) external returns (uint8 orderBookId);\n\n    function executeAutoRoll(\n        uint8 maturedOrderBookId,\n        uint8 newNearestOrderBookId,\n        uint256 autoRollUnitPrice\n    ) external;\n\n    function cancelOrder(uint8 orderBookId, address user, uint48 orderId) external;\n\n    function executeOrder(\n        uint8 orderBookId,\n        ProtocolTypes.Side side,\n        address account,\n        uint256 amount,\n        uint256 unitPrice\n    )\n        external\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 feeInFV\n        );\n\n    function executePreOrder(\n        uint8 orderBookId,\n        ProtocolTypes.Side side,\n        address user,\n        uint256 amount,\n        uint256 unitPrice\n    ) external;\n\n    function unwindPosition(\n        uint8 orderBookId,\n        ProtocolTypes.Side side,\n        address user,\n        uint256 futureValue\n    )\n        external\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 feeInFV\n        );\n\n    function executeItayoseCall(\n        uint8 orderBookId\n    )\n        external\n        returns (\n            uint256 openingUnitPrice,\n            uint256 totalOffsetAmount,\n            uint256 openingDate,\n            PartiallyFilledOrder memory partiallyFilledLendingOrder,\n            PartiallyFilledOrder memory partiallyFilledBorrowingOrder\n        );\n\n    function cleanUpOrders(\n        uint8 orderBookId,\n        address user\n    )\n        external\n        returns (\n            uint256 activeLendOrderCount,\n            uint256 activeBorrowOrderCount,\n            uint256 removedLendOrderFutureValue,\n            uint256 removedBorrowOrderFutureValue,\n            uint256 removedLendOrderAmount,\n            uint256 removedBorrowOrderAmount,\n            uint256 maturity\n        );\n\n    function updateOrderFeeRate(uint256 orderFeeRate) external;\n\n    function updateCircuitBreakerLimitRange(uint256 limitRange) external;\n\n    function pause() external;\n\n    function unpause() external;\n}\n"
      },
      "contracts/protocol/interfaces/ILendingMarketController.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {ProtocolTypes} from \"../types/ProtocolTypes.sol\";\nimport {ZCTokenInfo, TerminationCurrencyCache} from \"../storages/LendingMarketControllerStorage.sol\";\n\ninterface ILendingMarketController {\n    error InvalidMaturity();\n    error InvalidCurrency();\n    error MarketTerminated();\n    error NotTerminated();\n    error AlreadyInitialized();\n\n    struct AdditionalFunds {\n        bytes32 ccy;\n        uint256 workingLendOrdersAmount;\n        uint256 claimableAmount;\n        uint256 workingBorrowOrdersAmount;\n        uint256 debtAmount;\n        uint256 lentAmount;\n        uint256 borrowedAmount;\n    }\n\n    struct CalculatedTotalFunds {\n        uint256 plusDepositAmountInAdditionalFundsCcy;\n        uint256 minusDepositAmountInAdditionalFundsCcy;\n        uint256 workingLendOrdersAmount;\n        uint256 claimableAmount;\n        uint256 collateralAmount;\n        uint256 lentAmount;\n        uint256 workingBorrowOrdersAmount;\n        uint256 debtAmount;\n        uint256 borrowedAmount;\n    }\n\n    struct CalculatedFunds {\n        uint256 workingLendOrdersAmount;\n        uint256 claimableAmount;\n        uint256 collateralAmount;\n        uint256 unallocatedCollateralAmount;\n        uint256 lentAmount;\n        uint256 workingBorrowOrdersAmount;\n        uint256 debtAmount;\n        uint256 borrowedAmount;\n    }\n\n    struct GetOrderEstimationParams {\n        bytes32 ccy;\n        uint256 maturity;\n        address user;\n        ProtocolTypes.Side side;\n        uint256 amount;\n        uint256 unitPrice;\n        uint256 additionalDepositAmount;\n        bool ignoreBorrowedAmount;\n    }\n\n    function isValidMaturity(bytes32 _ccy, uint256 _maturity) external view returns (bool);\n\n    function isTerminated() external view returns (bool);\n\n    function isRedemptionRequired(address _user) external view returns (bool);\n\n    function getMarketBasePeriod() external view returns (uint256);\n\n    function getTerminationDate() external view returns (uint256);\n\n    function getTerminationCurrencyCache(\n        bytes32 _ccy\n    ) external view returns (TerminationCurrencyCache memory);\n\n    function getTerminationCollateralRatio(bytes32 _ccy) external view returns (uint256);\n\n    function getMinDebtUnitPrice(bytes32 _ccy) external view returns (uint256);\n\n    function getCurrentMinDebtUnitPrice(\n        bytes32 _ccy,\n        uint256 _maturity\n    ) external view returns (uint256);\n\n    function getGenesisDate(bytes32 ccy) external view returns (uint256);\n\n    function getLendingMarket(bytes32 ccy) external view returns (address);\n\n    function getFutureValueVault(bytes32 ccy) external view returns (address);\n\n    function getOrderBookId(bytes32 _ccy, uint256 _maturity) external view returns (uint8);\n\n    function getPendingOrderAmount(bytes32 _ccy, uint256 _maturity) external view returns (uint256);\n\n    function getOrderEstimation(\n        GetOrderEstimationParams calldata params\n    )\n        external\n        view\n        returns (\n            uint256 lastUnitPrice,\n            uint256 filledAmount,\n            uint256 filledAmountInFV,\n            uint256 orderFeeInFV,\n            uint256 placedAmount,\n            uint256 coverage,\n            bool isInsufficientDepositAmount\n        );\n\n    function getMaturities(bytes32 ccy) external view returns (uint256[] memory);\n\n    function getOrderBookIds(bytes32 ccy) external view returns (uint8[] memory);\n\n    function getUsedCurrencies(address user) external view returns (bytes32[] memory);\n\n    function getTotalPresentValue(bytes32 ccy, address user) external view returns (int256);\n\n    function getTotalPresentValueInBaseCurrency(\n        address user\n    ) external view returns (int256 totalPresentValue);\n\n    function getGenesisValue(\n        bytes32 ccy,\n        address user\n    ) external view returns (int256 amount, int256 amountInPV, int256 amountInFV);\n\n    function getPosition(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user\n    ) external view returns (int256 presentValue, int256 futureValue);\n\n    function getZCToken(bytes32 ccy, uint256 maturity) external view returns (address);\n\n    function getZCTokenInfo(address zcToken) external view returns (ZCTokenInfo memory);\n\n    function getWithdrawableZCTokenAmount(\n        bytes32 ccy,\n        uint256 maturity,\n        address user\n    ) external view returns (uint256 amount);\n\n    function calculateFunds(\n        bytes32 ccy,\n        address user,\n        uint256 liquidationThresholdRate\n    ) external view returns (CalculatedFunds memory funds);\n\n    function calculateTotalFundsInBaseCurrency(\n        address user,\n        AdditionalFunds calldata _additionalFunds,\n        uint256 liquidationThresholdRate\n    ) external view returns (CalculatedTotalFunds memory calculatedFunds);\n\n    function isInitializedLendingMarket(bytes32 ccy) external view returns (bool);\n\n    function initializeLendingMarket(\n        bytes32 ccy,\n        uint256 genesisDate,\n        uint256 compoundFactor,\n        uint256 orderFeeRate,\n        uint256 circuitBreakerLimitRange,\n        uint256 minDebtUnitPrice\n    ) external;\n\n    function createOrderBook(bytes32 ccy, uint256 openingDate, uint256 preOpeningDate) external;\n\n    function executeOrder(\n        bytes32 ccy,\n        uint256 maturity,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice\n    ) external returns (bool);\n\n    function depositAndExecuteOrder(\n        bytes32 ccy,\n        uint256 maturity,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice\n    ) external payable returns (bool);\n\n    function depositWithPermitAndExecuteOrder(\n        bytes32 ccy,\n        uint256 maturity,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice,\n        uint256 deadline,\n        uint8 permitV,\n        bytes32 permitR,\n        bytes32 permitS\n    ) external returns (bool);\n\n    function executePreOrder(\n        bytes32 ccy,\n        uint256 maturity,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice\n    ) external returns (bool);\n\n    function depositAndExecutesPreOrder(\n        bytes32 ccy,\n        uint256 maturity,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice\n    ) external payable returns (bool);\n\n    function depositWithPermitAndExecutePreOrder(\n        bytes32 _ccy,\n        uint256 _maturity,\n        ProtocolTypes.Side _side,\n        uint256 _amount,\n        uint256 _unitPrice,\n        uint256 _deadline,\n        uint8 _permitV,\n        bytes32 _permitR,\n        bytes32 _permitS\n    ) external returns (bool);\n\n    function unwindPosition(bytes32 ccy, uint256 maturity) external returns (bool);\n\n    function executeItayoseCall(bytes32 ccy, uint256 maturity) external returns (bool);\n\n    function executeRedemption(bytes32 _ccy, uint256 _maturity) external returns (bool);\n\n    function executeRepayment(bytes32 _ccy, uint256 _maturity) external returns (bool);\n\n    function executeEmergencySettlement() external returns (bool);\n\n    function executeLiquidationCall(\n        bytes32 collateralCcy,\n        bytes32 debtCcy,\n        uint256 debtMaturity,\n        address user\n    ) external returns (bool);\n\n    function executeForcedRepayment(\n        bytes32 _collateralCcy,\n        bytes32 _debtCcy,\n        uint256 _debtMaturity,\n        address _user\n    ) external returns (bool);\n\n    function cancelOrder(bytes32 ccy, uint256 maturity, uint48 orderId) external returns (bool);\n\n    function rotateOrderBooks(bytes32 ccy) external;\n\n    function executeEmergencyTermination() external;\n\n    function pauseLendingMarket(bytes32 ccy) external returns (bool);\n\n    function unpauseLendingMarket(bytes32 ccy) external returns (bool);\n\n    function cleanUpAllFunds(address user) external returns (bool);\n\n    function cleanUpFunds(bytes32 ccy, address user) external returns (uint256 activeOrderCount);\n\n    function updateMinDebtUnitPrice(bytes32 _ccy, uint256 _minDebtUnitPrice) external;\n\n    function withdrawZCToken(bytes32 _ccy, uint256 _maturity, uint256 _amount) external;\n\n    function depositZCToken(bytes32 _ccy, uint256 _maturity, uint256 _amount) external;\n}\n"
      },
      "contracts/protocol/interfaces/IReserveFund.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\ninterface IReserveFund {\n    event Pause(address account);\n    event Unpause(address account);\n\n    function isPaused() external view returns (bool);\n\n    function pause() external;\n\n    function unpause() external;\n\n    function deposit(bytes32 ccy, uint256 amount) external payable;\n\n    function withdraw(bytes32 ccy, uint256 amount) external;\n}\n"
      },
      "contracts/protocol/interfaces/ITokenVault.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {ProtocolTypes} from \"../types/ProtocolTypes.sol\";\nimport {ILendingMarketController} from \"../interfaces/ILendingMarketController.sol\";\n\ninterface ITokenVault {\n    error UnregisteredCurrency();\n    error InvalidCurrency();\n    error InvalidToken();\n    error InvalidAmount(bytes32 ccy, uint256 amount, uint256 msgValue);\n    error AmountIsZero();\n    error CallerNotBaseCurrency(address caller);\n    error MarketTerminated();\n    error RedemptionIsRequired();\n\n    event Deposit(address indexed user, bytes32 ccy, uint256 amount, address caller);\n    event Withdraw(address indexed user, bytes32 ccy, uint256 amount);\n    event Transfer(bytes32 indexed ccy, address indexed from, address indexed to, uint256 amount);\n    event CurrencyRegistered(bytes32 ccy, address tokenAddress, bool isCollateral);\n    event CurrencyUpdated(bytes32 ccy, bool isCollateral);\n\n    function isCovered(\n        address user,\n        bytes32 ccy\n    ) external view returns (bool isEnoughCollateral, bool isEnoughDepositInOrderCcy);\n\n    function isCollateral(bytes32 ccy) external view returns (bool);\n\n    function isCollateral(bytes32[] calldata ccys) external view returns (bool[] memory);\n\n    function isRegisteredCurrency(bytes32 ccy) external view returns (bool);\n\n    function getTokenAddress(bytes32 ccy) external view returns (address);\n\n    function getCollateralCurrencies() external view returns (bytes32[] memory);\n\n    function getWithdrawableCollateral(address user) external view returns (uint256);\n\n    function getWithdrawableCollateral(bytes32 ccy, address user) external view returns (uint256);\n\n    function getCoverage(address user) external view returns (uint256);\n\n    function getTotalUnusedCollateralAmount(address user) external view returns (uint256);\n\n    function getTotalCollateralAmount(address user) external view returns (uint256);\n\n    function getCollateralDetail(\n        address user\n    )\n        external\n        view\n        returns (uint256 totalCollateral, uint256 totalUsedCollateral, uint256 totalDeposit);\n\n    function getCollateralAmount(address user, bytes32 ccy) external view returns (uint256);\n\n    function getBorrowableAmount(address user, bytes32 ccy) external view returns (uint256);\n\n    function getLiquidationAmount(\n        address user,\n        bytes32 liquidationCcy,\n        uint256 liquidationAmountMaximum\n    ) external view returns (uint256 liquidationAmount, uint256 protocolFee, uint256 liquidatorFee);\n\n    function getTotalDepositAmount(bytes32 ccy) external view returns (uint256);\n\n    function getDepositAmount(address user, bytes32 ccy) external view returns (uint256);\n\n    function getUsedCurrencies(address user) external view returns (bytes32[] memory);\n\n    function calculateCoverage(\n        address user,\n        ILendingMarketController.AdditionalFunds memory funds\n    ) external view returns (uint256 coverage, bool isInsufficientDepositAmount);\n\n    function calculateLiquidationFees(\n        uint256 liquidationAmount\n    ) external view returns (uint256 protocolFee, uint256 liquidatorFee);\n\n    function registerCurrency(bytes32 ccy, address tokenAddress, bool isCollateral) external;\n\n    function updateCurrency(bytes32 ccy, bool isCollateral) external;\n\n    function deposit(bytes32 ccy, uint256 amount) external payable;\n\n    function depositTo(bytes32 ccy, uint256 amount, address onBehalfOf) external payable;\n\n    function depositFrom(address user, bytes32 ccy, uint256 amount) external payable;\n\n    function depositWithPermitTo(\n        bytes32 ccy,\n        uint256 amount,\n        address onBehalfOf,\n        uint256 deadline,\n        uint8 permitV,\n        bytes32 permitR,\n        bytes32 permitS\n    ) external;\n\n    function depositWithPermitFrom(\n        address user,\n        bytes32 ccy,\n        uint256 amount,\n        uint256 deadline,\n        uint8 permitV,\n        bytes32 permitR,\n        bytes32 permitS\n    ) external;\n\n    function withdraw(bytes32 ccy, uint256 amount) external;\n\n    function addDepositAmount(address user, bytes32 ccy, uint256 amount) external;\n\n    function removeDepositAmount(address user, bytes32 ccy, uint256 amount) external;\n\n    function cleanUpUsedCurrencies(address user, bytes32 ccy) external;\n\n    function executeForcedReset(address user, bytes32 ccy) external returns (uint256 removedAmount);\n\n    function transferFrom(\n        bytes32 ccy,\n        address sender,\n        address receiver,\n        uint256 amount\n    ) external returns (uint256 untransferredAmount);\n\n    function pause() external;\n\n    function unpause() external;\n\n    function getLiquidationThresholdRate() external view returns (uint256 rate);\n}\n"
      },
      "contracts/protocol/LendingMarket.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\n// interfaces\nimport {ILendingMarket} from \"./interfaces/ILendingMarket.sol\";\n// libraries\nimport {Contracts} from \"./libraries/Contracts.sol\";\nimport {Constants} from \"./libraries/Constants.sol\";\nimport {OrderActionLogic} from \"./libraries/logics/OrderActionLogic.sol\";\nimport {OrderBookLogic} from \"./libraries/logics/OrderBookLogic.sol\";\nimport {OrderReaderLogic} from \"./libraries/logics/OrderReaderLogic.sol\";\nimport {RoundingUint256} from \"./libraries/math/RoundingUint256.sol\";\nimport {FilledOrder, PartiallyFilledOrder} from \"./libraries/OrderBookLib.sol\";\n// mixins\nimport {MixinAddressResolver} from \"./mixins/MixinAddressResolver.sol\";\n// types\nimport {ProtocolTypes} from \"./types/ProtocolTypes.sol\";\n// utils\nimport {Pausable} from \"./utils/Pausable.sol\";\nimport {Proxyable} from \"./utils/Proxyable.sol\";\n// storages\nimport {LendingMarketStorage as Storage, ItayoseLog} from \"./storages/LendingMarketStorage.sol\";\n\n/**\n * @notice Implements the module that allows order book participants to execute/cancel/unwind orders.\n *\n * For updates, this contract is basically called from `LendingMarketController.sol`instead of being called\n * directly by the user.\n *\n * @dev Open orders is stored in structured red-black trees and doubly linked lists in each node.\n */\ncontract LendingMarket is ILendingMarket, MixinAddressResolver, Pausable, Proxyable {\n    using RoundingUint256 for uint256;\n\n    /// @dev Used for minimum reliable amount in base currency for block unit price\n    uint256 immutable MINIMUM_RELIABLE_AMOUNT_IN_BASE_CURRENCY;\n\n    /**\n     * @notice Modifier to make a function callable only by order maker.\n     * @param _orderBookId The order book id\n     * @param _user User's address\n     * @param _orderId Market order id\n     */\n    modifier onlyMaker(\n        uint8 _orderBookId,\n        address _user,\n        uint48 _orderId\n    ) {\n        (, , , address maker, , , ) = getOrder(_orderBookId, _orderId);\n        if (maker == address(0)) revert NoOrderExists();\n        if (_user != maker) revert CallerNotMaker();\n        _;\n    }\n\n    /**\n     * @notice Modifier to check if the market is opened.\n     * @param _orderBookId The order book id\n     */\n    modifier ifOpened(uint8 _orderBookId) {\n        if (!isOpened(_orderBookId)) revert MarketNotOpened();\n        _;\n    }\n\n    /**\n     * @notice Modifier to check if the market is under the Itayose period.\n     * @param _orderBookId The order book id\n     */\n    modifier ifItayosePeriod(uint8 _orderBookId) {\n        if (!isItayosePeriod(_orderBookId)) revert NotItayosePeriod();\n        _;\n    }\n\n    /**\n     * @notice Modifier to check if the market is not under the Itayose period.\n     * @param _orderBookId The order book id\n     */\n    modifier ifNotItayosePeriod(uint8 _orderBookId) {\n        if (isItayosePeriod(_orderBookId)) revert AlreadyItayosePeriod();\n        _;\n    }\n\n    /**\n     * @notice Modifier to check if the market is under the pre-order period.\n     * @param _orderBookId The order book id\n     */\n    modifier ifPreOrderPeriod(uint8 _orderBookId) {\n        if (!isPreOrderPeriod(_orderBookId)) revert NotPreOrderPeriod();\n        _;\n    }\n\n    /**\n     * @notice Contract constructor function.\n     * @param _minimumReliableAmount The minimum reliable amount the base currency for calculating block unit price\n     */\n    constructor(uint256 _minimumReliableAmount) {\n        MINIMUM_RELIABLE_AMOUNT_IN_BASE_CURRENCY = _minimumReliableAmount;\n    }\n\n    /**\n     * @notice Initializes the contract.\n     * @dev Function is invoked by the proxy contract when the contract is added to the ProxyController.\n     * @param _resolver The address of the Address Resolver contract\n     * @param _ccy The main currency for the order book\n     */\n    function initialize(\n        address _resolver,\n        bytes32 _ccy,\n        uint256 _orderFeeRate,\n        uint256 _cbLimitRange\n    ) public initializer onlyBeacon {\n        registerAddressResolver(_resolver);\n        Storage.slot().ccy = _ccy;\n\n        OrderBookLogic.updateOrderFeeRate(_orderFeeRate);\n        OrderBookLogic.updateCircuitBreakerLimitRange(_cbLimitRange);\n\n        buildCache();\n    }\n\n    // @inheritdoc MixinAddressResolver\n    function requiredContracts() public pure override returns (bytes32[] memory contracts) {\n        contracts = new bytes32[](2);\n        contracts[0] = Contracts.CURRENCY_CONTROLLER;\n        contracts[1] = Contracts.LENDING_MARKET_CONTROLLER;\n    }\n\n    /**\n     * @notice Gets if the market is ready.\n     * @param _orderBookId The order book id\n     * @return The boolean if the market is ready or not\n     */\n    function isReady(uint8 _orderBookId) public view override returns (bool) {\n        return OrderBookLogic.isReady(_orderBookId);\n    }\n\n    /**\n     * @notice Gets if the market is matured.\n     * @param _orderBookId The order book id\n     * @return The boolean if the market is matured or not\n     */\n    function isMatured(uint8 _orderBookId) public view override returns (bool) {\n        return OrderBookLogic.isMatured(_orderBookId);\n    }\n\n    /**\n     * @notice Gets if the market is opened.\n     * @param _orderBookId The order book id\n     * @return The boolean if the market is opened or not\n     */\n    function isOpened(uint8 _orderBookId) public view override returns (bool) {\n        return OrderBookLogic.isOpened(_orderBookId);\n    }\n\n    /**\n     * @notice Gets if the market is under the Itayose period.\n     * @param _orderBookId The order book id\n     * @return The boolean if the market is under the Itayose period.\n     */\n    function isItayosePeriod(uint8 _orderBookId) public view returns (bool) {\n        return OrderBookLogic.isItayosePeriod(_orderBookId);\n    }\n\n    /**\n     * @notice Gets if the market is under the pre-order period.\n     * @param _orderBookId The order book id\n     * @return The boolean if the market is under the pre-order period.\n     */\n    function isPreOrderPeriod(uint8 _orderBookId) public view override returns (bool) {\n        return OrderBookLogic.isPreOrderPeriod(_orderBookId);\n    }\n\n    // @inheritdoc Proxyable\n    function getRevision() external pure override returns (uint256) {\n        return 0x2;\n    }\n\n    /**\n     * @notice Gets the order book detail.\n     * @param _orderBookId The order book id\n     * @return ccy The currency of the order book\n     * @return maturity The maturity of the order book\n     * @return openingDate The opening date of the order book\n     * @return preOpeningDate The pre-opening date of the order book\n     */\n    function getOrderBookDetail(\n        uint8 _orderBookId\n    )\n        public\n        view\n        override\n        returns (bytes32 ccy, uint256 maturity, uint256 openingDate, uint256 preOpeningDate)\n    {\n        return OrderBookLogic.getOrderBookDetail(_orderBookId);\n    }\n\n    /**\n     * @notice Gets unit price Thresholds by CircuitBreaker.\n     * @param _orderBookId The order book id\n     * @return maxLendUnitPrice The maximum unit price for lending\n     * @return minBorrowUnitPrice The minimum unit price for borrowing\n     */\n    function getCircuitBreakerThresholds(\n        uint8 _orderBookId\n    ) external view override returns (uint256 maxLendUnitPrice, uint256 minBorrowUnitPrice) {\n        return OrderBookLogic.getCircuitBreakerThresholds(_orderBookId);\n    }\n\n    /**\n     * @notice Gets the best price for lending.\n     * @param _orderBookId The order book id\n     * @return The best price for lending\n     */\n    function getBestLendUnitPrice(uint8 _orderBookId) public view override returns (uint256) {\n        return OrderBookLogic.getBestLendUnitPrice(_orderBookId);\n    }\n\n    /**\n     * @notice Gets the best prices for lending.\n     * @return The array of the best price for lending\n     */\n    function getBestLendUnitPrices(\n        uint8[] calldata _orderBookIds\n    ) external view override returns (uint256[] memory) {\n        return OrderBookLogic.getBestLendUnitPrices(_orderBookIds);\n    }\n\n    /**\n     * @notice Gets the best price for borrowing.\n     * @param _orderBookId The order book id\n     * @return The best price for borrowing\n     */\n    function getBestBorrowUnitPrice(uint8 _orderBookId) public view override returns (uint256) {\n        return OrderBookLogic.getBestBorrowUnitPrice(_orderBookId);\n    }\n\n    /**\n     * @notice Gets the best prices for borrowing.\n     * @return The array of the best price for borrowing\n     */\n    function getBestBorrowUnitPrices(\n        uint8[] calldata _orderBookIds\n    ) external view override returns (uint256[] memory) {\n        return OrderBookLogic.getBestBorrowUnitPrices(_orderBookIds);\n    }\n\n    /**\n     * @notice Gets the market unit price\n     * @param _orderBookId The order book id\n     * @return The market unit price\n     */\n    function getMarketUnitPrice(uint8 _orderBookId) external view override returns (uint256) {\n        return OrderBookLogic.getMarketUnitPrice(_orderBookId);\n    }\n\n    /**\n     * @notice Gets the block timestamp of the last filled order.\n     * @param _orderBookId The order book id\n     * @return The block number\n     */\n    function getLastOrderTimestamp(uint8 _orderBookId) external view override returns (uint48) {\n        return OrderBookLogic.getLastOrderTimestamp(_orderBookId);\n    }\n\n    /**\n     * @notice Gets the block unit price history\n     * @param _orderBookId The order book id\n     * @return unitPrices The array of the block unit price\n     * @return timestamp Timestamp of the last block unit price\n     */\n    function getBlockUnitPriceHistory(\n        uint8 _orderBookId\n    ) external view override returns (uint256[] memory unitPrices, uint48 timestamp) {\n        return OrderBookLogic.getBlockUnitPriceHistory(_orderBookId);\n    }\n\n    /**\n     * @notice Gets the block unit price average.\n     * @param _orderBookId The order book id\n     * @param _count Count of data used for averaging\n     * @return The block unit price average\n     */\n    function getBlockUnitPriceAverage(\n        uint8 _orderBookId,\n        uint256 _count\n    ) external view override returns (uint256) {\n        return OrderBookLogic.getBlockUnitPriceAverage(_orderBookId, _count);\n    }\n\n    /**\n     * @notice Gets the order book of borrow orders.\n     * This function supports pagination. If a unit price is specified in `_start`,\n     * the orders are returned for the _limit from there. The unit price that can be\n     * set to `_start` of the next data fetching is set to the return value, `next`.\n     * If `_start` is 0, this function returns from the first order.\n     * @param _orderBookId The order book id\n     * @param _start The starting unit price to get order book\n     * @param _limit The max limit for getting unit prices\n     * @return unitPrices The array of order unit prices\n     * @return amounts The array of order amounts\n     * @return quantities The array of order quantities\n     * @return next The next starting unit price to get order book\n     */\n    function getBorrowOrderBook(\n        uint8 _orderBookId,\n        uint256 _start,\n        uint256 _limit\n    )\n        external\n        view\n        override\n        returns (\n            uint256[] memory unitPrices,\n            uint256[] memory amounts,\n            uint256[] memory quantities,\n            uint256 next\n        )\n    {\n        return OrderBookLogic.getBorrowOrderBook(_orderBookId, _start, _limit);\n    }\n\n    /**\n     * @notice Gets the order book of lend orders.\n     * This function supports pagination. If a unit price is specified in `_start`,\n     * the orders are returned for the _limit from there. The unit price that can be\n     * set to `_start` of the next data fetching is set to the return value, `next`.\n     * If `_start` is 0, this function returns from the first order.\n     * @param _orderBookId The order book id\n     * @param _start The starting unit price to get order book\n     * @param _limit The max limit for getting unit prices\n     * @return unitPrices The array of order unit prices\n     * @return amounts The array of order amounts\n     * @return quantities The array of order quantities\n     * @return next The next starting unit price to get order book\n     */\n    function getLendOrderBook(\n        uint8 _orderBookId,\n        uint256 _start,\n        uint256 _limit\n    )\n        external\n        view\n        override\n        returns (\n            uint256[] memory unitPrices,\n            uint256[] memory amounts,\n            uint256[] memory quantities,\n            uint256 next\n        )\n    {\n        return OrderBookLogic.getLendOrderBook(_orderBookId, _start, _limit);\n    }\n\n    /**\n     * @notice Gets the estimation of the Itayose process.\n     * @param _orderBookId The order book id\n     * @return openingUnitPrice The opening price when Itayose is executed\n     * @return lastLendUnitPrice The price of the last lend order filled by Itayose.\n     * @return lastBorrowUnitPrice The price of the last borrow order filled by Itayose.\n     * @return totalOffsetAmount The total amount of the orders filled by Itayose.\n     */\n    function getItayoseEstimation(\n        uint8 _orderBookId\n    )\n        external\n        view\n        returns (\n            uint256 openingUnitPrice,\n            uint256 lastLendUnitPrice,\n            uint256 lastBorrowUnitPrice,\n            uint256 totalOffsetAmount\n        )\n    {\n        return OrderBookLogic.getItayoseEstimation(_orderBookId);\n    }\n\n    /**\n     * @notice Gets the current market maturity.\n     * @param _orderBookId The order book id\n     * @return maturity The market maturity\n     */\n    function getMaturity(uint8 _orderBookId) public view override returns (uint256 maturity) {\n        return Storage.slot().orderBooks[_orderBookId].maturity;\n    }\n\n    /**\n     * @notice Gets the order book maturities.\n     * @return maturities The array of maturity\n     */\n    function getMaturities(\n        uint8[] calldata _orderBookIds\n    ) external view override returns (uint256[] memory maturities) {\n        return OrderBookLogic.getMaturities(_orderBookIds);\n    }\n\n    /**\n     * @notice Gets the market currency.\n     * @return currency The market currency\n     */\n    function getCurrency() external view override returns (bytes32 currency) {\n        return Storage.slot().ccy;\n    }\n\n    /**\n     * @notice Gets the order fee rate\n     * @return The order fee rate received by protocol\n     */\n    function getOrderFeeRate() external view override returns (uint256) {\n        return Storage.slot().orderFeeRate;\n    }\n\n    /**\n     * @notice Gets the limit range in unit price for the circuit breaker\n     * @return The limit range in unit price for the circuit breaker\n     */\n    function getCircuitBreakerLimitRange() external view override returns (uint256) {\n        return Storage.slot().circuitBreakerLimitRange;\n    }\n\n    /**\n     * @notice Gets the market opening date.\n     * @param _orderBookId The order book id\n     * @return openingDate The market opening date\n     */\n    function getOpeningDate(uint8 _orderBookId) public view override returns (uint256 openingDate) {\n        return Storage.slot().orderBooks[_orderBookId].openingDate;\n    }\n\n    /**\n     * @notice Gets the market itayose logs.\n     * @param _maturity The market maturity\n     * @return ItayoseLog of the market\n     */\n    function getItayoseLog(uint256 _maturity) external view override returns (ItayoseLog memory) {\n        return Storage.slot().itayoseLogs[_maturity];\n    }\n\n    /**\n     * @notice Gets the market order from the order book.\n     * @param _orderBookId The order book id\n     * @param _orderId The market order id\n     * @return side Order position type, Borrow or Lend\n     * @return unitPrice Amount of interest unit price\n     * @return maturity The maturity of the selected order\n     * @return maker The order maker\n     * @return amount Order amount\n     * @return timestamp Timestamp when the order was created\n     * @return isPreOrder The boolean if the order is a pre-order.\n     */\n    function getOrder(\n        uint8 _orderBookId,\n        uint48 _orderId\n    )\n        public\n        view\n        override\n        returns (\n            ProtocolTypes.Side side,\n            uint256 unitPrice,\n            uint256 maturity,\n            address maker,\n            uint256 amount,\n            uint256 timestamp,\n            bool isPreOrder\n        )\n    {\n        return OrderReaderLogic.getOrder(_orderBookId, _orderId);\n    }\n\n    /**\n     * @notice Calculates and gets the active and inactive amounts from the user orders of lending deals.\n     * @param _orderBookId The order book id\n     * @param _user User's address\n     * @return activeAmount The total amount of active order on the order book\n     * @return inactiveAmount The total amount of inactive orders filled on the order book\n     * @return inactiveFutureValue The total future value amount of inactive orders filled on the order book\n     * @return maturity The maturity of market that orders were placed.\n     */\n    function getTotalAmountFromLendOrders(\n        uint8 _orderBookId,\n        address _user\n    )\n        external\n        view\n        override\n        returns (\n            uint256 activeAmount,\n            uint256 inactiveAmount,\n            uint256 inactiveFutureValue,\n            uint256 maturity\n        )\n    {\n        return OrderReaderLogic.getTotalAmountFromLendOrders(_orderBookId, _user);\n    }\n\n    /**\n     * @notice Calculates and gets the active and inactive amounts from the user orders of borrowing deals.\n     * @param _orderBookId The order book id\n     * @param _user User's address\n     * @return activeAmount The total amount of active order on the order book\n     * @return inactiveAmount The total amount of inactive orders filled on the order book\n     * @return inactiveFutureValue The total future value amount of inactive orders filled on the order book\n     * @return maturity The maturity of market that orders were placed.\n     */\n    function getTotalAmountFromBorrowOrders(\n        uint8 _orderBookId,\n        address _user,\n        uint256 _minUnitPrice\n    )\n        external\n        view\n        override\n        returns (\n            uint256 activeAmount,\n            uint256 inactiveAmount,\n            uint256 inactiveFutureValue,\n            uint256 maturity\n        )\n    {\n        return OrderReaderLogic.getTotalAmountFromBorrowOrders(_orderBookId, _user, _minUnitPrice);\n    }\n\n    /**\n     * @notice Gets active and inactive order IDs in the lending order book.\n     * @param _orderBookId The order book id\n     * @param _user User's address\n     */\n    function getLendOrderIds(\n        uint8 _orderBookId,\n        address _user\n    )\n        external\n        view\n        override\n        returns (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds)\n    {\n        return OrderReaderLogic.getLendOrderIds(_orderBookId, _user);\n    }\n\n    /**\n     * @notice Gets active and inactive order IDs in the borrowing order book.\n     * @param _orderBookId The order book id\n     * @param _user User's address\n     */\n    function getBorrowOrderIds(\n        uint8 _orderBookId,\n        address _user\n    )\n        external\n        view\n        override\n        returns (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds)\n    {\n        return OrderReaderLogic.getBorrowOrderIds(_orderBookId, _user);\n    }\n\n    /**\n     * @notice Calculates the amount to be filled when executing an order in the order book.\n     * @param _orderBookId The order book id\n     * @param _side Order position type, Borrow or Lend\n     * @param _amount Amount of funds the user wants to borrow/lend\n     * @param _unitPrice Unit price user want to borrow/lend\n     * @return lastUnitPrice The last unit price that is filled on the order book\n     * @return filledAmount The amount that is filled on the order book\n     * @return filledAmountInFV The amount in the future value that is filled on the order book\n     * @return orderFeeInFV The order fee amount in the future value\n     * @return placedAmount The amount that is placed to the order book\n     */\n    function calculateFilledAmount(\n        uint8 _orderBookId,\n        ProtocolTypes.Side _side,\n        uint256 _amount,\n        uint256 _unitPrice\n    )\n        external\n        view\n        override\n        returns (\n            uint256 lastUnitPrice,\n            uint256 filledAmount,\n            uint256 filledAmountInFV,\n            uint256 orderFeeInFV,\n            uint256 placedAmount\n        )\n    {\n        return OrderReaderLogic.calculateFilledAmount(_orderBookId, _side, _amount, _unitPrice);\n    }\n\n    /**\n     * @notice Creates a new order book.\n     * @param _maturity The initial maturity of the order book\n     * @param _openingDate The timestamp when the order book opens\n     * @param _preOpeningDate The timestamp when the order book pre-opens\n     */\n    function createOrderBook(\n        uint256 _maturity,\n        uint256 _openingDate,\n        uint256 _preOpeningDate\n    ) external override onlyLendingMarketController returns (uint8 orderBookId) {\n        return OrderBookLogic.createOrderBook(_maturity, _openingDate, _preOpeningDate);\n    }\n\n    function executeAutoRoll(\n        uint8 _maturedOrderBookId,\n        uint8 _newNearestOrderBookId,\n        uint256 _autoRollUnitPrice\n    ) external override onlyLendingMarketController {\n        OrderBookLogic.executeAutoRoll(\n            _maturedOrderBookId,\n            _newNearestOrderBookId,\n            _autoRollUnitPrice\n        );\n    }\n\n    /**\n     * @notice Cancels the order.\n     * @param _orderBookId The order book id\n     * @param _user User address\n     * @param _orderId Market order id\n     */\n    function cancelOrder(\n        uint8 _orderBookId,\n        address _user,\n        uint48 _orderId\n    )\n        external\n        override\n        whenNotPaused\n        onlyLendingMarketController\n        onlyMaker(_orderBookId, _user, _orderId)\n        ifNotItayosePeriod(_orderBookId)\n    {\n        OrderActionLogic.cancelOrder(_orderBookId, _user, _orderId);\n    }\n\n    /**\n     * @notice Cleans up own orders to remove order ids that are already filled on the order book.\n     * @dev The order list per user is not updated in real-time when an order is filled.\n     * This function removes the filled order from that order list per user to reduce gas costs\n     * for lazy evaluation if the collateral is enough or not.\n     *\n     * @param _user User address\n     * @return activeLendOrderCount The total amount of active lend order on the order book\n     * @return activeBorrowOrderCount The total amount of active borrow order on the order book\n     * @return removedLendOrderFutureValue The total FV amount of the removed lend order amount from the order book\n     * @return removedBorrowOrderFutureValue The total FV amount of the removed borrow order amount from the order book\n     * @return removedLendOrderAmount The total PV amount of the removed lend order amount from the order book\n     * @return removedBorrowOrderAmount The total PV amount of the removed borrow order amount from the order book\n     * @return maturity The maturity of the removed orders\n     */\n    function cleanUpOrders(\n        uint8 _orderBookId,\n        address _user\n    )\n        external\n        override\n        onlyLendingMarketController\n        returns (\n            uint256 activeLendOrderCount,\n            uint256 activeBorrowOrderCount,\n            uint256 removedLendOrderFutureValue,\n            uint256 removedBorrowOrderFutureValue,\n            uint256 removedLendOrderAmount,\n            uint256 removedBorrowOrderAmount,\n            uint256 maturity\n        )\n    {\n        return OrderActionLogic.cleanUpOrders(_orderBookId, _user);\n    }\n\n    /**\n     * @notice Executes an order. Takes orders if the order is matched,\n     * and places new order if not match it.\n     * @param _orderBookId The order book id\n     * @param _side Order position type, Borrow or Lend\n     * @param _user User's address\n     * @param _amount Amount of funds the user wants to borrow/lend\n     * @param _unitPrice Unit price user wish to borrow/lend\n     * @return filledOrder User's Filled order of the user\n     * @return partiallyFilledOrder Partially filled order on the order book\n     */\n    function executeOrder(\n        uint8 _orderBookId,\n        ProtocolTypes.Side _side,\n        address _user,\n        uint256 _amount,\n        uint256 _unitPrice\n    )\n        external\n        override\n        whenNotPaused\n        onlyLendingMarketController\n        ifOpened(_orderBookId)\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 feeInFV\n        )\n    {\n        return\n            OrderActionLogic.executeOrder(\n                _orderBookId,\n                _side,\n                _user,\n                _amount,\n                _unitPrice,\n                currencyController().convertFromBaseCurrency(\n                    Storage.slot().ccy,\n                    MINIMUM_RELIABLE_AMOUNT_IN_BASE_CURRENCY\n                )\n            );\n    }\n\n    /**\n     * @notice Executes a pre-order. A pre-order will only be accepted from 168 hours (7 days) to 1 hour\n     * before the market opens (Pre-order period). At the end of this period, Itayose will be executed.\n     *\n     * @param _orderBookId The order book id\n     * @param _side Order position type, Borrow or Lend\n     * @param _amount Amount of funds the maker wants to borrow/lend\n     * @param _unitPrice Unit price taker wish to borrow/lend\n     */\n    function executePreOrder(\n        uint8 _orderBookId,\n        ProtocolTypes.Side _side,\n        address _user,\n        uint256 _amount,\n        uint256 _unitPrice\n    ) external override whenNotPaused onlyLendingMarketController ifPreOrderPeriod(_orderBookId) {\n        OrderActionLogic.executePreOrder(_orderBookId, _side, _user, _amount, _unitPrice);\n    }\n\n    /**\n     * @notice Unwinds lending or borrowing positions by a specified future value amount.\n     * @param _orderBookId The order book id\n     * @param _side Order position type, Borrow or Lend\n     * @param _user User's address\n     * @param _futureValue Amount of future value unwound\n     * @return filledOrder User's Filled order of the user\n     * @return partiallyFilledOrder Partially filled order\n     */\n    function unwindPosition(\n        uint8 _orderBookId,\n        ProtocolTypes.Side _side,\n        address _user,\n        uint256 _futureValue\n    )\n        external\n        override\n        whenNotPaused\n        onlyLendingMarketController\n        ifOpened(_orderBookId)\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 feeInFV\n        )\n    {\n        return\n            OrderActionLogic.unwindPosition(\n                _orderBookId,\n                _side,\n                _user,\n                _futureValue,\n                currencyController().convertFromBaseCurrency(\n                    Storage.slot().ccy,\n                    MINIMUM_RELIABLE_AMOUNT_IN_BASE_CURRENCY\n                )\n            );\n    }\n\n    /**\n     * @notice Executes Itayose to aggregate pre-orders and determine the opening unit price.\n     * After this action, the market opens.\n     * @dev If the opening date had already passed when this contract was created, this Itayose need not be executed.\n     * @param _orderBookId The order book id\n     * @return openingUnitPrice The opening price when Itayose is executed\n     * @return totalOffsetAmount The total filled amount when Itayose is executed\n     * @return openingDate The timestamp when the market opens\n     * @return partiallyFilledLendingOrder Partially filled lending order on the order book\n     * @return partiallyFilledBorrowingOrder Partially filled borrowing order on the order book\n     */\n    function executeItayoseCall(\n        uint8 _orderBookId\n    )\n        external\n        override\n        whenNotPaused\n        onlyLendingMarketController\n        ifItayosePeriod(_orderBookId)\n        returns (\n            uint256 openingUnitPrice,\n            uint256 totalOffsetAmount,\n            uint256 openingDate,\n            PartiallyFilledOrder memory partiallyFilledLendingOrder,\n            PartiallyFilledOrder memory partiallyFilledBorrowingOrder\n        )\n    {\n        return OrderBookLogic.executeItayoseCall(_orderBookId);\n    }\n\n    /**\n     * @notice Updates the order fee rate\n     * @param _orderFeeRate The order fee rate received by protocol\n     */\n    function updateOrderFeeRate(\n        uint256 _orderFeeRate\n    ) external override onlyLendingMarketController {\n        OrderBookLogic.updateOrderFeeRate(_orderFeeRate);\n    }\n\n    /**\n     * @notice Updates the auto-roll fee rate\n     * @param _cbLimitRange The circuit breaker limit range\n     */\n    function updateCircuitBreakerLimitRange(\n        uint256 _cbLimitRange\n    ) external override onlyLendingMarketController {\n        OrderBookLogic.updateCircuitBreakerLimitRange(_cbLimitRange);\n    }\n\n    /**\n     * @notice Pauses the lending market.\n     */\n    function pause() external override onlyLendingMarketController {\n        _pause();\n    }\n\n    /**\n     * @notice Unpauses the lending market.\n     */\n    function unpause() external override onlyLendingMarketController {\n        _unpause();\n    }\n}\n"
      },
      "contracts/protocol/libraries/AddressResolverLib.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {Contracts} from \"../libraries/Contracts.sol\";\nimport {IBeaconProxyController} from \"../interfaces/IBeaconProxyController.sol\";\nimport {ICurrencyController} from \"../interfaces/ICurrencyController.sol\";\nimport {IGenesisValueVault} from \"../interfaces/IGenesisValueVault.sol\";\nimport {ILendingMarketController} from \"../interfaces/ILendingMarketController.sol\";\nimport {IReserveFund} from \"../interfaces/IReserveFund.sol\";\nimport {ITokenVault} from \"../interfaces/ITokenVault.sol\";\nimport {MixinAddressResolverStorage as Storage} from \"../storages/mixins/MixinAddressResolverStorage.sol\";\n\nlibrary AddressResolverLib {\n    error MissingAddress(string name);\n\n    function getAddress(bytes32 name) internal view returns (address) {\n        address _foundAddress = Storage.slot().addressCache[name];\n        if (_foundAddress == address(0)) revert MissingAddress(string(abi.encodePacked(name)));\n        return _foundAddress;\n    }\n\n    function beaconProxyController() internal view returns (IBeaconProxyController) {\n        return IBeaconProxyController(getAddress(Contracts.BEACON_PROXY_CONTROLLER));\n    }\n\n    function currencyController() internal view returns (ICurrencyController) {\n        return ICurrencyController(getAddress(Contracts.CURRENCY_CONTROLLER));\n    }\n\n    function genesisValueVault() internal view returns (IGenesisValueVault) {\n        return IGenesisValueVault(getAddress(Contracts.GENESIS_VALUE_VAULT));\n    }\n\n    function lendingMarketController() internal view returns (ILendingMarketController) {\n        return ILendingMarketController(getAddress(Contracts.LENDING_MARKET_CONTROLLER));\n    }\n\n    function reserveFund() internal view returns (IReserveFund) {\n        return IReserveFund(getAddress(Contracts.RESERVE_FUND));\n    }\n\n    function tokenVault() internal view returns (ITokenVault) {\n        return ITokenVault(getAddress(Contracts.TOKEN_VAULT));\n    }\n}\n"
      },
      "contracts/protocol/libraries/Constants.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nlibrary Constants {\n    /// @dev Used for price digits in the basis (10000 -> 1)\n    uint256 internal constant PRICE_DIGIT = 10000;\n\n    /// @dev Used for percentage digits in the basis (10000 -> 100%)\n    uint256 internal constant PCT_DIGIT = 10000;\n\n    /// @dev Used for seconds in year (60 * 60 * 24 * 365)\n    uint256 internal constant SECONDS_IN_YEAR = 31536000;\n\n    /// @dev Used for maximum order count per currency\n    uint256 internal constant MAXIMUM_ORDER_COUNT = 20;\n}\n"
      },
      "contracts/protocol/libraries/Contracts.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nlibrary Contracts {\n    bytes32 internal constant BEACON_PROXY_CONTROLLER = \"BeaconProxyController\";\n    bytes32 internal constant CURRENCY_CONTROLLER = \"CurrencyController\";\n    bytes32 internal constant GENESIS_VALUE_VAULT = \"GenesisValueVault\";\n    bytes32 internal constant LENDING_MARKET_CONTROLLER = \"LendingMarketController\";\n    bytes32 internal constant RESERVE_FUND = \"ReserveFund\";\n    bytes32 internal constant TOKEN_VAULT = \"TokenVault\";\n}\n\nlibrary BeaconContracts {\n    bytes32 internal constant FUTURE_VALUE_VAULT = \"FutureValueVault\";\n    bytes32 internal constant LENDING_MARKET = \"LendingMarket\";\n    bytes32 internal constant ZC_TOKEN = \"ZCToken\";\n}\n"
      },
      "contracts/protocol/libraries/logics/OrderActionLogic.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {OrderBookLib, FilledOrder, PartiallyFilledOrder} from \"../OrderBookLib.sol\";\nimport {ProtocolTypes} from \"../../types/ProtocolTypes.sol\";\nimport {LendingMarketStorage as Storage} from \"../../storages/LendingMarketStorage.sol\";\n// libraries\nimport {Constants} from \"../Constants.sol\";\nimport {AddressResolverLib} from \"../AddressResolverLib.sol\";\nimport {RoundingUint256} from \"../math/RoundingUint256.sol\";\nimport {OrderReaderLogic} from \"./OrderReaderLogic.sol\";\n\nlibrary OrderActionLogic {\n    using OrderBookLib for OrderBookLib.OrderBook;\n    using RoundingUint256 for uint256;\n\n    error InvalidAmount();\n    error InvalidFutureValue();\n    error EmptyOrderBook();\n    error OppositeSideOrderExists();\n\n    struct OrderExecutionConditions {\n        bool isFilled;\n        uint256 executedUnitPrice;\n        bool ignoreRemainingAmount;\n        bool orderExists;\n    }\n\n    struct PlacedOrder {\n        uint48 orderId;\n        uint256 amount;\n        uint256 unitPrice;\n    }\n\n    struct ExecuteOrderVars {\n        OrderExecutionConditions conditions;\n        PlacedOrder placedOrder;\n        bool isCircuitBreakerTriggered;\n        uint256 maturity;\n    }\n\n    struct FillOrdersVars {\n        uint8 orderBookId;\n        uint256 remainingAmount;\n        bool orderExists;\n    }\n\n    event OrderCanceled(\n        uint48 orderId,\n        address indexed maker,\n        ProtocolTypes.Side side,\n        bytes32 ccy,\n        uint256 maturity,\n        uint256 amount,\n        uint256 unitPrice\n    );\n\n    event OrdersCleaned(\n        uint48[] orderIds,\n        address indexed maker,\n        ProtocolTypes.Side side,\n        bytes32 indexed ccy,\n        uint256 maturity,\n        uint256 amount,\n        uint256 futureValue\n    );\n\n    event OrderExecuted(\n        address indexed user,\n        ProtocolTypes.Side side,\n        bytes32 indexed ccy,\n        uint256 indexed maturity,\n        uint256 inputAmount,\n        uint256 inputUnitPrice,\n        uint256 filledAmount,\n        uint256 filledUnitPrice,\n        uint256 filledAmountInFV,\n        uint256 feeInFV,\n        uint48 placedOrderId,\n        uint256 placedAmount,\n        uint256 placedUnitPrice,\n        bool isCircuitBreakerTriggered\n    );\n\n    event PreOrderExecuted(\n        address indexed user,\n        ProtocolTypes.Side side,\n        bytes32 indexed ccy,\n        uint256 indexed maturity,\n        uint256 amount,\n        uint256 unitPrice,\n        uint48 orderId\n    );\n\n    event PositionUnwound(\n        address indexed user,\n        ProtocolTypes.Side side,\n        bytes32 indexed ccy,\n        uint256 indexed maturity,\n        uint256 inputFutureValue,\n        uint256 filledAmount,\n        uint256 filledUnitPrice,\n        uint256 filledAmountInFV,\n        uint256 feeInFV,\n        bool isCircuitBreakerTriggered\n    );\n\n    event BlockUnitPriceHistoryUpdated(\n        bytes32 indexed ccy,\n        uint256 indexed maturity,\n        uint256 blockUnitPrice\n    );\n\n    function cancelOrder(uint8 _orderBookId, address _user, uint48 _orderId) external {\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n\n        (ProtocolTypes.Side side, uint256 removedAmount, uint256 unitPrice) = orderBook.removeOrder(\n            _user,\n            _orderId\n        );\n\n        emit OrderCanceled(\n            _orderId,\n            _user,\n            side,\n            Storage.slot().ccy,\n            orderBook.maturity,\n            removedAmount,\n            unitPrice\n        );\n    }\n\n    function cleanUpOrders(\n        uint8 _orderBookId,\n        address _user\n    )\n        external\n        returns (\n            uint256 activeLendOrderCount,\n            uint256 activeBorrowOrderCount,\n            uint256 removedLendOrderFutureValue,\n            uint256 removedBorrowOrderFutureValue,\n            uint256 removedLendOrderAmount,\n            uint256 removedBorrowOrderAmount,\n            uint256 maturity\n        )\n    {\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n        maturity = orderBook.userCurrentMaturities[_user];\n\n        uint48[] memory lendOrderIds;\n        uint48[] memory borrowOrderIds;\n\n        (\n            lendOrderIds,\n            activeLendOrderCount,\n            removedLendOrderFutureValue,\n            removedLendOrderAmount\n        ) = _cleanLendOrders(_orderBookId, _user);\n\n        (\n            borrowOrderIds,\n            activeBorrowOrderCount,\n            removedBorrowOrderFutureValue,\n            removedBorrowOrderAmount\n        ) = _cleanBorrowOrders(_orderBookId, _user);\n\n        if (removedLendOrderAmount > 0) {\n            emit OrdersCleaned(\n                lendOrderIds,\n                _user,\n                ProtocolTypes.Side.LEND,\n                Storage.slot().ccy,\n                orderBook.maturity,\n                removedLendOrderAmount,\n                removedLendOrderFutureValue\n            );\n        }\n\n        if (removedBorrowOrderAmount > 0) {\n            emit OrdersCleaned(\n                borrowOrderIds,\n                _user,\n                ProtocolTypes.Side.BORROW,\n                Storage.slot().ccy,\n                orderBook.maturity,\n                removedBorrowOrderAmount,\n                removedBorrowOrderFutureValue\n            );\n        }\n    }\n\n    function executeOrder(\n        uint8 _orderBookId,\n        ProtocolTypes.Side _side,\n        address _user,\n        uint256 _amount,\n        uint256 _unitPrice,\n        uint256 _minimumReliableAmount\n    )\n        external\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 feeInFV\n        )\n    {\n        if (_amount == 0) revert InvalidAmount();\n\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n        orderBook.updateUserMaturity(_user);\n\n        ExecuteOrderVars memory vars;\n        vars.maturity = orderBook.maturity;\n\n        (\n            vars.conditions.isFilled,\n            vars.conditions.executedUnitPrice,\n            vars.conditions.ignoreRemainingAmount,\n            vars.conditions.orderExists\n        ) = orderBook.getOrderExecutionConditions(\n            _side,\n            _unitPrice,\n            Storage.slot().circuitBreakerLimitRange,\n            false\n        );\n\n        if (_unitPrice == 0 && !vars.conditions.orderExists) revert EmptyOrderBook();\n\n        if (vars.conditions.isFilled) {\n            (\n                filledOrder,\n                partiallyFilledOrder,\n                vars.placedOrder,\n                vars.isCircuitBreakerTriggered\n            ) = _fillOrders(\n                _orderBookId,\n                _side,\n                _user,\n                _amount,\n                vars.conditions.executedUnitPrice,\n                vars.conditions.ignoreRemainingAmount\n            );\n            feeInFV = OrderReaderLogic.calculateOrderFeeAmount(\n                vars.maturity,\n                filledOrder.futureValue\n            );\n            (uint256 latestBlockUnitPrice, bool isUpdated) = orderBook.updateBlockUnitPriceHistory(\n                filledOrder.amount,\n                filledOrder.futureValue,\n                _minimumReliableAmount\n            );\n\n            if (isUpdated) {\n                emit BlockUnitPriceHistoryUpdated(\n                    Storage.slot().ccy,\n                    vars.maturity,\n                    latestBlockUnitPrice\n                );\n            }\n        } else {\n            if (!vars.conditions.ignoreRemainingAmount) {\n                vars.placedOrder = PlacedOrder(\n                    orderBook.placeOrder(_side, _user, _amount, vars.conditions.executedUnitPrice),\n                    _amount,\n                    vars.conditions.executedUnitPrice\n                );\n            }\n\n            vars.isCircuitBreakerTriggered = _unitPrice == 0\n                ? vars.conditions.orderExists\n                : _unitPrice != vars.conditions.executedUnitPrice;\n        }\n\n        emit OrderExecuted(\n            _user,\n            _side,\n            Storage.slot().ccy,\n            vars.maturity,\n            _amount,\n            _unitPrice,\n            filledOrder.amount,\n            filledOrder.unitPrice,\n            filledOrder.futureValue,\n            feeInFV,\n            vars.placedOrder.orderId,\n            vars.placedOrder.amount,\n            vars.placedOrder.unitPrice,\n            vars.isCircuitBreakerTriggered\n        );\n    }\n\n    function executePreOrder(\n        uint8 _orderBookId,\n        ProtocolTypes.Side _side,\n        address _user,\n        uint256 _amount,\n        uint256 _unitPrice\n    ) external {\n        if (_amount == 0) revert InvalidAmount();\n\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n        orderBook.updateUserMaturity(_user);\n\n        if (\n            (_side == ProtocolTypes.Side.LEND && orderBook.hasBorrowOrder(_user)) ||\n            (_side == ProtocolTypes.Side.BORROW && orderBook.hasLendOrder(_user))\n        ) {\n            revert OppositeSideOrderExists();\n        }\n\n        uint48 orderId = orderBook.placeOrder(_side, _user, _amount, _unitPrice);\n        orderBook.isPreOrder[orderId] = true;\n\n        emit PreOrderExecuted(\n            _user,\n            _side,\n            Storage.slot().ccy,\n            orderBook.maturity,\n            _amount,\n            _unitPrice,\n            orderId\n        );\n    }\n\n    function unwindPosition(\n        uint8 _orderBookId,\n        ProtocolTypes.Side _side,\n        address _user,\n        uint256 _futureValue,\n        uint256 _minimumReliableAmount\n    )\n        external\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 feeInFV\n        )\n    {\n        if (_futureValue == 0) revert InvalidFutureValue();\n\n        OrderExecutionConditions memory conditions;\n        bool isCircuitBreakerTriggered;\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n        uint256 maturity = orderBook.maturity;\n\n        (\n            conditions.isFilled,\n            conditions.executedUnitPrice,\n            conditions.ignoreRemainingAmount,\n            conditions.orderExists\n        ) = orderBook.getOrderExecutionConditions(\n            _side,\n            0,\n            Storage.slot().circuitBreakerLimitRange,\n            false\n        );\n\n        if (!conditions.orderExists) revert EmptyOrderBook();\n\n        if (conditions.isFilled) {\n            (filledOrder, partiallyFilledOrder, isCircuitBreakerTriggered) = _unwindPosition(\n                _orderBookId,\n                _side,\n                _futureValue,\n                conditions.executedUnitPrice\n            );\n            feeInFV = OrderReaderLogic.calculateOrderFeeAmount(maturity, filledOrder.futureValue);\n            (uint256 latestBlockUnitPrice, bool isUpdated) = orderBook.updateBlockUnitPriceHistory(\n                filledOrder.amount,\n                filledOrder.futureValue,\n                _minimumReliableAmount\n            );\n\n            if (isUpdated) {\n                emit BlockUnitPriceHistoryUpdated(\n                    Storage.slot().ccy,\n                    maturity,\n                    latestBlockUnitPrice\n                );\n            }\n        } else {\n            isCircuitBreakerTriggered = true;\n        }\n\n        emit PositionUnwound(\n            _user,\n            _side,\n            Storage.slot().ccy,\n            maturity,\n            _futureValue,\n            filledOrder.amount,\n            filledOrder.unitPrice,\n            filledOrder.futureValue,\n            feeInFV,\n            isCircuitBreakerTriggered\n        );\n    }\n\n    function _cleanLendOrders(\n        uint8 _orderBookId,\n        address _user\n    )\n        internal\n        returns (\n            uint48[] memory orderIds,\n            uint256 activeOrderCount,\n            uint256 removedFutureValue,\n            uint256 removedOrderAmount\n        )\n    {\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n\n        (uint48[] memory activeLendOrderIds, uint48[] memory inActiveLendOrderIds) = orderBook\n            .getLendOrderIds(_user);\n\n        orderBook.activeLendOrderIds[_user] = activeLendOrderIds;\n        activeOrderCount = activeLendOrderIds.length;\n        uint256 inactiveOrderCount = inActiveLendOrderIds.length;\n        orderIds = new uint48[](inactiveOrderCount);\n\n        for (uint256 i; i < inactiveOrderCount; i++) {\n            (uint256 presentValue, uint256 futureValue) = OrderReaderLogic.getLendOrderAmounts(\n                orderBook,\n                inActiveLendOrderIds[i]\n            );\n\n            removedOrderAmount += presentValue;\n            removedFutureValue += futureValue;\n            orderIds[i] = inActiveLendOrderIds[i];\n        }\n    }\n\n    function _cleanBorrowOrders(\n        uint8 _orderBookId,\n        address _user\n    )\n        internal\n        returns (\n            uint48[] memory orderIds,\n            uint256 activeOrderCount,\n            uint256 removedFutureValue,\n            uint256 removedOrderAmount\n        )\n    {\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n\n        (uint48[] memory activeBorrowOrderIds, uint48[] memory inActiveBorrowOrderIds) = orderBook\n            .getBorrowOrderIds(_user);\n\n        orderBook.activeBorrowOrderIds[_user] = activeBorrowOrderIds;\n        activeOrderCount = activeBorrowOrderIds.length;\n        uint256 inactiveOrderCount = inActiveBorrowOrderIds.length;\n        orderIds = new uint48[](inactiveOrderCount);\n\n        for (uint256 i; i < inactiveOrderCount; i++) {\n            (uint256 presentValue, uint256 futureValue, ) = OrderReaderLogic.getBorrowOrderAmounts(\n                orderBook,\n                inActiveBorrowOrderIds[i]\n            );\n\n            removedOrderAmount += presentValue;\n            removedFutureValue += futureValue;\n            orderIds[i] = inActiveBorrowOrderIds[i];\n        }\n    }\n\n    /**\n     * @notice Fills orders in the order book.\n     * @param _side Order position type, Borrow or Lend\n     * @param _user User's address\n     * @param _amount Amount of funds the maker wants to borrow/lend\n     * @param _unitPrice Unit price taken\n     * @param _ignoreRemainingAmount Boolean for whether to ignore the remaining amount after filling orders\n     */\n    function _fillOrders(\n        uint8 _orderBookId,\n        ProtocolTypes.Side _side,\n        address _user,\n        uint256 _amount,\n        uint256 _unitPrice,\n        bool _ignoreRemainingAmount\n    )\n        private\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            PlacedOrder memory placedOrder,\n            bool isCircuitBreakerTriggered\n        )\n    {\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n        FillOrdersVars memory vars;\n        vars.orderBookId = _orderBookId;\n\n        (filledOrder, partiallyFilledOrder, vars.remainingAmount, vars.orderExists) = orderBook\n            .fillOrders(_side, _amount, 0, _unitPrice);\n\n        if (vars.remainingAmount > 0) {\n            if (_ignoreRemainingAmount) {\n                filledOrder.ignoredAmount = vars.remainingAmount;\n            } else {\n                // Place a new order with the remaining amount of the input amount\n                placedOrder = PlacedOrder(\n                    orderBook.placeOrder(_side, _user, vars.remainingAmount, _unitPrice),\n                    vars.remainingAmount,\n                    _unitPrice\n                );\n            }\n        }\n\n        isCircuitBreakerTriggered =\n            vars.orderExists &&\n            _ignoreRemainingAmount &&\n            _amount != filledOrder.amount;\n    }\n\n    function _unwindPosition(\n        uint8 _orderBookId,\n        ProtocolTypes.Side _side,\n        uint256 _futureValue,\n        uint256 _unitPrice\n    )\n        private\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            bool isCircuitBreakerTriggered\n        )\n    {\n        bool orderExists;\n        uint256 futureValueWithFee;\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n        uint256 maturity = orderBook.maturity;\n        uint256 currentMaturity = maturity >= block.timestamp ? maturity - block.timestamp : 0;\n\n        if (_side == ProtocolTypes.Side.BORROW) {\n            // To unwind all positions, calculate the future value taking into account\n            // the added portion of the fee.\n            // NOTE: The formula is:\n            // actualRate = feeRate * (currentMaturity / SECONDS_IN_YEAR)\n            // amount = totalAmountInFV / (1 + actualRate)\n            futureValueWithFee = (_futureValue * Constants.SECONDS_IN_YEAR * Constants.PCT_DIGIT)\n                .div(\n                    Constants.SECONDS_IN_YEAR *\n                        Constants.PCT_DIGIT +\n                        (Storage.slot().orderFeeRate * currentMaturity)\n                );\n        } else {\n            // To unwind all positions, calculate the future value taking into account\n            // the subtracted portion of the fee.\n            // NOTE: The formula is:\n            // actualRate = feeRate * (currentMaturity / SECONDS_IN_YEAR)\n            // amount = totalAmountInFV / (1 - actualRate)\n            futureValueWithFee = (_futureValue * Constants.SECONDS_IN_YEAR * Constants.PCT_DIGIT)\n                .div(\n                    Constants.SECONDS_IN_YEAR *\n                        Constants.PCT_DIGIT -\n                        (Storage.slot().orderFeeRate * currentMaturity)\n                );\n        }\n\n        (filledOrder, partiallyFilledOrder, , orderExists) = orderBook.fillOrders(\n            _side,\n            0,\n            futureValueWithFee,\n            _unitPrice\n        );\n\n        isCircuitBreakerTriggered = orderExists && _futureValue != filledOrder.futureValue;\n    }\n\n    function _getOrderBook(\n        uint8 _orderBookId\n    ) private view returns (OrderBookLib.OrderBook storage) {\n        return Storage.slot().orderBooks[_orderBookId];\n    }\n}\n"
      },
      "contracts/protocol/libraries/logics/OrderBookLogic.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {Constants} from \"../Constants.sol\";\nimport {OrderBookLib, FilledOrder, PartiallyFilledOrder} from \"../OrderBookLib.sol\";\nimport {ProtocolTypes} from \"../../types/ProtocolTypes.sol\";\nimport {LendingMarketStorage as Storage, ItayoseLog} from \"../../storages/LendingMarketStorage.sol\";\nimport {RoundingUint256} from \"../math/RoundingUint256.sol\";\nimport {ILendingMarket} from \"../../interfaces/ILendingMarket.sol\";\n\nlibrary OrderBookLogic {\n    using OrderBookLib for OrderBookLib.OrderBook;\n    using RoundingUint256 for uint256;\n\n    error InvalidOrderFeeRate();\n    error InvalidCircuitBreakerLimitRange();\n    error OrderBookNotMatured();\n\n    event OrderFeeRateUpdated(bytes32 ccy, uint256 previousRate, uint256 rate);\n    event CircuitBreakerLimitRangeUpdated(bytes32 ccy, uint256 previousRate, uint256 rate);\n    event OrderBookCreated(uint8 orderBookId, uint256 maturity, uint256 openingDate);\n\n    event ItayoseExecuted(\n        bytes32 ccy,\n        uint256 maturity,\n        uint256 openingUnitPrice,\n        uint256 lastLendUnitPrice,\n        uint256 lastBorrowUnitPrice,\n        uint256 offsetAmount\n    );\n\n    function isReady(uint8 _orderBookId) public view returns (bool) {\n        return Storage.slot().isReady[_getOrderBook(_orderBookId).maturity];\n    }\n\n    function isMatured(uint8 _orderBookId) public view returns (bool) {\n        return _getOrderBook(_orderBookId).isMatured();\n    }\n\n    function isOpened(uint8 _orderBookId) public view returns (bool) {\n        return\n            isReady(_orderBookId) &&\n            !isMatured(_orderBookId) &&\n            block.timestamp >= _getOrderBook(_orderBookId).openingDate;\n    }\n\n    function isItayosePeriod(uint8 _orderBookId) public view returns (bool) {\n        return\n            block.timestamp >=\n            (_getOrderBook(_orderBookId).openingDate - OrderBookLib.ITAYOSE_PERIOD) &&\n            !isReady(_orderBookId);\n    }\n\n    function isPreOrderPeriod(uint8 _orderBookId) public view returns (bool) {\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n        return\n            block.timestamp >= orderBook.preOpeningDate &&\n            block.timestamp < (orderBook.openingDate - OrderBookLib.ITAYOSE_PERIOD);\n    }\n\n    function getOrderBookDetail(\n        uint8 _orderBookId\n    )\n        public\n        view\n        returns (bytes32 ccy, uint256 maturity, uint256 openingDate, uint256 preOpeningDate)\n    {\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n\n        ccy = Storage.slot().ccy;\n        maturity = orderBook.maturity;\n        openingDate = orderBook.openingDate;\n        preOpeningDate = orderBook.preOpeningDate;\n    }\n\n    function getLastOrderTimestamp(uint8 _orderBookId) external view returns (uint48) {\n        return _getOrderBook(_orderBookId).lastOrderTimestamp;\n    }\n\n    function getBlockUnitPriceHistory(\n        uint8 _orderBookId\n    ) external view returns (uint256[] memory unitPrices, uint48 timestamp) {\n        return _getOrderBook(_orderBookId).getBlockUnitPriceHistory(true);\n    }\n\n    function getMarketUnitPrice(uint8 _orderBookId) external view returns (uint256) {\n        return _getOrderBook(_orderBookId).getMarketUnitPrice(true);\n    }\n\n    function getBlockUnitPriceAverage(\n        uint8 _orderBookId,\n        uint256 _count\n    ) external view returns (uint256) {\n        return _getOrderBook(_orderBookId).getBlockUnitPriceAverage(_count, true);\n    }\n\n    function getCircuitBreakerThresholds(\n        uint8 _orderBookId\n    ) external view returns (uint256 maxLendUnitPrice, uint256 minBorrowUnitPrice) {\n        maxLendUnitPrice = _getOrderBook(_orderBookId).getLendCircuitBreakerThreshold(\n            Storage.slot().circuitBreakerLimitRange,\n            true\n        );\n        minBorrowUnitPrice = _getOrderBook(_orderBookId).getBorrowCircuitBreakerThreshold(\n            Storage.slot().circuitBreakerLimitRange,\n            true\n        );\n    }\n\n    function getBestLendUnitPrice(uint8 _orderBookId) public view returns (uint256) {\n        return _getOrderBook(_orderBookId).getBestLendUnitPrice();\n    }\n\n    function getBestLendUnitPrices(\n        uint8[] memory _orderBookIds\n    ) external view returns (uint256[] memory unitPrices) {\n        unitPrices = new uint256[](_orderBookIds.length);\n\n        for (uint256 i; i < _orderBookIds.length; i++) {\n            unitPrices[i] = _getOrderBook(_orderBookIds[i]).getBestLendUnitPrice();\n        }\n    }\n\n    function getBestBorrowUnitPrice(uint8 _orderBookId) public view returns (uint256) {\n        return _getOrderBook(_orderBookId).getBestBorrowUnitPrice();\n    }\n\n    function getBestBorrowUnitPrices(\n        uint8[] memory _orderBookIds\n    ) external view returns (uint256[] memory unitPrices) {\n        unitPrices = new uint256[](_orderBookIds.length);\n\n        for (uint256 i; i < _orderBookIds.length; i++) {\n            unitPrices[i] = _getOrderBook(_orderBookIds[i]).getBestBorrowUnitPrice();\n        }\n    }\n\n    function getBorrowOrderBook(\n        uint8 _orderBookId,\n        uint256 _start,\n        uint256 _limit\n    )\n        external\n        view\n        returns (\n            uint256[] memory unitPrices,\n            uint256[] memory amounts,\n            uint256[] memory quantities,\n            uint256 next\n        )\n    {\n        return _getOrderBook(_orderBookId).getBorrowOrderBook(_start, _limit);\n    }\n\n    function getLendOrderBook(\n        uint8 _orderBookId,\n        uint256 _start,\n        uint256 _limit\n    )\n        external\n        view\n        returns (\n            uint256[] memory unitPrices,\n            uint256[] memory amounts,\n            uint256[] memory quantities,\n            uint256 next\n        )\n    {\n        return _getOrderBook(_orderBookId).getLendOrderBook(_start, _limit);\n    }\n\n    function getItayoseEstimation(\n        uint8 _orderBookId\n    )\n        external\n        view\n        returns (\n            uint256 openingUnitPrice,\n            uint256 lastLendUnitPrice,\n            uint256 lastBorrowUnitPrice,\n            uint256 totalOffsetAmount\n        )\n    {\n        return _getOrderBook(_orderBookId).calculateItayoseResult();\n    }\n\n    function getMaturities(\n        uint8[] memory _orderBookIds\n    ) public view returns (uint256[] memory maturities) {\n        maturities = new uint256[](_orderBookIds.length);\n\n        for (uint256 i; i < _orderBookIds.length; i++) {\n            maturities[i] = _getOrderBook(_orderBookIds[i]).maturity;\n        }\n    }\n\n    function updateOrderFeeRate(uint256 _orderFeeRate) external {\n        if (_orderFeeRate >= Constants.PCT_DIGIT) revert InvalidOrderFeeRate();\n\n        uint256 previousRate = Storage.slot().orderFeeRate;\n\n        if (_orderFeeRate != previousRate) {\n            Storage.slot().orderFeeRate = _orderFeeRate;\n\n            emit OrderFeeRateUpdated(Storage.slot().ccy, previousRate, _orderFeeRate);\n        }\n    }\n\n    function updateCircuitBreakerLimitRange(uint256 _cbLimitRange) external {\n        if (_cbLimitRange >= Constants.PCT_DIGIT) revert InvalidCircuitBreakerLimitRange();\n\n        uint256 previousRange = Storage.slot().circuitBreakerLimitRange;\n\n        if (_cbLimitRange != previousRange) {\n            Storage.slot().circuitBreakerLimitRange = _cbLimitRange;\n\n            emit CircuitBreakerLimitRangeUpdated(Storage.slot().ccy, previousRange, _cbLimitRange);\n        }\n    }\n\n    function createOrderBook(\n        uint256 _maturity,\n        uint256 _openingDate,\n        uint256 _preOpeningDate\n    ) public returns (uint8 orderBookId) {\n        orderBookId = _nextOrderBookId();\n\n        Storage.slot().isReady[_maturity] = _getOrderBook(orderBookId).initialize(\n            _maturity,\n            _openingDate,\n            _preOpeningDate\n        );\n\n        emit OrderBookCreated(orderBookId, _maturity, _openingDate);\n    }\n\n    function executeAutoRoll(\n        uint8 _maturedOrderBookId,\n        uint8 _destinationOrderBookId,\n        uint256 _autoRollUnitPrice\n    ) external {\n        OrderBookLib.OrderBook storage maturedOrderBook = Storage.slot().orderBooks[\n            _maturedOrderBookId\n        ];\n\n        if (!maturedOrderBook.isMatured()) revert OrderBookNotMatured();\n\n        OrderBookLib.OrderBook storage destinationOrderBook = Storage.slot().orderBooks[\n            _destinationOrderBookId\n        ];\n\n        // NOTE: The auto-roll destination order book has no market unit price if the order has never been filled before.\n        // In this case, the market unit price is updated with the unit price of the auto-roll.\n        if (destinationOrderBook.getMarketUnitPrice(false) == 0) {\n            destinationOrderBook.setInitialBlockUnitPrice(_autoRollUnitPrice);\n        }\n    }\n\n    function executeItayoseCall(\n        uint8 _orderBookId\n    )\n        external\n        returns (\n            uint256 openingUnitPrice,\n            uint256 totalOffsetAmount,\n            uint256 openingDate,\n            PartiallyFilledOrder memory partiallyFilledLendingOrder,\n            PartiallyFilledOrder memory partiallyFilledBorrowingOrder\n        )\n    {\n        uint256 lastLendUnitPrice;\n        uint256 lastBorrowUnitPrice;\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n\n        (openingUnitPrice, lastLendUnitPrice, lastBorrowUnitPrice, totalOffsetAmount) = orderBook\n            .calculateItayoseResult();\n\n        if (totalOffsetAmount > 0) {\n            ProtocolTypes.Side[2] memory sides = [\n                ProtocolTypes.Side.LEND,\n                ProtocolTypes.Side.BORROW\n            ];\n\n            for (uint256 i; i < sides.length; i++) {\n                ProtocolTypes.Side partiallyFilledOrderSide;\n                PartiallyFilledOrder memory partiallyFilledOrder;\n                FilledOrder memory filledOrder;\n                (filledOrder, partiallyFilledOrder, , ) = orderBook.fillOrders(\n                    sides[i],\n                    totalOffsetAmount,\n                    0,\n                    0\n                );\n\n                if (filledOrder.futureValue > 0) {\n                    orderBook.setInitialBlockUnitPrice(openingUnitPrice);\n                }\n\n                if (partiallyFilledOrder.futureValue > 0) {\n                    if (sides[i] == ProtocolTypes.Side.LEND) {\n                        partiallyFilledOrderSide = ProtocolTypes.Side.BORROW;\n                        partiallyFilledBorrowingOrder = partiallyFilledOrder;\n                    } else {\n                        partiallyFilledOrderSide = ProtocolTypes.Side.LEND;\n                        partiallyFilledLendingOrder = partiallyFilledOrder;\n                    }\n                }\n            }\n\n            emit ItayoseExecuted(\n                Storage.slot().ccy,\n                orderBook.maturity,\n                openingUnitPrice,\n                lastLendUnitPrice,\n                lastBorrowUnitPrice,\n                totalOffsetAmount\n            );\n        }\n\n        Storage.slot().isReady[orderBook.maturity] = true;\n        Storage.slot().itayoseLogs[orderBook.maturity] = ItayoseLog(\n            openingUnitPrice,\n            lastLendUnitPrice,\n            lastBorrowUnitPrice\n        );\n        openingDate = orderBook.openingDate;\n    }\n\n    function _nextOrderBookId() internal returns (uint8) {\n        // NOTE: Originally, matured ordebooks were reused as new orderbooks after auto-rolls, but this reusing logic has been removed.\n        // This means that `orderBookId` is increased in proportion to the number of auto-rolls executed. To avoid overflow of `orderBookId`,\n        // `orderBookId` circulate between 1 and 255.\n        if (Storage.slot().lastOrderBookId == type(uint8).max) {\n            Storage.slot().lastOrderBookId = 1;\n        } else {\n            Storage.slot().lastOrderBookId++;\n        }\n        return Storage.slot().lastOrderBookId;\n    }\n\n    function _getOrderBook(\n        uint8 _orderBookId\n    ) private view returns (OrderBookLib.OrderBook storage) {\n        return Storage.slot().orderBooks[_orderBookId];\n    }\n}\n"
      },
      "contracts/protocol/libraries/logics/OrderReaderLogic.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {Constants} from \"../Constants.sol\";\nimport {OrderBookLib, PlacedOrder} from \"../OrderBookLib.sol\";\nimport {ProtocolTypes} from \"../../types/ProtocolTypes.sol\";\nimport {LendingMarketStorage as Storage, ItayoseLog} from \"../../storages/LendingMarketStorage.sol\";\nimport {OrderStatisticsTreeLib} from \"../OrderStatisticsTreeLib.sol\";\nimport {RoundingUint256} from \"../math/RoundingUint256.sol\";\n\nlibrary OrderReaderLogic {\n    using OrderBookLib for OrderBookLib.OrderBook;\n    using OrderStatisticsTreeLib for OrderStatisticsTreeLib.Tree;\n    using RoundingUint256 for uint256;\n\n    function getOrder(\n        uint8 _orderBookId,\n        uint48 _orderId\n    )\n        external\n        view\n        returns (\n            ProtocolTypes.Side side,\n            uint256 unitPrice,\n            uint256 maturity,\n            address maker,\n            uint256 amount,\n            uint256 timestamp,\n            bool isPreOrder\n        )\n    {\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n        PlacedOrder memory order = orderBook.getOrder(_orderId);\n\n        if (order.side == ProtocolTypes.Side.LEND) {\n            (maker, amount) = orderBook.lendOrders[order.maturity].getOrderById(\n                order.unitPrice,\n                _orderId\n            );\n        } else {\n            (maker, amount) = orderBook.borrowOrders[order.maturity].getOrderById(\n                order.unitPrice,\n                _orderId\n            );\n        }\n\n        if (maker != address(0)) {\n            side = order.side;\n            maturity = order.maturity;\n            timestamp = order.timestamp;\n            isPreOrder = orderBook.isPreOrder[_orderId];\n            unitPrice = _getOrderUnitPrice(side, maturity, order.unitPrice, isPreOrder);\n        }\n    }\n\n    function getTotalAmountFromLendOrders(\n        uint8 _orderBookId,\n        address _user\n    )\n        external\n        view\n        returns (\n            uint256 activeAmount,\n            uint256 inactiveAmount,\n            uint256 inactiveFutureValue,\n            uint256 maturity\n        )\n    {\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n\n        (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds) = orderBook\n            .getLendOrderIds(_user);\n        maturity = orderBook.userCurrentMaturities[_user];\n\n        for (uint256 i; i < activeOrderIds.length; ) {\n            PlacedOrder memory order = orderBook.getOrder(activeOrderIds[i]);\n            // Sum future values in the current maturity.\n            // If the market is rotated and maturity is updated, it will be 0 by treating it\n            // as an order canceled in the past market.\n            (, uint256 orderAmount) = orderBook.lendOrders[orderBook.maturity].getOrderById(\n                order.unitPrice,\n                activeOrderIds[i]\n            );\n            activeAmount += orderAmount;\n\n            unchecked {\n                i++;\n            }\n        }\n\n        for (uint256 i; i < inActiveOrderIds.length; ) {\n            // Sum future values in the maturity of orders.\n            // It will be the future value when the order is created, even if the market is rotated\n            // and maturity is updated.\n            (uint256 presentValue, uint256 futureValue) = getLendOrderAmounts(\n                orderBook,\n                inActiveOrderIds[i]\n            );\n            inactiveAmount += presentValue;\n            inactiveFutureValue += futureValue;\n\n            unchecked {\n                i++;\n            }\n        }\n    }\n\n    function getTotalAmountFromBorrowOrders(\n        uint8 _orderBookId,\n        address _user,\n        uint256 _minUnitPrice\n    )\n        external\n        view\n        returns (\n            uint256 activeAmount,\n            uint256 inactiveAmount,\n            uint256 inactiveFutureValue,\n            uint256 maturity\n        )\n    {\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n\n        (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds) = orderBook\n            .getBorrowOrderIds(_user);\n        maturity = orderBook.userCurrentMaturities[_user];\n\n        for (uint256 i; i < activeOrderIds.length; ) {\n            // Sum future values in the current maturity.\n            // If the market is rotated and maturity is updated, it will be 0 by treating it\n            // as an order canceled in the past market.\n            (uint256 presentValue, uint256 futureValue, uint256 unitPrice) = getBorrowOrderAmounts(\n                orderBook,\n                activeOrderIds[i]\n            );\n\n            activeAmount += unitPrice >= _minUnitPrice\n                ? presentValue\n                : (futureValue * _minUnitPrice).div(Constants.PRICE_DIGIT);\n\n            unchecked {\n                i++;\n            }\n        }\n\n        for (uint256 i; i < inActiveOrderIds.length; ) {\n            // Sum future values in the maturity of orders\n            // It will be the future value when the order is created, even if the market is rotated\n            // and maturity is updated.\n            (uint256 presentValue, uint256 futureValue, ) = getBorrowOrderAmounts(\n                orderBook,\n                inActiveOrderIds[i]\n            );\n\n            inactiveAmount += presentValue;\n            inactiveFutureValue += futureValue;\n\n            unchecked {\n                i++;\n            }\n        }\n    }\n\n    function getLendOrderIds(\n        uint8 _orderBookId,\n        address _user\n    ) external view returns (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds) {\n        (activeOrderIds, inActiveOrderIds) = _getOrderBook(_orderBookId).getLendOrderIds(_user);\n    }\n\n    function getBorrowOrderIds(\n        uint8 _orderBookId,\n        address _user\n    ) external view returns (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds) {\n        (activeOrderIds, inActiveOrderIds) = _getOrderBook(_orderBookId).getBorrowOrderIds(_user);\n    }\n\n    function calculateFilledAmount(\n        uint8 _orderBookId,\n        ProtocolTypes.Side _side,\n        uint256 _amount,\n        uint256 _unitPrice\n    )\n        external\n        view\n        returns (\n            uint256 lastUnitPrice,\n            uint256 filledAmount,\n            uint256 filledAmountInFV,\n            uint256 orderFeeInFV,\n            uint256 placedAmount\n        )\n    {\n        OrderBookLib.OrderBook storage orderBook = _getOrderBook(_orderBookId);\n\n        (bool isFilled, uint256 executedUnitPrice, bool ignoreRemainingAmount, ) = orderBook\n            .getOrderExecutionConditions(\n                _side,\n                _unitPrice,\n                Storage.slot().circuitBreakerLimitRange,\n                true\n            );\n\n        if (isFilled) {\n            (lastUnitPrice, filledAmount, filledAmountInFV) = orderBook.calculateFilledAmount(\n                _side,\n                _amount,\n                executedUnitPrice\n            );\n            placedAmount = _amount - filledAmount;\n            orderFeeInFV = calculateOrderFeeAmount(orderBook.maturity, filledAmountInFV);\n        } else {\n            if (!ignoreRemainingAmount) {\n                placedAmount = _amount;\n            }\n        }\n    }\n\n    function calculateOrderFeeAmount(\n        uint256 _maturity,\n        uint256 _amount\n    ) public view returns (uint256 orderFeeAmount) {\n        if (block.timestamp >= _maturity) return 0;\n\n        uint256 currentMaturity = _maturity - block.timestamp;\n\n        // NOTE: The formula is:\n        // actualRate = feeRate * (currentMaturity / SECONDS_IN_YEAR)\n        // orderFeeAmount = amount * actualRate\n        orderFeeAmount = (Storage.slot().orderFeeRate * currentMaturity * _amount).div(\n            Constants.SECONDS_IN_YEAR * Constants.PCT_DIGIT\n        );\n    }\n\n    function getLendOrderAmounts(\n        OrderBookLib.OrderBook storage orderBook,\n        uint48 _orderId\n    ) public view returns (uint256 presentValue, uint256 futureValue) {\n        PlacedOrder memory order = orderBook.getOrder(_orderId);\n        (, uint256 orderAmount) = orderBook.lendOrders[order.maturity].getOrderById(\n            order.unitPrice,\n            _orderId\n        );\n\n        uint256 unitPrice = _getOrderUnitPrice(\n            order.side,\n            order.maturity,\n            order.unitPrice,\n            orderBook.isPreOrder[_orderId]\n        );\n\n        presentValue = orderAmount;\n        futureValue = (orderAmount * Constants.PRICE_DIGIT).div(unitPrice);\n    }\n\n    function getBorrowOrderAmounts(\n        OrderBookLib.OrderBook storage orderBook,\n        uint48 _orderId\n    ) public view returns (uint256 presentValue, uint256 futureValue, uint256 unitPrice) {\n        PlacedOrder memory order = orderBook.getOrder(_orderId);\n        (, uint256 orderAmount) = orderBook.borrowOrders[order.maturity].getOrderById(\n            order.unitPrice,\n            _orderId\n        );\n        unitPrice = _getOrderUnitPrice(\n            order.side,\n            order.maturity,\n            order.unitPrice,\n            orderBook.isPreOrder[_orderId]\n        );\n\n        presentValue = orderAmount;\n        futureValue = (orderAmount * Constants.PRICE_DIGIT).div(unitPrice);\n    }\n\n    function _getOrderUnitPrice(\n        ProtocolTypes.Side _side,\n        uint256 _maturity,\n        uint256 _unitPrice,\n        bool _isPreOrder\n    ) private view returns (uint256) {\n        if (!_isPreOrder) return _unitPrice;\n        ItayoseLog memory itayoseLog = Storage.slot().itayoseLogs[_maturity];\n        if (\n            itayoseLog.openingUnitPrice != 0 &&\n            ((_side == ProtocolTypes.Side.BORROW && _unitPrice <= itayoseLog.lastBorrowUnitPrice) ||\n                (_side == ProtocolTypes.Side.LEND && _unitPrice >= itayoseLog.lastLendUnitPrice))\n        ) {\n            return itayoseLog.openingUnitPrice;\n        } else {\n            return _unitPrice;\n        }\n    }\n\n    function _getOrderBook(\n        uint8 _orderBookId\n    ) private view returns (OrderBookLib.OrderBook storage) {\n        return Storage.slot().orderBooks[_orderBookId];\n    }\n}\n"
      },
      "contracts/protocol/libraries/math/RoundingUint256.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nlibrary RoundingUint256 {\n    function div(uint256 a, uint256 b) internal pure returns (uint256) {\n        require(b > 0);\n\n        if ((a * 10) / b - (a / b) * 10 < 5) {\n            return a / b;\n        } else {\n            return (a / b) + 1;\n        }\n    }\n}\n"
      },
      "contracts/protocol/libraries/OrderBookLib.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {Constants} from \"./Constants.sol\";\nimport {ProtocolTypes} from \"../types/ProtocolTypes.sol\";\nimport {OrderStatisticsTreeLib, PartiallyRemovedOrder} from \"./OrderStatisticsTreeLib.sol\";\nimport {RoundingUint256} from \"./math/RoundingUint256.sol\";\n\nstruct PlacedOrder {\n    ProtocolTypes.Side side;\n    uint256 unitPrice;\n    uint256 maturity;\n    uint256 timestamp;\n}\n\nstruct FilledOrder {\n    uint256 amount;\n    uint256 unitPrice;\n    uint256 futureValue;\n    uint256 ignoredAmount;\n}\n\nstruct PartiallyFilledOrder {\n    uint48 orderId;\n    address maker;\n    uint256 amount;\n    uint256 futureValue;\n}\n\nlibrary OrderBookLib {\n    using OrderStatisticsTreeLib for OrderStatisticsTreeLib.Tree;\n    using RoundingUint256 for uint256;\n\n    uint256 public constant PRE_ORDER_BASE_PERIOD = 7 days;\n    uint256 public constant ITAYOSE_PERIOD = 1 hours;\n    uint256 public constant CIRCUIT_BREAKER_MINIMUM_LEND_RANGE = 700;\n    uint256 public constant CIRCUIT_BREAKER_MINIMUM_BORROW_RANGE = 200;\n\n    error EmptyOrderBook();\n    error PastMaturityOrderExists();\n\n    struct OrderBook {\n        uint256 maturity;\n        uint256 openingDate;\n        uint256 preOpeningDate;\n        uint48 lastOrderId;\n        uint48 lastOrderTimestamp;\n        bool isReliableBlock;\n        // Micro slots for block unit price history\n        uint80 blockUnitPriceHistory;\n        uint48 lastBlockUnitPriceTimestamp;\n        uint256 blockTotalAmount;\n        uint256 blockTotalFutureValue;\n        mapping(address user => uint48[] orderIds) activeLendOrderIds;\n        mapping(address user => uint48[] orderIds) activeBorrowOrderIds;\n        // Maturity when user last executes order\n        mapping(address user => uint256 maturity) userCurrentMaturities;\n        // Micro slots for order\n        mapping(uint48 orderId => uint256 slots) orders;\n        mapping(uint48 orderId => bool isPreOrder) isPreOrder;\n        mapping(uint256 maturity => OrderStatisticsTreeLib.Tree orders) lendOrders;\n        mapping(uint256 maturity => OrderStatisticsTreeLib.Tree orders) borrowOrders;\n    }\n\n    function initialize(\n        OrderBook storage self,\n        uint256 _maturity,\n        uint256 _openingDate,\n        uint256 _preOpeningDate\n    ) internal returns (bool isReady) {\n        self.maturity = _maturity;\n        self.openingDate = _openingDate;\n        self.preOpeningDate = _preOpeningDate;\n\n        self.blockTotalAmount = 0;\n        self.blockTotalFutureValue = 0;\n        self.blockUnitPriceHistory = 0;\n        self.lastOrderTimestamp = 0;\n        self.lastBlockUnitPriceTimestamp = 0;\n        self.isReliableBlock = false;\n\n        if (block.timestamp >= (_openingDate - ITAYOSE_PERIOD)) {\n            isReady = true;\n        }\n    }\n\n    function isMatured(OrderBook storage self) internal view returns (bool) {\n        return block.timestamp >= self.maturity;\n    }\n\n    function getBestBorrowUnitPrice(OrderBook storage self) internal view returns (uint256) {\n        return self.lendOrders[self.maturity].last();\n    }\n\n    function getBestLendUnitPrice(OrderBook storage self) internal view returns (uint256) {\n        uint256 unitPrice = self.borrowOrders[self.maturity].first();\n        return unitPrice == 0 ? Constants.PRICE_DIGIT : unitPrice;\n    }\n\n    function hasBorrowOrder(OrderBook storage self, address _user) internal view returns (bool) {\n        return self.activeBorrowOrderIds[_user].length != 0;\n    }\n\n    function hasLendOrder(OrderBook storage self, address _user) internal view returns (bool) {\n        return self.activeLendOrderIds[_user].length != 0;\n    }\n\n    function getOrder(\n        OrderBook storage self,\n        uint48 _orderId\n    ) internal view returns (PlacedOrder memory order) {\n        (\n            ProtocolTypes.Side side,\n            uint256 unitPrice,\n            uint256 maturity,\n            uint256 timestamp\n        ) = _unpackOrder(self.orders[_orderId]);\n        order = PlacedOrder(side, unitPrice, maturity, timestamp);\n    }\n\n    function getBlockUnitPriceHistory(\n        OrderBook storage self,\n        bool _isReadOnly\n    ) internal view returns (uint256[] memory prices, uint48 timestamp) {\n        prices = _unpackBlockUnitPriceHistory(self.blockUnitPriceHistory);\n\n        // NOTE: If an order is in the first block of the order book, the block unit price history is empty.\n        // In this case, the first history record is calculated from the current block total amount and total future value\n        // along with the `getMarketUnitPrice` function logic.\n        if (\n            (self.lastOrderTimestamp != block.timestamp || prices[0] == 0 || _isReadOnly) &&\n            self.isReliableBlock\n        ) {\n            for (uint256 i = prices.length - 1; i > 0; i--) {\n                prices[i] = prices[i - 1];\n            }\n\n            prices[0] = (self.blockTotalAmount * Constants.PRICE_DIGIT).div(\n                self.blockTotalFutureValue\n            );\n            timestamp = self.lastOrderTimestamp;\n        } else {\n            timestamp = self.lastBlockUnitPriceTimestamp;\n        }\n    }\n\n    function getMarketUnitPrice(\n        OrderBook storage self,\n        bool _isReadOnly\n    ) internal view returns (uint256 unitPrice) {\n        unitPrice = _unpackBlockUnitPriceHistory(self.blockUnitPriceHistory)[0];\n\n        // NOTE: If an order is in the first block of the order book, the block unit price history is empty.\n        // In this case, the market unit price is calculated from the current block total amount and total future value\n        // to avoid unwinding or liquidation the order in the same block using 0 as the market unit price.\n        if (\n            (self.lastOrderTimestamp != block.timestamp || unitPrice == 0 || _isReadOnly) &&\n            self.isReliableBlock\n        ) {\n            unitPrice = (self.blockTotalAmount * Constants.PRICE_DIGIT).div(\n                self.blockTotalFutureValue\n            );\n        }\n    }\n\n    function getBlockUnitPriceAverage(\n        OrderBook storage self,\n        uint256 maxCount,\n        bool _isReadOnly\n    ) internal view returns (uint256 unitPrice) {\n        uint256[] memory unitPrices = _unpackBlockUnitPriceHistory(self.blockUnitPriceHistory);\n        uint256 length = unitPrices.length;\n        uint256 sum;\n        uint256 count;\n\n        if ((self.lastOrderTimestamp != block.timestamp || _isReadOnly) && self.isReliableBlock) {\n            sum = (self.blockTotalAmount * Constants.PRICE_DIGIT).div(self.blockTotalFutureValue);\n            count = 1;\n            maxCount--;\n        }\n\n        for (uint256 i; i < maxCount; i++) {\n            if (i >= length || unitPrices[i] == 0) {\n                break;\n            }\n\n            sum += unitPrices[i];\n            count++;\n        }\n\n        unitPrice = count > 0 ? sum.div(count) : 0;\n    }\n\n    function getLendOrderBook(\n        OrderBook storage self,\n        uint256 _start,\n        uint256 _limit\n    )\n        internal\n        view\n        returns (\n            uint256[] memory unitPrices,\n            uint256[] memory amounts,\n            uint256[] memory quantities,\n            uint256 next\n        )\n    {\n        unitPrices = new uint256[](_limit);\n        amounts = new uint256[](_limit);\n        quantities = new uint256[](_limit);\n\n        uint256 unitPrice;\n\n        if (_start == 0) {\n            unitPrice = self.lendOrders[self.maturity].last();\n        } else {\n            (bool exists, uint256 parent) = self.lendOrders[self.maturity].search(_start);\n\n            if (exists) {\n                unitPrice = _start;\n            } else if (parent < _start) {\n                unitPrice = parent;\n            } else {\n                unitPrice = self.lendOrders[self.maturity].prev(parent);\n            }\n        }\n\n        unitPrices[0] = unitPrice;\n        amounts[0] = self.lendOrders[self.maturity].getNodeTotalAmount(unitPrice);\n        quantities[0] = self.lendOrders[self.maturity].getNodeCount(unitPrice);\n\n        for (uint256 i = 1; i < unitPrices.length; i++) {\n            if (unitPrice == 0) {\n                break;\n            }\n\n            unitPrice = self.lendOrders[self.maturity].prev(unitPrice);\n            unitPrices[i] = unitPrice;\n            amounts[i] = self.lendOrders[self.maturity].getNodeTotalAmount(unitPrice);\n            quantities[i] = self.lendOrders[self.maturity].getNodeCount(unitPrice);\n        }\n\n        if (unitPrice != 0) {\n            next = self.lendOrders[self.maturity].prev(unitPrice);\n        }\n    }\n\n    function getBorrowOrderBook(\n        OrderBook storage self,\n        uint256 _start,\n        uint256 _limit\n    )\n        internal\n        view\n        returns (\n            uint256[] memory unitPrices,\n            uint256[] memory amounts,\n            uint256[] memory quantities,\n            uint256 next\n        )\n    {\n        unitPrices = new uint256[](_limit);\n        amounts = new uint256[](_limit);\n        quantities = new uint256[](_limit);\n\n        uint256 unitPrice;\n\n        if (_start == 0) {\n            unitPrice = self.borrowOrders[self.maturity].first();\n        } else {\n            (bool exists, uint256 parent) = self.borrowOrders[self.maturity].search(_start);\n\n            if (exists) {\n                unitPrice = _start;\n            } else if (parent > _start) {\n                unitPrice = parent;\n            } else {\n                unitPrice = self.borrowOrders[self.maturity].next(parent);\n            }\n        }\n\n        unitPrices[0] = unitPrice;\n        amounts[0] = self.borrowOrders[self.maturity].getNodeTotalAmount(unitPrice);\n        quantities[0] = self.borrowOrders[self.maturity].getNodeCount(unitPrice);\n\n        for (uint256 i = 1; i < unitPrices.length; i++) {\n            if (unitPrice == 0) {\n                break;\n            }\n\n            unitPrice = self.borrowOrders[self.maturity].next(unitPrice);\n            unitPrices[i] = unitPrice;\n            amounts[i] = self.borrowOrders[self.maturity].getNodeTotalAmount(unitPrice);\n            quantities[i] = self.borrowOrders[self.maturity].getNodeCount(unitPrice);\n        }\n\n        if (unitPrice != 0) {\n            next = self.borrowOrders[self.maturity].next(unitPrice);\n        }\n    }\n\n    function getLendOrderIds(\n        OrderBook storage self,\n        address _user\n    ) internal view returns (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds) {\n        uint256 activeOrderCount = 0;\n        uint256 inActiveOrderCount = 0;\n        uint256 userMaturity = self.userCurrentMaturities[_user];\n        bool isPastMaturity = block.timestamp >= userMaturity;\n\n        uint48[] memory orderIds = self.activeLendOrderIds[_user];\n        uint256 orderIdLength = orderIds.length;\n        activeOrderIds = new uint48[](isPastMaturity ? 0 : orderIdLength);\n        inActiveOrderIds = new uint48[](orderIdLength);\n\n        for (uint256 i; i < orderIdLength; i++) {\n            uint48 orderId = orderIds[i];\n            (, uint256 unitPrice, , ) = _unpackOrder(self.orders[orderId]);\n\n            if (!self.lendOrders[userMaturity].isActiveOrderId(unitPrice, orderId)) {\n                unchecked {\n                    inActiveOrderCount += 1;\n                }\n                inActiveOrderIds[i - activeOrderCount] = orderId;\n                if (!isPastMaturity) {\n                    assembly {\n                        mstore(activeOrderIds, sub(mload(activeOrderIds), 1))\n                    }\n                }\n            } else {\n                unchecked {\n                    activeOrderCount += 1;\n                }\n                if (!isPastMaturity) {\n                    activeOrderIds[i - inActiveOrderCount] = orderId;\n                }\n                assembly {\n                    mstore(inActiveOrderIds, sub(mload(inActiveOrderIds), 1))\n                }\n            }\n        }\n    }\n\n    function getBorrowOrderIds(\n        OrderBook storage self,\n        address _user\n    ) internal view returns (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds) {\n        uint256 activeOrderCount = 0;\n        uint256 inActiveOrderCount = 0;\n        uint256 userMaturity = self.userCurrentMaturities[_user];\n        bool isPastMaturity = block.timestamp >= userMaturity;\n\n        uint48[] memory orderIds = self.activeBorrowOrderIds[_user];\n        uint256 orderIdLength = orderIds.length;\n        activeOrderIds = new uint48[](isPastMaturity ? 0 : orderIdLength);\n        inActiveOrderIds = new uint48[](orderIdLength);\n\n        for (uint256 i; i < orderIdLength; i++) {\n            uint48 orderId = orderIds[i];\n            (, uint256 unitPrice, , ) = _unpackOrder(self.orders[orderId]);\n\n            if (!self.borrowOrders[userMaturity].isActiveOrderId(unitPrice, orderId)) {\n                unchecked {\n                    inActiveOrderCount += 1;\n                }\n                inActiveOrderIds[i - activeOrderCount] = orderId;\n                if (!isPastMaturity) {\n                    assembly {\n                        mstore(activeOrderIds, sub(mload(activeOrderIds), 1))\n                    }\n                }\n            } else {\n                unchecked {\n                    activeOrderCount += 1;\n                }\n                if (!isPastMaturity) {\n                    activeOrderIds[i - inActiveOrderCount] = orderId;\n                }\n                assembly {\n                    mstore(inActiveOrderIds, sub(mload(inActiveOrderIds), 1))\n                }\n            }\n        }\n    }\n\n    function calculateFilledAmount(\n        OrderBook storage self,\n        ProtocolTypes.Side _side,\n        uint256 _amount,\n        uint256 _unitPrice\n    )\n        internal\n        view\n        returns (uint256 lastUnitPrice, uint256 filledAmount, uint256 filledAmountInFV)\n    {\n        if (_amount == 0) return (0, 0, 0);\n\n        if (_side == ProtocolTypes.Side.LEND) {\n            return\n                self.borrowOrders[self.maturity].calculateDroppedAmountFromLeft(\n                    _amount,\n                    0,\n                    _unitPrice\n                );\n        } else {\n            return\n                self.lendOrders[self.maturity].calculateDroppedAmountFromRight(\n                    _amount,\n                    0,\n                    _unitPrice\n                );\n        }\n    }\n\n    function updateUserMaturity(OrderBook storage self, address _user) internal {\n        uint256 userMaturity = self.userCurrentMaturities[_user];\n        uint256 orderBookMaturity = self.maturity;\n\n        if (userMaturity != orderBookMaturity) {\n            if (\n                self.activeLendOrderIds[_user].length > 0 ||\n                self.activeBorrowOrderIds[_user].length > 0\n            ) {\n                revert PastMaturityOrderExists();\n            }\n\n            self.userCurrentMaturities[_user] = orderBookMaturity;\n        }\n    }\n\n    function placeOrder(\n        OrderBook storage self,\n        ProtocolTypes.Side _side,\n        address _user,\n        uint256 _amount,\n        uint256 _unitPrice\n    ) internal returns (uint48 orderId) {\n        orderId = _nextOrderId(self);\n        self.orders[orderId] = _packOrder(_side, _unitPrice, self.maturity, block.timestamp);\n\n        if (_side == ProtocolTypes.Side.LEND) {\n            self.lendOrders[self.maturity].insertOrder(_unitPrice, orderId, _user, _amount);\n            self.activeLendOrderIds[_user].push(orderId);\n        } else if (_side == ProtocolTypes.Side.BORROW) {\n            self.borrowOrders[self.maturity].insertOrder(_unitPrice, orderId, _user, _amount);\n            self.activeBorrowOrderIds[_user].push(orderId);\n        }\n    }\n\n    function fillOrders(\n        OrderBook storage self,\n        ProtocolTypes.Side _side,\n        uint256 _amount,\n        uint256 _amountInFV,\n        uint256 _unitPrice\n    )\n        internal\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 remainingAmount,\n            bool orderExists\n        )\n    {\n        PartiallyRemovedOrder memory partiallyRemovedOrder;\n\n        if (_side == ProtocolTypes.Side.BORROW) {\n            OrderStatisticsTreeLib.Tree storage orders = self.lendOrders[self.maturity];\n            (\n                filledOrder.unitPrice,\n                filledOrder.amount,\n                filledOrder.futureValue,\n                remainingAmount,\n                partiallyRemovedOrder\n            ) = orders.dropRight(_amount, _amountInFV, _unitPrice);\n            orderExists = orders.hasOrders();\n        } else if (_side == ProtocolTypes.Side.LEND) {\n            OrderStatisticsTreeLib.Tree storage orders = self.borrowOrders[self.maturity];\n            (\n                filledOrder.unitPrice,\n                filledOrder.amount,\n                filledOrder.futureValue,\n                remainingAmount,\n                partiallyRemovedOrder\n            ) = orders.dropLeft(_amount, _amountInFV, _unitPrice);\n            orderExists = orders.hasOrders();\n        }\n\n        partiallyFilledOrder = PartiallyFilledOrder(\n            partiallyRemovedOrder.orderId,\n            partiallyRemovedOrder.maker,\n            partiallyRemovedOrder.amount,\n            partiallyRemovedOrder.futureValue\n        );\n    }\n\n    function setInitialBlockUnitPrice(OrderBook storage self, uint256 _unitPrice) internal {\n        self.blockUnitPriceHistory = uint16(_unitPrice);\n        self.lastBlockUnitPriceTimestamp = uint48(block.timestamp);\n        self.lastOrderTimestamp = uint48(block.timestamp);\n    }\n\n    function updateBlockUnitPriceHistory(\n        OrderBook storage self,\n        uint256 _filledAmount,\n        uint256 _filledFutureValue,\n        uint256 _minimumReliableAmount\n    ) internal returns (uint256 latestBlockUnitPrice, bool isUpdated) {\n        latestBlockUnitPrice = _unpackBlockUnitPriceHistory(self.blockUnitPriceHistory)[0];\n\n        if (self.lastOrderTimestamp != block.timestamp) {\n            if (self.isReliableBlock) {\n                latestBlockUnitPrice = (self.blockTotalAmount * Constants.PRICE_DIGIT).div(\n                    self.blockTotalFutureValue\n                );\n\n                // Remove the oldest block unit price and add the latest block unit price\n                self.blockUnitPriceHistory =\n                    uint16(latestBlockUnitPrice) |\n                    (self.blockUnitPriceHistory << 16);\n                self.lastBlockUnitPriceTimestamp = uint48(block.timestamp);\n                isUpdated = true;\n            }\n\n            self.lastOrderTimestamp = uint48(block.timestamp);\n            self.blockTotalAmount = _filledAmount;\n            self.blockTotalFutureValue = _filledFutureValue;\n            self.isReliableBlock = false;\n        } else {\n            self.blockTotalAmount += _filledAmount;\n            self.blockTotalFutureValue += _filledFutureValue;\n        }\n\n        if (\n            self.blockTotalAmount >= _minimumReliableAmount ||\n            (self.blockTotalAmount > 0 && latestBlockUnitPrice == 0)\n        ) {\n            self.isReliableBlock = true;\n        }\n    }\n\n    function removeOrder(\n        OrderBook storage self,\n        address _user,\n        uint48 _orderId\n    ) internal returns (ProtocolTypes.Side, uint256, uint256) {\n        (ProtocolTypes.Side side, uint256 unitPrice, , ) = _unpackOrder(self.orders[_orderId]);\n        uint256 removedAmount;\n\n        if (side == ProtocolTypes.Side.LEND) {\n            removedAmount = self.lendOrders[self.maturity].removeOrder(unitPrice, _orderId);\n            _removeOrderIdFromOrders(self.activeLendOrderIds[_user], _orderId);\n        } else if (side == ProtocolTypes.Side.BORROW) {\n            removedAmount = self.borrowOrders[self.maturity].removeOrder(unitPrice, _orderId);\n            _removeOrderIdFromOrders(self.activeBorrowOrderIds[_user], _orderId);\n        }\n\n        delete self.orders[_orderId];\n\n        return (side, removedAmount, unitPrice);\n    }\n\n    function calculateItayoseResult(\n        OrderBook storage self\n    )\n        internal\n        view\n        returns (\n            uint256 openingUnitPrice,\n            uint256 lastLendUnitPrice,\n            uint256 lastBorrowUnitPrice,\n            uint256 totalOffsetAmount\n        )\n    {\n        uint256 lendUnitPrice = self.lendOrders[self.maturity].last();\n        uint256 borrowUnitPrice = self.borrowOrders[self.maturity].first();\n        uint256 lendAmount = self.lendOrders[self.maturity].getNodeTotalAmount(lendUnitPrice);\n        uint256 borrowAmount = self.borrowOrders[self.maturity].getNodeTotalAmount(borrowUnitPrice);\n\n        OrderStatisticsTreeLib.Tree storage borrowOrders = self.borrowOrders[self.maturity];\n        OrderStatisticsTreeLib.Tree storage lendOrders = self.lendOrders[self.maturity];\n\n        // Return 0 if no orders is filled\n        if (borrowUnitPrice > lendUnitPrice || borrowUnitPrice == 0 || lendUnitPrice == 0) {\n            return (0, 0, 0, 0);\n        }\n\n        while (borrowUnitPrice <= lendUnitPrice && borrowUnitPrice > 0 && lendUnitPrice > 0) {\n            lastLendUnitPrice = lendUnitPrice;\n            lastBorrowUnitPrice = borrowUnitPrice;\n\n            if (lendAmount > borrowAmount) {\n                openingUnitPrice = lendUnitPrice;\n                totalOffsetAmount += borrowAmount;\n                lendAmount -= borrowAmount;\n                borrowUnitPrice = borrowOrders.next(borrowUnitPrice);\n                borrowAmount = borrowOrders.getNodeTotalAmount(borrowUnitPrice);\n            } else if (lendAmount < borrowAmount) {\n                openingUnitPrice = borrowUnitPrice;\n                totalOffsetAmount += lendAmount;\n                borrowAmount -= lendAmount;\n                lendUnitPrice = lendOrders.prev(lendUnitPrice);\n                lendAmount = lendOrders.getNodeTotalAmount(lendUnitPrice);\n            } else {\n                openingUnitPrice = (lendUnitPrice + borrowUnitPrice).div(2);\n                totalOffsetAmount += lendAmount;\n                lendUnitPrice = lendOrders.prev(lendUnitPrice);\n                borrowUnitPrice = borrowOrders.next(borrowUnitPrice);\n                lendAmount = lendOrders.getNodeTotalAmount(lendUnitPrice);\n                borrowAmount = borrowOrders.getNodeTotalAmount(borrowUnitPrice);\n            }\n        }\n    }\n\n    function getOrderExecutionConditions(\n        OrderBook storage self,\n        ProtocolTypes.Side _side,\n        uint256 _unitPrice,\n        uint256 _circuitBreakerLimitRange,\n        bool _isReadOnly\n    )\n        internal\n        view\n        returns (\n            bool isFilled,\n            uint256 executedUnitPrice,\n            bool ignoreRemainingAmount,\n            bool orderExists\n        )\n    {\n        bool isLend = _side == ProtocolTypes.Side.LEND;\n        uint256 cbThresholdUnitPrice;\n        uint256 bestUnitPrice;\n\n        if (isLend) {\n            bestUnitPrice = self.borrowOrders[self.maturity].first();\n            cbThresholdUnitPrice = getLendCircuitBreakerThreshold(\n                self,\n                _circuitBreakerLimitRange,\n                _isReadOnly\n            );\n        } else {\n            bestUnitPrice = self.lendOrders[self.maturity].last();\n            cbThresholdUnitPrice = getBorrowCircuitBreakerThreshold(\n                self,\n                _circuitBreakerLimitRange,\n                _isReadOnly\n            );\n        }\n\n        orderExists = bestUnitPrice != 0;\n\n        if (\n            _unitPrice == 0 ||\n            (orderExists &&\n                cbThresholdUnitPrice != 0 &&\n                ((isLend && _unitPrice > cbThresholdUnitPrice) ||\n                    (!isLend && _unitPrice < cbThresholdUnitPrice)))\n        ) {\n            executedUnitPrice = cbThresholdUnitPrice;\n            ignoreRemainingAmount = true;\n        } else {\n            executedUnitPrice = _unitPrice;\n            ignoreRemainingAmount = false;\n        }\n\n        if (orderExists) {\n            isFilled = isLend\n                ? bestUnitPrice <= executedUnitPrice\n                : bestUnitPrice >= executedUnitPrice;\n        }\n    }\n\n    function getLendCircuitBreakerThreshold(\n        OrderBook storage self,\n        uint256 _circuitBreakerLimitRange,\n        bool _isReadOnly\n    ) internal view returns (uint256 cbThresholdUnitPrice) {\n        uint256 blockUnitPriceAverage = getBlockUnitPriceAverage(self, 3, _isReadOnly);\n        cbThresholdUnitPrice = (blockUnitPriceAverage *\n            (Constants.PCT_DIGIT + _circuitBreakerLimitRange * 2)).div(Constants.PCT_DIGIT);\n\n        if (cbThresholdUnitPrice > Constants.PRICE_DIGIT || blockUnitPriceAverage == 0) {\n            cbThresholdUnitPrice = Constants.PRICE_DIGIT;\n        } else if (\n            cbThresholdUnitPrice < blockUnitPriceAverage + CIRCUIT_BREAKER_MINIMUM_LEND_RANGE\n        ) {\n            cbThresholdUnitPrice = blockUnitPriceAverage + CIRCUIT_BREAKER_MINIMUM_LEND_RANGE;\n        }\n    }\n\n    function getBorrowCircuitBreakerThreshold(\n        OrderBook storage self,\n        uint256 _circuitBreakerLimitRange,\n        bool _isReadOnly\n    ) internal view returns (uint256 cbThresholdUnitPrice) {\n        uint256 blockUnitPriceAverage = getBlockUnitPriceAverage(self, 5, _isReadOnly);\n        cbThresholdUnitPrice = (blockUnitPriceAverage *\n            (Constants.PCT_DIGIT - _circuitBreakerLimitRange)).div(Constants.PCT_DIGIT);\n\n        if (\n            cbThresholdUnitPrice == 0 ||\n            blockUnitPriceAverage == 0 ||\n            blockUnitPriceAverage <= CIRCUIT_BREAKER_MINIMUM_BORROW_RANGE\n        ) {\n            cbThresholdUnitPrice = 1;\n        } else if (\n            blockUnitPriceAverage < cbThresholdUnitPrice + CIRCUIT_BREAKER_MINIMUM_BORROW_RANGE\n        ) {\n            cbThresholdUnitPrice = blockUnitPriceAverage - CIRCUIT_BREAKER_MINIMUM_BORROW_RANGE;\n        }\n    }\n\n    /**\n     * @notice Increases and returns id of last order in order book.\n     * @return The new order id\n     */\n    function _nextOrderId(OrderBook storage self) private returns (uint48) {\n        self.lastOrderId++;\n        return self.lastOrderId;\n    }\n\n    function _removeOrderIdFromOrders(uint48[] storage orders, uint256 orderId) private {\n        uint256 lastOrderIndex = orders.length - 1;\n        for (uint256 i; i <= lastOrderIndex; i++) {\n            if (orders[i] == orderId) {\n                if (i != lastOrderIndex) {\n                    uint48 lastOrderId = orders[lastOrderIndex];\n                    orders[i] = lastOrderId;\n                }\n\n                orders.pop();\n                break;\n            }\n        }\n    }\n\n    /**\n     * @notice Packs order parameters into uint256\n     */\n    function _packOrder(\n        ProtocolTypes.Side _side,\n        uint256 _unitPrice,\n        uint256 _maturity,\n        uint256 _timestamp\n    ) private pure returns (uint256) {\n        return uint256(_side) | (_unitPrice << 8) | (_maturity << 24) | (_timestamp << 88);\n    }\n\n    /**\n     * @notice Unpacks order parameters from uint256\n     */\n    function _unpackOrder(\n        uint256 _order\n    )\n        private\n        pure\n        returns (ProtocolTypes.Side side, uint256 unitPrice, uint256 maturity, uint256 timestamp)\n    {\n        side = ProtocolTypes.Side(uint8(_order));\n        unitPrice = uint16(_order >> 8);\n        maturity = uint64(_order >> 24);\n        timestamp = uint64(_order >> 88);\n    }\n\n    function _unpackBlockUnitPriceHistory(\n        uint80 _blockUnitPriceHistory\n    ) private pure returns (uint256[] memory prices) {\n        prices = new uint256[](5);\n\n        prices[0] = uint16(_blockUnitPriceHistory);\n        prices[1] = uint16(_blockUnitPriceHistory >> 16);\n        prices[2] = uint16(_blockUnitPriceHistory >> 32);\n        prices[3] = uint16(_blockUnitPriceHistory >> 48);\n        prices[4] = uint16(_blockUnitPriceHistory >> 64);\n    }\n}\n"
      },
      "contracts/protocol/libraries/OrderStatisticsTreeLib.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\n// libraries\nimport {Constants} from \"../libraries/Constants.sol\";\n// types\nimport {RoundingUint256} from \"./math/RoundingUint256.sol\";\n\nstruct RemainingOrder {\n    uint48 orderId;\n    address maker;\n    uint256 amount;\n    uint256 unitPrice;\n}\n\nstruct PartiallyRemovedOrder {\n    uint48 orderId;\n    address maker;\n    uint256 amount;\n    uint256 futureValue;\n}\n\n/**\n * @notice OrderStatisticsTreeLib is a Red-Black Tree binary search library\n * based on the following library that is extended to manage order data.\n *\n * https://github.com/rob-Hitchens/OrderStatisticsTree\n *\n */\nlibrary OrderStatisticsTreeLib {\n    using RoundingUint256 for uint256;\n    uint256 private constant EMPTY = 0;\n\n    struct Node {\n        uint256 parent;\n        uint256 left;\n        uint256 right;\n        bool red;\n        uint48 head;\n        uint48 tail;\n        uint256 orderCounter;\n        uint256 orderTotalAmount;\n        mapping(uint48 orderId => OrderItem) orders;\n    }\n\n    struct Tree {\n        uint256 root;\n        mapping(uint256 value => Node) nodes;\n    }\n\n    struct OrderItem {\n        uint48 orderId;\n        uint48 next;\n        uint48 prev;\n        address maker;\n        uint256 amount;\n    }\n\n    function first(Tree storage self) internal view returns (uint256 value) {\n        value = self.root;\n        if (value == EMPTY) return 0;\n        while (self.nodes[value].left != EMPTY) {\n            value = self.nodes[value].left;\n        }\n    }\n\n    function last(Tree storage self) internal view returns (uint256 value) {\n        value = self.root;\n        if (value == EMPTY) return 0;\n        while (self.nodes[value].right != EMPTY) {\n            value = self.nodes[value].right;\n        }\n    }\n\n    function hasOrders(Tree storage self) internal view returns (bool) {\n        return self.root != EMPTY;\n    }\n\n    function next(Tree storage self, uint256 value) internal view returns (uint256 cursor) {\n        require(value != EMPTY, \"OrderStatisticsTreeLib: Starting value cannot be zero\");\n        if (self.nodes[value].right != EMPTY) {\n            cursor = treeMinimum(self, self.nodes[value].right);\n        } else {\n            cursor = self.nodes[value].parent;\n            while (cursor != EMPTY && value == self.nodes[cursor].right) {\n                value = cursor;\n                cursor = self.nodes[cursor].parent;\n            }\n        }\n    }\n\n    function prev(Tree storage self, uint256 value) internal view returns (uint256 cursor) {\n        require(value != EMPTY, \"OrderStatisticsTreeLib: Starting value cannot be zero\");\n        if (self.nodes[value].left != EMPTY) {\n            cursor = treeMaximum(self, self.nodes[value].left);\n        } else {\n            cursor = self.nodes[value].parent;\n            while (cursor != EMPTY && value == self.nodes[cursor].left) {\n                value = cursor;\n                cursor = self.nodes[cursor].parent;\n            }\n        }\n    }\n\n    function search(\n        Tree storage self,\n        uint256 value\n    ) internal view returns (bool valueExists, uint256 parent) {\n        uint256 cursor = self.root;\n\n        while (cursor != EMPTY) {\n            if (value < cursor) {\n                parent = cursor;\n                cursor = self.nodes[cursor].left;\n            } else if (value > cursor) {\n                parent = cursor;\n                cursor = self.nodes[cursor].right;\n            }\n\n            if (value == cursor) {\n                break;\n            }\n        }\n\n        valueExists = cursor != EMPTY;\n    }\n\n    function exists(Tree storage self, uint256 value) internal view returns (bool) {\n        if (value == self.root) return true;\n\n        uint256 cursor = value;\n        while (self.nodes[cursor].parent != EMPTY) {\n            uint256 parent = self.nodes[cursor].parent;\n            Node storage gn = self.nodes[parent];\n            if (gn.left != cursor && gn.right != cursor) {\n                return false;\n            }\n            if (parent == self.root) {\n                return true;\n            }\n            cursor = parent;\n        }\n        return false;\n    }\n\n    function isActiveOrderId(\n        Tree storage self,\n        uint256 value,\n        uint48 orderId\n    ) internal view returns (bool) {\n        return orderIdExists(self, value, orderId) && exists(self, value);\n    }\n\n    function getNode(\n        Tree storage self,\n        uint256 value\n    ) internal view returns (uint256, uint256, uint256, bool, uint256, uint256, uint256, uint256) {\n        require(exists(self, value), \"OrderStatisticsTreeLib: Value does not exist\");\n        Node storage gn = self.nodes[value];\n        return (\n            gn.parent,\n            gn.left,\n            gn.right,\n            gn.red,\n            gn.head,\n            gn.tail,\n            gn.orderCounter,\n            gn.orderTotalAmount\n        );\n    }\n\n    function getNodeCount(Tree storage self, uint256 value) internal view returns (uint256) {\n        Node storage gn = self.nodes[value];\n        return gn.orderCounter;\n    }\n\n    function getNodeTotalAmount(\n        Tree storage self,\n        uint256 value\n    ) internal view returns (uint256 totalAmount) {\n        return self.nodes[value].orderTotalAmount;\n    }\n\n    function getNodeOrderIds(\n        Tree storage self,\n        uint256 value\n    ) internal view returns (uint48[] memory orderIds) {\n        Node storage gn = self.nodes[value];\n        OrderItem memory order = gn.orders[gn.head];\n        orderIds = new uint48[](gn.orderCounter);\n\n        for (uint256 i; i < gn.orderCounter; i++) {\n            orderIds[i] = order.orderId;\n            order = gn.orders[order.next];\n        }\n    }\n\n    function count(Tree storage self) internal view returns (uint256) {\n        return getNodeCount(self, self.root);\n    }\n\n    function insert(Tree storage self, uint256 value) internal {\n        require(value != EMPTY, \"OrderStatisticsTreeLib: Value to insert cannot be zero\");\n        uint256 cursor;\n        uint256 probe = self.root;\n        while (probe != EMPTY) {\n            cursor = probe;\n            if (value < probe) {\n                probe = self.nodes[probe].left;\n            } else if (value > probe) {\n                probe = self.nodes[probe].right;\n            } else if (value == probe) {\n                return;\n            }\n        }\n\n        Node storage nValue = self.nodes[value];\n        // Update order info as a new one if there is already an old node\n        if (self.root == EMPTY || nValue.orderCounter != 0) {\n            nValue.orderCounter = 0;\n            nValue.orderTotalAmount = 0;\n            _setHead(self, value, 0);\n            _setTail(self, value, 0);\n        }\n        nValue.parent = cursor;\n        nValue.left = EMPTY;\n        nValue.right = EMPTY;\n        nValue.red = true;\n\n        if (cursor == EMPTY) {\n            self.root = value;\n        } else if (value < cursor) {\n            self.nodes[cursor].left = value;\n        } else {\n            self.nodes[cursor].right = value;\n        }\n        insertFixup(self, value);\n    }\n\n    function remove(Tree storage self, uint256 value) internal {\n        require(value != EMPTY, \"OrderStatisticsTreeLib: Value to remove cannot be zero\");\n        Node storage nValue = self.nodes[value];\n        uint256 probe;\n        uint256 cursor;\n        if (nValue.orderCounter == 0) {\n            if (self.nodes[value].left == EMPTY || self.nodes[value].right == EMPTY) {\n                cursor = value;\n            } else {\n                cursor = self.nodes[value].right;\n                while (self.nodes[cursor].left != EMPTY) {\n                    cursor = self.nodes[cursor].left;\n                }\n            }\n            if (self.nodes[cursor].left != EMPTY) {\n                probe = self.nodes[cursor].left;\n            } else {\n                probe = self.nodes[cursor].right;\n            }\n            uint256 cursorParent = self.nodes[cursor].parent;\n            self.nodes[probe].parent = cursorParent;\n            if (cursorParent != EMPTY) {\n                if (cursor == self.nodes[cursorParent].left) {\n                    self.nodes[cursorParent].left = probe;\n                } else {\n                    self.nodes[cursorParent].right = probe;\n                }\n            } else {\n                self.root = probe;\n            }\n            bool doFixup = !self.nodes[cursor].red;\n            if (cursor != value) {\n                replaceParent(self, cursor, value);\n                self.nodes[cursor].left = self.nodes[value].left;\n                self.nodes[self.nodes[cursor].left].parent = cursor;\n                self.nodes[cursor].right = self.nodes[value].right;\n                self.nodes[self.nodes[cursor].right].parent = cursor;\n                self.nodes[cursor].red = self.nodes[value].red;\n                (cursor, value) = (value, cursor);\n            }\n            if (doFixup) {\n                removeFixup(self, probe);\n            }\n            delete self.nodes[cursor];\n        }\n    }\n\n    function treeMinimum(Tree storage self, uint256 value) private view returns (uint256) {\n        while (self.nodes[value].left != EMPTY) {\n            value = self.nodes[value].left;\n        }\n        return value;\n    }\n\n    function treeMaximum(Tree storage self, uint256 value) private view returns (uint256) {\n        while (self.nodes[value].right != EMPTY) {\n            value = self.nodes[value].right;\n        }\n        return value;\n    }\n\n    function rotateLeft(Tree storage self, uint256 value) private {\n        uint256 cursor = self.nodes[value].right;\n        uint256 parent = self.nodes[value].parent;\n        uint256 cursorLeft = self.nodes[cursor].left;\n        self.nodes[value].right = cursorLeft;\n        if (cursorLeft != EMPTY) {\n            self.nodes[cursorLeft].parent = value;\n        }\n        self.nodes[cursor].parent = parent;\n        if (parent == EMPTY) {\n            self.root = cursor;\n        } else if (value == self.nodes[parent].left) {\n            self.nodes[parent].left = cursor;\n        } else {\n            self.nodes[parent].right = cursor;\n        }\n        self.nodes[cursor].left = value;\n        self.nodes[value].parent = cursor;\n    }\n\n    function rotateRight(Tree storage self, uint256 value) private {\n        uint256 cursor = self.nodes[value].left;\n        uint256 parent = self.nodes[value].parent;\n        uint256 cursorRight = self.nodes[cursor].right;\n        self.nodes[value].left = cursorRight;\n        if (cursorRight != EMPTY) {\n            self.nodes[cursorRight].parent = value;\n        }\n        self.nodes[cursor].parent = parent;\n        if (parent == EMPTY) {\n            self.root = cursor;\n        } else if (value == self.nodes[parent].right) {\n            self.nodes[parent].right = cursor;\n        } else {\n            self.nodes[parent].left = cursor;\n        }\n        self.nodes[cursor].right = value;\n        self.nodes[value].parent = cursor;\n    }\n\n    function insertFixup(Tree storage self, uint256 value) private {\n        uint256 cursor;\n        while (value != self.root && self.nodes[self.nodes[value].parent].red) {\n            uint256 valueParent = self.nodes[value].parent;\n            if (valueParent == self.nodes[self.nodes[valueParent].parent].left) {\n                cursor = self.nodes[self.nodes[valueParent].parent].right;\n                if (self.nodes[cursor].red) {\n                    self.nodes[valueParent].red = false;\n                    self.nodes[cursor].red = false;\n                    self.nodes[self.nodes[valueParent].parent].red = true;\n                    value = self.nodes[valueParent].parent;\n                } else {\n                    if (value == self.nodes[valueParent].right) {\n                        value = valueParent;\n                        rotateLeft(self, value);\n                    }\n                    valueParent = self.nodes[value].parent;\n                    self.nodes[valueParent].red = false;\n                    self.nodes[self.nodes[valueParent].parent].red = true;\n                    rotateRight(self, self.nodes[valueParent].parent);\n                }\n            } else {\n                cursor = self.nodes[self.nodes[valueParent].parent].left;\n                if (self.nodes[cursor].red) {\n                    self.nodes[valueParent].red = false;\n                    self.nodes[cursor].red = false;\n                    self.nodes[self.nodes[valueParent].parent].red = true;\n                    value = self.nodes[valueParent].parent;\n                } else {\n                    if (value == self.nodes[valueParent].left) {\n                        value = valueParent;\n                        rotateRight(self, value);\n                    }\n                    valueParent = self.nodes[value].parent;\n                    self.nodes[valueParent].red = false;\n                    self.nodes[self.nodes[valueParent].parent].red = true;\n                    rotateLeft(self, self.nodes[valueParent].parent);\n                }\n            }\n        }\n        self.nodes[self.root].red = false;\n    }\n\n    function replaceParent(Tree storage self, uint256 a, uint256 b) private {\n        uint256 bParent = self.nodes[b].parent;\n        self.nodes[a].parent = bParent;\n        if (bParent == EMPTY) {\n            self.root = a;\n        } else {\n            if (b == self.nodes[bParent].left) {\n                self.nodes[bParent].left = a;\n            } else {\n                self.nodes[bParent].right = a;\n            }\n        }\n    }\n\n    function removeFixup(Tree storage self, uint256 value) private {\n        uint256 cursor;\n        while (value != self.root && !self.nodes[value].red) {\n            uint256 valueParent = self.nodes[value].parent;\n            if (value == self.nodes[valueParent].left) {\n                cursor = self.nodes[valueParent].right;\n                if (self.nodes[cursor].red) {\n                    self.nodes[cursor].red = false;\n                    self.nodes[valueParent].red = true;\n                    rotateLeft(self, valueParent);\n                    cursor = self.nodes[valueParent].right;\n                }\n                if (\n                    !self.nodes[self.nodes[cursor].left].red &&\n                    !self.nodes[self.nodes[cursor].right].red\n                ) {\n                    self.nodes[cursor].red = true;\n                    value = valueParent;\n                } else {\n                    if (!self.nodes[self.nodes[cursor].right].red) {\n                        self.nodes[self.nodes[cursor].left].red = false;\n                        self.nodes[cursor].red = true;\n                        rotateRight(self, cursor);\n                        cursor = self.nodes[valueParent].right;\n                    }\n                    self.nodes[cursor].red = self.nodes[valueParent].red;\n                    self.nodes[valueParent].red = false;\n                    self.nodes[self.nodes[cursor].right].red = false;\n                    rotateLeft(self, valueParent);\n                    value = self.root;\n                }\n            } else {\n                cursor = self.nodes[valueParent].left;\n                if (self.nodes[cursor].red) {\n                    self.nodes[cursor].red = false;\n                    self.nodes[valueParent].red = true;\n                    rotateRight(self, valueParent);\n                    cursor = self.nodes[valueParent].left;\n                }\n                if (\n                    !self.nodes[self.nodes[cursor].right].red &&\n                    !self.nodes[self.nodes[cursor].left].red\n                ) {\n                    self.nodes[cursor].red = true;\n                    value = valueParent;\n                } else {\n                    if (!self.nodes[self.nodes[cursor].left].red) {\n                        self.nodes[self.nodes[cursor].right].red = false;\n                        self.nodes[cursor].red = true;\n                        rotateLeft(self, cursor);\n                        cursor = self.nodes[valueParent].left;\n                    }\n                    self.nodes[cursor].red = self.nodes[valueParent].red;\n                    self.nodes[valueParent].red = false;\n                    self.nodes[self.nodes[cursor].left].red = false;\n                    rotateRight(self, valueParent);\n                    value = self.root;\n                }\n            }\n        }\n        self.nodes[value].red = false;\n    }\n\n    function calculateDroppedAmountFromLeft(\n        Tree storage self,\n        uint256 amount,\n        uint256 amountInFV,\n        uint256 limitValue\n    )\n        internal\n        view\n        returns (uint256 droppedValue, uint256 droppedAmount, uint256 droppedAmountInFV)\n    {\n        (droppedValue, , , droppedAmount, droppedAmountInFV, , ) = _calculateDroppedAmountFromLeft(\n            self,\n            amount,\n            amountInFV,\n            limitValue,\n            first(self)\n        );\n    }\n\n    function calculateDroppedAmountFromRight(\n        Tree storage self,\n        uint256 amount,\n        uint256 amountInFV,\n        uint256 limitValue\n    )\n        internal\n        view\n        returns (uint256 droppedValue, uint256 droppedAmount, uint256 droppedAmountInFV)\n    {\n        (droppedValue, , , droppedAmount, droppedAmountInFV, , ) = _calculateDroppedAmountFromRight(\n            self,\n            amount,\n            amountInFV,\n            limitValue,\n            last(self)\n        );\n    }\n\n    function dropLeft(\n        Tree storage self,\n        uint256 amount,\n        uint256 amountInFV,\n        uint256 limitValue\n    )\n        internal\n        returns (\n            uint256 droppedValue,\n            uint256 droppedAmount,\n            uint256 droppedAmountInFV,\n            uint256 remainingAmount,\n            PartiallyRemovedOrder memory partiallyRemovedOrder\n        )\n    {\n        uint256 cursor = first(self);\n        uint256 cursorNodeAmount;\n        uint256 exceededAmount;\n        uint256 exceededAmountInFV;\n\n        require(cursor <= limitValue || limitValue == 0, \"Insufficient limit value\");\n\n        (\n            droppedValue,\n            cursor,\n            cursorNodeAmount,\n            droppedAmount,\n            droppedAmountInFV,\n            exceededAmount,\n            exceededAmountInFV\n        ) = _calculateDroppedAmountFromLeft(self, amount, amountInFV, limitValue, cursor);\n\n        uint256 totalNodeAmount = droppedAmount + exceededAmount;\n\n        if (totalNodeAmount > 0) {\n            if (exceededAmount > 0) {\n                cursor = droppedValue;\n                // Update order ids in the node.\n                partiallyRemovedOrder = removeOrders(\n                    self,\n                    cursor,\n                    cursorNodeAmount - exceededAmount\n                );\n            } else if (exceededAmountInFV > 0) {\n                cursor = droppedValue;\n                // Update order ids in the node.\n                partiallyRemovedOrder = removeOrders(\n                    self,\n                    cursor,\n                    cursorNodeAmount - _calculatePresentValue(cursor, exceededAmountInFV)\n                );\n            }\n\n            self.nodes[cursor].left = 0;\n\n            uint256 parent = self.nodes[cursor].parent;\n\n            if (cursor != EMPTY) {\n                while (parent != EMPTY) {\n                    if (parent > cursor) {\n                        // Relink the nodes\n                        if (self.nodes[cursor].parent != parent) {\n                            self.nodes[cursor].parent = parent;\n                            self.nodes[parent].left = cursor;\n                        }\n\n                        cursor = parent;\n                    }\n\n                    parent = self.nodes[parent].parent;\n                }\n            }\n        }\n\n        if (amount > totalNodeAmount) {\n            remainingAmount = amount - totalNodeAmount;\n        }\n\n        uint256 lastNode = last(self);\n\n        if (lastNode == droppedValue && self.nodes[lastNode].orderTotalAmount == 0) {\n            // The case that all node is dropped.\n            self.root = EMPTY;\n        } else if (\n            droppedValue > self.root ||\n            (droppedValue == self.root && droppedAmount == totalNodeAmount)\n        ) {\n            // The case that the root node is dropped\n            self.root = cursor;\n            self.nodes[cursor].parent = 0;\n        }\n\n        rotateTreeToLeft(self);\n    }\n\n    function dropRight(\n        Tree storage self,\n        uint256 amount,\n        uint256 amountInFV,\n        uint256 limitValue\n    )\n        internal\n        returns (\n            uint256 droppedValue,\n            uint256 droppedAmount,\n            uint256 droppedAmountInFV,\n            uint256 remainingAmount,\n            PartiallyRemovedOrder memory partiallyRemovedOrder\n        )\n    {\n        uint256 cursor = last(self);\n        uint256 cursorNodeAmount;\n        uint256 exceededAmount;\n        uint256 exceededAmountInFV;\n\n        require(cursor >= limitValue || limitValue == 0, \"Insufficient limit value\");\n\n        (\n            droppedValue,\n            cursor,\n            cursorNodeAmount,\n            droppedAmount,\n            droppedAmountInFV,\n            exceededAmount,\n            exceededAmountInFV\n        ) = _calculateDroppedAmountFromRight(self, amount, amountInFV, limitValue, cursor);\n\n        uint256 totalNodeAmount = droppedAmount + exceededAmount;\n\n        if (totalNodeAmount > 0) {\n            if (exceededAmount > 0) {\n                cursor = droppedValue;\n                // Update order ids in the node.\n                partiallyRemovedOrder = removeOrders(\n                    self,\n                    cursor,\n                    cursorNodeAmount - exceededAmount\n                );\n            } else if (exceededAmountInFV > 0) {\n                cursor = droppedValue;\n                // Update order ids in the node.\n                partiallyRemovedOrder = removeOrders(\n                    self,\n                    cursor,\n                    cursorNodeAmount - _calculatePresentValue(cursor, exceededAmountInFV)\n                );\n            }\n\n            self.nodes[cursor].right = 0;\n\n            uint256 parent = self.nodes[cursor].parent;\n\n            if (cursor != EMPTY) {\n                while (parent != EMPTY) {\n                    if (parent < cursor) {\n                        // Relink the nodes\n                        if (self.nodes[cursor].parent != parent) {\n                            self.nodes[cursor].parent = parent;\n                            self.nodes[parent].right = cursor;\n                        }\n\n                        cursor = parent;\n                    }\n\n                    parent = self.nodes[parent].parent;\n                }\n            }\n        }\n\n        if (amount > totalNodeAmount) {\n            remainingAmount = amount - totalNodeAmount;\n        }\n\n        uint256 firstNode = first(self);\n\n        if (firstNode == droppedValue && self.nodes[firstNode].orderTotalAmount == 0) {\n            // The case that all node is dropped.\n            self.root = EMPTY;\n        } else if (\n            droppedValue < self.root ||\n            (droppedValue == self.root && droppedAmount == totalNodeAmount)\n        ) {\n            // The case that the root node is dropped\n            self.root = cursor;\n            self.nodes[cursor].parent = 0;\n        }\n\n        rotateTreeToRight(self);\n    }\n\n    function rotateTreeToLeft(Tree storage self) internal {\n        if (self.nodes[self.root].left == 0 && self.nodes[self.root].right != 0) {\n            if (self.nodes[self.nodes[self.root].right].left != 0) {\n                rotateRight(self, self.nodes[self.root].right);\n            }\n            rotateLeft(self, self.root);\n        }\n\n        if (self.nodes[self.root].red) {\n            self.nodes[self.root].red = false;\n        }\n    }\n\n    function rotateTreeToRight(Tree storage self) internal {\n        if (self.nodes[self.root].right == 0 && self.nodes[self.root].left != 0) {\n            if (self.nodes[self.nodes[self.root].left].right != 0) {\n                rotateLeft(self, self.nodes[self.root].left);\n            }\n            rotateRight(self, self.root);\n        }\n\n        if (self.nodes[self.root].red) {\n            self.nodes[self.root].red = false;\n        }\n    }\n\n    function getFutureValue(\n        Tree storage self,\n        uint256 value,\n        uint48 orderId\n    ) internal view returns (uint256) {\n        return _calculateFutureValue(value, self.nodes[value].orders[orderId].amount);\n    }\n\n    // Double linked list functions\n    /**\n     * @dev Retrieves the Object denoted by `_id`.\n     */\n    function getOrderById(\n        Tree storage self,\n        uint256 value,\n        uint48 orderId\n    ) internal view returns (address maker, uint256 amount) {\n        Node storage gn = self.nodes[value];\n        OrderItem memory order = gn.orders[orderId];\n\n        maker = order.maker;\n        amount = order.amount;\n    }\n\n    /**\n     * @dev Return boolean if value, amount and orderId exist in doubly linked list\n     */\n    function orderIdExists(\n        Tree storage self,\n        uint256 value,\n        uint48 orderId\n    ) internal view returns (bool) {\n        uint48 cursor = orderId;\n        Node storage gn = self.nodes[value];\n        OrderItem memory order = gn.orders[cursor];\n\n        if (order.orderId != cursor) {\n            return false;\n        }\n\n        while (order.prev != EMPTY) {\n            cursor = order.prev;\n            order = gn.orders[cursor];\n        }\n\n        return cursor == gn.head;\n    }\n\n    function insertOrder(\n        Tree storage self,\n        uint256 value,\n        uint48 orderId,\n        address user,\n        uint256 amount\n    ) internal {\n        require(amount > 0, \"Insufficient amount\");\n        require(value <= Constants.PRICE_DIGIT, \"Value too high\");\n        insert(self, value);\n\n        addTail(self, value, orderId, user, amount);\n    }\n\n    function removeOrder(\n        Tree storage self,\n        uint256 value,\n        uint48 orderId\n    ) internal returns (uint256 amount) {\n        amount = _removeOrder(self, value, orderId);\n        remove(self, value);\n    }\n\n    function removeOrders(\n        Tree storage self,\n        uint256 value,\n        uint256 _amount\n    ) internal returns (PartiallyRemovedOrder memory partiallyRemovedOrder) {\n        Node storage gn = self.nodes[value];\n\n        require(\n            gn.orderTotalAmount >= _amount,\n            \"OrderStatisticsTreeLib: Amount to remove is insufficient\"\n        );\n\n        uint256 remainingAmount = _amount;\n        OrderItem memory currentOrder = gn.orders[gn.head];\n        uint48 orderId = gn.head;\n\n        while (orderId != 0 && remainingAmount != 0) {\n            currentOrder = gn.orders[orderId];\n\n            if (currentOrder.amount <= remainingAmount) {\n                remainingAmount -= currentOrder.amount;\n                orderId = currentOrder.next;\n            } else {\n                partiallyRemovedOrder = PartiallyRemovedOrder(\n                    currentOrder.orderId,\n                    currentOrder.maker,\n                    remainingAmount,\n                    _calculateFutureValue(value, remainingAmount)\n                );\n                currentOrder = gn.orders[currentOrder.prev];\n                break;\n            }\n        }\n\n        if (currentOrder.orderId != 0) {\n            _removeOrders(self, value, currentOrder.orderId);\n        }\n\n        if (partiallyRemovedOrder.amount > 0) {\n            self.nodes[value].orders[partiallyRemovedOrder.orderId].amount -= partiallyRemovedOrder\n                .amount;\n            self.nodes[value].orderTotalAmount -= partiallyRemovedOrder.amount;\n        }\n    }\n\n    /**\n     * @dev Insert a new OrderItem as the new Head with `_amount` in the amount field, and orderId.\n     */\n    function addHead(\n        Tree storage self,\n        uint256 _value,\n        uint48 _orderId,\n        address _user,\n        uint256 _amount\n    ) internal {\n        Node storage gn = self.nodes[_value];\n        uint48 orderId = _createOrder(self, _value, _orderId, _user, _amount);\n        _link(self, _value, orderId, gn.head);\n        _setHead(self, _value, orderId);\n        if (gn.tail == 0) _setTail(self, _value, orderId);\n    }\n\n    /**\n     * @dev Insert a new OrderItem as the new Tail with `_amount` in the amount field, and orderId.\n     */\n    function addTail(\n        Tree storage self,\n        uint256 _value,\n        uint48 _orderId,\n        address _user,\n        uint256 _amount\n    ) internal {\n        Node storage gn = self.nodes[_value];\n\n        if (gn.head == 0) {\n            addHead(self, _value, _orderId, _user, _amount);\n        } else {\n            uint48 orderId = _createOrder(self, _value, _orderId, _user, _amount);\n            _link(self, _value, gn.tail, orderId);\n            _setTail(self, _value, orderId);\n        }\n    }\n\n    function _calculateDroppedAmountFromLeft(\n        Tree storage self,\n        uint256 amount,\n        uint256 amountInFV,\n        uint256 limitValue,\n        uint256 firstValue\n    )\n        private\n        view\n        returns (\n            uint256 droppedValue,\n            uint256 cursor,\n            uint256 cursorNodeAmount,\n            uint256 droppedAmount,\n            uint256 droppedAmountInFV,\n            uint256 exceededAmount,\n            uint256 exceededAmountInFV\n        )\n    {\n        cursor = firstValue;\n\n        while (\n            (droppedAmount < amount || amount == EMPTY) &&\n            (droppedAmountInFV < amountInFV || amountInFV == EMPTY) &&\n            cursor != EMPTY &&\n            (cursor <= limitValue || limitValue == EMPTY)\n        ) {\n            cursorNodeAmount = self.nodes[cursor].orderTotalAmount;\n            droppedValue = cursor;\n\n            droppedAmountInFV += _calculateFutureValue(cursor, cursorNodeAmount);\n            droppedAmount += cursorNodeAmount;\n\n            if (droppedAmount > amount && amount != EMPTY) {\n                exceededAmount = droppedAmount - amount;\n                exceededAmountInFV = _calculateFutureValue(cursor, exceededAmount);\n            } else if (droppedAmountInFV > amountInFV && amountInFV != EMPTY) {\n                exceededAmountInFV = droppedAmountInFV - amountInFV;\n                exceededAmount = _calculatePresentValue(cursor, exceededAmountInFV);\n            }\n\n            cursor = next(self, cursor);\n        }\n\n        if (exceededAmount > 0) {\n            droppedAmount -= exceededAmount;\n        }\n        if (exceededAmountInFV > 0) {\n            droppedAmountInFV -= exceededAmountInFV;\n        }\n    }\n\n    function _calculateDroppedAmountFromRight(\n        Tree storage self,\n        uint256 amount,\n        uint256 amountInFV,\n        uint256 limitValue,\n        uint256 lastValue\n    )\n        private\n        view\n        returns (\n            uint256 droppedValue,\n            uint256 cursor,\n            uint256 cursorNodeAmount,\n            uint256 droppedAmount,\n            uint256 droppedAmountInFV,\n            uint256 exceededAmount,\n            uint256 exceededAmountInFV\n        )\n    {\n        cursor = lastValue;\n\n        while (\n            (droppedAmount < amount || amount == EMPTY) &&\n            (droppedAmountInFV < amountInFV || amountInFV == EMPTY) &&\n            cursor != EMPTY &&\n            (cursor >= limitValue || limitValue == EMPTY)\n        ) {\n            cursorNodeAmount = self.nodes[cursor].orderTotalAmount;\n            droppedValue = cursor;\n\n            droppedAmountInFV += _calculateFutureValue(cursor, cursorNodeAmount);\n            droppedAmount += cursorNodeAmount;\n\n            if (droppedAmount > amount && amount != EMPTY) {\n                exceededAmount = droppedAmount - amount;\n                exceededAmountInFV = _calculateFutureValue(cursor, exceededAmount);\n            } else if (droppedAmountInFV > amountInFV && amountInFV != EMPTY) {\n                exceededAmountInFV = droppedAmountInFV - amountInFV;\n                exceededAmount = _calculatePresentValue(cursor, exceededAmountInFV);\n            }\n\n            cursor = prev(self, cursor);\n        }\n\n        if (exceededAmount > 0) {\n            droppedAmount -= exceededAmount;\n        }\n        if (exceededAmountInFV > 0) {\n            droppedAmountInFV -= exceededAmountInFV;\n        }\n    }\n\n    /**\n     * @dev Internal function to create an unlinked Order.\n     */\n    function _createOrder(\n        Tree storage self,\n        uint256 value,\n        uint48 orderId,\n        address user,\n        uint256 amount\n    ) internal returns (uint48) {\n        Node storage gn = self.nodes[value];\n        require(\n            gn.orders[orderId].maker == address(0),\n            \"OrderStatisticsTreeLib: Order id already exists\"\n        );\n\n        gn.orderCounter += 1;\n        gn.orderTotalAmount += amount;\n        OrderItem memory order = OrderItem(orderId, 0, 0, user, amount);\n        gn.orders[orderId] = order;\n        return order.orderId;\n    }\n\n    /**\n     * @dev Remove the OrderItem denoted by `_id` from the list.\n     */\n    function _removeOrder(\n        Tree storage self,\n        uint256 value,\n        uint48 orderId\n    ) internal returns (uint256 amount) {\n        require(\n            isActiveOrderId(self, value, orderId),\n            \"OrderStatisticsTreeLib: Order does not exist\"\n        );\n        Node storage gn = self.nodes[value];\n\n        OrderItem memory order = gn.orders[orderId];\n        amount = order.amount;\n\n        if (gn.head == orderId && gn.tail == orderId) {\n            _setHead(self, value, 0);\n            _setTail(self, value, 0);\n        } else if (gn.head == orderId) {\n            _setHead(self, value, order.next);\n            gn.orders[order.next].prev = 0;\n        } else if (gn.tail == orderId) {\n            _setTail(self, value, order.prev);\n            gn.orders[order.prev].next = 0;\n        } else {\n            _link(self, value, order.prev, order.next);\n        }\n        delete gn.orders[order.orderId];\n        gn.orderCounter -= 1;\n        gn.orderTotalAmount -= order.amount;\n    }\n\n    /**\n     * @dev Remove the OrderItems older than or equal `orderId` from the list\n     */\n    function _removeOrders(Tree storage self, uint256 value, uint48 orderId) internal {\n        require(\n            isActiveOrderId(self, value, orderId),\n            \"OrderStatisticsTreeLib: Order does not exist\"\n        );\n        Node storage gn = self.nodes[value];\n\n        OrderItem memory order = gn.orders[orderId];\n        uint48 cursor = gn.head;\n        uint256 removedCount = 1;\n        uint256 removedAmount = gn.orders[cursor].amount;\n\n        while (cursor != orderId) {\n            cursor = gn.orders[cursor].next;\n            removedCount++;\n            removedAmount += gn.orders[cursor].amount;\n        }\n\n        if (gn.tail == orderId) {\n            _setHead(self, value, 0);\n            _setTail(self, value, 0);\n        } else {\n            _setHead(self, value, order.next);\n            gn.orders[order.next].prev = 0;\n        }\n\n        gn.orderCounter -= removedCount;\n        gn.orderTotalAmount -= removedAmount;\n    }\n\n    /**\n     * @dev Internal function to update the Head pointer.\n     */\n    function _setHead(Tree storage self, uint256 value, uint48 orderId) internal {\n        Node storage gn = self.nodes[value];\n\n        gn.head = orderId;\n    }\n\n    /**\n     * @dev Internal function to update the Tail pointer.\n     */\n    function _setTail(Tree storage self, uint256 value, uint48 orderId) internal {\n        Node storage gn = self.nodes[value];\n\n        gn.tail = orderId;\n    }\n\n    /**\n     * @dev Internal function to link an Object to another.\n     */\n    function _link(Tree storage self, uint256 value, uint48 prevId, uint48 nextId) internal {\n        Node storage gn = self.nodes[value];\n\n        gn.orders[prevId].next = nextId;\n        gn.orders[nextId].prev = prevId;\n    }\n\n    function _calculateFutureValue(\n        uint256 unitPrice,\n        uint256 amount\n    ) internal pure returns (uint256) {\n        return (amount * Constants.PRICE_DIGIT).div(unitPrice);\n    }\n\n    function _calculatePresentValue(\n        uint256 unitPrice,\n        uint256 amount\n    ) internal pure returns (uint256) {\n        return (amount * unitPrice).div(Constants.PRICE_DIGIT);\n    }\n}\n"
      },
      "contracts/protocol/mixins/MixinAddressResolver.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {AddressResolverLib} from \"../libraries/AddressResolverLib.sol\";\nimport {IAddressResolver} from \"../interfaces/IAddressResolver.sol\";\nimport {IBeaconProxyController} from \"../interfaces/IBeaconProxyController.sol\";\nimport {ICurrencyController} from \"../interfaces/ICurrencyController.sol\";\nimport {IGenesisValueVault} from \"../interfaces/IGenesisValueVault.sol\";\nimport {ILendingMarketController} from \"../interfaces/ILendingMarketController.sol\";\nimport {IReserveFund} from \"../interfaces/IReserveFund.sol\";\nimport {ITokenVault} from \"../interfaces/ITokenVault.sol\";\nimport {MixinAddressResolverStorage as Storage} from \"../storages/mixins/MixinAddressResolverStorage.sol\";\n\ncontract MixinAddressResolver {\n    event CacheUpdated(bytes32 name, address destination);\n\n    error OnlyAcceptedContract(string name);\n    error ResolverAlreadyRegistered();\n\n    modifier onlyLendingMarketController() {\n        if (msg.sender != address(AddressResolverLib.lendingMarketController()))\n            revert OnlyAcceptedContract(\"LendingMarketController\");\n        _;\n    }\n\n    /**\n     * @notice Returns the contract names used in this contract.\n     * @dev The contract name list is in `./libraries/Contracts.sol`.\n     */\n    function requiredContracts() public pure virtual returns (bytes32[] memory contracts) {}\n\n    function buildCache() public {\n        // The resolver must call this function whenever it updates its state\n        bytes32[] memory contractNames = requiredContracts();\n        for (uint256 i; i < contractNames.length; i++) {\n            bytes32 name = contractNames[i];\n            // Note: can only be invoked once the resolver has all the targets needed added\n            address destination = Storage.slot().resolver.getAddress(\n                name,\n                string(abi.encodePacked(\"Resolver missing target: \", name))\n            );\n            Storage.slot().addressCache[name] = destination;\n            emit CacheUpdated(name, destination);\n        }\n    }\n\n    function isResolverCached() external view returns (bool) {\n        bytes32[] memory contractNames = requiredContracts();\n        for (uint256 i; i < contractNames.length; i++) {\n            bytes32 name = contractNames[i];\n            // false if our cache is invalid or if the resolver doesn't have the required address\n            if (\n                Storage.slot().resolver.getAddress(name) != Storage.slot().addressCache[name] ||\n                Storage.slot().addressCache[name] == address(0)\n            ) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * @dev Register the Address Resolver contract\n     * @param _resolver The address of the Address Resolver contract\n     */\n    function registerAddressResolver(address _resolver) internal {\n        if (address(Storage.slot().resolver) != address(0)) revert ResolverAlreadyRegistered();\n        Storage.slot().resolver = IAddressResolver(_resolver);\n    }\n\n    function getAddress(bytes32 name) internal view returns (address) {\n        return AddressResolverLib.getAddress(name);\n    }\n\n    function resolver() public view returns (IAddressResolver) {\n        return Storage.slot().resolver;\n    }\n\n    function beaconProxyController() internal view returns (IBeaconProxyController) {\n        return AddressResolverLib.beaconProxyController();\n    }\n\n    function currencyController() internal view returns (ICurrencyController) {\n        return AddressResolverLib.currencyController();\n    }\n\n    function genesisValueVault() internal view returns (IGenesisValueVault) {\n        return AddressResolverLib.genesisValueVault();\n    }\n\n    function reserveFund() internal view returns (IReserveFund) {\n        return AddressResolverLib.reserveFund();\n    }\n\n    function lendingMarketController() internal view returns (ILendingMarketController) {\n        return AddressResolverLib.lendingMarketController();\n    }\n\n    function tokenVault() internal view returns (ITokenVault) {\n        return AddressResolverLib.tokenVault();\n    }\n}\n"
      },
      "contracts/protocol/storages/CurrencyControllerStorage.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport \"../../dependencies/chainlink/AggregatorV3Interface.sol\";\nimport \"../../dependencies/openzeppelin/utils/structs/EnumerableSet.sol\";\n\nstruct PriceFeed {\n    AggregatorV3Interface[] instances;\n    uint256[] heartbeats;\n}\n\nlibrary CurrencyControllerStorage {\n    using EnumerableSet for EnumerableSet.Bytes32Set;\n\n    bytes32 internal constant STORAGE_SLOT =\n        bytes32(uint256(keccak256(\"sf.storage.currencyController\")) - 1);\n\n    struct Storage {\n        // Protocol currencies\n        EnumerableSet.Bytes32Set currencies;\n        mapping(bytes32 ccy => uint256 haircut) haircuts;\n        // Total cached decimals of the price feeds\n        mapping(bytes32 ccy => uint8 decimals) decimalsCaches;\n        mapping(bytes32 ccy => PriceFeed priceFeed) priceFeeds;\n    }\n\n    function slot() internal pure returns (Storage storage r) {\n        bytes32 _slot = STORAGE_SLOT;\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := _slot\n        }\n    }\n}\n"
      },
      "contracts/protocol/storages/GenesisValueVaultStorage.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nstruct MaturityUnitPrice {\n    uint256 unitPrice;\n    uint256 compoundFactor;\n    uint256 next;\n    uint256 prev;\n}\n\nstruct AutoRollLog {\n    uint256 unitPrice;\n    uint256 lendingCompoundFactor;\n    uint256 borrowingCompoundFactor;\n    uint256 next;\n    uint256 prev;\n}\n\nlibrary GenesisValueVaultStorage {\n    bytes32 internal constant STORAGE_SLOT =\n        bytes32(uint256(keccak256(\"sf.storage.genesisValueVault\")) - 1);\n\n    struct Storage {\n        mapping(bytes32 ccy => bool isInitialized) isInitialized;\n        mapping(bytes32 ccy => uint256 compoundFactor) initialCompoundFactors;\n        mapping(bytes32 ccy => uint256 compoundFactor) lendingCompoundFactors;\n        mapping(bytes32 ccy => uint256 compoundFactor) borrowingCompoundFactors;\n        mapping(bytes32 ccy => uint256 maturity) currentMaturity;\n        mapping(bytes32 ccy => uint8 decimals) decimals;\n        mapping(bytes32 ccy => mapping(address user => int256 balance)) balances;\n        mapping(bytes32 ccy => uint256 amount) totalLendingSupplies;\n        mapping(bytes32 ccy => uint256 amount) totalBorrowingSupplies;\n        // Total amount supplied per maturity\n        mapping(bytes32 ccy => mapping(uint256 maturity => int256 balance)) maturityBalances;\n        mapping(bytes32 ccy => mapping(uint256 maturity => AutoRollLog log)) autoRollLogs;\n        // Maturity when the user receives the balance on the target currency\n        mapping(bytes32 ccy => mapping(address user => uint256 maturity)) userMaturities;\n        mapping(bytes32 ccy => uint256 balance) totalLockedBalances;\n    }\n\n    function slot() internal pure returns (Storage storage r) {\n        bytes32 _slot = STORAGE_SLOT;\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := _slot\n        }\n    }\n}\n"
      },
      "contracts/protocol/storages/LendingMarketControllerStorage.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport \"../../dependencies/openzeppelin/utils/structs/EnumerableSet.sol\";\n\nstruct ObservationPeriodLog {\n    uint256 totalAmount;\n    uint256 totalFutureValue;\n}\n\nstruct TerminationCurrencyCache {\n    int256 price;\n    uint8 decimals;\n}\n\nstruct ZCTokenInfo {\n    bytes32 ccy;\n    uint256 maturity;\n}\n\nlibrary LendingMarketControllerStorage {\n    using EnumerableSet for EnumerableSet.Bytes32Set;\n    using EnumerableSet for EnumerableSet.UintSet;\n\n    bytes32 internal constant STORAGE_SLOT =\n        bytes32(uint256(keccak256(\"sf.storage.lendingMarketController\")) - 1);\n\n    struct Storage {\n        uint256 marketBasePeriod;\n        uint256 terminationDate;\n        mapping(bytes32 ccy => TerminationCurrencyCache currency) terminationCurrencyCaches;\n        mapping(bytes32 ccy => uint256 ratio) terminationCollateralRatios;\n        mapping(bytes32 ccy => uint8[] orderBookIds) orderBookIdLists;\n        mapping(bytes32 ccy => address lendingMarket) lendingMarkets;\n        mapping(bytes32 ccy => address futureValueVault) futureValueVaults;\n        mapping(bytes32 ccy => uint256 unitPrice) minDebtUnitPrices;\n        mapping(bytes32 ccy => uint256 genesisDate) genesisDates;\n        mapping(bytes32 ccy => mapping(uint256 maturity => uint256 amount)) pendingOrderAmounts;\n        // Order book id history to get order book id from maturity\n        mapping(bytes32 ccy => mapping(uint256 maturity => uint8 orderBookIds)) maturityOrderBookIds;\n        // List of maturity that the user has open orders or positions\n        mapping(bytes32 ccy => mapping(address user => EnumerableSet.UintSet maturities)) usedMaturities;\n        // Observation period logs that is used for auto-rolls\n        mapping(bytes32 ccy => mapping(uint256 maturity => ObservationPeriodLog log)) observationPeriodLogs;\n        // List of currency that the user has open orders or positions\n        mapping(address user => EnumerableSet.Bytes32Set currency) usedCurrencies;\n        mapping(address user => bool isRedeemed) isRedeemed;\n        mapping(bytes32 ccy => mapping(uint256 maturity => address token)) zcTokens;\n        mapping(address token => ZCTokenInfo) zcTokenInfo;\n    }\n\n    function slot() internal pure returns (Storage storage r) {\n        bytes32 _slot = STORAGE_SLOT;\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := _slot\n        }\n    }\n}\n"
      },
      "contracts/protocol/storages/LendingMarketStorage.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {ProtocolTypes} from \"../types/ProtocolTypes.sol\";\nimport {OrderBookLib} from \"../libraries/OrderBookLib.sol\";\n\nstruct ItayoseLog {\n    uint256 openingUnitPrice;\n    uint256 lastLendUnitPrice;\n    uint256 lastBorrowUnitPrice;\n}\n\nlibrary LendingMarketStorage {\n    bytes32 internal constant STORAGE_SLOT =\n        bytes32(uint256(keccak256(\"sf.storage.lendingMarket\")) - 1);\n\n    struct Storage {\n        bytes32 ccy;\n        uint8 lastOrderBookId;\n        // Order fee rate received by protocol (in basis point)\n        uint256 orderFeeRate;\n        // Rate limit range of yield for the circuit breaker\n        uint256 circuitBreakerLimitRange;\n        // Mapping from order book id to order book\n        mapping(uint8 orderBookId => OrderBookLib.OrderBook orderBook) orderBooks;\n        mapping(uint256 maturity => bool isReady) isReady;\n        mapping(uint256 maturity => ItayoseLog log) itayoseLogs;\n    }\n\n    function slot() internal pure returns (Storage storage r) {\n        bytes32 _slot = STORAGE_SLOT;\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := _slot\n        }\n    }\n}\n"
      },
      "contracts/protocol/storages/mixins/MixinAddressResolverStorage.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {IAddressResolver} from \"../../interfaces/IAddressResolver.sol\";\n\nlibrary MixinAddressResolverStorage {\n    bytes32 internal constant STORAGE_SLOT =\n        bytes32(uint256(keccak256(\"sf.storage.mixinAddressResolver\")) - 1);\n\n    struct Storage {\n        IAddressResolver resolver;\n        mapping(bytes32 contractName => address contractAddress) addressCache;\n    }\n\n    function slot() internal pure returns (Storage storage r) {\n        bytes32 _slot = STORAGE_SLOT;\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := _slot\n        }\n    }\n}\n"
      },
      "contracts/protocol/storages/utils/PausableStorage.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nlibrary PausableStorage {\n    bytes32 internal constant STORAGE_SLOT = bytes32(uint256(keccak256(\"sf.storage.pausable\")) - 1);\n\n    struct Storage {\n        bool paused;\n    }\n\n    function slot() internal pure returns (Storage storage r) {\n        bytes32 _slot = STORAGE_SLOT;\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := _slot\n        }\n    }\n}\n"
      },
      "contracts/protocol/types/ProtocolTypes.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\n/**\n * @dev ProtocolTypes is a base-level contract that holds common Secured Finance protocol types\n * @author Secured Finance\n */\nlibrary ProtocolTypes {\n    enum Side {\n        LEND,\n        BORROW\n    }\n}\n"
      },
      "contracts/protocol/utils/Pausable.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport \"../../dependencies/openzeppelin/utils/Context.sol\";\nimport \"../storages/utils/PausableStorage.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 Pausable is Context {\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    /**\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 PausableStorage.slot().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        PausableStorage.slot().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        PausableStorage.slot().paused = false;\n        emit Unpaused(_msgSender());\n    }\n}\n"
      },
      "contracts/protocol/utils/Proxyable.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport \"../../dependencies/openzeppelin/utils/StorageSlot.sol\";\nimport \"../../dependencies/openzeppelin/proxy/utils/Initializable.sol\";\n\nabstract contract Proxyable is Initializable {\n    bytes32 internal constant _IMPLEMENTATION_SLOT =\n        0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n    bytes32 internal constant _BEACON_SLOT =\n        0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\n\n    modifier onlyProxy() {\n        require(_getImplementation() != address(0), \"Must be called from proxy contract\");\n        _;\n    }\n\n    modifier onlyBeacon() {\n        require(_getBeacon() != address(0), \"Must be called from beacon contract\");\n        _;\n    }\n\n    /**\n     * @notice Gets the revision number of the contract\n     * @return The revision number\n     */\n    function getRevision() external pure virtual returns (uint256) {\n        return 0x1;\n    }\n\n    function _getImplementation() private view returns (address) {\n        return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n    }\n\n    function _getBeacon() internal view returns (address) {\n        return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\n    }\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "metadata": {
        "useLiteralContent": true
      },
      "libraries": {
        "contracts/protocol/libraries/logics/OrderActionLogic.sol": {
          "OrderActionLogic": "0x54a3f4ef9854c43926563348508d1e9c0f1d7926"
        },
        "contracts/protocol/libraries/logics/OrderBookLogic.sol": {
          "OrderBookLogic": "0xef430f8296a63aabc0df51c1ca17da622c185120"
        },
        "contracts/protocol/libraries/logics/OrderReaderLogic.sol": {
          "OrderReaderLogic": "0xacd8f57119106a97fcf09d8b0024b358cec30941"
        }
      }
    }
  }}