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/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/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/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/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/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/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"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "metadata": {
        "useLiteralContent": true
      },
      "libraries": {
        "contracts/protocol/libraries/logics/OrderReaderLogic.sol": {
          "OrderReaderLogic": "0xacd8f57119106a97fcf09d8b0024b358cec30941"
        }
      }
    }
  }}