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/token/ERC20/extensions/IERC20Metadata.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n"
      },
      "contracts/dependencies/openzeppelin/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n"
      },
      "contracts/dependencies/openzeppelin/utils/math/Math.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    enum Rounding {\n        Down, // Toward negative infinity\n        Up, // Toward infinity\n        Zero // Toward zero\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds up instead\n     * of rounding down.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n     * with further edits by Uniswap Labs also under MIT license.\n     */\n    function mulDiv(\n        uint256 x,\n        uint256 y,\n        uint256 denominator\n    ) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod0 := mul(x, y)\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n                // The surrounding unchecked block does not change this fact.\n                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            require(denominator > prod1, \"Math: mulDiv overflow\");\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n            // See https://cs.stackexchange.com/q/138556/92363.\n\n            // Does not overflow because the denominator cannot be zero at this stage in the function.\n            uint256 twos = denominator & (~denominator + 1);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n            // in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(\n        uint256 x,\n        uint256 y,\n        uint256 denominator,\n        Rounding rounding\n    ) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10 ** 64) {\n                value /= 10 ** 64;\n                result += 64;\n            }\n            if (value >= 10 ** 32) {\n                value /= 10 ** 32;\n                result += 32;\n            }\n            if (value >= 10 ** 16) {\n                value /= 10 ** 16;\n                result += 16;\n            }\n            if (value >= 10 ** 8) {\n                value /= 10 ** 8;\n                result += 8;\n            }\n            if (value >= 10 ** 4) {\n                value /= 10 ** 4;\n                result += 4;\n            }\n            if (value >= 10 ** 2) {\n                value /= 10 ** 2;\n                result += 2;\n            }\n            if (value >= 10 ** 1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);\n        }\n    }\n}\n"
      },
      "contracts/dependencies/openzeppelin/utils/math/SafeCast.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n    /**\n     * @dev Returns the downcasted uint248 from uint256, reverting on\n     * overflow (when the input is greater than largest uint248).\n     *\n     * Counterpart to Solidity's `uint248` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 248 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint248(uint256 value) internal pure returns (uint248) {\n        require(value <= type(uint248).max, \"SafeCast: value doesn't fit in 248 bits\");\n        return uint248(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint240 from uint256, reverting on\n     * overflow (when the input is greater than largest uint240).\n     *\n     * Counterpart to Solidity's `uint240` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 240 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint240(uint256 value) internal pure returns (uint240) {\n        require(value <= type(uint240).max, \"SafeCast: value doesn't fit in 240 bits\");\n        return uint240(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint232 from uint256, reverting on\n     * overflow (when the input is greater than largest uint232).\n     *\n     * Counterpart to Solidity's `uint232` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 232 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint232(uint256 value) internal pure returns (uint232) {\n        require(value <= type(uint232).max, \"SafeCast: value doesn't fit in 232 bits\");\n        return uint232(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint224 from uint256, reverting on\n     * overflow (when the input is greater than largest uint224).\n     *\n     * Counterpart to Solidity's `uint224` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 224 bits\n     *\n     * _Available since v4.2._\n     */\n    function toUint224(uint256 value) internal pure returns (uint224) {\n        require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n        return uint224(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint216 from uint256, reverting on\n     * overflow (when the input is greater than largest uint216).\n     *\n     * Counterpart to Solidity's `uint216` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 216 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint216(uint256 value) internal pure returns (uint216) {\n        require(value <= type(uint216).max, \"SafeCast: value doesn't fit in 216 bits\");\n        return uint216(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint208 from uint256, reverting on\n     * overflow (when the input is greater than largest uint208).\n     *\n     * Counterpart to Solidity's `uint208` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 208 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint208(uint256 value) internal pure returns (uint208) {\n        require(value <= type(uint208).max, \"SafeCast: value doesn't fit in 208 bits\");\n        return uint208(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint200 from uint256, reverting on\n     * overflow (when the input is greater than largest uint200).\n     *\n     * Counterpart to Solidity's `uint200` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 200 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint200(uint256 value) internal pure returns (uint200) {\n        require(value <= type(uint200).max, \"SafeCast: value doesn't fit in 200 bits\");\n        return uint200(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint192 from uint256, reverting on\n     * overflow (when the input is greater than largest uint192).\n     *\n     * Counterpart to Solidity's `uint192` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 192 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint192(uint256 value) internal pure returns (uint192) {\n        require(value <= type(uint192).max, \"SafeCast: value doesn't fit in 192 bits\");\n        return uint192(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint184 from uint256, reverting on\n     * overflow (when the input is greater than largest uint184).\n     *\n     * Counterpart to Solidity's `uint184` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 184 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint184(uint256 value) internal pure returns (uint184) {\n        require(value <= type(uint184).max, \"SafeCast: value doesn't fit in 184 bits\");\n        return uint184(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint176 from uint256, reverting on\n     * overflow (when the input is greater than largest uint176).\n     *\n     * Counterpart to Solidity's `uint176` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 176 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint176(uint256 value) internal pure returns (uint176) {\n        require(value <= type(uint176).max, \"SafeCast: value doesn't fit in 176 bits\");\n        return uint176(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint168 from uint256, reverting on\n     * overflow (when the input is greater than largest uint168).\n     *\n     * Counterpart to Solidity's `uint168` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 168 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint168(uint256 value) internal pure returns (uint168) {\n        require(value <= type(uint168).max, \"SafeCast: value doesn't fit in 168 bits\");\n        return uint168(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint160 from uint256, reverting on\n     * overflow (when the input is greater than largest uint160).\n     *\n     * Counterpart to Solidity's `uint160` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 160 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint160(uint256 value) internal pure returns (uint160) {\n        require(value <= type(uint160).max, \"SafeCast: value doesn't fit in 160 bits\");\n        return uint160(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint152 from uint256, reverting on\n     * overflow (when the input is greater than largest uint152).\n     *\n     * Counterpart to Solidity's `uint152` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 152 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint152(uint256 value) internal pure returns (uint152) {\n        require(value <= type(uint152).max, \"SafeCast: value doesn't fit in 152 bits\");\n        return uint152(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint144 from uint256, reverting on\n     * overflow (when the input is greater than largest uint144).\n     *\n     * Counterpart to Solidity's `uint144` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 144 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint144(uint256 value) internal pure returns (uint144) {\n        require(value <= type(uint144).max, \"SafeCast: value doesn't fit in 144 bits\");\n        return uint144(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint136 from uint256, reverting on\n     * overflow (when the input is greater than largest uint136).\n     *\n     * Counterpart to Solidity's `uint136` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 136 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint136(uint256 value) internal pure returns (uint136) {\n        require(value <= type(uint136).max, \"SafeCast: value doesn't fit in 136 bits\");\n        return uint136(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint128 from uint256, reverting on\n     * overflow (when the input is greater than largest uint128).\n     *\n     * Counterpart to Solidity's `uint128` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 128 bits\n     *\n     * _Available since v2.5._\n     */\n    function toUint128(uint256 value) internal pure returns (uint128) {\n        require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n        return uint128(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint120 from uint256, reverting on\n     * overflow (when the input is greater than largest uint120).\n     *\n     * Counterpart to Solidity's `uint120` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 120 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint120(uint256 value) internal pure returns (uint120) {\n        require(value <= type(uint120).max, \"SafeCast: value doesn't fit in 120 bits\");\n        return uint120(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint112 from uint256, reverting on\n     * overflow (when the input is greater than largest uint112).\n     *\n     * Counterpart to Solidity's `uint112` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 112 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint112(uint256 value) internal pure returns (uint112) {\n        require(value <= type(uint112).max, \"SafeCast: value doesn't fit in 112 bits\");\n        return uint112(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint104 from uint256, reverting on\n     * overflow (when the input is greater than largest uint104).\n     *\n     * Counterpart to Solidity's `uint104` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 104 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint104(uint256 value) internal pure returns (uint104) {\n        require(value <= type(uint104).max, \"SafeCast: value doesn't fit in 104 bits\");\n        return uint104(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint96 from uint256, reverting on\n     * overflow (when the input is greater than largest uint96).\n     *\n     * Counterpart to Solidity's `uint96` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 96 bits\n     *\n     * _Available since v4.2._\n     */\n    function toUint96(uint256 value) internal pure returns (uint96) {\n        require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n        return uint96(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint88 from uint256, reverting on\n     * overflow (when the input is greater than largest uint88).\n     *\n     * Counterpart to Solidity's `uint88` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 88 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint88(uint256 value) internal pure returns (uint88) {\n        require(value <= type(uint88).max, \"SafeCast: value doesn't fit in 88 bits\");\n        return uint88(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint80 from uint256, reverting on\n     * overflow (when the input is greater than largest uint80).\n     *\n     * Counterpart to Solidity's `uint80` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 80 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint80(uint256 value) internal pure returns (uint80) {\n        require(value <= type(uint80).max, \"SafeCast: value doesn't fit in 80 bits\");\n        return uint80(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint72 from uint256, reverting on\n     * overflow (when the input is greater than largest uint72).\n     *\n     * Counterpart to Solidity's `uint72` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 72 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint72(uint256 value) internal pure returns (uint72) {\n        require(value <= type(uint72).max, \"SafeCast: value doesn't fit in 72 bits\");\n        return uint72(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint64 from uint256, reverting on\n     * overflow (when the input is greater than largest uint64).\n     *\n     * Counterpart to Solidity's `uint64` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 64 bits\n     *\n     * _Available since v2.5._\n     */\n    function toUint64(uint256 value) internal pure returns (uint64) {\n        require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n        return uint64(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint56 from uint256, reverting on\n     * overflow (when the input is greater than largest uint56).\n     *\n     * Counterpart to Solidity's `uint56` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 56 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint56(uint256 value) internal pure returns (uint56) {\n        require(value <= type(uint56).max, \"SafeCast: value doesn't fit in 56 bits\");\n        return uint56(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint48 from uint256, reverting on\n     * overflow (when the input is greater than largest uint48).\n     *\n     * Counterpart to Solidity's `uint48` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 48 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint48(uint256 value) internal pure returns (uint48) {\n        require(value <= type(uint48).max, \"SafeCast: value doesn't fit in 48 bits\");\n        return uint48(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint40 from uint256, reverting on\n     * overflow (when the input is greater than largest uint40).\n     *\n     * Counterpart to Solidity's `uint40` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 40 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint40(uint256 value) internal pure returns (uint40) {\n        require(value <= type(uint40).max, \"SafeCast: value doesn't fit in 40 bits\");\n        return uint40(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint32 from uint256, reverting on\n     * overflow (when the input is greater than largest uint32).\n     *\n     * Counterpart to Solidity's `uint32` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 32 bits\n     *\n     * _Available since v2.5._\n     */\n    function toUint32(uint256 value) internal pure returns (uint32) {\n        require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n        return uint32(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint24 from uint256, reverting on\n     * overflow (when the input is greater than largest uint24).\n     *\n     * Counterpart to Solidity's `uint24` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 24 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint24(uint256 value) internal pure returns (uint24) {\n        require(value <= type(uint24).max, \"SafeCast: value doesn't fit in 24 bits\");\n        return uint24(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint16 from uint256, reverting on\n     * overflow (when the input is greater than largest uint16).\n     *\n     * Counterpart to Solidity's `uint16` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 16 bits\n     *\n     * _Available since v2.5._\n     */\n    function toUint16(uint256 value) internal pure returns (uint16) {\n        require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n        return uint16(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint8 from uint256, reverting on\n     * overflow (when the input is greater than largest uint8).\n     *\n     * Counterpart to Solidity's `uint8` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 8 bits\n     *\n     * _Available since v2.5._\n     */\n    function toUint8(uint256 value) internal pure returns (uint8) {\n        require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n        return uint8(value);\n    }\n\n    /**\n     * @dev Converts a signed int256 into an unsigned uint256.\n     *\n     * Requirements:\n     *\n     * - input must be greater than or equal to 0.\n     *\n     * _Available since v3.0._\n     */\n    function toUint256(int256 value) internal pure returns (uint256) {\n        require(value >= 0, \"SafeCast: value must be positive\");\n        return uint256(value);\n    }\n\n    /**\n     * @dev Returns the downcasted int248 from int256, reverting on\n     * overflow (when the input is less than smallest int248 or\n     * greater than largest int248).\n     *\n     * Counterpart to Solidity's `int248` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 248 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt248(int256 value) internal pure returns (int248 downcasted) {\n        downcasted = int248(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 248 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int240 from int256, reverting on\n     * overflow (when the input is less than smallest int240 or\n     * greater than largest int240).\n     *\n     * Counterpart to Solidity's `int240` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 240 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt240(int256 value) internal pure returns (int240 downcasted) {\n        downcasted = int240(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 240 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int232 from int256, reverting on\n     * overflow (when the input is less than smallest int232 or\n     * greater than largest int232).\n     *\n     * Counterpart to Solidity's `int232` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 232 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt232(int256 value) internal pure returns (int232 downcasted) {\n        downcasted = int232(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 232 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int224 from int256, reverting on\n     * overflow (when the input is less than smallest int224 or\n     * greater than largest int224).\n     *\n     * Counterpart to Solidity's `int224` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 224 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt224(int256 value) internal pure returns (int224 downcasted) {\n        downcasted = int224(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 224 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int216 from int256, reverting on\n     * overflow (when the input is less than smallest int216 or\n     * greater than largest int216).\n     *\n     * Counterpart to Solidity's `int216` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 216 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt216(int256 value) internal pure returns (int216 downcasted) {\n        downcasted = int216(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 216 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int208 from int256, reverting on\n     * overflow (when the input is less than smallest int208 or\n     * greater than largest int208).\n     *\n     * Counterpart to Solidity's `int208` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 208 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt208(int256 value) internal pure returns (int208 downcasted) {\n        downcasted = int208(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 208 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int200 from int256, reverting on\n     * overflow (when the input is less than smallest int200 or\n     * greater than largest int200).\n     *\n     * Counterpart to Solidity's `int200` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 200 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt200(int256 value) internal pure returns (int200 downcasted) {\n        downcasted = int200(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 200 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int192 from int256, reverting on\n     * overflow (when the input is less than smallest int192 or\n     * greater than largest int192).\n     *\n     * Counterpart to Solidity's `int192` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 192 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt192(int256 value) internal pure returns (int192 downcasted) {\n        downcasted = int192(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 192 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int184 from int256, reverting on\n     * overflow (when the input is less than smallest int184 or\n     * greater than largest int184).\n     *\n     * Counterpart to Solidity's `int184` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 184 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt184(int256 value) internal pure returns (int184 downcasted) {\n        downcasted = int184(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 184 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int176 from int256, reverting on\n     * overflow (when the input is less than smallest int176 or\n     * greater than largest int176).\n     *\n     * Counterpart to Solidity's `int176` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 176 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt176(int256 value) internal pure returns (int176 downcasted) {\n        downcasted = int176(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 176 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int168 from int256, reverting on\n     * overflow (when the input is less than smallest int168 or\n     * greater than largest int168).\n     *\n     * Counterpart to Solidity's `int168` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 168 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt168(int256 value) internal pure returns (int168 downcasted) {\n        downcasted = int168(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 168 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int160 from int256, reverting on\n     * overflow (when the input is less than smallest int160 or\n     * greater than largest int160).\n     *\n     * Counterpart to Solidity's `int160` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 160 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt160(int256 value) internal pure returns (int160 downcasted) {\n        downcasted = int160(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 160 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int152 from int256, reverting on\n     * overflow (when the input is less than smallest int152 or\n     * greater than largest int152).\n     *\n     * Counterpart to Solidity's `int152` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 152 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt152(int256 value) internal pure returns (int152 downcasted) {\n        downcasted = int152(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 152 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int144 from int256, reverting on\n     * overflow (when the input is less than smallest int144 or\n     * greater than largest int144).\n     *\n     * Counterpart to Solidity's `int144` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 144 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt144(int256 value) internal pure returns (int144 downcasted) {\n        downcasted = int144(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 144 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int136 from int256, reverting on\n     * overflow (when the input is less than smallest int136 or\n     * greater than largest int136).\n     *\n     * Counterpart to Solidity's `int136` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 136 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt136(int256 value) internal pure returns (int136 downcasted) {\n        downcasted = int136(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 136 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int128 from int256, reverting on\n     * overflow (when the input is less than smallest int128 or\n     * greater than largest int128).\n     *\n     * Counterpart to Solidity's `int128` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 128 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt128(int256 value) internal pure returns (int128 downcasted) {\n        downcasted = int128(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 128 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int120 from int256, reverting on\n     * overflow (when the input is less than smallest int120 or\n     * greater than largest int120).\n     *\n     * Counterpart to Solidity's `int120` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 120 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt120(int256 value) internal pure returns (int120 downcasted) {\n        downcasted = int120(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 120 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int112 from int256, reverting on\n     * overflow (when the input is less than smallest int112 or\n     * greater than largest int112).\n     *\n     * Counterpart to Solidity's `int112` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 112 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt112(int256 value) internal pure returns (int112 downcasted) {\n        downcasted = int112(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 112 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int104 from int256, reverting on\n     * overflow (when the input is less than smallest int104 or\n     * greater than largest int104).\n     *\n     * Counterpart to Solidity's `int104` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 104 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt104(int256 value) internal pure returns (int104 downcasted) {\n        downcasted = int104(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 104 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int96 from int256, reverting on\n     * overflow (when the input is less than smallest int96 or\n     * greater than largest int96).\n     *\n     * Counterpart to Solidity's `int96` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 96 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt96(int256 value) internal pure returns (int96 downcasted) {\n        downcasted = int96(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 96 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int88 from int256, reverting on\n     * overflow (when the input is less than smallest int88 or\n     * greater than largest int88).\n     *\n     * Counterpart to Solidity's `int88` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 88 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt88(int256 value) internal pure returns (int88 downcasted) {\n        downcasted = int88(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 88 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int80 from int256, reverting on\n     * overflow (when the input is less than smallest int80 or\n     * greater than largest int80).\n     *\n     * Counterpart to Solidity's `int80` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 80 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt80(int256 value) internal pure returns (int80 downcasted) {\n        downcasted = int80(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 80 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int72 from int256, reverting on\n     * overflow (when the input is less than smallest int72 or\n     * greater than largest int72).\n     *\n     * Counterpart to Solidity's `int72` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 72 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt72(int256 value) internal pure returns (int72 downcasted) {\n        downcasted = int72(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 72 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int64 from int256, reverting on\n     * overflow (when the input is less than smallest int64 or\n     * greater than largest int64).\n     *\n     * Counterpart to Solidity's `int64` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 64 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt64(int256 value) internal pure returns (int64 downcasted) {\n        downcasted = int64(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 64 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int56 from int256, reverting on\n     * overflow (when the input is less than smallest int56 or\n     * greater than largest int56).\n     *\n     * Counterpart to Solidity's `int56` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 56 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt56(int256 value) internal pure returns (int56 downcasted) {\n        downcasted = int56(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 56 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int48 from int256, reverting on\n     * overflow (when the input is less than smallest int48 or\n     * greater than largest int48).\n     *\n     * Counterpart to Solidity's `int48` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 48 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt48(int256 value) internal pure returns (int48 downcasted) {\n        downcasted = int48(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 48 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int40 from int256, reverting on\n     * overflow (when the input is less than smallest int40 or\n     * greater than largest int40).\n     *\n     * Counterpart to Solidity's `int40` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 40 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt40(int256 value) internal pure returns (int40 downcasted) {\n        downcasted = int40(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 40 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int32 from int256, reverting on\n     * overflow (when the input is less than smallest int32 or\n     * greater than largest int32).\n     *\n     * Counterpart to Solidity's `int32` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 32 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt32(int256 value) internal pure returns (int32 downcasted) {\n        downcasted = int32(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 32 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int24 from int256, reverting on\n     * overflow (when the input is less than smallest int24 or\n     * greater than largest int24).\n     *\n     * Counterpart to Solidity's `int24` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 24 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt24(int256 value) internal pure returns (int24 downcasted) {\n        downcasted = int24(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 24 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int16 from int256, reverting on\n     * overflow (when the input is less than smallest int16 or\n     * greater than largest int16).\n     *\n     * Counterpart to Solidity's `int16` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 16 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt16(int256 value) internal pure returns (int16 downcasted) {\n        downcasted = int16(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 16 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int8 from int256, reverting on\n     * overflow (when the input is less than smallest int8 or\n     * greater than largest int8).\n     *\n     * Counterpart to Solidity's `int8` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 8 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt8(int256 value) internal pure returns (int8 downcasted) {\n        downcasted = int8(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 8 bits\");\n    }\n\n    /**\n     * @dev Converts an unsigned uint256 into a signed int256.\n     *\n     * Requirements:\n     *\n     * - input must be less than or equal to maxInt256.\n     *\n     * _Available since v3.0._\n     */\n    function toInt256(uint256 value) internal pure returns (int256) {\n        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n        require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n        return int256(value);\n    }\n}\n"
      },
      "contracts/dependencies/openzeppelin/utils/math/SignedMath.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard signed math utilities missing in the Solidity language.\n */\nlibrary SignedMath {\n    /**\n     * @dev Returns the largest of two signed numbers.\n     */\n    function max(int256 a, int256 b) internal pure returns (int256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two signed numbers.\n     */\n    function min(int256 a, int256 b) internal pure returns (int256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two signed numbers without overflow.\n     * The result is rounded towards zero.\n     */\n    function average(int256 a, int256 b) internal pure returns (int256) {\n        // Formula from the book \"Hacker's Delight\"\n        int256 x = (a & b) + ((a ^ b) >> 1);\n        return x + (int256(uint256(x) >> 255) & (a ^ b));\n    }\n\n    /**\n     * @dev Returns the absolute unsigned value of a signed value.\n     */\n    function abs(int256 n) internal pure returns (uint256) {\n        unchecked {\n            // must be unchecked in order to support `n = type(int256).min`\n            return uint256(n >= 0 ? n : -n);\n        }\n    }\n}\n"
      },
      "contracts/dependencies/openzeppelin/utils/Strings.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\nimport \"./math/SignedMath.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n    bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n    uint8 private constant _ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            uint256 length = Math.log10(value) + 1;\n            string memory buffer = new string(length);\n            uint256 ptr;\n            /// @solidity memory-safe-assembly\n            assembly {\n                ptr := add(buffer, add(32, length))\n            }\n            while (true) {\n                ptr--;\n                /// @solidity memory-safe-assembly\n                assembly {\n                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n                }\n                value /= 10;\n                if (value == 0) break;\n            }\n            return buffer;\n        }\n    }\n\n    /**\n     * @dev Converts a `int256` to its ASCII `string` decimal representation.\n     */\n    function toString(int256 value) internal pure returns (string memory) {\n        return string(abi.encodePacked(value < 0 ? \"-\" : \"\", toString(SignedMath.abs(value))));\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            return toHexString(value, Math.log256(value) + 1);\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = _SYMBOLS[value & 0xf];\n            value >>= 4;\n        }\n        require(value == 0, \"Strings: hex length insufficient\");\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n    }\n\n    /**\n     * @dev Returns true if the two strings are equal.\n     */\n    function equal(string memory a, string memory b) internal pure returns (bool) {\n        return keccak256(bytes(a)) == keccak256(bytes(b));\n    }\n}\n"
      },
      "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/IFutureValueVault.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\ninterface IFutureValueVault {\n    error UserIsZero();\n    error PastMaturityBalanceExists(address user);\n    error TotalSupplyNotZero();\n    error InvalidResetAmount();\n    error InsufficientBalance();\n    error InsufficientLockedBalance();\n\n    event Transfer(\n        address indexed from,\n        address indexed to,\n        uint8 orderBookId,\n        uint256 maturity,\n        int256 value\n    );\n    event BalanceLocked(\n        uint8 indexed orderBookId,\n        uint256 indexed maturity,\n        address indexed user,\n        uint256 value\n    );\n    event BalanceUnlocked(\n        uint8 indexed orderBookId,\n        uint256 indexed maturity,\n        address indexed user,\n        uint256 value\n    );\n\n    function getTotalLendingSupply(uint256 maturity) external view returns (uint256);\n\n    function getTotalBorrowingSupply(uint256 maturity) external view returns (uint256);\n\n    function getBalance(\n        uint8 orderBookId,\n        address user\n    ) external view returns (int256 futureValue, uint256 maturity);\n\n    function getTotalLockedBalance(uint8 orderBookId) external view returns (uint256);\n\n    function hasBalanceAtPastMaturity(\n        uint8 orderBookId,\n        address user,\n        uint256 maturity\n    ) external view returns (bool);\n\n    function increase(uint8 orderBookId, address user, uint256 amount, uint256 maturity) external;\n\n    function decrease(uint8 orderBookId, address user, uint256 amount, uint256 maturity) external;\n\n    function lock(\n        uint8 orderBookId,\n        address user,\n        uint256 amount,\n        uint256 maturity\n    ) external returns (uint256 lockedAmount);\n\n    function unlock(uint8 orderBookId, address user, uint256 amount, uint256 maturity) external;\n\n    function transferFrom(\n        uint8 orderBookId,\n        address sender,\n        address receiver,\n        int256 amount,\n        uint256 maturity\n    ) external;\n\n    function reset(\n        uint8 orderBookId,\n        address user\n    )\n        external\n        returns (int256 removedAmount, int256 currentAmount, uint256 maturity, bool isAllRemoved);\n\n    function executeForcedReset(uint8 orderBookId, address user) external;\n\n    function executeForcedReset(\n        uint8 orderBookId,\n        address user,\n        int256 amount\n    ) external returns (int256 removedAmount, int256 balance);\n}\n"
      },
      "contracts/protocol/interfaces/IGenesisValueVault.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {AutoRollLog} from \"../storages/GenesisValueVaultStorage.sol\";\n\ninterface IGenesisValueVault {\n    error NoCompoundFactorExists(uint256 maturity);\n    error CompoundFactorIsZero();\n    error ResidualAmountIsNotZero();\n    error UnitPriceIsZero();\n    error InvalidMaturity();\n    error InvalidAmount();\n    error InvalidOrderFeeRate();\n    error CurrencyAlreadyInitialized();\n    error InitialCompoundFactorAlreadyFinalized();\n    error AutoRollLogAlreadyUpdated(uint256 currentMaturity, uint256 nextMaturity);\n    error InsufficientBalance();\n    error InsufficientLockedBalance();\n\n    event Transfer(bytes32 indexed ccy, address indexed from, address indexed to, int256 value);\n    event AutoRollExecuted(\n        bytes32 indexed ccy,\n        uint256 lendingCompoundFactor,\n        uint256 borrowingCompoundFactor,\n        uint256 unitPrice,\n        uint256 currentMaturity,\n        uint256 previousMaturity\n    );\n    event BalanceLocked(bytes32 indexed ccy, address indexed user, uint256 value);\n    event BalanceUnlocked(bytes32 indexed ccy, address indexed user, uint256 value);\n\n    function isAutoRolled(bytes32 _ccy, uint256 _maturity) external view returns (bool);\n\n    function isInitialized(bytes32 ccy) external view returns (bool);\n\n    function decimals(bytes32 ccy) external view returns (uint8);\n\n    function getTotalLendingSupply(bytes32 ccy) external view returns (uint256);\n\n    function getTotalBorrowingSupply(bytes32 ccy) external view returns (uint256);\n\n    function getBalance(bytes32 ccy, address user) external view returns (int256);\n\n    function getMaturityGenesisValue(bytes32 ccy, uint256 maturity) external view returns (int256);\n\n    function getCurrentMaturity(bytes32 ccy) external view returns (uint256);\n\n    function getLendingCompoundFactor(bytes32 ccy) external view returns (uint256);\n\n    function getBorrowingCompoundFactor(bytes32 ccy) external view returns (uint256);\n\n    function getAutoRollLog(\n        bytes32 ccy,\n        uint256 maturity\n    ) external view returns (AutoRollLog memory);\n\n    function getLatestAutoRollLog(bytes32 ccy) external view returns (AutoRollLog memory);\n\n    function getTotalLockedBalance(bytes32 ccy) external view returns (uint256);\n\n    function calculateFVFromFV(\n        bytes32 ccy,\n        uint256 basisMaturity,\n        uint256 destinationMaturity,\n        int256 futureValue\n    ) external view returns (int256);\n\n    function calculateGVFromFV(\n        bytes32 ccy,\n        uint256 basisMaturity,\n        int256 futureValue\n    ) external view returns (int256);\n\n    function calculateFVFromGV(\n        bytes32 ccy,\n        uint256 basisMaturity,\n        int256 genesisValue\n    ) external view returns (int256);\n\n    function getBalanceFluctuationByAutoRolls(\n        bytes32 ccy,\n        address user,\n        uint256 maturity\n    ) external view returns (int256 fluctuation);\n\n    function calculateBalanceFluctuationByAutoRolls(\n        bytes32 ccy,\n        int256 balance,\n        uint256 fromMaturity,\n        uint256 toMaturity\n    ) external view returns (int256 fluctuation);\n\n    function initializeCurrencySetting(\n        bytes32 ccy,\n        uint8 decimals,\n        uint256 compoundFactor,\n        uint256 maturity\n    ) external;\n\n    function updateInitialCompoundFactor(bytes32 ccy, uint256 unitPrice) external;\n\n    function updateDecimals(bytes32 _ccy, uint8 _decimals) external;\n\n    function executeAutoRoll(\n        bytes32 ccy,\n        uint256 maturity,\n        uint256 nextMaturity,\n        uint256 unitPrice,\n        uint256 orderFeeRate\n    ) external;\n\n    function updateGenesisValueWithFutureValue(\n        bytes32 ccy,\n        address user,\n        uint256 basisMaturity,\n        int256 fvAmount\n    ) external;\n\n    function updateGenesisValueWithResidualAmount(\n        bytes32 ccy,\n        address user,\n        uint256 basisMaturity\n    ) external;\n\n    function lock(\n        bytes32 ccy,\n        address user,\n        uint256 amount\n    ) external returns (uint256 lockedAmount);\n\n    function unlock(bytes32 ccy, address user, uint256 amount) external;\n\n    function transferFrom(bytes32 ccy, address sender, address receiver, int256 amount) external;\n\n    function cleanUpBalance(bytes32 ccy, address user, uint256 maturity) external;\n\n    function executeForcedReset(bytes32 _ccy, address _user) external;\n\n    function executeForcedReset(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        int256 _amountInFV\n    ) external returns (int256 removedAmount, int256 balance);\n}\n"
      },
      "contracts/protocol/interfaces/ILendingMarket.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport \"../types/ProtocolTypes.sol\";\nimport {ItayoseLog} from \"../storages/LendingMarketStorage.sol\";\nimport {OrderBookLib, FilledOrder, PartiallyFilledOrder} from \"../libraries/OrderBookLib.sol\";\n\ninterface ILendingMarket {\n    error NoOrderExists();\n    error CallerNotMaker();\n    error MarketNotOpened();\n    error AlreadyItayosePeriod();\n    error NotItayosePeriod();\n    error NotPreOrderPeriod();\n\n    function getOrderBookDetail(\n        uint8 orderBookId\n    )\n        external\n        view\n        returns (bytes32 ccy, uint256 maturity, uint256 openingDate, uint256 preOpeningDate);\n\n    function getCircuitBreakerThresholds(\n        uint8 orderBookId\n    )\n        external\n        view\n        returns (uint256 lendCircuitBreakerThreshold, uint256 borrowCircuitBreakerThreshold);\n\n    function getBestLendUnitPrice(uint8 orderBookId) external view returns (uint256 unitPrice);\n\n    function getBestLendUnitPrices(\n        uint8[] calldata orderBookIds\n    ) external view returns (uint256[] memory);\n\n    function getBestBorrowUnitPrice(uint8 orderBookId) external view returns (uint256 unitPrice);\n\n    function getBestBorrowUnitPrices(\n        uint8[] calldata orderBookIds\n    ) external view returns (uint256[] memory);\n\n    function getMarketUnitPrice(uint8 orderBookId) external view returns (uint256);\n\n    function getLastOrderTimestamp(uint8 orderBookId) external view returns (uint48);\n\n    function getBlockUnitPriceHistory(\n        uint8 orderBookId\n    ) external view returns (uint256[] memory unitPrices, uint48 timestamp);\n\n    function getBlockUnitPriceAverage(\n        uint8 orderBookId,\n        uint256 count\n    ) external view returns (uint256);\n\n    function getBorrowOrderBook(\n        uint8 orderBookId,\n        uint256 start,\n        uint256 limit\n    )\n        external\n        view\n        returns (\n            uint256[] memory unitPrices,\n            uint256[] memory amounts,\n            uint256[] memory quantities,\n            uint256 next\n        );\n\n    function getLendOrderBook(\n        uint8 orderBookId,\n        uint256 start,\n        uint256 limit\n    )\n        external\n        view\n        returns (\n            uint256[] memory unitPrices,\n            uint256[] memory amounts,\n            uint256[] memory quantities,\n            uint256 next\n        );\n\n    function getItayoseEstimation(\n        uint8 orderBookId\n    )\n        external\n        view\n        returns (\n            uint256 openingUnitPrice,\n            uint256 lastLendUnitPrice,\n            uint256 lastBorrowUnitPrice,\n            uint256 totalOffsetAmount\n        );\n\n    function getMaturity(uint8 orderBookId) external view returns (uint256);\n\n    function getMaturities(\n        uint8[] calldata orderBookIds\n    ) external view returns (uint256[] memory maturities);\n\n    function getCurrency() external view returns (bytes32);\n\n    function getOrderFeeRate() external view returns (uint256);\n\n    function getCircuitBreakerLimitRange() external view returns (uint256);\n\n    function getOpeningDate(uint8 orderBookId) external view returns (uint256);\n\n    function isReady(uint8 orderBookId) external view returns (bool);\n\n    function isMatured(uint8 orderBookId) external view returns (bool);\n\n    function isOpened(uint8 orderBookId) external view returns (bool);\n\n    function isItayosePeriod(uint8 orderBookId) external view returns (bool);\n\n    function isPreOrderPeriod(uint8 orderBookId) external returns (bool);\n\n    function getItayoseLog(uint256 maturity) external view returns (ItayoseLog memory);\n\n    function getOrder(\n        uint8 orderBookId,\n        uint48 orderId\n    )\n        external\n        view\n        returns (\n            ProtocolTypes.Side,\n            uint256 unitPrice,\n            uint256 maturity,\n            address maker,\n            uint256 amount,\n            uint256 timestamp,\n            bool isPreOrder\n        );\n\n    function getTotalAmountFromLendOrders(\n        uint8 orderBookId,\n        address user\n    )\n        external\n        view\n        returns (\n            uint256 activeAmount,\n            uint256 inactiveAmount,\n            uint256 inactiveFutureValue,\n            uint256 maturity\n        );\n\n    function getTotalAmountFromBorrowOrders(\n        uint8 orderBookId,\n        address user,\n        uint256 _minUnitPrice\n    )\n        external\n        view\n        returns (\n            uint256 activeAmount,\n            uint256 inactiveAmount,\n            uint256 inactiveFutureValue,\n            uint256 maturity\n        );\n\n    function getLendOrderIds(\n        uint8 orderBookId,\n        address user\n    ) external view returns (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds);\n\n    function getBorrowOrderIds(\n        uint8 orderBookId,\n        address user\n    ) external view returns (uint48[] memory activeOrderIds, uint48[] memory inActiveOrderIds);\n\n    function calculateFilledAmount(\n        uint8 orderBookId,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice\n    )\n        external\n        view\n        returns (\n            uint256 lastUnitPrice,\n            uint256 filledAmount,\n            uint256 filledAmountInFV,\n            uint256 feeInFV,\n            uint256 placedAmount\n        );\n\n    function createOrderBook(\n        uint256 maturity,\n        uint256 openingDate,\n        uint256 preOpeningDate\n    ) external returns (uint8 orderBookId);\n\n    function executeAutoRoll(\n        uint8 maturedOrderBookId,\n        uint8 newNearestOrderBookId,\n        uint256 autoRollUnitPrice\n    ) external;\n\n    function cancelOrder(uint8 orderBookId, address user, uint48 orderId) external;\n\n    function executeOrder(\n        uint8 orderBookId,\n        ProtocolTypes.Side side,\n        address account,\n        uint256 amount,\n        uint256 unitPrice\n    )\n        external\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 feeInFV\n        );\n\n    function executePreOrder(\n        uint8 orderBookId,\n        ProtocolTypes.Side side,\n        address user,\n        uint256 amount,\n        uint256 unitPrice\n    ) external;\n\n    function unwindPosition(\n        uint8 orderBookId,\n        ProtocolTypes.Side side,\n        address user,\n        uint256 futureValue\n    )\n        external\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 feeInFV\n        );\n\n    function executeItayoseCall(\n        uint8 orderBookId\n    )\n        external\n        returns (\n            uint256 openingUnitPrice,\n            uint256 totalOffsetAmount,\n            uint256 openingDate,\n            PartiallyFilledOrder memory partiallyFilledLendingOrder,\n            PartiallyFilledOrder memory partiallyFilledBorrowingOrder\n        );\n\n    function cleanUpOrders(\n        uint8 orderBookId,\n        address user\n    )\n        external\n        returns (\n            uint256 activeLendOrderCount,\n            uint256 activeBorrowOrderCount,\n            uint256 removedLendOrderFutureValue,\n            uint256 removedBorrowOrderFutureValue,\n            uint256 removedLendOrderAmount,\n            uint256 removedBorrowOrderAmount,\n            uint256 maturity\n        );\n\n    function updateOrderFeeRate(uint256 orderFeeRate) external;\n\n    function updateCircuitBreakerLimitRange(uint256 limitRange) external;\n\n    function pause() external;\n\n    function unpause() external;\n}\n"
      },
      "contracts/protocol/interfaces/ILendingMarketController.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {ProtocolTypes} from \"../types/ProtocolTypes.sol\";\nimport {ZCTokenInfo, TerminationCurrencyCache} from \"../storages/LendingMarketControllerStorage.sol\";\n\ninterface ILendingMarketController {\n    error InvalidMaturity();\n    error InvalidCurrency();\n    error MarketTerminated();\n    error NotTerminated();\n    error AlreadyInitialized();\n\n    struct AdditionalFunds {\n        bytes32 ccy;\n        uint256 workingLendOrdersAmount;\n        uint256 claimableAmount;\n        uint256 workingBorrowOrdersAmount;\n        uint256 debtAmount;\n        uint256 lentAmount;\n        uint256 borrowedAmount;\n    }\n\n    struct CalculatedTotalFunds {\n        uint256 plusDepositAmountInAdditionalFundsCcy;\n        uint256 minusDepositAmountInAdditionalFundsCcy;\n        uint256 workingLendOrdersAmount;\n        uint256 claimableAmount;\n        uint256 collateralAmount;\n        uint256 lentAmount;\n        uint256 workingBorrowOrdersAmount;\n        uint256 debtAmount;\n        uint256 borrowedAmount;\n    }\n\n    struct CalculatedFunds {\n        uint256 workingLendOrdersAmount;\n        uint256 claimableAmount;\n        uint256 collateralAmount;\n        uint256 unallocatedCollateralAmount;\n        uint256 lentAmount;\n        uint256 workingBorrowOrdersAmount;\n        uint256 debtAmount;\n        uint256 borrowedAmount;\n    }\n\n    struct GetOrderEstimationParams {\n        bytes32 ccy;\n        uint256 maturity;\n        address user;\n        ProtocolTypes.Side side;\n        uint256 amount;\n        uint256 unitPrice;\n        uint256 additionalDepositAmount;\n        bool ignoreBorrowedAmount;\n    }\n\n    function isValidMaturity(bytes32 _ccy, uint256 _maturity) external view returns (bool);\n\n    function isTerminated() external view returns (bool);\n\n    function isRedemptionRequired(address _user) external view returns (bool);\n\n    function getMarketBasePeriod() external view returns (uint256);\n\n    function getTerminationDate() external view returns (uint256);\n\n    function getTerminationCurrencyCache(\n        bytes32 _ccy\n    ) external view returns (TerminationCurrencyCache memory);\n\n    function getTerminationCollateralRatio(bytes32 _ccy) external view returns (uint256);\n\n    function getMinDebtUnitPrice(bytes32 _ccy) external view returns (uint256);\n\n    function getCurrentMinDebtUnitPrice(\n        bytes32 _ccy,\n        uint256 _maturity\n    ) external view returns (uint256);\n\n    function getGenesisDate(bytes32 ccy) external view returns (uint256);\n\n    function getLendingMarket(bytes32 ccy) external view returns (address);\n\n    function getFutureValueVault(bytes32 ccy) external view returns (address);\n\n    function getOrderBookId(bytes32 _ccy, uint256 _maturity) external view returns (uint8);\n\n    function getPendingOrderAmount(bytes32 _ccy, uint256 _maturity) external view returns (uint256);\n\n    function getOrderEstimation(\n        GetOrderEstimationParams calldata params\n    )\n        external\n        view\n        returns (\n            uint256 lastUnitPrice,\n            uint256 filledAmount,\n            uint256 filledAmountInFV,\n            uint256 orderFeeInFV,\n            uint256 placedAmount,\n            uint256 coverage,\n            bool isInsufficientDepositAmount\n        );\n\n    function getMaturities(bytes32 ccy) external view returns (uint256[] memory);\n\n    function getOrderBookIds(bytes32 ccy) external view returns (uint8[] memory);\n\n    function getUsedCurrencies(address user) external view returns (bytes32[] memory);\n\n    function getTotalPresentValue(bytes32 ccy, address user) external view returns (int256);\n\n    function getTotalPresentValueInBaseCurrency(\n        address user\n    ) external view returns (int256 totalPresentValue);\n\n    function getGenesisValue(\n        bytes32 ccy,\n        address user\n    ) external view returns (int256 amount, int256 amountInPV, int256 amountInFV);\n\n    function getPosition(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user\n    ) external view returns (int256 presentValue, int256 futureValue);\n\n    function getZCToken(bytes32 ccy, uint256 maturity) external view returns (address);\n\n    function getZCTokenInfo(address zcToken) external view returns (ZCTokenInfo memory);\n\n    function getWithdrawableZCTokenAmount(\n        bytes32 ccy,\n        uint256 maturity,\n        address user\n    ) external view returns (uint256 amount);\n\n    function calculateFunds(\n        bytes32 ccy,\n        address user,\n        uint256 liquidationThresholdRate\n    ) external view returns (CalculatedFunds memory funds);\n\n    function calculateTotalFundsInBaseCurrency(\n        address user,\n        AdditionalFunds calldata _additionalFunds,\n        uint256 liquidationThresholdRate\n    ) external view returns (CalculatedTotalFunds memory calculatedFunds);\n\n    function isInitializedLendingMarket(bytes32 ccy) external view returns (bool);\n\n    function initializeLendingMarket(\n        bytes32 ccy,\n        uint256 genesisDate,\n        uint256 compoundFactor,\n        uint256 orderFeeRate,\n        uint256 circuitBreakerLimitRange,\n        uint256 minDebtUnitPrice\n    ) external;\n\n    function createOrderBook(bytes32 ccy, uint256 openingDate, uint256 preOpeningDate) external;\n\n    function executeOrder(\n        bytes32 ccy,\n        uint256 maturity,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice\n    ) external returns (bool);\n\n    function depositAndExecuteOrder(\n        bytes32 ccy,\n        uint256 maturity,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice\n    ) external payable returns (bool);\n\n    function depositWithPermitAndExecuteOrder(\n        bytes32 ccy,\n        uint256 maturity,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice,\n        uint256 deadline,\n        uint8 permitV,\n        bytes32 permitR,\n        bytes32 permitS\n    ) external returns (bool);\n\n    function executePreOrder(\n        bytes32 ccy,\n        uint256 maturity,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice\n    ) external returns (bool);\n\n    function depositAndExecutesPreOrder(\n        bytes32 ccy,\n        uint256 maturity,\n        ProtocolTypes.Side side,\n        uint256 amount,\n        uint256 unitPrice\n    ) external payable returns (bool);\n\n    function depositWithPermitAndExecutePreOrder(\n        bytes32 _ccy,\n        uint256 _maturity,\n        ProtocolTypes.Side _side,\n        uint256 _amount,\n        uint256 _unitPrice,\n        uint256 _deadline,\n        uint8 _permitV,\n        bytes32 _permitR,\n        bytes32 _permitS\n    ) external returns (bool);\n\n    function unwindPosition(bytes32 ccy, uint256 maturity) external returns (bool);\n\n    function executeItayoseCall(bytes32 ccy, uint256 maturity) external returns (bool);\n\n    function executeRedemption(bytes32 _ccy, uint256 _maturity) external returns (bool);\n\n    function executeRepayment(bytes32 _ccy, uint256 _maturity) external returns (bool);\n\n    function executeEmergencySettlement() external returns (bool);\n\n    function executeLiquidationCall(\n        bytes32 collateralCcy,\n        bytes32 debtCcy,\n        uint256 debtMaturity,\n        address user\n    ) external returns (bool);\n\n    function executeForcedRepayment(\n        bytes32 _collateralCcy,\n        bytes32 _debtCcy,\n        uint256 _debtMaturity,\n        address _user\n    ) external returns (bool);\n\n    function cancelOrder(bytes32 ccy, uint256 maturity, uint48 orderId) external returns (bool);\n\n    function rotateOrderBooks(bytes32 ccy) external;\n\n    function executeEmergencyTermination() external;\n\n    function pauseLendingMarket(bytes32 ccy) external returns (bool);\n\n    function unpauseLendingMarket(bytes32 ccy) external returns (bool);\n\n    function cleanUpAllFunds(address user) external returns (bool);\n\n    function cleanUpFunds(bytes32 ccy, address user) external returns (uint256 activeOrderCount);\n\n    function updateMinDebtUnitPrice(bytes32 _ccy, uint256 _minDebtUnitPrice) external;\n\n    function withdrawZCToken(bytes32 _ccy, uint256 _maturity, uint256 _amount) external;\n\n    function depositZCToken(bytes32 _ccy, uint256 _maturity, uint256 _amount) external;\n}\n"
      },
      "contracts/protocol/interfaces/ILiquidationReceiver.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\ninterface ILiquidationReceiver {\n    error InvalidOperationExecution();\n\n    event OperationExecuteForCollateral(\n        address liquidator,\n        address user,\n        bytes32 ccy,\n        uint256 receivedAmount\n    );\n    event OperationExecuteForDebt(\n        address liquidator,\n        address user,\n        bytes32 collateralCcy,\n        uint256 receivedCollateralAmount,\n        bytes32 debtCcy,\n        uint256 debtMaturity,\n        uint256 receivedDebtAmount\n    );\n\n    function executeOperationForCollateral(\n        address liquidator,\n        address user,\n        bytes32 ccy,\n        uint256 receivedAmount\n    ) external returns (bool);\n\n    function executeOperationForDebt(\n        address liquidator,\n        address user,\n        bytes32 collateralCcy,\n        uint256 receivedCollateralAmount,\n        bytes32 debtCcy,\n        uint256 debtMaturity,\n        uint256 receivedDebtAmount\n    ) external returns (bool);\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/interfaces/IZCToken.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {IERC20} from \"../../dependencies/openzeppelin/token/ERC20/IERC20.sol\";\n\ninterface IZCToken is IERC20 {\n    function asset() external view returns (address);\n\n    function maturity() external view returns (uint256);\n\n    function mint(address to, uint256 amount) external;\n\n    function burn(address from, uint256 amount) external;\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/BokkyPooBahsDateTimeLibrary.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\n// ----------------------------------------------------------------------------\n// BokkyPooBah's DateTime Library v1.01\n//\n// A gas-efficient Solidity date and time library\n//\n// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary\n//\n// Tested date range 1970/01/01 to 2345/12/31\n//\n// Conventions:\n// Unit      | Range         | Notes\n// :-------- |:-------------:|:-----\n// timestamp | >= 0          | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC\n// year      | 1970 ... 2345 |\n// month     | 1 ... 12      |\n// day       | 1 ... 31      |\n// hour      | 0 ... 23      |\n// minute    | 0 ... 59      |\n// second    | 0 ... 59      |\n// dayOfWeek | 1 ... 7       | 1 = Monday, ..., 7 = Sunday\n//\n//\n// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018-2019. The MIT Licence.\n// ----------------------------------------------------------------------------\n\nlibrary BokkyPooBahsDateTimeLibrary {\n    uint256 constant SECONDS_PER_DAY = 24 * 60 * 60;\n    uint256 constant SECONDS_PER_HOUR = 60 * 60;\n    uint256 constant SECONDS_PER_MINUTE = 60;\n    int256 constant OFFSET19700101 = 2440588;\n\n    uint256 constant DOW_MON = 1;\n    uint256 constant DOW_TUE = 2;\n    uint256 constant DOW_WED = 3;\n    uint256 constant DOW_THU = 4;\n    uint256 constant DOW_FRI = 5;\n    uint256 constant DOW_SAT = 6;\n    uint256 constant DOW_SUN = 7;\n\n    // ------------------------------------------------------------------------\n    // Calculate the number of days from 1970/01/01 to year/month/day using\n    // the date conversion algorithm from\n    //   http://aa.usno.navy.mil/faq/docs/JD_Formula.php\n    // and subtracting the offset 2440588 so that 1970/01/01 is day 0\n    //\n    // days = day\n    //      - 32075\n    //      + 1461 * (year + 4800 + (month - 14) / 12) / 4\n    //      + 367 * (month - 2 - (month - 14) / 12 * 12) / 12\n    //      - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4\n    //      - offset\n    // ------------------------------------------------------------------------\n    function _daysFromDate(\n        uint256 year,\n        uint256 month,\n        uint256 day\n    ) internal pure returns (uint256 _days) {\n        require(year >= 1970);\n        int256 _year = int256(year);\n        int256 _month = int256(month);\n        int256 _day = int256(day);\n\n        int256 __days = _day -\n            32075 +\n            (1461 * (_year + 4800 + (_month - 14) / 12)) /\n            4 +\n            (367 * (_month - 2 - ((_month - 14) / 12) * 12)) /\n            12 -\n            (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) /\n            4 -\n            OFFSET19700101;\n\n        _days = uint256(__days);\n    }\n\n    // ------------------------------------------------------------------------\n    // Calculate year/month/day from the number of days since 1970/01/01 using\n    // the date conversion algorithm from\n    //   http://aa.usno.navy.mil/faq/docs/JD_Formula.php\n    // and adding the offset 2440588 so that 1970/01/01 is day 0\n    //\n    // int L = days + 68569 + offset\n    // int N = 4 * L / 146097\n    // L = L - (146097 * N + 3) / 4\n    // year = 4000 * (L + 1) / 1461001\n    // L = L - 1461 * year / 4 + 31\n    // month = 80 * L / 2447\n    // dd = L - 2447 * month / 80\n    // L = month / 11\n    // month = month + 2 - 12 * L\n    // year = 100 * (N - 49) + year + L\n    // ------------------------------------------------------------------------\n    function _daysToDate(\n        uint256 _days\n    ) internal pure returns (uint256 year, uint256 month, uint256 day) {\n        int256 __days = int256(_days);\n\n        int256 L = __days + 68569 + OFFSET19700101;\n        int256 N = (4 * L) / 146097;\n        L = L - (146097 * N + 3) / 4;\n        int256 _year = (4000 * (L + 1)) / 1461001;\n        L = L - (1461 * _year) / 4 + 31;\n        int256 _month = (80 * L) / 2447;\n        int256 _day = L - (2447 * _month) / 80;\n        L = _month / 11;\n        _month = _month + 2 - 12 * L;\n        _year = 100 * (N - 49) + _year + L;\n\n        year = uint256(_year);\n        month = uint256(_month);\n        day = uint256(_day);\n    }\n\n    function timestampFromDate(\n        uint256 year,\n        uint256 month,\n        uint256 day\n    ) internal pure returns (uint256 timestamp) {\n        timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;\n    }\n\n    function timestampFromDateTime(\n        uint256 year,\n        uint256 month,\n        uint256 day,\n        uint256 hour,\n        uint256 minute,\n        uint256 second\n    ) internal pure returns (uint256 timestamp) {\n        timestamp =\n            _daysFromDate(year, month, day) *\n            SECONDS_PER_DAY +\n            hour *\n            SECONDS_PER_HOUR +\n            minute *\n            SECONDS_PER_MINUTE +\n            second;\n    }\n\n    function timestampToDate(\n        uint256 timestamp\n    ) internal pure returns (uint256 year, uint256 month, uint256 day) {\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n    }\n\n    function timestampToDateTime(\n        uint256 timestamp\n    )\n        internal\n        pure\n        returns (\n            uint256 year,\n            uint256 month,\n            uint256 day,\n            uint256 hour,\n            uint256 minute,\n            uint256 second\n        )\n    {\n        (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        uint256 secs = timestamp % SECONDS_PER_DAY;\n        hour = secs / SECONDS_PER_HOUR;\n        secs = secs % SECONDS_PER_HOUR;\n        minute = secs / SECONDS_PER_MINUTE;\n        second = secs % SECONDS_PER_MINUTE;\n    }\n\n    function isValidDate(\n        uint256 year,\n        uint256 month,\n        uint256 day\n    ) internal pure returns (bool valid) {\n        if (year >= 1970 && month > 0 && month <= 12) {\n            uint256 daysInMonth = _getDaysInMonth(year, month);\n            if (day > 0 && day <= daysInMonth) {\n                valid = true;\n            }\n        }\n    }\n\n    function isValidDateTime(\n        uint256 year,\n        uint256 month,\n        uint256 day,\n        uint256 hour,\n        uint256 minute,\n        uint256 second\n    ) internal pure returns (bool valid) {\n        if (isValidDate(year, month, day)) {\n            if (hour < 24 && minute < 60 && second < 60) {\n                valid = true;\n            }\n        }\n    }\n\n    function isLeapYear(uint256 timestamp) internal pure returns (bool leapYear) {\n        (uint256 year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        leapYear = _isLeapYear(year);\n    }\n\n    function _isLeapYear(uint256 year) internal pure returns (bool leapYear) {\n        leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);\n    }\n\n    function isWeekDay(uint256 timestamp) internal pure returns (bool weekDay) {\n        weekDay = getDayOfWeek(timestamp) <= DOW_FRI;\n    }\n\n    function isWeekEnd(uint256 timestamp) internal pure returns (bool weekEnd) {\n        weekEnd = getDayOfWeek(timestamp) >= DOW_SAT;\n    }\n\n    function getDaysInMonth(uint256 timestamp) internal pure returns (uint256 daysInMonth) {\n        (uint256 year, uint256 month, ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        daysInMonth = _getDaysInMonth(year, month);\n    }\n\n    function _getDaysInMonth(\n        uint256 year,\n        uint256 month\n    ) internal pure returns (uint256 daysInMonth) {\n        if (\n            month == 1 ||\n            month == 3 ||\n            month == 5 ||\n            month == 7 ||\n            month == 8 ||\n            month == 10 ||\n            month == 12\n        ) {\n            daysInMonth = 31;\n        } else if (month != 2) {\n            daysInMonth = 30;\n        } else {\n            daysInMonth = _isLeapYear(year) ? 29 : 28;\n        }\n    }\n\n    // 1 = Monday, 7 = Sunday\n    function getDayOfWeek(uint256 timestamp) internal pure returns (uint256 dayOfWeek) {\n        uint256 _days = timestamp / SECONDS_PER_DAY;\n        dayOfWeek = ((_days + 3) % 7) + 1;\n    }\n\n    function getYear(uint256 timestamp) internal pure returns (uint256 year) {\n        (year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n    }\n\n    function getMonth(uint256 timestamp) internal pure returns (uint256 month) {\n        (, month, ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n    }\n\n    function getDay(uint256 timestamp) internal pure returns (uint256 day) {\n        (, , day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n    }\n\n    function getHour(uint256 timestamp) internal pure returns (uint256 hour) {\n        uint256 secs = timestamp % SECONDS_PER_DAY;\n        hour = secs / SECONDS_PER_HOUR;\n    }\n\n    function getMinute(uint256 timestamp) internal pure returns (uint256 minute) {\n        uint256 secs = timestamp % SECONDS_PER_HOUR;\n        minute = secs / SECONDS_PER_MINUTE;\n    }\n\n    function getSecond(uint256 timestamp) internal pure returns (uint256 second) {\n        second = timestamp % SECONDS_PER_MINUTE;\n    }\n\n    function addYears(\n        uint256 timestamp,\n        uint256 _years\n    ) internal pure returns (uint256 newTimestamp) {\n        (uint256 year, uint256 month, uint256 day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        year += _years;\n        uint256 daysInMonth = _getDaysInMonth(year, month);\n        if (day > daysInMonth) {\n            day = daysInMonth;\n        }\n        newTimestamp =\n            _daysFromDate(year, month, day) *\n            SECONDS_PER_DAY +\n            (timestamp % SECONDS_PER_DAY);\n        require(newTimestamp >= timestamp);\n    }\n\n    function addMonths(\n        uint256 timestamp,\n        uint256 _months\n    ) internal pure returns (uint256 newTimestamp) {\n        (uint256 year, uint256 month, uint256 day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        month += _months;\n        year += (month - 1) / 12;\n        month = ((month - 1) % 12) + 1;\n        uint256 daysInMonth = _getDaysInMonth(year, month);\n        if (day > daysInMonth) {\n            day = daysInMonth;\n        }\n        newTimestamp =\n            _daysFromDate(year, month, day) *\n            SECONDS_PER_DAY +\n            (timestamp % SECONDS_PER_DAY);\n        require(newTimestamp >= timestamp);\n    }\n\n    function addDays(\n        uint256 timestamp,\n        uint256 _days\n    ) internal pure returns (uint256 newTimestamp) {\n        newTimestamp = timestamp + _days * SECONDS_PER_DAY;\n        require(newTimestamp >= timestamp);\n    }\n\n    function addHours(\n        uint256 timestamp,\n        uint256 _hours\n    ) internal pure returns (uint256 newTimestamp) {\n        newTimestamp = timestamp + _hours * SECONDS_PER_HOUR;\n        require(newTimestamp >= timestamp);\n    }\n\n    function addMinutes(\n        uint256 timestamp,\n        uint256 _minutes\n    ) internal pure returns (uint256 newTimestamp) {\n        newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE;\n        require(newTimestamp >= timestamp);\n    }\n\n    function addSeconds(\n        uint256 timestamp,\n        uint256 _seconds\n    ) internal pure returns (uint256 newTimestamp) {\n        newTimestamp = timestamp + _seconds;\n        require(newTimestamp >= timestamp);\n    }\n\n    function subYears(\n        uint256 timestamp,\n        uint256 _years\n    ) internal pure returns (uint256 newTimestamp) {\n        (uint256 year, uint256 month, uint256 day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        year -= _years;\n        uint256 daysInMonth = _getDaysInMonth(year, month);\n        if (day > daysInMonth) {\n            day = daysInMonth;\n        }\n        newTimestamp =\n            _daysFromDate(year, month, day) *\n            SECONDS_PER_DAY +\n            (timestamp % SECONDS_PER_DAY);\n        require(newTimestamp <= timestamp);\n    }\n\n    function subMonths(\n        uint256 timestamp,\n        uint256 _months\n    ) internal pure returns (uint256 newTimestamp) {\n        (uint256 year, uint256 month, uint256 day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n        uint256 yearMonth = year * 12 + (month - 1) - _months;\n        year = yearMonth / 12;\n        month = (yearMonth % 12) + 1;\n        uint256 daysInMonth = _getDaysInMonth(year, month);\n        if (day > daysInMonth) {\n            day = daysInMonth;\n        }\n        newTimestamp =\n            _daysFromDate(year, month, day) *\n            SECONDS_PER_DAY +\n            (timestamp % SECONDS_PER_DAY);\n        require(newTimestamp <= timestamp);\n    }\n\n    function subDays(\n        uint256 timestamp,\n        uint256 _days\n    ) internal pure returns (uint256 newTimestamp) {\n        newTimestamp = timestamp - _days * SECONDS_PER_DAY;\n        require(newTimestamp <= timestamp);\n    }\n\n    function subHours(\n        uint256 timestamp,\n        uint256 _hours\n    ) internal pure returns (uint256 newTimestamp) {\n        newTimestamp = timestamp - _hours * SECONDS_PER_HOUR;\n        require(newTimestamp <= timestamp);\n    }\n\n    function subMinutes(\n        uint256 timestamp,\n        uint256 _minutes\n    ) internal pure returns (uint256 newTimestamp) {\n        newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE;\n        require(newTimestamp <= timestamp);\n    }\n\n    function subSeconds(\n        uint256 timestamp,\n        uint256 _seconds\n    ) internal pure returns (uint256 newTimestamp) {\n        newTimestamp = timestamp - _seconds;\n        require(newTimestamp <= timestamp);\n    }\n\n    function diffYears(\n        uint256 fromTimestamp,\n        uint256 toTimestamp\n    ) internal pure returns (uint256 _years) {\n        require(fromTimestamp <= toTimestamp);\n        (uint256 fromYear, , ) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);\n        (uint256 toYear, , ) = _daysToDate(toTimestamp / SECONDS_PER_DAY);\n        _years = toYear - fromYear;\n    }\n\n    function diffMonths(\n        uint256 fromTimestamp,\n        uint256 toTimestamp\n    ) internal pure returns (uint256 _months) {\n        require(fromTimestamp <= toTimestamp);\n        (uint256 fromYear, uint256 fromMonth, ) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);\n        (uint256 toYear, uint256 toMonth, ) = _daysToDate(toTimestamp / SECONDS_PER_DAY);\n        _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth;\n    }\n\n    function diffDays(\n        uint256 fromTimestamp,\n        uint256 toTimestamp\n    ) internal pure returns (uint256 _days) {\n        require(fromTimestamp <= toTimestamp);\n        _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY;\n    }\n\n    function diffHours(\n        uint256 fromTimestamp,\n        uint256 toTimestamp\n    ) internal pure returns (uint256 _hours) {\n        require(fromTimestamp <= toTimestamp);\n        _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR;\n    }\n\n    function diffMinutes(\n        uint256 fromTimestamp,\n        uint256 toTimestamp\n    ) internal pure returns (uint256 _minutes) {\n        require(fromTimestamp <= toTimestamp);\n        _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE;\n    }\n\n    function diffSeconds(\n        uint256 fromTimestamp,\n        uint256 toTimestamp\n    ) internal pure returns (uint256 _seconds) {\n        require(fromTimestamp <= toTimestamp);\n        _seconds = toTimestamp - fromTimestamp;\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/FundManagementLogic.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\n// dependencies\nimport {EnumerableSet} from \"../../../dependencies/openzeppelin/utils/structs/EnumerableSet.sol\";\nimport {SafeCast} from \"../../../dependencies/openzeppelin/utils/math/SafeCast.sol\";\n// interfaces\nimport {ILendingMarket} from \"../../interfaces/ILendingMarket.sol\";\nimport {ILendingMarketController} from \"../../interfaces/ILendingMarketController.sol\";\nimport {IFutureValueVault} from \"../../interfaces/IFutureValueVault.sol\";\nimport {ILiquidationReceiver} from \"../../interfaces/ILiquidationReceiver.sol\";\n\n// libraries\nimport {AddressResolverLib} from \"../AddressResolverLib.sol\";\nimport {QuickSort} from \"../QuickSort.sol\";\nimport {Constants} from \"../Constants.sol\";\nimport {RoundingUint256} from \"../math/RoundingUint256.sol\";\nimport {RoundingInt256} from \"../math/RoundingInt256.sol\";\n// types\nimport {ProtocolTypes} from \"../../types/ProtocolTypes.sol\";\n// storages\nimport {LendingMarketControllerStorage as Storage, TerminationCurrencyCache} from \"../../storages/LendingMarketControllerStorage.sol\";\n\nlibrary FundManagementLogic {\n    using EnumerableSet for EnumerableSet.Bytes32Set;\n    using EnumerableSet for EnumerableSet.UintSet;\n    using SafeCast for uint256;\n    using SafeCast for int256;\n    using RoundingUint256 for uint256;\n    using RoundingInt256 for int256;\n\n    uint256 public constant BASE_MIN_DEBT_UNIT_PRICE = 9600;\n\n    error NotRedemptionPeriod();\n    error NotRepaymentPeriod();\n    error NoRedemptionAmount();\n    error NoRepaymentAmount();\n    error AlreadyRedeemed();\n    error InsufficientCollateral();\n\n    struct CalculatedTotalFundInBaseCurrencyVars {\n        address user;\n        ILendingMarketController.AdditionalFunds additionalFunds;\n        uint256 liquidationThresholdRate;\n        bool[] isCollateral;\n        bytes32[] ccys;\n    }\n\n    struct ActualFunds {\n        int256 presentValue;\n        uint256 claimableAmount;\n        uint256 debtAmount;\n        int256 futureValue;\n        uint256 workingLendOrdersAmount;\n        uint256 lentAmount;\n        uint256 workingBorrowOrdersAmount;\n        uint256 borrowedAmount;\n        int256 genesisValue;\n        int256 genesisValueInPV;\n        int256 genesisValueInFV;\n    }\n\n    struct CalculateActualFundsVars {\n        bool isTotal;\n        bool isDefaultMarket;\n        uint8 orderBookId;\n        uint8 defaultOrderBookId;\n        uint256 defaultOrderBookMarketUnitPrice;\n        uint256[] maturities;\n        int256 presentValueOfDefaultMarket;\n        ILendingMarket market;\n        IFutureValueVault futureValueVault;\n        uint256 minDebtUnitPrice;\n        uint256 defaultOrderBookMinDebtUnitPrice;\n    }\n\n    struct FutureValueVaultFunds {\n        int256 genesisValue;\n        int256 presentValue;\n        int256 futureValue;\n    }\n\n    struct InactiveBorrowOrdersFunds {\n        int256 genesisValue;\n        int256 presentValue;\n        int256 futureValue;\n        uint256 workingOrdersAmount;\n        uint256 borrowedAmount;\n    }\n\n    struct InactiveLendOrdersFunds {\n        int256 genesisValue;\n        int256 presentValue;\n        int256 futureValue;\n        uint256 workingOrdersAmount;\n        uint256 lentAmount;\n    }\n\n    event OrderFilled(\n        address indexed taker,\n        bytes32 indexed ccy,\n        ProtocolTypes.Side side,\n        uint256 indexed maturity,\n        uint256 amount,\n        uint256 amountInFV,\n        uint256 feeInFV\n    );\n\n    event OrdersFilledInAsync(\n        address indexed taker,\n        bytes32 indexed ccy,\n        ProtocolTypes.Side side,\n        uint256 indexed maturity,\n        uint256 amount,\n        uint256 amountInFV\n    );\n\n    event OrderPartiallyFilled(\n        uint48 orderId,\n        address indexed maker,\n        bytes32 indexed ccy,\n        ProtocolTypes.Side side,\n        uint256 maturity,\n        uint256 amount,\n        uint256 amountInFV\n    );\n\n    event RedemptionExecuted(\n        address indexed user,\n        bytes32 indexed ccy,\n        uint256 indexed maturity,\n        uint256 amount\n    );\n\n    event RepaymentExecuted(\n        address indexed user,\n        bytes32 indexed ccy,\n        uint256 indexed maturity,\n        uint256 amount\n    );\n\n    event EmergencySettlementExecuted(address indexed user, uint256 amount);\n\n    /**\n     * @notice Converts the future value to the genesis value if there is balance in the past maturity.\n     * @param _ccy Currency for pausing all lending markets\n     * @param _user User's address\n     * @return Current future value amount after update\n     */\n    function convertFutureValueToGenesisValue(\n        bytes32 _ccy,\n        uint8 _orderBookId,\n        uint256 _maturity,\n        address _user\n    ) public returns (int256) {\n        address futureValueVault = Storage.slot().futureValueVaults[_ccy];\n        bool isAutoRolled = AddressResolverLib.genesisValueVault().isAutoRolled(_ccy, _maturity);\n\n        if (!isAutoRolled) {\n            (int256 amount, ) = IFutureValueVault(futureValueVault).getBalance(_orderBookId, _user);\n            return amount;\n        }\n\n        (\n            int256 removedAmount,\n            int256 currentAmount,\n            uint256 basisMaturity,\n            bool isAllRemoved\n        ) = IFutureValueVault(futureValueVault).reset(_orderBookId, _user);\n\n        if (removedAmount != 0) {\n            // Overwrite the `removedAmount` with the unsettled amount left of the Genesis Value\n            // to handle the fractional amount generated by the lazy evaluation when the user is\n            // the last user who is converting their own future value in the selected maturity.\n            if (isAllRemoved && Storage.slot().pendingOrderAmounts[_ccy][basisMaturity] == 0) {\n                AddressResolverLib.genesisValueVault().updateGenesisValueWithResidualAmount(\n                    _ccy,\n                    _user,\n                    basisMaturity\n                );\n            } else {\n                AddressResolverLib.genesisValueVault().updateGenesisValueWithFutureValue(\n                    _ccy,\n                    _user,\n                    basisMaturity,\n                    removedAmount\n                );\n            }\n        }\n\n        return currentAmount;\n    }\n\n    function updateFunds(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        ProtocolTypes.Side _side,\n        uint256 _filledAmount,\n        uint256 _filledAmountInFV,\n        uint256 _feeInFV\n    ) external {\n        address futureValueVault = Storage.slot().futureValueVaults[_ccy];\n        uint8 orderBookId = Storage.slot().maturityOrderBookIds[_ccy][_maturity];\n\n        if (_side == ProtocolTypes.Side.BORROW) {\n            AddressResolverLib.tokenVault().addDepositAmount(_user, _ccy, _filledAmount);\n            IFutureValueVault(futureValueVault).decrease(\n                orderBookId,\n                _user,\n                _filledAmountInFV + _feeInFV,\n                _maturity\n            );\n        } else {\n            AddressResolverLib.tokenVault().removeDepositAmount(_user, _ccy, _filledAmount);\n            IFutureValueVault(futureValueVault).increase(\n                orderBookId,\n                _user,\n                _filledAmountInFV - _feeInFV,\n                _maturity\n            );\n        }\n\n        if (_feeInFV > 0) {\n            address reserveFundAddr = address(AddressResolverLib.reserveFund());\n            IFutureValueVault(futureValueVault).increase(\n                orderBookId,\n                reserveFundAddr,\n                _feeInFV,\n                _maturity\n            );\n\n            registerCurrencyAndMaturity(_ccy, _maturity, reserveFundAddr);\n        }\n    }\n\n    function registerCurrencyAndMaturity(bytes32 _ccy, uint256 _maturity, address _user) public {\n        if (!Storage.slot().usedMaturities[_ccy][_user].contains(_maturity)) {\n            Storage.slot().usedMaturities[_ccy][_user].add(_maturity);\n\n            registerCurrency(_ccy, _user);\n        }\n    }\n\n    function registerCurrency(bytes32 _ccy, address _user) public {\n        if (!Storage.slot().usedCurrencies[_user].contains(_ccy)) {\n            Storage.slot().usedCurrencies[_user].add(_ccy);\n        }\n    }\n\n    function executeRedemption(bytes32 _ccy, uint256 _maturity, address _user) external {\n        if (\n            AddressResolverLib.currencyController().currencyExists(_ccy) ||\n            block.timestamp < _maturity + 1 weeks\n        ) revert NotRedemptionPeriod();\n\n        cleanUpFunds(_ccy, _user);\n\n        int256 amount = getActualFunds(_ccy, _maturity, _user, 0).futureValue;\n        if (amount <= 0) revert NoRedemptionAmount();\n\n        uint256 redemptionAmount = _resetFundsPerMaturity(_ccy, _maturity, _user, amount)\n            .toUint256();\n        AddressResolverLib.tokenVault().addDepositAmount(_user, _ccy, redemptionAmount);\n\n        emit RedemptionExecuted(_user, _ccy, _maturity, redemptionAmount);\n    }\n\n    function executeRepayment(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        uint256 _amount\n    ) public returns (uint256 repaymentAmount) {\n        if (\n            AddressResolverLib.currencyController().currencyExists(_ccy) ||\n            block.timestamp < _maturity\n        ) revert NotRepaymentPeriod();\n\n        cleanUpFunds(_ccy, _user);\n\n        int256 resetAmount = _amount == 0\n            ? getActualFunds(_ccy, _maturity, _user, 0).futureValue\n            : -_amount.toInt256();\n\n        if (resetAmount >= 0) revert NoRepaymentAmount();\n\n        repaymentAmount = (-_resetFundsPerMaturity(_ccy, _maturity, _user, resetAmount))\n            .toUint256();\n        AddressResolverLib.tokenVault().removeDepositAmount(_user, _ccy, repaymentAmount);\n\n        emit RepaymentExecuted(_user, _ccy, _maturity, repaymentAmount);\n    }\n\n    function executeEmergencySettlement(address _user) external {\n        if (Storage.slot().isRedeemed[_user]) revert AlreadyRedeemed();\n\n        int256 redemptionAmountInBaseCurrency;\n\n        bytes32[] memory currencies = Storage.slot().usedCurrencies[_user].values();\n\n        for (uint256 i; i < currencies.length; i++) {\n            bytes32 ccy = currencies[i];\n            // First, clean up future values and genesis values to redeem those amounts.\n            cleanUpFunds(ccy, _user);\n\n            int256 amountInCcy = _resetFundsPerCurrency(ccy, _user);\n            redemptionAmountInBaseCurrency += _convertToBaseCurrencyAtMarketTerminationPrice(\n                ccy,\n                amountInCcy\n            );\n        }\n\n        bytes32[] memory collateralCurrencies = AddressResolverLib\n            .tokenVault()\n            .getCollateralCurrencies();\n\n        for (uint256 i; i < collateralCurrencies.length; i++) {\n            int256 amountInCcy = AddressResolverLib\n                .tokenVault()\n                .executeForcedReset(_user, collateralCurrencies[i])\n                .toInt256();\n\n            redemptionAmountInBaseCurrency += _convertToBaseCurrencyAtMarketTerminationPrice(\n                collateralCurrencies[i],\n                amountInCcy\n            );\n        }\n\n        if (redemptionAmountInBaseCurrency > 0) {\n            uint256[] memory terminationCollateralRatios = new uint256[](\n                collateralCurrencies.length\n            );\n            uint256 terminationRatioTotal;\n\n            for (uint256 i; i < collateralCurrencies.length; i++) {\n                bytes32 ccy = collateralCurrencies[i];\n                terminationCollateralRatios[i] = Storage.slot().terminationCollateralRatios[ccy];\n                terminationRatioTotal += terminationCollateralRatios[i];\n            }\n\n            for (uint256 i; i < collateralCurrencies.length; i++) {\n                bytes32 ccy = collateralCurrencies[i];\n                uint256 addedAmount = _convertFromBaseCurrencyAtMarketTerminationPrice(\n                    ccy,\n                    (redemptionAmountInBaseCurrency.toUint256() * terminationCollateralRatios[i])\n                        .div(terminationRatioTotal)\n                );\n\n                AddressResolverLib.tokenVault().addDepositAmount(_user, ccy, addedAmount);\n            }\n        } else if (redemptionAmountInBaseCurrency < 0) {\n            revert InsufficientCollateral();\n        }\n\n        Storage.slot().isRedeemed[_user] = true;\n        emit EmergencySettlementExecuted(_user, redemptionAmountInBaseCurrency.toUint256());\n    }\n\n    function getActualFunds(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        uint256 _minDebtUnitPrice\n    ) public view returns (ActualFunds memory actualFunds) {\n        CalculateActualFundsVars memory vars;\n        vars.market = ILendingMarket(Storage.slot().lendingMarkets[_ccy]);\n        vars.futureValueVault = IFutureValueVault(Storage.slot().futureValueVaults[_ccy]);\n        vars.defaultOrderBookId = Storage.slot().orderBookIdLists[_ccy][0];\n        vars.minDebtUnitPrice = _minDebtUnitPrice;\n\n        if (_maturity == 0) {\n            vars.isTotal = true;\n            vars.orderBookId = vars.defaultOrderBookId;\n            vars.isDefaultMarket = true;\n        } else {\n            vars.isTotal = false;\n            vars.orderBookId = Storage.slot().maturityOrderBookIds[_ccy][_maturity];\n            vars.isDefaultMarket = vars.orderBookId == vars.defaultOrderBookId;\n        }\n        actualFunds.genesisValue = AddressResolverLib.genesisValueVault().getBalance(_ccy, _user);\n\n        vars.maturities = getUsedMaturities(_ccy, _user);\n\n        for (uint256 i; i < vars.maturities.length; i++) {\n            uint256 currentMaturity = vars.maturities[i];\n            uint8 currentOrderBookId = Storage.slot().maturityOrderBookIds[_ccy][currentMaturity];\n            bool isAutoRolled = AddressResolverLib.genesisValueVault().isAutoRolled(\n                _ccy,\n                currentMaturity\n            );\n\n            if (vars.isDefaultMarket || currentOrderBookId == vars.orderBookId) {\n                {\n                    bool isDefaultMarket = currentOrderBookId == vars.defaultOrderBookId;\n\n                    // Get current funds from Future Value Vault by lazy evaluations.\n                    FutureValueVaultFunds\n                        memory futureValueVaultFunds = _getFundsFromFutureValueVault(\n                            _ccy,\n                            _user,\n                            vars,\n                            currentOrderBookId,\n                            isDefaultMarket,\n                            isAutoRolled\n                        );\n                    // Get current funds from borrowing orders by lazy evaluations.\n                    InactiveBorrowOrdersFunds\n                        memory borrowOrdersFunds = _getFundsFromInactiveBorrowOrders(\n                            _ccy,\n                            _user,\n                            vars,\n                            currentOrderBookId,\n                            currentMaturity,\n                            isDefaultMarket,\n                            isAutoRolled\n                        );\n                    // Get current funds from lending orders by lazy evaluations.\n                    InactiveLendOrdersFunds\n                        memory lendOrdersFunds = _getFundsFromInactiveLendOrders(\n                            _ccy,\n                            _user,\n                            vars,\n                            currentOrderBookId,\n                            isDefaultMarket,\n                            isAutoRolled\n                        );\n\n                    // Set genesis value.\n                    actualFunds.genesisValue +=\n                        futureValueVaultFunds.genesisValue -\n                        borrowOrdersFunds.genesisValue +\n                        lendOrdersFunds.genesisValue;\n\n                    // Set present value.\n                    int256 presentValue = futureValueVaultFunds.presentValue -\n                        borrowOrdersFunds.presentValue +\n                        lendOrdersFunds.presentValue;\n                    int256 futureValue = futureValueVaultFunds.futureValue -\n                        borrowOrdersFunds.futureValue +\n                        lendOrdersFunds.futureValue;\n\n                    actualFunds.presentValue += presentValue;\n\n                    if (isDefaultMarket) {\n                        vars.presentValueOfDefaultMarket = presentValue;\n                    }\n\n                    if (presentValue > 0) {\n                        actualFunds.claimableAmount += presentValue.toUint256();\n                    } else if (presentValue < 0) {\n                        actualFunds.debtAmount += (-presentValue).toUint256();\n                    }\n\n                    // Set future value.\n                    // Note: When calculating total funds, total future value will be 0 because different maturities can not be added.\n                    if (!vars.isTotal) {\n                        actualFunds.futureValue += futureValue;\n                    }\n\n                    actualFunds.workingBorrowOrdersAmount += borrowOrdersFunds.workingOrdersAmount;\n                    actualFunds.workingLendOrdersAmount += lendOrdersFunds.workingOrdersAmount;\n                    actualFunds.borrowedAmount += borrowOrdersFunds.borrowedAmount;\n                    actualFunds.lentAmount += lendOrdersFunds.lentAmount;\n                }\n\n                // Get balance fluctuation amount by auto-rolls\n                if (actualFunds.genesisValue < 0) {\n                    actualFunds.genesisValue += AddressResolverLib\n                        .genesisValueVault()\n                        .calculateBalanceFluctuationByAutoRolls(\n                            _ccy,\n                            actualFunds.genesisValue,\n                            vars.maturities[i],\n                            i == vars.maturities.length - 1 ? 0 : vars.maturities[i + 1]\n                        );\n                }\n            }\n        }\n\n        // Add GV to PV & FV if the market is that the lending position is rolled to.\n        if (vars.isDefaultMarket && actualFunds.genesisValue != 0) {\n            int256 futureValue = AddressResolverLib.genesisValueVault().calculateFVFromGV(\n                _ccy,\n                0,\n                actualFunds.genesisValue\n            );\n\n            uint256 unitPrice = _getDefaultOrderBookMarketUnitPrice(vars);\n            uint256 defaultOrderBookMinDebtUnitPrice = _getDefaultOrderBookMinDebtUnitPrice(vars);\n\n            int256 presentValue = _calculatePVFromFV(\n                futureValue,\n                unitPrice >= defaultOrderBookMinDebtUnitPrice\n                    ? unitPrice\n                    : defaultOrderBookMinDebtUnitPrice\n            );\n\n            actualFunds.presentValue += presentValue;\n            actualFunds.genesisValueInPV = presentValue;\n\n            // Add GV to the claimable amount or debt amount.\n            // Before that, offset the present value of the default market and the genesis value in addition.\n            if (presentValue > 0) {\n                if (vars.presentValueOfDefaultMarket < 0) {\n                    int256 offsetAmount = presentValue > -vars.presentValueOfDefaultMarket\n                        ? -vars.presentValueOfDefaultMarket\n                        : presentValue;\n                    actualFunds.debtAmount -= (offsetAmount).toUint256();\n                    presentValue -= offsetAmount;\n                }\n\n                actualFunds.claimableAmount += presentValue.toUint256();\n            } else if (presentValue < 0) {\n                if (vars.presentValueOfDefaultMarket > 0) {\n                    int256 offsetAmount = -presentValue > vars.presentValueOfDefaultMarket\n                        ? vars.presentValueOfDefaultMarket\n                        : -presentValue;\n\n                    actualFunds.claimableAmount -= (offsetAmount).toUint256();\n                    presentValue += offsetAmount;\n                }\n\n                actualFunds.debtAmount += (-presentValue).toUint256();\n            }\n\n            actualFunds.genesisValueInFV = futureValue;\n            if (!vars.isTotal) {\n                actualFunds.futureValue += futureValue;\n            }\n        }\n    }\n\n    function getCurrentMinDebtUnitPrice(\n        uint256 _maturity,\n        uint256 _minDebtUnitPrice\n    ) public view returns (uint256) {\n        if (_minDebtUnitPrice == 0) return 0;\n\n        return\n            _maturity > block.timestamp\n                ? BASE_MIN_DEBT_UNIT_PRICE -\n                    ((BASE_MIN_DEBT_UNIT_PRICE - _minDebtUnitPrice) *\n                        (_maturity - block.timestamp)) /\n                    Constants.SECONDS_IN_YEAR\n                : BASE_MIN_DEBT_UNIT_PRICE;\n    }\n\n    function calculateFunds(\n        bytes32 _ccy,\n        address _user,\n        ILendingMarketController.AdditionalFunds memory _additionalFunds,\n        uint256 _liquidationThresholdRate\n    ) public view returns (ILendingMarketController.CalculatedFunds memory funds) {\n        ActualFunds memory actualFunds = getActualFunds(\n            _ccy,\n            0,\n            _user,\n            Storage.slot().minDebtUnitPrices[_ccy]\n        );\n\n        funds.workingLendOrdersAmount =\n            actualFunds.workingLendOrdersAmount +\n            _additionalFunds.workingLendOrdersAmount;\n        funds.claimableAmount = actualFunds.claimableAmount + _additionalFunds.claimableAmount;\n        funds.lentAmount = actualFunds.lentAmount + _additionalFunds.lentAmount;\n        funds.workingBorrowOrdersAmount =\n            actualFunds.workingBorrowOrdersAmount +\n            _additionalFunds.workingBorrowOrdersAmount;\n        funds.debtAmount = actualFunds.debtAmount + _additionalFunds.debtAmount;\n        funds.borrowedAmount = actualFunds.borrowedAmount + _additionalFunds.borrowedAmount;\n\n        if (funds.claimableAmount > 0) {\n            // If the debt and claimable amount are in the same currency, the claimable amount can be allocated\n            // as collateral up to the amount that the liquidation threshold is reached.\n            // For calculation purposes, the working amount for borrowing orders is treated as potential debt in addition.\n            uint256 maxAllocableCollateralAmountInSameCcy = ((funds.debtAmount +\n                funds.workingBorrowOrdersAmount) * _liquidationThresholdRate).div(\n                    Constants.PCT_DIGIT\n                );\n\n            // If the claimable amount is over the allocable amount as collateral, the over amount is used as collateral\n            // for the other currency after being multiplied by a haircut.\n            if (funds.claimableAmount > maxAllocableCollateralAmountInSameCcy) {\n                uint256 haircut = AddressResolverLib.currencyController().getHaircut(_ccy);\n                funds.unallocatedCollateralAmount =\n                    funds.claimableAmount -\n                    maxAllocableCollateralAmountInSameCcy;\n                funds.collateralAmount =\n                    maxAllocableCollateralAmountInSameCcy +\n                    (haircut * (funds.unallocatedCollateralAmount)).div(Constants.PCT_DIGIT);\n            } else {\n                funds.collateralAmount = funds.claimableAmount;\n                funds.unallocatedCollateralAmount = 0;\n            }\n        }\n    }\n\n    function calculateTotalFundsInBaseCurrency(\n        address _user,\n        ILendingMarketController.AdditionalFunds calldata _additionalFunds,\n        uint256 _liquidationThresholdRate\n    ) external view returns (ILendingMarketController.CalculatedTotalFunds memory totalFunds) {\n        EnumerableSet.Bytes32Set storage currencySet = Storage.slot().usedCurrencies[_user];\n        CalculatedTotalFundInBaseCurrencyVars memory vars;\n\n        if (\n            !currencySet.contains(_additionalFunds.ccy) &&\n            AddressResolverLib.currencyController().currencyExists(_additionalFunds.ccy)\n        ) {\n            uint256 length = currencySet.length();\n            vars.ccys = new bytes32[](length + 1);\n            for (uint256 i; i < length; i++) {\n                vars.ccys[i] = currencySet.at(i);\n            }\n            vars.ccys[length] = _additionalFunds.ccy;\n        } else {\n            vars.ccys = currencySet.values();\n        }\n\n        vars.user = _user;\n        vars.additionalFunds = _additionalFunds;\n        vars.liquidationThresholdRate = _liquidationThresholdRate;\n        vars.isCollateral = AddressResolverLib.tokenVault().isCollateral(vars.ccys);\n\n        // Calculate total funds from the user's order list\n        for (uint256 i; i < vars.ccys.length; i++) {\n            // bytes32 ccy = vars.ccys[i];\n            ILendingMarketController.AdditionalFunds memory additionalFunds;\n\n            if (vars.ccys[i] == vars.additionalFunds.ccy) {\n                additionalFunds = vars.additionalFunds;\n            }\n\n            uint256[] memory amounts = new uint256[](7);\n\n            // 0: workingLendOrdersAmount\n            // 1: claimableAmount\n            // 2: collateralAmount\n            // 3: lentAmount\n            // 4: workingBorrowOrdersAmount\n            // 5: debtAmount\n            // 6: borrowedAmount\n            ILendingMarketController.CalculatedFunds memory funds = calculateFunds(\n                vars.ccys[i],\n                vars.user,\n                additionalFunds,\n                vars.liquidationThresholdRate\n            );\n\n            amounts[0] = funds.workingLendOrdersAmount;\n            amounts[1] = funds.claimableAmount;\n            amounts[2] = funds.collateralAmount;\n            amounts[3] = funds.lentAmount;\n            amounts[4] = funds.workingBorrowOrdersAmount;\n            amounts[5] = funds.debtAmount;\n            amounts[6] = funds.borrowedAmount;\n\n            if (vars.ccys[i] == vars.additionalFunds.ccy) {\n                // plusDepositAmount: borrowedAmount\n                // minusDepositAmount: workingLendOrdersAmount + lentAmount\n                totalFunds.plusDepositAmountInAdditionalFundsCcy += amounts[6];\n                totalFunds.minusDepositAmountInAdditionalFundsCcy += amounts[0] + amounts[3];\n            }\n\n            uint256[] memory amountsInBaseCurrency = AddressResolverLib\n                .currencyController()\n                .convertToBaseCurrency(vars.ccys[i], amounts);\n\n            totalFunds.claimableAmount += amountsInBaseCurrency[1];\n            totalFunds.collateralAmount += amountsInBaseCurrency[2];\n            totalFunds.workingBorrowOrdersAmount += amountsInBaseCurrency[4];\n            totalFunds.debtAmount += amountsInBaseCurrency[5];\n\n            // NOTE: Lent amount and working lend orders amount are excluded here as they are not used\n            // for the collateral calculation.\n            // Those amounts need only to check whether there is enough deposit amount in the selected currency.\n            if (vars.isCollateral[i]) {\n                totalFunds.workingLendOrdersAmount += amountsInBaseCurrency[0];\n                totalFunds.lentAmount += amountsInBaseCurrency[3];\n                totalFunds.borrowedAmount += amountsInBaseCurrency[6];\n            }\n        }\n    }\n\n    function getUsedMaturities(\n        bytes32 _ccy,\n        address _user\n    ) public view returns (uint256[] memory maturities) {\n        maturities = Storage.slot().usedMaturities[_ccy][_user].values();\n        if (maturities.length > 0) {\n            maturities = QuickSort.sort(maturities);\n        }\n    }\n\n    function getGenesisValue(\n        bytes32 _ccy,\n        address _user\n    ) public view returns (int256 amount, int256 amountInPV, int256 amountInFV) {\n        FundManagementLogic.ActualFunds memory funds = getActualFunds(_ccy, 0, _user, 0);\n        amount = funds.genesisValue;\n        amountInPV = funds.genesisValueInPV;\n        amountInFV = funds.genesisValueInFV;\n    }\n\n    function getPosition(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user\n    ) public view returns (int256 presentValue, int256 futureValue) {\n        FundManagementLogic.ActualFunds memory funds = getActualFunds(_ccy, _maturity, _user, 0);\n        presentValue = funds.presentValue;\n        futureValue = funds.futureValue;\n    }\n\n    function cleanUpAllFunds(address _user) external {\n        bytes32[] memory currencies = Storage.slot().usedCurrencies[_user].values();\n        uint256 length = currencies.length;\n        for (uint256 i; i < length; i++) {\n            cleanUpFunds(currencies[i], _user);\n        }\n    }\n\n    function cleanUpFunds(\n        bytes32 _ccy,\n        address _user\n    ) public returns (uint256 totalActiveOrderCount) {\n        bool futureValueExists = false;\n        uint256[] memory maturities = getUsedMaturities(_ccy, _user);\n\n        for (uint256 i; i < maturities.length; i++) {\n            uint256 maturity = maturities[i];\n            uint8 orderBookId = Storage.slot().maturityOrderBookIds[_ccy][maturity];\n            uint256 activeOrderCount = _cleanUpOrders(_ccy, orderBookId, _user);\n            totalActiveOrderCount += activeOrderCount;\n\n            int256 currentFutureValue = convertFutureValueToGenesisValue(\n                _ccy,\n                orderBookId,\n                maturity,\n                _user\n            );\n\n            if (currentFutureValue != 0) {\n                futureValueExists = true;\n            }\n\n            if (currentFutureValue == 0 && activeOrderCount == 0) {\n                Storage.slot().usedMaturities[_ccy][_user].remove(maturity);\n            }\n\n            AddressResolverLib.genesisValueVault().cleanUpBalance(\n                _ccy,\n                _user,\n                i == maturities.length - 1 ? 0 : maturities[i + 1]\n            );\n        }\n\n        if (totalActiveOrderCount == 0) {\n            AddressResolverLib.tokenVault().cleanUpUsedCurrencies(_user, _ccy);\n\n            if (\n                !futureValueExists &&\n                AddressResolverLib.genesisValueVault().getBalance(_ccy, _user) == 0\n            ) {\n                Storage.slot().usedCurrencies[_user].remove(_ccy);\n            }\n        }\n    }\n\n    function _cleanUpOrders(\n        bytes32 _ccy,\n        uint8 _orderBookId,\n        address _user\n    ) internal returns (uint256 activeOrderCount) {\n        (\n            uint256 activeLendOrderCount,\n            uint256 activeBorrowOrderCount,\n            uint256 removedLendOrderFutureValue,\n            uint256 removedBorrowOrderFutureValue,\n            uint256 removedLendOrderAmount,\n            uint256 removedBorrowOrderAmount,\n            uint256 userCurrentMaturity\n        ) = ILendingMarket(Storage.slot().lendingMarkets[_ccy]).cleanUpOrders(_orderBookId, _user);\n\n        if (removedLendOrderAmount > removedBorrowOrderAmount) {\n            AddressResolverLib.tokenVault().removeDepositAmount(\n                _user,\n                _ccy,\n                removedLendOrderAmount - removedBorrowOrderAmount\n            );\n        } else if (removedLendOrderAmount < removedBorrowOrderAmount) {\n            AddressResolverLib.tokenVault().addDepositAmount(\n                _user,\n                _ccy,\n                removedBorrowOrderAmount - removedLendOrderAmount\n            );\n        }\n\n        if (removedLendOrderFutureValue > 0) {\n            IFutureValueVault(Storage.slot().futureValueVaults[_ccy]).increase(\n                _orderBookId,\n                _user,\n                removedLendOrderFutureValue,\n                userCurrentMaturity\n            );\n            emit OrdersFilledInAsync(\n                _user,\n                _ccy,\n                ProtocolTypes.Side.LEND,\n                userCurrentMaturity,\n                removedLendOrderAmount,\n                removedLendOrderFutureValue\n            );\n        }\n\n        if (removedBorrowOrderFutureValue > 0) {\n            IFutureValueVault(Storage.slot().futureValueVaults[_ccy]).decrease(\n                _orderBookId,\n                _user,\n                removedBorrowOrderFutureValue,\n                userCurrentMaturity\n            );\n            emit OrdersFilledInAsync(\n                _user,\n                _ccy,\n                ProtocolTypes.Side.BORROW,\n                userCurrentMaturity,\n                removedBorrowOrderAmount,\n                removedBorrowOrderFutureValue\n            );\n        }\n\n        Storage.slot().pendingOrderAmounts[_ccy][userCurrentMaturity] -=\n            removedLendOrderAmount +\n            removedBorrowOrderAmount;\n\n        activeOrderCount = activeLendOrderCount + activeBorrowOrderCount;\n    }\n\n    function _getFundsFromFutureValueVault(\n        bytes32 _ccy,\n        address _user,\n        CalculateActualFundsVars memory vars,\n        uint8 currentOrderBookId,\n        bool isDefaultMarket,\n        bool isAutoRolled\n    ) internal view returns (FutureValueVaultFunds memory funds) {\n        (int256 futureValueInMaturity, uint256 fvMaturity) = vars.futureValueVault.getBalance(\n            currentOrderBookId,\n            _user\n        );\n\n        if (futureValueInMaturity != 0) {\n            if (isAutoRolled) {\n                if (vars.isDefaultMarket) {\n                    funds.genesisValue = AddressResolverLib.genesisValueVault().calculateGVFromFV(\n                        _ccy,\n                        fvMaturity,\n                        futureValueInMaturity\n                    );\n                }\n            } else {\n                if (vars.isTotal && !isDefaultMarket) {\n                    uint256 unitPrice = _getDefaultOrderBookMarketUnitPrice(vars);\n\n                    (funds.presentValue, funds.futureValue) = _convertFVtoOtherMaturity(\n                        _ccy,\n                        vars.market,\n                        fvMaturity,\n                        futureValueInMaturity,\n                        unitPrice\n                    );\n\n                    if (funds.futureValue < 0) {\n                        uint256 defaultOrderBookMinDebtUnitPrice = _getDefaultOrderBookMinDebtUnitPrice(\n                                vars\n                            );\n\n                        if (unitPrice < defaultOrderBookMinDebtUnitPrice) {\n                            funds.presentValue = _calculatePVFromFV(\n                                funds.futureValue,\n                                defaultOrderBookMinDebtUnitPrice\n                            );\n                        }\n                    }\n                } else if (vars.isTotal || !vars.isDefaultMarket || isDefaultMarket) {\n                    uint256 unitPrice = vars.market.getMarketUnitPrice(vars.orderBookId);\n                    funds.futureValue = futureValueInMaturity;\n\n                    // Apply min debt unit price if the future value is negative (debt).\n                    if (funds.futureValue < 0) {\n                        uint256 currentMinDebtUnitPrice = getCurrentMinDebtUnitPrice(\n                            fvMaturity,\n                            vars.minDebtUnitPrice\n                        );\n\n                        funds.presentValue = _calculatePVFromFV(\n                            futureValueInMaturity,\n                            unitPrice < currentMinDebtUnitPrice\n                                ? currentMinDebtUnitPrice\n                                : unitPrice\n                        );\n                    } else {\n                        funds.presentValue = _calculatePVFromFV(futureValueInMaturity, unitPrice);\n                    }\n                }\n            }\n        }\n    }\n\n    function _getFundsFromInactiveBorrowOrders(\n        bytes32 _ccy,\n        address _user,\n        CalculateActualFundsVars memory vars,\n        uint8 currentOrderBookId,\n        uint256 currentMaturity,\n        bool isDefaultMarket,\n        bool isAutoRolled\n    ) internal view returns (InactiveBorrowOrdersFunds memory funds) {\n        uint256 filledFutureValue;\n        uint256 orderMaturity;\n        uint256 currentMinDebtUnitPrice = getCurrentMinDebtUnitPrice(\n            currentMaturity,\n            vars.minDebtUnitPrice\n        );\n\n        (funds.workingOrdersAmount, funds.borrowedAmount, filledFutureValue, orderMaturity) = vars\n            .market\n            .getTotalAmountFromBorrowOrders(currentOrderBookId, _user, currentMinDebtUnitPrice);\n\n        if (isAutoRolled) {\n            funds.workingOrdersAmount = 0;\n        }\n\n        if (filledFutureValue != 0) {\n            if (isAutoRolled) {\n                if (vars.isDefaultMarket) {\n                    funds.genesisValue = AddressResolverLib.genesisValueVault().calculateGVFromFV(\n                        _ccy,\n                        orderMaturity,\n                        filledFutureValue.toInt256()\n                    );\n                }\n            } else {\n                if (vars.isTotal && !isDefaultMarket) {\n                    uint256 unitPrice = _getDefaultOrderBookMarketUnitPrice(vars);\n\n                    (funds.presentValue, funds.futureValue) = _convertFVtoOtherMaturity(\n                        _ccy,\n                        vars.market,\n                        orderMaturity,\n                        filledFutureValue.toInt256(),\n                        unitPrice\n                    );\n\n                    uint256 defaultOrderBookMinDebtUnitPrice = _getDefaultOrderBookMinDebtUnitPrice(\n                        vars\n                    );\n\n                    if (unitPrice < defaultOrderBookMinDebtUnitPrice) {\n                        funds.presentValue = _calculatePVFromFV(\n                            funds.futureValue,\n                            defaultOrderBookMinDebtUnitPrice\n                        );\n                    }\n                } else if (vars.isTotal || !vars.isDefaultMarket || isDefaultMarket) {\n                    uint256 unitPrice = vars.market.getMarketUnitPrice(vars.orderBookId);\n\n                    funds.futureValue = filledFutureValue.toInt256();\n                    funds.presentValue = _calculatePVFromFV(\n                        funds.futureValue,\n                        unitPrice < currentMinDebtUnitPrice ? currentMinDebtUnitPrice : unitPrice\n                    );\n                }\n            }\n        }\n    }\n\n    function _getFundsFromInactiveLendOrders(\n        bytes32 _ccy,\n        address _user,\n        CalculateActualFundsVars memory vars,\n        uint8 currentOrderBookId,\n        bool isDefaultMarket,\n        bool isAutoRolled\n    ) internal view returns (InactiveLendOrdersFunds memory funds) {\n        uint256 filledFutureValue;\n        uint256 orderMaturity;\n        (funds.workingOrdersAmount, funds.lentAmount, filledFutureValue, orderMaturity) = vars\n            .market\n            .getTotalAmountFromLendOrders(currentOrderBookId, _user);\n\n        if (isAutoRolled) {\n            funds.workingOrdersAmount = 0;\n        }\n\n        if (filledFutureValue != 0) {\n            if (isAutoRolled) {\n                if (vars.isDefaultMarket) {\n                    funds.genesisValue = AddressResolverLib.genesisValueVault().calculateGVFromFV(\n                        _ccy,\n                        orderMaturity,\n                        filledFutureValue.toInt256()\n                    );\n                }\n            } else {\n                if (vars.isTotal && !isDefaultMarket) {\n                    (funds.presentValue, funds.futureValue) = _convertFVtoOtherMaturity(\n                        _ccy,\n                        vars.market,\n                        orderMaturity,\n                        filledFutureValue.toInt256(),\n                        vars.market.getMarketUnitPrice(vars.defaultOrderBookId)\n                    );\n                } else if (vars.isTotal || !vars.isDefaultMarket || isDefaultMarket) {\n                    funds.futureValue = filledFutureValue.toInt256();\n                    funds.presentValue = _calculatePVFromFV(\n                        vars.market,\n                        vars.orderBookId,\n                        funds.futureValue\n                    );\n                }\n            }\n        }\n    }\n\n    function _convertFVtoOtherMaturity(\n        bytes32 _ccy,\n        ILendingMarket _market,\n        uint256 _fromMaturity,\n        int256 _fromFutureValue,\n        uint256 _toUnitPrice\n    ) internal view returns (int256 presentValue, int256 futureValue) {\n        if (\n            AddressResolverLib.genesisValueVault().getAutoRollLog(_ccy, _fromMaturity).unitPrice ==\n            0\n        ) {\n            presentValue = _calculatePVFromFV(\n                _market,\n                Storage.slot().maturityOrderBookIds[_ccy][_fromMaturity],\n                _fromFutureValue\n            );\n            futureValue = _calculateFVFromPV(presentValue, _toUnitPrice);\n        } else {\n            futureValue = AddressResolverLib.genesisValueVault().calculateFVFromFV(\n                _ccy,\n                _fromMaturity,\n                0,\n                _fromFutureValue\n            );\n            presentValue = _calculatePVFromFV(futureValue, _toUnitPrice);\n        }\n    }\n\n    function calculatePVFromFV(\n        bytes32 _ccy,\n        uint256 _maturity,\n        int256 _futureValue\n    ) public view returns (int256 presentValue) {\n        presentValue = _calculatePVFromFV(\n            ILendingMarket(Storage.slot().lendingMarkets[_ccy]),\n            Storage.slot().maturityOrderBookIds[_ccy][_maturity],\n            _futureValue\n        );\n    }\n\n    function calculateFVFromPV(\n        bytes32 _ccy,\n        uint256 _maturity,\n        int256 _presentValue\n    ) public view returns (int256 futureValue) {\n        uint256 unitPrice = ILendingMarket(Storage.slot().lendingMarkets[_ccy]).getMarketUnitPrice(\n            Storage.slot().maturityOrderBookIds[_ccy][_maturity]\n        );\n        futureValue = _calculateFVFromPV(_presentValue, unitPrice);\n    }\n\n    function calculateFVFromPV(\n        bytes32 _ccy,\n        uint256 _maturity,\n        uint256 _presentValue\n    ) public view returns (uint256 futureValue) {\n        uint256 unitPrice = ILendingMarket(Storage.slot().lendingMarkets[_ccy]).getMarketUnitPrice(\n            Storage.slot().maturityOrderBookIds[_ccy][_maturity]\n        );\n        futureValue = _calculateFVFromPV(_presentValue, unitPrice);\n    }\n\n    function _convertToBaseCurrencyAtMarketTerminationPrice(\n        bytes32 _ccy,\n        int256 _amount\n    ) internal view returns (int256) {\n        TerminationCurrencyCache memory currency = Storage.slot().terminationCurrencyCaches[_ccy];\n        return (_amount * currency.price).div((10 ** currency.decimals).toInt256());\n    }\n\n    function _convertFromBaseCurrencyAtMarketTerminationPrice(\n        bytes32 _ccy,\n        uint256 _amount\n    ) internal view returns (uint256) {\n        TerminationCurrencyCache memory currency = Storage.slot().terminationCurrencyCaches[_ccy];\n        return (_amount * 10 ** currency.decimals).div(currency.price.toUint256());\n    }\n\n    function _resetFundsPerCurrency(bytes32 _ccy, address _user) internal returns (int256 amount) {\n        amount = getActualFunds(_ccy, 0, _user, 0).presentValue;\n\n        uint256[] memory maturities = Storage.slot().usedMaturities[_ccy][_user].values();\n        for (uint256 j; j < maturities.length; j++) {\n            IFutureValueVault(Storage.slot().futureValueVaults[_ccy]).executeForcedReset(\n                Storage.slot().maturityOrderBookIds[_ccy][maturities[j]],\n                _user\n            );\n        }\n\n        AddressResolverLib.genesisValueVault().executeForcedReset(_ccy, _user);\n\n        Storage.slot().usedCurrencies[_user].remove(_ccy);\n    }\n\n    function _resetFundsPerMaturity(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        int256 _amount\n    ) internal returns (int256 totalRemovedAmount) {\n        int256 currentFVAmount;\n        int256 currentGVAmount;\n\n        (totalRemovedAmount, currentFVAmount) = IFutureValueVault(\n            Storage.slot().futureValueVaults[_ccy]\n        ).executeForcedReset(Storage.slot().maturityOrderBookIds[_ccy][_maturity], _user, _amount);\n\n        int256 remainingAmount = _amount - totalRemovedAmount;\n\n        bool isDefaultMarket = Storage.slot().maturityOrderBookIds[_ccy][_maturity] ==\n            Storage.slot().orderBookIdLists[_ccy][0];\n\n        if (isDefaultMarket && remainingAmount != 0) {\n            int256 removedAmount;\n            (removedAmount, currentGVAmount) = AddressResolverLib\n                .genesisValueVault()\n                .executeForcedReset(_ccy, _maturity, _user, remainingAmount);\n            totalRemovedAmount += removedAmount;\n        }\n\n        if (currentFVAmount == 0 && currentGVAmount == 0) {\n            Storage.slot().usedMaturities[_ccy][_user].remove(_maturity);\n\n            if (Storage.slot().usedMaturities[_ccy][_user].length() == 0) {\n                Storage.slot().usedCurrencies[_user].remove(_ccy);\n            }\n        }\n    }\n\n    function _getDefaultOrderBookMinDebtUnitPrice(\n        CalculateActualFundsVars memory vars\n    ) private view returns (uint256) {\n        if (vars.defaultOrderBookMinDebtUnitPrice == 0 && vars.minDebtUnitPrice != 0) {\n            vars.defaultOrderBookMinDebtUnitPrice = getCurrentMinDebtUnitPrice(\n                vars.market.getMaturity(vars.defaultOrderBookId),\n                vars.minDebtUnitPrice\n            );\n        }\n\n        return vars.defaultOrderBookMinDebtUnitPrice;\n    }\n\n    function _getDefaultOrderBookMarketUnitPrice(\n        CalculateActualFundsVars memory vars\n    ) private view returns (uint256) {\n        if (vars.defaultOrderBookMarketUnitPrice == 0) {\n            vars.defaultOrderBookMarketUnitPrice = vars.market.getMarketUnitPrice(\n                vars.defaultOrderBookId\n            );\n        }\n\n        return vars.defaultOrderBookMarketUnitPrice;\n    }\n\n    function _calculatePVFromFV(\n        ILendingMarket _market,\n        uint8 _orderBookId,\n        int256 _futureValue\n    ) internal view returns (int256 presentValue) {\n        uint256 unitPrice = _market.getMarketUnitPrice(_orderBookId);\n        presentValue = _calculatePVFromFV(_futureValue, unitPrice);\n    }\n\n    function _calculatePVFromFV(\n        int256 _futureValue,\n        uint256 _unitPrice\n    ) internal pure returns (int256) {\n        uint256 unitPrice = _unitPrice == 0 ? Constants.PRICE_DIGIT : _unitPrice;\n        // NOTE: The formula is: presentValue = futureValue * unitPrice.\n        return (_futureValue * unitPrice.toInt256()).div(Constants.PRICE_DIGIT.toInt256());\n    }\n\n    function _calculateFVFromPV(\n        int256 _presentValue,\n        uint256 _unitPrice\n    ) internal pure returns (int256) {\n        uint256 unitPrice = _unitPrice == 0 ? Constants.PRICE_DIGIT : _unitPrice;\n        // NOTE: The formula is: futureValue = presentValue / unitPrice.\n        return (_presentValue * Constants.PRICE_DIGIT.toInt256()).div(unitPrice.toInt256());\n    }\n\n    function _calculateFVFromPV(\n        uint256 _presentValue,\n        uint256 _unitPrice\n    ) internal pure returns (uint256) {\n        uint256 unitPrice = _unitPrice == 0 ? Constants.PRICE_DIGIT : _unitPrice;\n        // NOTE: The formula is: futureValue = presentValue / unitPrice.\n        return (_presentValue * Constants.PRICE_DIGIT).div(unitPrice);\n    }\n}\n"
      },
      "contracts/protocol/libraries/logics/LendingMarketOperationLogic.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\n// dependencies\nimport {IERC20} from \"../../../dependencies/openzeppelin/token/ERC20/IERC20.sol\";\nimport {IERC20Metadata} from \"../../../dependencies/openzeppelin/token/ERC20/extensions/IERC20Metadata.sol\";\nimport {SafeCast} from \"../../../dependencies/openzeppelin/utils/math/SafeCast.sol\";\nimport {Strings} from \"../../../dependencies/openzeppelin/utils/Strings.sol\";\n// interfaces\nimport {ILendingMarket} from \"../../interfaces/ILendingMarket.sol\";\nimport {ILendingMarketController} from \"../../interfaces/ILendingMarketController.sol\";\nimport {IFutureValueVault} from \"../../interfaces/IFutureValueVault.sol\";\nimport {AutoRollLog} from \"../../interfaces/IGenesisValueVault.sol\";\n// libraries\nimport {AddressResolverLib} from \"../AddressResolverLib.sol\";\nimport {BokkyPooBahsDateTimeLibrary as TimeLibrary} from \"../BokkyPooBahsDateTimeLibrary.sol\";\nimport {Constants} from \"../Constants.sol\";\nimport {FilledOrder, PartiallyFilledOrder} from \"../OrderBookLib.sol\";\nimport {RoundingUint256} from \"../math/RoundingUint256.sol\";\nimport {RoundingInt256} from \"../math/RoundingInt256.sol\";\n// types\nimport {ProtocolTypes} from \"../../types/ProtocolTypes.sol\";\n// storages\nimport {LendingMarketControllerStorage as Storage, ZCTokenInfo, TerminationCurrencyCache, ObservationPeriodLog} from \"../../storages/LendingMarketControllerStorage.sol\";\n\nlibrary LendingMarketOperationLogic {\n    using SafeCast for uint256;\n    using RoundingUint256 for uint256;\n    using SafeCast for uint256;\n    using RoundingInt256 for int256;\n\n    uint256 public constant OBSERVATION_PERIOD = 6 hours;\n    uint8 public constant COMPOUND_FACTOR_DECIMALS = 18;\n    uint8 public constant ZC_TOKEN_BASE_DECIMALS = 26;\n    uint256 public constant PRE_ORDER_BASE_PERIOD = 7 days;\n\n    error InvalidCompoundFactor();\n    error TooManyTokenDecimals(address tokenAddress, uint8 decimals);\n    error InvalidCurrency();\n    error InvalidOpeningDate();\n    error InvalidPreOpeningDate();\n    error InvalidTimestamp();\n    error InvalidMinDebtUnitPrice();\n    error LendingMarketNotInitialized();\n    error NotEnoughOrderBooks();\n    error AlreadyZCTokenExists(address tokenAddress);\n    error InvalidMaturity(uint256 maturity);\n    error ZcTokenIsZero();\n\n    event LendingMarketInitialized(\n        bytes32 indexed ccy,\n        uint256 genesisDate,\n        uint256 compoundFactor,\n        uint256 orderFeeRate,\n        uint256 circuitBreakerLimitRange,\n        address lendingMarket,\n        address futureValueVault\n    );\n\n    event MinDebtUnitPriceUpdated(bytes32 indexed ccy, uint256 minDebtUnitPrice);\n\n    event OrderBookCreated(\n        bytes32 indexed ccy,\n        uint8 indexed orderBookId,\n        uint256 openingDate,\n        uint256 preOpeningDate,\n        uint256 maturity\n    );\n\n    event OrderBooksRotated(bytes32 ccy, uint256 oldMaturity, uint256 newMaturity);\n    event EmergencyTerminationExecuted(uint256 timestamp);\n\n    event ZCTokenCreated(\n        bytes32 indexed ccy,\n        uint256 indexed maturity,\n        string name,\n        string symbol,\n        uint8 decimals,\n        address tokenAddress\n    );\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        if (!AddressResolverLib.currencyController().currencyExists(_ccy)) {\n            revert InvalidCurrency();\n        }\n\n        if (_compoundFactor == 0) revert InvalidCompoundFactor();\n\n        address tokenAddress = AddressResolverLib.tokenVault().getTokenAddress(_ccy);\n        uint8 tokenDecimals = IERC20Metadata(tokenAddress).decimals();\n\n        if (tokenDecimals > COMPOUND_FACTOR_DECIMALS + ZC_TOKEN_BASE_DECIMALS) {\n            revert TooManyTokenDecimals(tokenAddress, tokenDecimals);\n        }\n\n        AddressResolverLib.genesisValueVault().initializeCurrencySetting(\n            _ccy,\n            COMPOUND_FACTOR_DECIMALS + ZC_TOKEN_BASE_DECIMALS - tokenDecimals,\n            _compoundFactor,\n            calculateNextMaturity(_genesisDate, Storage.slot().marketBasePeriod)\n        );\n\n        address lendingMarket = AddressResolverLib.beaconProxyController().deployLendingMarket(\n            _ccy,\n            _orderFeeRate,\n            _circuitBreakerLimitRange\n        );\n        address futureValueVault = AddressResolverLib\n            .beaconProxyController()\n            .deployFutureValueVault();\n\n        Storage.slot().genesisDates[_ccy] = _genesisDate;\n        Storage.slot().lendingMarkets[_ccy] = lendingMarket;\n        Storage.slot().futureValueVaults[_ccy] = futureValueVault;\n\n        updateMinDebtUnitPrice(_ccy, _minDebtUnitPrice);\n        createZCToken(_ccy, 0, tokenAddress);\n\n        emit LendingMarketInitialized(\n            _ccy,\n            _genesisDate,\n            _compoundFactor,\n            _orderFeeRate,\n            _circuitBreakerLimitRange,\n            lendingMarket,\n            futureValueVault\n        );\n    }\n\n    function updateMinDebtUnitPrice(bytes32 _ccy, uint256 _minDebtUnitPrice) public {\n        if (_minDebtUnitPrice > Constants.PRICE_DIGIT) revert InvalidMinDebtUnitPrice();\n\n        Storage.slot().minDebtUnitPrices[_ccy] = _minDebtUnitPrice;\n        emit MinDebtUnitPriceUpdated(_ccy, _minDebtUnitPrice);\n    }\n\n    function createOrderBook(bytes32 _ccy, uint256 _openingDate, uint256 _preOpeningDate) public {\n        if (!AddressResolverLib.genesisValueVault().isInitialized(_ccy)) {\n            revert LendingMarketNotInitialized();\n        }\n        if (!AddressResolverLib.currencyController().currencyExists(_ccy)) {\n            revert InvalidCurrency();\n        }\n        if (_preOpeningDate > _openingDate) revert InvalidPreOpeningDate();\n\n        ILendingMarket market = ILendingMarket(Storage.slot().lendingMarkets[_ccy]);\n\n        uint256[] memory maturities = market.getMaturities(Storage.slot().orderBookIdLists[_ccy]);\n        uint256 newMaturity;\n\n        if (maturities.length == 0) {\n            newMaturity = AddressResolverLib.genesisValueVault().getCurrentMaturity(_ccy);\n        } else {\n            uint256 lastMaturity = maturities[maturities.length - 1];\n            newMaturity = calculateNextMaturity(lastMaturity, Storage.slot().marketBasePeriod);\n        }\n\n        if (_openingDate >= newMaturity) revert InvalidOpeningDate();\n\n        uint8 orderBookId = market.createOrderBook(newMaturity, _openingDate, _preOpeningDate);\n\n        Storage.slot().orderBookIdLists[_ccy].push(orderBookId);\n        Storage.slot().maturityOrderBookIds[_ccy][newMaturity] = orderBookId;\n\n        address tokenAddress = AddressResolverLib.tokenVault().getTokenAddress(_ccy);\n        createZCToken(_ccy, newMaturity, tokenAddress);\n\n        emit OrderBookCreated(_ccy, orderBookId, _openingDate, _preOpeningDate, newMaturity);\n    }\n\n    function executeItayoseCall(\n        bytes32 _ccy,\n        uint256 _maturity\n    )\n        external\n        returns (\n            PartiallyFilledOrder memory partiallyFilledLendingOrder,\n            PartiallyFilledOrder memory partiallyFilledBorrowingOrder\n        )\n    {\n        ILendingMarket market = ILendingMarket(Storage.slot().lendingMarkets[_ccy]);\n        uint8 orderBookId = Storage.slot().maturityOrderBookIds[_ccy][_maturity];\n        uint256 openingUnitPrice;\n        uint256 openingDate;\n        uint256 totalOffsetAmount;\n\n        (\n            openingUnitPrice,\n            totalOffsetAmount,\n            openingDate,\n            partiallyFilledLendingOrder,\n            partiallyFilledBorrowingOrder\n        ) = market.executeItayoseCall(orderBookId);\n\n        // Updates the pending order amount for both side orders.\n        // Since the partially filled orders are updated with `updateFundsForMaker()`,\n        // their amount is subtracted from `pendingOrderAmounts`.\n        Storage.slot().pendingOrderAmounts[_ccy][_maturity] +=\n            (totalOffsetAmount * 2) -\n            partiallyFilledLendingOrder.amount -\n            partiallyFilledBorrowingOrder.amount;\n\n        // Save the openingUnitPrice as first compound factor\n        // if it is a first Itayose call at the nearest market.\n        if (openingUnitPrice > 0 && Storage.slot().orderBookIdLists[_ccy][0] == orderBookId) {\n            // Convert the openingUnitPrice determined by Itayose to the unit price on the Genesis Date.\n            uint256 convertedUnitPrice = _convertUnitPrice(\n                openingUnitPrice,\n                _maturity,\n                openingDate,\n                Storage.slot().genesisDates[_ccy]\n            );\n\n            AddressResolverLib.genesisValueVault().updateInitialCompoundFactor(\n                _ccy,\n                convertedUnitPrice\n            );\n        }\n    }\n\n    function rotateOrderBooks(bytes32 _ccy) external {\n        // NOTE: Before the contract upgrade, the ZCToken did not exist, but the upgrade added it.\n        // This check is to prevent the error that occurs when the ZCToken is not created by the\n        // `migrateLendingMarket` function. After ZCTokens are added for all currencies and maturities,\n        // this check can be removed.\n        if (Storage.slot().zcTokens[_ccy][0] == address(0)) {\n            revert ZcTokenIsZero();\n        }\n\n        if (!AddressResolverLib.currencyController().currencyExists(_ccy)) {\n            revert InvalidCurrency();\n        }\n\n        uint8[] storage orderBookIds = Storage.slot().orderBookIdLists[_ccy];\n\n        if (orderBookIds.length < 2) revert NotEnoughOrderBooks();\n\n        ILendingMarket market = ILendingMarket(Storage.slot().lendingMarkets[_ccy]);\n        uint256[] memory maturities = market.getMaturities(orderBookIds);\n\n        uint8 maturedOrderBookId = orderBookIds[0];\n        uint8 destinationOrderBookId = orderBookIds[1];\n        uint256 maturedOrderBookMaturity = maturities[0];\n        uint256 destinationOrderBookMaturity = maturities[1];\n\n        uint256 newMaturity = calculateNextMaturity(\n            maturities[maturities.length - 1],\n            Storage.slot().marketBasePeriod\n        );\n\n        // Delete the matured order book from the list\n        for (uint256 i; i < orderBookIds.length - 1; i++) {\n            orderBookIds[i] = orderBookIds[i + 1];\n        }\n        orderBookIds.pop();\n\n        uint256 autoRollUnitPrice = _calculateAutoRollUnitPrice(\n            _ccy,\n            maturedOrderBookMaturity,\n            destinationOrderBookMaturity,\n            destinationOrderBookId,\n            market\n        );\n\n        market.executeAutoRoll(maturedOrderBookId, destinationOrderBookId, autoRollUnitPrice);\n\n        createOrderBook(\n            _ccy,\n            destinationOrderBookMaturity,\n            destinationOrderBookMaturity - PRE_ORDER_BASE_PERIOD\n        );\n\n        AddressResolverLib.genesisValueVault().executeAutoRoll(\n            _ccy,\n            maturedOrderBookMaturity,\n            destinationOrderBookMaturity,\n            autoRollUnitPrice,\n            market.getOrderFeeRate()\n        );\n\n        emit OrderBooksRotated(_ccy, maturedOrderBookMaturity, newMaturity);\n    }\n\n    function executeEmergencyTermination() external {\n        Storage.slot().terminationDate = block.timestamp;\n\n        bytes32[] memory currencies = AddressResolverLib.currencyController().getCurrencies();\n        bytes32[] memory collateralCurrencies = AddressResolverLib\n            .tokenVault()\n            .getCollateralCurrencies();\n\n        for (uint256 i; i < currencies.length; i++) {\n            bytes32 ccy = currencies[i];\n\n            Storage.slot().terminationCurrencyCaches[ccy] = TerminationCurrencyCache({\n                price: AddressResolverLib.currencyController().getAggregatedLastPrice(ccy),\n                decimals: AddressResolverLib.currencyController().getDecimals(ccy)\n            });\n        }\n\n        for (uint256 i; i < collateralCurrencies.length; i++) {\n            bytes32 ccy = collateralCurrencies[i];\n            address tokenAddress = AddressResolverLib.tokenVault().getTokenAddress(ccy);\n            uint256 balance = IERC20(tokenAddress).balanceOf(\n                address(AddressResolverLib.tokenVault())\n            );\n\n            Storage.slot().terminationCollateralRatios[ccy] = AddressResolverLib\n                .currencyController()\n                .convertToBaseCurrency(ccy, balance);\n        }\n\n        emit EmergencyTerminationExecuted(block.timestamp);\n    }\n\n    function pauseLendingMarket(bytes32 _ccy) public {\n        ILendingMarket market = ILendingMarket(Storage.slot().lendingMarkets[_ccy]);\n        market.pause();\n    }\n\n    function unpauseLendingMarket(bytes32 _ccy) public {\n        ILendingMarket market = ILendingMarket(Storage.slot().lendingMarkets[_ccy]);\n        market.unpause();\n    }\n\n    function updateOrderLogs(\n        bytes32 _ccy,\n        uint256 _maturity,\n        uint256 _filledAmount,\n        uint256 _filledFutureValue\n    ) external {\n        uint8 orderBookId = Storage.slot().maturityOrderBookIds[_ccy][_maturity];\n\n        if (Storage.slot().orderBookIdLists[_ccy][1] == orderBookId) {\n            uint256 nearestMaturity = ILendingMarket(Storage.slot().lendingMarkets[_ccy])\n                .getMaturity(Storage.slot().orderBookIdLists[_ccy][0]);\n\n            if (\n                (block.timestamp < nearestMaturity) &&\n                (block.timestamp >= (nearestMaturity - OBSERVATION_PERIOD))\n            ) {\n                Storage.slot().observationPeriodLogs[_ccy][_maturity].totalAmount += _filledAmount;\n                Storage\n                .slot()\n                .observationPeriodLogs[_ccy][_maturity].totalFutureValue += _filledFutureValue;\n            }\n        }\n    }\n\n    function migrateLendingMarket(bytes32 _ccy, uint256 _maturity) external {\n        address tokenAddress = AddressResolverLib.tokenVault().getTokenAddress(_ccy);\n\n        if (_maturity == 0) {\n            uint8 tokenDecimals = IERC20Metadata(tokenAddress).decimals();\n\n            if (tokenDecimals > COMPOUND_FACTOR_DECIMALS + ZC_TOKEN_BASE_DECIMALS) {\n                revert TooManyTokenDecimals(tokenAddress, tokenDecimals);\n            }\n\n            AddressResolverLib.genesisValueVault().updateDecimals(\n                _ccy,\n                COMPOUND_FACTOR_DECIMALS + ZC_TOKEN_BASE_DECIMALS - tokenDecimals\n            );\n        }\n\n        createZCToken(_ccy, _maturity, tokenAddress);\n    }\n\n    function createZCToken(bytes32 _ccy, uint256 _maturity, address _tokenAddress) public {\n        if (Storage.slot().zcTokens[_ccy][_maturity] != address(0)) {\n            revert AlreadyZCTokenExists(Storage.slot().zcTokens[_ccy][_maturity]);\n        }\n\n        if (_maturity != 0 && Storage.slot().maturityOrderBookIds[_ccy][_maturity] == 0) {\n            revert InvalidMaturity(_maturity);\n        }\n\n        string memory tokenSymbol = bytes32ToString(_ccy);\n\n        string memory symbol = string.concat(\"zc\", tokenSymbol);\n        string memory name = string.concat(\"ZC \", tokenSymbol);\n        // NOTE: The amount of genesis value generated gradually decreases as the compound factor increases.\n        // The values of ZCToken decimals are subtracted by 2 to prevent the display from becoming too small.\n        // Therefore, if the lending position is 1ETH and the compound factor is 10^18, the amount of ZCToken\n        // will be 100zcETH.\n        uint8 decimals = ZC_TOKEN_BASE_DECIMALS - 2;\n\n        // If the maturity is 0, the ZCToken is created as a perpetual one.\n        // Otherwise, the ZCToken is created per maturity.\n        if (_maturity != 0) {\n            (uint256 year, uint256 month, ) = TimeLibrary.timestampToDate(_maturity);\n\n            string memory formattedMaturity = string.concat(\n                Strings.toString(year),\n                \"-\",\n                month < 10 ? string.concat(\"0\", Strings.toString(month)) : Strings.toString(month)\n            );\n\n            symbol = string.concat(symbol, \"-\", formattedMaturity);\n            name = string.concat(name, \" \", _getShortMonthYearString(_maturity));\n            decimals = IERC20Metadata(_tokenAddress).decimals();\n        }\n\n        address zcToken = AddressResolverLib.beaconProxyController().deployZCToken(\n            name,\n            symbol,\n            decimals,\n            _tokenAddress,\n            _maturity\n        );\n\n        Storage.slot().zcTokens[_ccy][_maturity] = zcToken;\n        Storage.slot().zcTokenInfo[zcToken] = ZCTokenInfo({ccy: _ccy, maturity: _maturity});\n\n        emit ZCTokenCreated(_ccy, _maturity, name, symbol, decimals, zcToken);\n    }\n\n    function calculateNextMaturity(\n        uint256 _timestamp,\n        uint256 _period\n    ) public pure returns (uint256) {\n        if (_period == 0) {\n            return TimeLibrary.addDays(_timestamp, 7);\n        } else {\n            return _getLastFridayAfterMonths(_timestamp, _period);\n        }\n    }\n\n    function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) {\n        uint256 i = 0;\n        while (i < 32 && _bytes32[i] != 0) {\n            i++;\n        }\n\n        bytes memory bytesArray = new bytes(i);\n        for (i = 0; i < 32 && _bytes32[i] != 0; i++) {\n            bytesArray[i] = _bytes32[i];\n        }\n\n        return string(bytesArray);\n    }\n\n    function _getLastFridayAfterMonths(\n        uint256 _timestamp,\n        uint256 _months\n    ) internal pure returns (uint256 lastFridayTimestamp) {\n        (uint256 year, uint256 month, ) = TimeLibrary.timestampToDate(\n            TimeLibrary.addMonths(_timestamp, _months + 1)\n        );\n        uint256 thirdMonthEndTimestamp = TimeLibrary.timestampFromDate(year, month, 0);\n        uint256 dayOfWeek = TimeLibrary.getDayOfWeek(thirdMonthEndTimestamp);\n        uint256 diff = (dayOfWeek < TimeLibrary.DOW_FRI ? 7 : 0) + dayOfWeek - TimeLibrary.DOW_FRI;\n        lastFridayTimestamp = TimeLibrary.subDays(thirdMonthEndTimestamp, diff);\n\n        if (lastFridayTimestamp == 0) revert InvalidTimestamp();\n\n        return lastFridayTimestamp;\n    }\n\n    function _getShortMonthYearString(uint256 timestamp) internal pure returns (string memory) {\n        (uint256 year, uint256 month, ) = TimeLibrary.timestampToDate(timestamp);\n        string[12] memory months = [\n            \"JAN\",\n            \"FEB\",\n            \"MAR\",\n            \"APR\",\n            \"MAY\",\n            \"JUN\",\n            \"JUL\",\n            \"AUG\",\n            \"SEP\",\n            \"OCT\",\n            \"NOV\",\n            \"DEC\"\n        ];\n        return string(abi.encodePacked(months[month - 1], Strings.toString(year)));\n    }\n\n    function _calculateAutoRollUnitPrice(\n        bytes32 _ccy,\n        uint256 _nearestMaturity,\n        uint256 _destinationMaturity,\n        uint8 _destinationOrderBookId,\n        ILendingMarket _market\n    ) internal view returns (uint256 autoRollUnitPrice) {\n        ObservationPeriodLog memory log = Storage.slot().observationPeriodLogs[_ccy][\n            _destinationMaturity\n        ];\n\n        // The auto-roll unit price is calculated based on the volume-weighted average price of orders that are filled\n        // in the observation period. If there is no order filled in that period, the auto-roll unit price is calculated\n        // using the last block price. If the last block price is older than the last auto-roll date,\n        // the last auto-roll unit price is reused as the current auto-roll unit price.\n        if (log.totalFutureValue != 0) {\n            autoRollUnitPrice = (log.totalAmount * Constants.PRICE_DIGIT).div(log.totalFutureValue);\n        } else {\n            (uint256[] memory unitPrices, uint48 timestamp) = _market.getBlockUnitPriceHistory(\n                _destinationOrderBookId\n            );\n\n            AutoRollLog memory autoRollLog = AddressResolverLib\n                .genesisValueVault()\n                .getLatestAutoRollLog(_ccy);\n\n            if (unitPrices[0] != 0 && timestamp >= autoRollLog.prev) {\n                autoRollUnitPrice = _convertUnitPrice(\n                    unitPrices[0],\n                    _destinationMaturity,\n                    timestamp,\n                    _nearestMaturity\n                );\n            } else {\n                autoRollUnitPrice = autoRollLog.unitPrice;\n            }\n        }\n    }\n\n    function _convertUnitPrice(\n        uint256 _unitPrice,\n        uint256 _maturity,\n        uint256 _currentTimestamp,\n        uint256 _destinationTimestamp\n    ) internal pure returns (uint256) {\n        // NOTE:The formula is:\n        // 1) currentDuration = maturity - currentTimestamp\n        // 2) destinationDuration = maturity - destinationTimestamp\n        // 3) unitPrice = (currentUnitPrice * currentDuration)\n        //      / ((1 - currentUnitPrice) * destinationDuration + currentUnitPrice * currentDuration)\n\n        uint256 currentDuration = _maturity - _currentTimestamp;\n        uint256 destinationDuration = _maturity - _destinationTimestamp;\n        return\n            (Constants.PRICE_DIGIT * _unitPrice * currentDuration) /\n            (((Constants.PRICE_DIGIT - _unitPrice) * destinationDuration) +\n                (_unitPrice * currentDuration));\n    }\n}\n"
      },
      "contracts/protocol/libraries/logics/LendingMarketUserLogic.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\n// dependencies\nimport {EnumerableSet} from \"../../../dependencies/openzeppelin/utils/structs/EnumerableSet.sol\";\nimport {SafeCast} from \"../../../dependencies/openzeppelin/utils/math/SafeCast.sol\";\n// interfaces\nimport {ILendingMarket} from \"../../interfaces/ILendingMarket.sol\";\nimport {ILendingMarketController} from \"../../interfaces/ILendingMarketController.sol\";\nimport {IFutureValueVault} from \"../../interfaces/IFutureValueVault.sol\";\nimport {IZCToken} from \"../../interfaces/IZCToken.sol\";\n// libraries\nimport {AddressResolverLib} from \"../AddressResolverLib.sol\";\nimport {Constants} from \"../Constants.sol\";\nimport {FilledOrder, PartiallyFilledOrder} from \"../OrderBookLib.sol\";\nimport {RoundingUint256} from \"../math/RoundingUint256.sol\";\nimport {LendingMarketOperationLogic} from \"./LendingMarketOperationLogic.sol\";\nimport {FundManagementLogic} from \"./FundManagementLogic.sol\";\n// types\nimport {ProtocolTypes} from \"../../types/ProtocolTypes.sol\";\n// storages\nimport {LendingMarketControllerStorage as Storage} from \"../../storages/LendingMarketControllerStorage.sol\";\n\nlibrary LendingMarketUserLogic {\n    using EnumerableSet for EnumerableSet.Bytes32Set;\n    using EnumerableSet for EnumerableSet.UintSet;\n    using SafeCast for int256;\n    using SafeCast for uint256;\n    using RoundingUint256 for uint256;\n\n    error InvalidAmount();\n    error AmountIsZero();\n    error FutureValueIsZero();\n    error TooManyActiveOrders();\n    error NotEnoughCollateral();\n    error NotEnoughDeposit(bytes32 ccy);\n\n    struct EstimateCollateralCoverageParams {\n        bytes32 ccy;\n        uint256 maturity;\n        address user;\n        ProtocolTypes.Side side;\n        uint256 unitPrice;\n        uint256 additionalDepositAmount;\n        bool ignoreBorrowedAmount;\n        uint256 filledAmount;\n        uint256 filledAmountInFV;\n        uint256 orderFeeInFV;\n        uint256 placedAmount;\n    }\n\n    function getOrderEstimation(\n        ILendingMarketController.GetOrderEstimationParams memory input\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        (\n            lastUnitPrice,\n            filledAmount,\n            filledAmountInFV,\n            orderFeeInFV,\n            placedAmount\n        ) = _calculateFilledAmount(\n            input.ccy,\n            input.maturity,\n            input.side,\n            input.amount,\n            input.unitPrice\n        );\n\n        (coverage, isInsufficientDepositAmount) = _estimateCollateralCoverage(\n            EstimateCollateralCoverageParams(\n                input.ccy,\n                input.maturity,\n                input.user,\n                input.side,\n                input.unitPrice,\n                input.additionalDepositAmount,\n                input.ignoreBorrowedAmount,\n                filledAmount,\n                filledAmountInFV,\n                orderFeeInFV,\n                placedAmount\n            )\n        );\n    }\n\n    function executeOrder(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        ProtocolTypes.Side _side,\n        uint256 _amount,\n        uint256 _unitPrice\n    ) external {\n        if (_amount == 0) revert InvalidAmount();\n\n        uint256 activeOrderCount = FundManagementLogic.cleanUpFunds(_ccy, _user);\n        FundManagementLogic.registerCurrencyAndMaturity(_ccy, _maturity, _user);\n\n        (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 feeInFV\n        ) = ILendingMarket(Storage.slot().lendingMarkets[_ccy]).executeOrder(\n                Storage.slot().maturityOrderBookIds[_ccy][_maturity],\n                _side,\n                _user,\n                _amount,\n                _unitPrice\n            );\n\n        uint256 filledAmount = filledOrder.amount;\n\n        // The case that an order is placed in the order book\n        if ((filledAmount + filledOrder.ignoredAmount) != _amount) {\n            unchecked {\n                activeOrderCount += 1;\n            }\n        }\n\n        if (activeOrderCount > Constants.MAXIMUM_ORDER_COUNT) revert TooManyActiveOrders();\n\n        updateFundsForTaker(\n            _ccy,\n            _maturity,\n            _user,\n            _side,\n            filledAmount,\n            filledOrder.futureValue,\n            feeInFV\n        );\n\n        updateFundsForMaker(\n            _ccy,\n            _maturity,\n            _side == ProtocolTypes.Side.LEND ? ProtocolTypes.Side.BORROW : ProtocolTypes.Side.LEND,\n            partiallyFilledOrder\n        );\n\n        // Updates the pending order amount for marker's orders.\n        // Since the partially filled order is updated with `updateFundsForMaker()`,\n        // its amount is subtracted from `pendingOrderAmounts`.\n        Storage.slot().pendingOrderAmounts[_ccy][_maturity] +=\n            filledAmount -\n            partiallyFilledOrder.amount;\n\n        Storage.slot().usedCurrencies[_user].add(_ccy);\n\n        _isCovered(_user, _ccy);\n    }\n\n    function executePreOrder(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        ProtocolTypes.Side _side,\n        uint256 _amount,\n        uint256 _unitPrice\n    ) external {\n        if (_amount == 0) revert InvalidAmount();\n\n        uint256 activeOrderCount = FundManagementLogic.cleanUpFunds(_ccy, _user);\n\n        if (activeOrderCount + 1 > Constants.MAXIMUM_ORDER_COUNT) revert TooManyActiveOrders();\n\n        FundManagementLogic.registerCurrencyAndMaturity(_ccy, _maturity, _user);\n\n        ILendingMarket(Storage.slot().lendingMarkets[_ccy]).executePreOrder(\n            Storage.slot().maturityOrderBookIds[_ccy][_maturity],\n            _side,\n            _user,\n            _amount,\n            _unitPrice\n        );\n\n        Storage.slot().usedCurrencies[_user].add(_ccy);\n\n        _isCovered(_user, _ccy);\n    }\n\n    function unwindPosition(bytes32 _ccy, uint256 _maturity, address _user) external {\n        FundManagementLogic.cleanUpFunds(_ccy, _user);\n\n        int256 futureValue = FundManagementLogic\n            .getActualFunds(_ccy, _maturity, _user, 0)\n            .futureValue;\n\n        (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 feeInFV,\n            ProtocolTypes.Side side\n        ) = _unwindPosition(_ccy, _maturity, _user, futureValue);\n\n        updateFundsForTaker(\n            _ccy,\n            _maturity,\n            _user,\n            side,\n            filledOrder.amount,\n            filledOrder.futureValue,\n            feeInFV\n        );\n\n        updateFundsForMaker(\n            _ccy,\n            _maturity,\n            side == ProtocolTypes.Side.LEND ? ProtocolTypes.Side.BORROW : ProtocolTypes.Side.LEND,\n            partiallyFilledOrder\n        );\n\n        // Updates the pending order amount for marker's orders.\n        // Since the partially filled order is updated with `updateFundsForMaker()`,\n        // its amount is subtracted from `pendingOrderAmounts`.\n        Storage.slot().pendingOrderAmounts[_ccy][_maturity] +=\n            filledOrder.amount -\n            partiallyFilledOrder.amount;\n\n        // When the market is the nearest market and the user has only GV, a user still has future value after unwinding.\n        // For that case, the `registerCurrencyAndMaturity` function needs to be called again.\n        (int256 currentFutureValue, ) = IFutureValueVault(Storage.slot().futureValueVaults[_ccy])\n            .getBalance(Storage.slot().maturityOrderBookIds[_ccy][_maturity], _user);\n\n        if (currentFutureValue != 0) {\n            FundManagementLogic.registerCurrencyAndMaturity(_ccy, _maturity, _user);\n        }\n\n        _isCovered(_user, _ccy);\n    }\n\n    function updateFundsForTaker(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        ProtocolTypes.Side _side,\n        uint256 _filledAmount,\n        uint256 _filledAmountInFV,\n        uint256 _feeInFV\n    ) public {\n        if (_filledAmountInFV != 0) {\n            FundManagementLogic.updateFunds(\n                _ccy,\n                _maturity,\n                _user,\n                _side,\n                _filledAmount,\n                _filledAmountInFV,\n                _feeInFV\n            );\n\n            LendingMarketOperationLogic.updateOrderLogs(\n                _ccy,\n                _maturity,\n                _filledAmount,\n                _filledAmountInFV\n            );\n\n            emit FundManagementLogic.OrderFilled(\n                _user,\n                _ccy,\n                _side,\n                _maturity,\n                _filledAmount,\n                _filledAmountInFV,\n                _feeInFV\n            );\n        }\n    }\n\n    function updateFundsForMaker(\n        bytes32 _ccy,\n        uint256 _maturity,\n        ProtocolTypes.Side _side,\n        PartiallyFilledOrder memory partiallyFilledOrder\n    ) public {\n        if (partiallyFilledOrder.futureValue != 0) {\n            FundManagementLogic.updateFunds(\n                _ccy,\n                _maturity,\n                partiallyFilledOrder.maker,\n                _side,\n                partiallyFilledOrder.amount,\n                partiallyFilledOrder.futureValue,\n                0\n            );\n\n            emit FundManagementLogic.OrderPartiallyFilled(\n                partiallyFilledOrder.orderId,\n                partiallyFilledOrder.maker,\n                _ccy,\n                _side,\n                _maturity,\n                partiallyFilledOrder.amount,\n                partiallyFilledOrder.futureValue\n            );\n        }\n    }\n\n    function withdrawZCToken(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        uint256 _amount\n    ) public {\n        FundManagementLogic.cleanUpFunds(_ccy, _user);\n\n        if (_maturity == 0) {\n            _withdrawZCPerpetualToken(_ccy, _user, _amount);\n        } else {\n            _withdrawZCToken(_ccy, _maturity, _user, _amount);\n        }\n    }\n\n    function depositZCToken(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        uint256 _amount\n    ) public {\n        FundManagementLogic.cleanUpFunds(_ccy, _user);\n\n        if (_maturity == 0) {\n            _depositZCPerpetualToken(_ccy, _user, _amount);\n        } else {\n            _depositZCToken(_ccy, _maturity, _user, _amount);\n        }\n\n        FundManagementLogic.registerCurrencyAndMaturity(_ccy, _maturity, _user);\n    }\n\n    function getWithdrawableZCTokenAmount(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user\n    ) public view returns (uint256 amount) {\n        if (_maturity == 0) {\n            return _getWithdrawableZCPerpetualTokenAmount(_ccy, _user);\n        } else {\n            return _getWithdrawableZCTokenAmount(_ccy, _maturity, _user);\n        }\n    }\n\n    function _calculateFilledAmount(\n        bytes32 _ccy,\n        uint256 _maturity,\n        ProtocolTypes.Side _side,\n        uint256 _amount,\n        uint256 _unitPrice\n    )\n        internal\n        view\n        returns (\n            uint256 lastUnitPrice,\n            uint256 filledAmount,\n            uint256 filledAmountInFV,\n            uint256 orderFeeInFV,\n            uint256 placedAmount\n        )\n    {\n        (\n            lastUnitPrice,\n            filledAmount,\n            filledAmountInFV,\n            orderFeeInFV,\n            placedAmount\n        ) = ILendingMarket(Storage.slot().lendingMarkets[_ccy]).calculateFilledAmount(\n            Storage.slot().maturityOrderBookIds[_ccy][_maturity],\n            _side,\n            _amount,\n            _unitPrice\n        );\n    }\n\n    function _estimateCollateralCoverage(\n        EstimateCollateralCoverageParams memory _params\n    ) internal view returns (uint256 coverage, bool isInsufficientDepositAmount) {\n        uint256 filledAmountWithFeeInFV = _params.filledAmountInFV;\n\n        if (_params.side == ProtocolTypes.Side.LEND) {\n            filledAmountWithFeeInFV -= _params.orderFeeInFV;\n        } else {\n            filledAmountWithFeeInFV += _params.orderFeeInFV;\n        }\n\n        uint256 filledAmountWithFeeInPV = _estimatePVFromFV(\n            _params.ccy,\n            _params.maturity,\n            filledAmountWithFeeInFV,\n            _params.unitPrice\n        );\n\n        ILendingMarketController.AdditionalFunds memory funds;\n        funds.ccy = _params.ccy;\n        // Store the _additionalDepositAmount in the borrowedAmount,\n        // because the borrowedAmount is used as collateral.\n        funds.borrowedAmount = _params.additionalDepositAmount;\n\n        if (_params.placedAmount != 0) {\n            if (_params.side == ProtocolTypes.Side.BORROW) {\n                uint256 minUnitPrice = FundManagementLogic.getCurrentMinDebtUnitPrice(\n                    _params.maturity,\n                    Storage.slot().minDebtUnitPrices[_params.ccy]\n                );\n\n                if (_params.unitPrice >= minUnitPrice) {\n                    funds.workingBorrowOrdersAmount = _params.placedAmount;\n                } else {\n                    // NOTE: The formula is:\n                    // futureValue = placedAmount / unitPrice\n                    // workingBorrowOrdersAmount = futureValue * minUnitPrice\n                    funds.workingBorrowOrdersAmount = (_params.placedAmount * minUnitPrice).div(\n                        _params.unitPrice\n                    );\n                }\n            } else {\n                funds.workingLendOrdersAmount = _params.placedAmount;\n            }\n        }\n\n        if (filledAmountWithFeeInPV > 0) {\n            if (_params.side == ProtocolTypes.Side.BORROW) {\n                if (!_params.ignoreBorrowedAmount) {\n                    funds.borrowedAmount += _params.filledAmount;\n                }\n                funds.debtAmount += filledAmountWithFeeInPV;\n            } else {\n                funds.lentAmount += _params.filledAmount;\n                funds.claimableAmount += filledAmountWithFeeInPV;\n            }\n        }\n\n        (coverage, isInsufficientDepositAmount) = AddressResolverLib.tokenVault().calculateCoverage(\n            _params.user,\n            funds\n        );\n    }\n\n    function _estimatePVFromFV(\n        bytes32 _ccy,\n        uint256 _maturity,\n        uint256 _amount,\n        uint256 _unitPrice\n    ) internal view returns (uint256) {\n        uint256 marketUnitPrice = ILendingMarket(Storage.slot().lendingMarkets[_ccy])\n            .getMarketUnitPrice(Storage.slot().maturityOrderBookIds[_ccy][_maturity]);\n\n        if (marketUnitPrice == 0) {\n            marketUnitPrice = _unitPrice;\n        }\n\n        return (_amount * marketUnitPrice).div(Constants.PRICE_DIGIT);\n    }\n\n    function _unwindPosition(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        int256 _futureValue\n    )\n        internal\n        returns (\n            FilledOrder memory filledOrder,\n            PartiallyFilledOrder memory partiallyFilledOrder,\n            uint256 feeInFV,\n            ProtocolTypes.Side side\n        )\n    {\n        if (_futureValue == 0) revert FutureValueIsZero();\n\n        if (_futureValue > 0) {\n            side = ProtocolTypes.Side.BORROW;\n\n            (filledOrder, partiallyFilledOrder, feeInFV) = ILendingMarket(\n                Storage.slot().lendingMarkets[_ccy]\n            ).unwindPosition(\n                    Storage.slot().maturityOrderBookIds[_ccy][_maturity],\n                    side,\n                    _user,\n                    _futureValue.toUint256()\n                );\n        } else if (_futureValue < 0) {\n            side = ProtocolTypes.Side.LEND;\n\n            (filledOrder, partiallyFilledOrder, feeInFV) = ILendingMarket(\n                Storage.slot().lendingMarkets[_ccy]\n            ).unwindPosition(\n                    Storage.slot().maturityOrderBookIds[_ccy][_maturity],\n                    side,\n                    _user,\n                    (-_futureValue).toUint256()\n                );\n        }\n    }\n\n    function _withdrawZCToken(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        uint256 _amount\n    ) internal {\n        uint8 orderBookId = Storage.slot().maturityOrderBookIds[_ccy][_maturity];\n        uint256 maxWithdrawableAmount = _getWithdrawableZCTokenAmount(_ccy, _maturity, _user);\n\n        if (maxWithdrawableAmount < _amount) {\n            _amount = maxWithdrawableAmount;\n        }\n\n        if (_amount == 0) revert AmountIsZero();\n\n        uint256 lockedAmount = IFutureValueVault(Storage.slot().futureValueVaults[_ccy]).lock(\n            orderBookId,\n            _user,\n            _amount,\n            _maturity\n        );\n        IZCToken(Storage.slot().zcTokens[_ccy][_maturity]).mint(_user, lockedAmount);\n    }\n\n    function _depositZCToken(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user,\n        uint256 _amount\n    ) internal {\n        IZCToken token = IZCToken(Storage.slot().zcTokens[_ccy][_maturity]);\n        uint256 balance = token.balanceOf(_user);\n\n        if (balance == 0) {\n            revert AmountIsZero();\n        }\n\n        if (balance < _amount) {\n            _amount = balance;\n        }\n\n        token.burn(_user, _amount);\n        IFutureValueVault(Storage.slot().futureValueVaults[_ccy]).unlock(\n            Storage.slot().maturityOrderBookIds[_ccy][_maturity],\n            _user,\n            _amount,\n            _maturity\n        );\n    }\n\n    function _withdrawZCPerpetualToken(bytes32 _ccy, address _user, uint256 _amount) internal {\n        uint256 maxWithdrawableAmount = _getWithdrawableZCPerpetualTokenAmount(_ccy, _user);\n\n        if (maxWithdrawableAmount < _amount) {\n            _amount = maxWithdrawableAmount;\n        }\n\n        if (_amount == 0) revert AmountIsZero();\n\n        uint256 lockedAmount = AddressResolverLib.genesisValueVault().lock(_ccy, _user, _amount);\n        IZCToken(Storage.slot().zcTokens[_ccy][0]).mint(_user, lockedAmount);\n    }\n\n    function _depositZCPerpetualToken(bytes32 _ccy, address _user, uint256 _amount) internal {\n        IZCToken token = IZCToken(Storage.slot().zcTokens[_ccy][0]);\n        uint256 balance = token.balanceOf(_user);\n\n        if (balance == 0) {\n            revert AmountIsZero();\n        }\n\n        if (balance < _amount) {\n            _amount = balance;\n        }\n\n        token.burn(_user, _amount);\n        AddressResolverLib.genesisValueVault().unlock(_ccy, _user, _amount);\n    }\n\n    function _getWithdrawableZCTokenAmount(\n        bytes32 _ccy,\n        uint256 _maturity,\n        address _user\n    ) internal view returns (uint256 amount) {\n        (uint256 withdrawableAmount, bool hasAllocatedCollateral) = _getWithdrawableAmount(\n            _ccy,\n            _user\n        );\n\n        FundManagementLogic.ActualFunds memory funds = FundManagementLogic.getActualFunds(\n            _ccy,\n            _maturity,\n            _user,\n            0\n        );\n        int256 presentValue = funds.presentValue - funds.genesisValueInPV;\n        int256 futureValue = funds.futureValue - funds.genesisValueInFV;\n\n        if (futureValue <= 0) {\n            return 0;\n        } else if (!hasAllocatedCollateral || withdrawableAmount >= presentValue.toUint256()) {\n            return futureValue.toUint256();\n        } else {\n            return FundManagementLogic.calculateFVFromPV(_ccy, _maturity, withdrawableAmount);\n        }\n    }\n\n    function _getWithdrawableZCPerpetualTokenAmount(\n        bytes32 _ccy,\n        address _user\n    ) internal view returns (uint256 amount) {\n        (uint256 withdrawableAmount, bool hasAllocatedCollateral) = _getWithdrawableAmount(\n            _ccy,\n            _user\n        );\n\n        FundManagementLogic.ActualFunds memory funds = FundManagementLogic.getActualFunds(\n            _ccy,\n            0,\n            _user,\n            0\n        );\n\n        if (funds.genesisValue <= 0) {\n            return 0;\n        } else if (\n            !hasAllocatedCollateral || withdrawableAmount >= funds.genesisValueInPV.toUint256()\n        ) {\n            return funds.genesisValue.toUint256();\n        } else {\n            int256 withdrawableAmountInFV = FundManagementLogic.calculateFVFromPV(\n                _ccy,\n                AddressResolverLib.genesisValueVault().getCurrentMaturity(_ccy),\n                withdrawableAmount.toInt256()\n            );\n\n            return\n                AddressResolverLib\n                    .genesisValueVault()\n                    .calculateGVFromFV(_ccy, 0, withdrawableAmountInFV)\n                    .toUint256();\n        }\n    }\n\n    function _getWithdrawableAmount(\n        bytes32 _ccy,\n        address _user\n    ) internal view returns (uint256 withdrawableAmount, bool hasAllocatedCollateral) {\n        ILendingMarketController.AdditionalFunds memory emptyAdditionalFunds;\n        uint256 liquidationThresholdRate = AddressResolverLib\n            .tokenVault()\n            .getLiquidationThresholdRate();\n        ILendingMarketController.CalculatedFunds memory funds = FundManagementLogic.calculateFunds(\n            _ccy,\n            _user,\n            emptyAdditionalFunds,\n            liquidationThresholdRate\n        );\n\n        uint256[] memory amounts = new uint256[](2);\n        (amounts[0], amounts[1], ) = AddressResolverLib.tokenVault().getCollateralDetail(_user);\n        amounts = AddressResolverLib.currencyController().convertFromBaseCurrency(_ccy, amounts);\n\n        uint256 totalCollateral = amounts[0];\n        uint256 totalUsedCollateral = amounts[1];\n\n        if (totalUsedCollateral == 0) {\n            return (totalCollateral, false);\n        }\n\n        uint256 haircut = AddressResolverLib.currencyController().getHaircut(_ccy);\n        uint256 discountedUnallocatedCollateralAmount = (funds.unallocatedCollateralAmount *\n            haircut).div(Constants.PCT_DIGIT);\n\n        uint256 availableAmount = (totalCollateral *\n            Constants.PCT_DIGIT -\n            totalUsedCollateral *\n            liquidationThresholdRate).div(Constants.PCT_DIGIT);\n\n        if (haircut != 0 && funds.unallocatedCollateralAmount != 0) {\n            uint256 allocatedAmount = funds.claimableAmount - funds.unallocatedCollateralAmount;\n\n            if (availableAmount <= discountedUnallocatedCollateralAmount) {\n                return ((availableAmount * Constants.PCT_DIGIT).div(haircut), true);\n            } else if (availableAmount <= discountedUnallocatedCollateralAmount + allocatedAmount) {\n                // If the available amount is insufficient, unallocated collateral, which is discounted by a haircut and used between different currencies,\n                // is used first. Then, the allocated collateral, which is used to offset positions in the same currency, is used for the rest of the amount.\n                // NOTE: The formula is:\n                // allocatedCollateralAmount = availableAmount - discountedUnallocatedCollateralAmount\n                // totalWithdrawableAmount = allocatedCollateralAmount + unallocatedCollateralAmount\n                return (\n                    funds.unallocatedCollateralAmount +\n                        availableAmount -\n                        discountedUnallocatedCollateralAmount,\n                    true\n                );\n            } else {\n                return (availableAmount, true);\n            }\n        } else {\n            return (availableAmount, funds.unallocatedCollateralAmount != 0);\n        }\n    }\n\n    function _isCovered(address _user, bytes32 _ccy) internal view {\n        (bool isEnoughCollateral, bool isEnoughDepositInOrderCcy) = AddressResolverLib\n            .tokenVault()\n            .isCovered(_user, _ccy);\n\n        if (!isEnoughDepositInOrderCcy) revert NotEnoughDeposit(_ccy);\n        if (!isEnoughCollateral) revert NotEnoughCollateral();\n    }\n}\n"
      },
      "contracts/protocol/libraries/math/RoundingInt256.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nlibrary RoundingInt256 {\n    function div(int256 a, int256 b) internal pure returns (int256) {\n        require(b != 0);\n\n        int256 diff = (a * 10) / b - (a / b) * 10;\n        if (diff >= 5) {\n            return (a / b) + 1;\n        } else if (diff <= -5) {\n            return (a / b) - 1;\n        } else {\n            return a / b;\n        }\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/libraries/QuickSort.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nlibrary QuickSort {\n    function sort(uint256[] memory data) public pure returns (uint256[] memory) {\n        quickSort(data, int256(0), int256(data.length - 1));\n        return data;\n    }\n\n    function quickSort(uint256[] memory arr, int256 left, int256 right) internal pure {\n        int256 i = left;\n        int256 j = right;\n        if (i == j) return;\n        uint256 pivot = arr[uint256(left + (right - left) / 2)];\n        while (i <= j) {\n            while (arr[uint256(i)] < pivot) i++;\n            while (pivot < arr[uint256(j)]) j--;\n            if (i <= j) {\n                (arr[uint256(i)], arr[uint256(j)]) = (arr[uint256(j)], arr[uint256(i)]);\n                i++;\n                j--;\n            }\n        }\n        if (left < j) quickSort(arr, left, j);\n        if (i < right) quickSort(arr, i, right);\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/FundManagementLogic.sol": {
          "FundManagementLogic": "0xd4e37bd9e8b95a883158375a5ad6b3a9bf824223"
        },
        "contracts/protocol/libraries/logics/LendingMarketOperationLogic.sol": {
          "LendingMarketOperationLogic": "0x1c9579874a3bd0088b85faa520e88ea0677e2f22"
        }
      }
    }
  }}