creation_sourcecode: {{
    "language": "Solidity",
    "settings": {
      "evmVersion": "paris",
      "libraries": {},
      "metadata": {
        "bytecodeHash": "none",
        "useLiteralContent": true
      },
      "optimizer": {
        "enabled": true,
        "runs": 10
      },
      "remappings": [],
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      }
    },
    "sources": {
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/draft-IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    function safeTransfer(\n        IERC20 token,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    function safeTransferFrom(\n        IERC20 token,\n        address from,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n        require(\n            (value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    function safeIncreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        uint256 newAllowance = token.allowance(address(this), spender) + value;\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    function safeDecreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            uint256 newAllowance = oldAllowance - value;\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n        }\n    }\n\n    function safePermit(\n        IERC20Permit token,\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal {\n        uint256 nonceBefore = token.nonces(owner);\n        token.permit(owner, spender, value, deadline, v, r, s);\n        uint256 nonceAfter = token.nonces(owner);\n        require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        if (returndata.length > 0) {\n            // Return data is optional\n            require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/math/Math.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.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                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            require(denominator > prod1);\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 10, 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 * 8) < value ? 1 : 0);\n        }\n    }\n}\n"
      },
      "contracts/core/TroveManager.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"../interfaces/IBorrowerOperations.sol\";\nimport \"../interfaces/IDebtToken.sol\";\nimport \"../interfaces/ISortedTroves.sol\";\nimport \"../interfaces/IVault.sol\";\nimport \"../interfaces/IPriceFeed.sol\";\nimport { ReedemedTrove, ITroveRedemptionsCallback } from \"../interfaces/IPrismaCallbacks.sol\";\nimport \"../dependencies/SystemStart.sol\";\nimport \"../dependencies/PrismaBase.sol\";\nimport \"../dependencies/PrismaMath.sol\";\nimport \"../dependencies/PrismaOwnable.sol\";\n\n/**\n    @title Prisma Trove Manager\n    @notice Based on Liquity's `TroveManager`\n            https://github.com/liquity/dev/blob/main/packages/contracts/contracts/TroveManager.sol\n\n            Prisma's implementation is modified so that multiple `TroveManager` and `SortedTroves`\n            contracts are deployed in tandem, with each pair managing troves of a single collateral\n            type.\n\n            Functionality related to liquidations has been moved to `LiquidationManager`. This was\n            necessary to avoid the restriction on deployed bytecode size.\n */\ncontract TroveManager is PrismaBase, PrismaOwnable, SystemStart {\n    using SafeERC20 for IERC20;\n\n    // --- Connected contract declarations ---\n\n    address public immutable borrowerOperationsAddress;\n    address public immutable liquidationManager;\n    address immutable gasPoolAddress;\n    IDebtToken public immutable debtToken;\n    IPrismaVault public immutable vault;\n    // During bootsrap period redemptions are not allowed\n    uint256 public immutable bootstrapPeriod;\n    IPriceFeed public priceFeed;\n    IERC20 public collateralToken;\n\n    // A doubly linked list of Troves, sorted by their collateral ratios\n    ISortedTroves public sortedTroves;\n\n    EmissionId public emissionId;\n    // Minimum collateral ratio for individual troves\n    uint256 public MCR;\n\n    uint256 constant SECONDS_IN_ONE_MINUTE = 60;\n    uint256 constant INTEREST_PRECISION = 1e27;\n    uint256 constant SECONDS_IN_YEAR = 365 days;\n    uint256 constant REWARD_DURATION = 1 weeks;\n\n    // volume-based amounts are divided by this value to allow storing as uint32\n    uint256 constant VOLUME_MULTIPLIER = 1e20;\n\n    uint256 public constant SUNSETTING_INTEREST_RATE = (INTEREST_PRECISION * 5000) / (10000 * SECONDS_IN_YEAR); //50%    // During bootsrap period redemptions are not allowed\n    /*\n     * BETA: 18 digit decimal. Parameter by which to divide the redeemed fraction, in order to calc the new base rate from a redemption.\n     * Corresponds to (1 / ALPHA) in the white paper.\n     */\n    uint256 constant BETA = 2;\n\n    // commented values are Liquity's fixed settings for each parameter\n    uint256 public minuteDecayFactor; // 999037758833783000  (half-life of 12 hours)\n    uint256 public redemptionFeeFloor; // DECIMAL_PRECISION / 1000 * 5  (0.5%)\n    uint256 public maxRedemptionFee; // DECIMAL_PRECISION  (100%)\n    uint256 public borrowingFeeFloor; // DECIMAL_PRECISION / 1000 * 5  (0.5%)\n    uint256 public maxBorrowingFee; // DECIMAL_PRECISION / 100 * 5  (5%)\n    uint256 public maxSystemDebt;\n\n    uint256 public interestRate;\n    uint256 public activeInterestIndex;\n    uint256 public lastActiveIndexUpdate;\n\n    uint256 public systemDeploymentTime;\n    bool public sunsetting;\n    bool public paused;\n\n    uint256 public baseRate;\n\n    // The timestamp of the latest fee operation (redemption or new debt issuance)\n    uint256 public lastFeeOperationTime;\n\n    uint256 public totalStakes;\n\n    // Snapshot of the value of totalStakes, taken immediately after the latest liquidation\n    uint256 public totalStakesSnapshot;\n\n    // Snapshot of the total collateral taken immediately after the latest liquidation.\n    uint256 public totalCollateralSnapshot;\n\n    /*\n     * L_collateral and L_debt track the sums of accumulated liquidation rewards per unit staked. During its lifetime, each stake earns:\n     *\n     * An collateral gain of ( stake * [L_collateral - L_collateral(0)] )\n     * A debt increase  of ( stake * [L_debt - L_debt(0)] )\n     *\n     * Where L_collateral(0) and L_debt(0) are snapshots of L_collateral and L_debt for the active Trove taken at the instant the stake was made\n     */\n    uint256 public L_collateral;\n    uint256 public L_debt;\n\n    // Error trackers for the trove redistribution calculation\n    uint256 public lastCollateralError_Redistribution;\n    uint256 public lastDebtError_Redistribution;\n\n    uint256 internal totalActiveCollateral;\n    uint256 internal totalActiveDebt;\n    uint256 public interestPayable;\n\n    uint256 public defaultedCollateral;\n    uint256 public defaultedDebt;\n\n    uint256 public rewardIntegral;\n    uint128 public rewardRate;\n    uint32 public lastUpdate;\n    uint32 public periodFinish;\n    uint16 public redemptionFeesRebate;\n\n    ITroveRedemptionsCallback private _redemptionsCallback;\n\n    mapping(address => uint256) public rewardIntegralFor;\n    mapping(address => uint256) private storedPendingReward;\n\n    // week -> total available rewards for 1 day within this week\n    uint256[65535] public dailyMintReward;\n\n    // week -> day -> total amount redeemed this day\n    uint32[7][65535] private totalMints;\n\n    // account -> data for latest activity\n    mapping(address => VolumeData) public accountLatestMint;\n\n    mapping(address => Trove) public Troves;\n    mapping(address => uint256) public surplusBalances;\n\n    // Map addresses with active troves to their RewardSnapshot\n    mapping(address => RewardSnapshot) public rewardSnapshots;\n\n    // Array of all active trove addresses - used to to compute an approximate hint off-chain, for the sorted list insertion\n    address[] TroveOwners;\n\n    struct VolumeData {\n        uint32 amount;\n        uint32 week;\n        uint32 day;\n    }\n\n    struct EmissionId {\n        uint16 debt;\n        uint16 minting;\n    }\n\n    // Store the necessary data for a trove\n    struct Trove {\n        uint256 debt;\n        uint256 coll;\n        uint256 stake;\n        Status status;\n        uint128 arrayIndex;\n        uint256 activeInterestIndex;\n    }\n\n    struct RedemptionTotals {\n        uint256 remainingDebt;\n        uint256 totalDebtToRedeem;\n        uint256 totalCollateralDrawn;\n        uint256 collateralFee;\n        uint256 collateralToSendToRedeemer;\n        uint256 decayedBaseRate;\n        uint256 price;\n        uint256 totalDebtSupplyAtStart;\n        uint256 numberOfRedemptions;\n        ReedemedTrove[] trovesRedeemed;\n    }\n\n    struct SingleRedemptionValues {\n        uint256 debtLot;\n        uint256 collateralLot;\n        bool cancelledPartial;\n    }\n\n    // Object containing the collateral and debt snapshots for a given active trove\n    struct RewardSnapshot {\n        uint256 collateral;\n        uint256 debt;\n    }\n\n    enum TroveManagerOperation {\n        open,\n        close,\n        adjust,\n        liquidate,\n        redeemCollateral\n    }\n\n    enum Status {\n        nonExistent,\n        active,\n        closedByOwner,\n        closedByLiquidation,\n        closedByRedemption\n    }\n\n    event TroveUpdated(\n        address indexed _borrower,\n        uint256 _debt,\n        uint256 _coll,\n        uint256 _stake,\n        TroveManagerOperation _operation\n    );\n\n    event Redemption(\n        uint256 _attemptedDebtAmount,\n        uint256 _actualDebtAmount,\n        uint256 _collateralSent,\n        uint256 _collateralFee\n    );\n    event BaseRateUpdated(uint256 _baseRate);\n    event LastFeeOpTimeUpdated(uint256 _lastFeeOpTime);\n    event TotalStakesUpdated(uint256 _newTotalStakes);\n    event SystemSnapshotsUpdated(uint256 _totalStakesSnapshot, uint256 _totalCollateralSnapshot);\n    event LTermsUpdated(uint256 _L_collateral, uint256 _L_debt);\n    event TroveSnapshotsUpdated(uint256 _L_collateral, uint256 _L_debt);\n    event TroveIndexUpdated(address _borrower, uint256 _newIndex);\n    event CollateralSent(address _to, uint256 _amount);\n    event RewardClaimed(address indexed account, address indexed recipient, uint256 claimed);\n    event RedemptionFeesRebateSet(uint16 redemptionFeesRebate);\n\n    modifier whenNotPaused() {\n        require(!paused, \"Collateral Paused\");\n        _;\n    }\n\n    constructor(\n        address _prismaCore,\n        address _gasPoolAddress,\n        address _debtTokenAddress,\n        address _borrowerOperationsAddress,\n        address _vault,\n        address _liquidationManager,\n        uint256 _gasCompensation,\n        uint256 _bootstrapPeriod\n    ) PrismaOwnable(_prismaCore) PrismaBase(_gasCompensation) SystemStart(_prismaCore) {\n        gasPoolAddress = _gasPoolAddress;\n        debtToken = IDebtToken(_debtTokenAddress);\n        borrowerOperationsAddress = _borrowerOperationsAddress;\n        vault = IPrismaVault(_vault);\n        liquidationManager = _liquidationManager;\n        bootstrapPeriod = _bootstrapPeriod;\n    }\n\n    function setAddresses(address _priceFeedAddress, address _sortedTrovesAddress, address _collateralToken) external {\n        require(address(sortedTroves) == address(0));\n        priceFeed = IPriceFeed(_priceFeedAddress);\n        sortedTroves = ISortedTroves(_sortedTrovesAddress);\n        collateralToken = IERC20(_collateralToken);\n\n        systemDeploymentTime = block.timestamp;\n        sunsetting = false;\n        activeInterestIndex = INTEREST_PRECISION;\n        lastActiveIndexUpdate = block.timestamp;\n    }\n\n    function notifyRegisteredId(uint256[] calldata _assignedIds) external returns (bool) {\n        require(msg.sender == address(vault));\n        require(emissionId.debt == 0, \"Already assigned\");\n        uint256 length = _assignedIds.length;\n        require(length == 2, \"Incorrect ID count\");\n        emissionId = EmissionId({ debt: uint16(_assignedIds[0]), minting: uint16(_assignedIds[1]) });\n        periodFinish = uint32(((block.timestamp / 1 weeks) + 1) * 1 weeks);\n\n        return true;\n    }\n\n    /**\n     * @notice Sets the pause state for this trove manager\n     *         Pausing is used to mitigate risks in exceptional circumstances\n     *         Functionalities affected by pausing are:\n     *         - New borrowing is not possible\n     *         - New collateral deposits are not possible\n     * @param _paused If true the protocol is paused\n     */\n    function setPaused(bool _paused) external {\n        require((_paused && msg.sender == guardian()) || msg.sender == owner(), \"Unauthorized\");\n        paused = _paused;\n    }\n\n    /**\n     * @notice Sets a custom price feed for this trove manager\n     * @param _priceFeedAddress Price feed address\n     */\n    function setPriceFeed(address _priceFeedAddress) external onlyOwner {\n        priceFeed = IPriceFeed(_priceFeedAddress);\n    }\n\n    /**\n     * @notice Sets a callback contract for redemptions on this trove manager\n     * @param redemptionsCallback Callback contract\n     */\n    function setRedemptionsCallback(ITroveRedemptionsCallback redemptionsCallback) external onlyOwner {\n        _redemptionsCallback = redemptionsCallback;\n    }\n\n    /**\n     * @notice Starts sunsetting a collateral\n     *         During sunsetting only the following are possible:\n               1) Disable collateral handoff to SP\n               2) Greatly Increase interest rate to incentivize redemptions\n               3) Remove redemptions fees\n               4) Disable new loans\n        @dev IMPORTANT: When sunsetting a collateral altogether this function should be called on\n                        all TM linked to that collateral as well as `StabilityPool.startCollateralSunset`\n     */\n    function startSunset() external onlyOwner {\n        sunsetting = true;\n        _accrueActiveInterests();\n        interestRate = SUNSETTING_INTEREST_RATE;\n        // accrual function doesn't update timestamp if interest was 0\n        lastActiveIndexUpdate = block.timestamp;\n        redemptionFeeFloor = 0;\n        maxSystemDebt = 0;\n    }\n\n    /**\n     * @notice Sets the redemption fees rebate percentage\n     * @param _redemptionFeesRebate Percentage of redemption fees rebated to users expressed in bps\n     */\n    function setRedemptionFeesRebate(uint16 _redemptionFeesRebate) external onlyOwner {\n        require(_redemptionFeesRebate <= 10000, \"Too large\");\n        redemptionFeesRebate = _redemptionFeesRebate;\n        emit RedemptionFeesRebateSet(_redemptionFeesRebate);\n    }\n\n    /*\n        _minuteDecayFactor is calculated as\n\n            10**18 * (1/2)**(1/n)\n\n        where n = the half-life in minutes\n     */\n    function setParameters(\n        uint256 _minuteDecayFactor,\n        uint256 _redemptionFeeFloor,\n        uint256 _maxRedemptionFee,\n        uint256 _borrowingFeeFloor,\n        uint256 _maxBorrowingFee,\n        uint256 _interestRateInBPS,\n        uint256 _maxSystemDebt,\n        uint256 _MCR\n    ) public {\n        require(!sunsetting, \"Cannot change after sunset\");\n        require(_MCR <= CCR && _MCR >= 1100000000000000000, \"MCR cannot be > CCR or < 110%\");\n        if (minuteDecayFactor != 0) {\n            require(msg.sender == owner(), \"Only owner\");\n        }\n        require(\n            _minuteDecayFactor >= 977159968434245000 && // half-life of 30 minutes\n                _minuteDecayFactor <= 999931237762985000 // half-life of 1 week\n        );\n        require(_redemptionFeeFloor <= _maxRedemptionFee && _maxRedemptionFee <= DECIMAL_PRECISION);\n        require(_borrowingFeeFloor <= _maxBorrowingFee && _maxBorrowingFee <= DECIMAL_PRECISION);\n\n        _decayBaseRate();\n\n        minuteDecayFactor = _minuteDecayFactor;\n        redemptionFeeFloor = _redemptionFeeFloor;\n        maxRedemptionFee = _maxRedemptionFee;\n        borrowingFeeFloor = _borrowingFeeFloor;\n        maxBorrowingFee = _maxBorrowingFee;\n        maxSystemDebt = _maxSystemDebt;\n\n        uint256 newInterestRate = (INTEREST_PRECISION * _interestRateInBPS) / (10000 * SECONDS_IN_YEAR);\n        if (newInterestRate != interestRate) {\n            _accrueActiveInterests();\n            // accrual function doesn't update timestamp if interest was 0\n            lastActiveIndexUpdate = block.timestamp;\n            interestRate = newInterestRate;\n        }\n        MCR = _MCR;\n    }\n\n    function collectInterests() external {\n        uint256 interestPayableCached = interestPayable;\n        require(interestPayableCached > 0, \"Nothing to collect\");\n        debtToken.mint(PRISMA_CORE.feeReceiver(), interestPayableCached);\n        interestPayable = 0;\n    }\n\n    // --- Getters ---\n\n    function fetchPrice() public returns (uint256) {\n        IPriceFeed _priceFeed = priceFeed;\n        if (address(_priceFeed) == address(0)) {\n            _priceFeed = IPriceFeed(PRISMA_CORE.priceFeed());\n        }\n        return _priceFeed.fetchPrice(address(collateralToken));\n    }\n\n    function getWeekAndDay() public view returns (uint256, uint256) {\n        uint256 duration = (block.timestamp - startTime);\n        uint256 week = duration / 1 weeks;\n        uint256 day = (duration % 1 weeks) / 1 days;\n        return (week, day);\n    }\n\n    function getTotalMints(uint256 week) external view returns (uint32[7] memory) {\n        return totalMints[week];\n    }\n\n    function getTroveOwnersCount() external view returns (uint256) {\n        return TroveOwners.length;\n    }\n\n    function getInterestRateInBps() external view returns (uint256) {\n        return (interestRate * 10000 * SECONDS_IN_YEAR) / INTEREST_PRECISION;\n    }\n\n    function getTroveFromTroveOwnersArray(uint256 _index) external view returns (address) {\n        return TroveOwners[_index];\n    }\n\n    function getTroveStatus(address _borrower) external view returns (uint256) {\n        return uint256(Troves[_borrower].status);\n    }\n\n    function getTroveStake(address _borrower) external view returns (uint256) {\n        return Troves[_borrower].stake;\n    }\n\n    /**\n        @notice Get the current total collateral and debt amounts for a trove\n        @dev Also includes pending rewards from redistribution\n     */\n    function getTroveCollAndDebt(address _borrower) public view returns (uint256 coll, uint256 debt) {\n        (debt, coll, , ) = getEntireDebtAndColl(_borrower);\n        return (coll, debt);\n    }\n\n    /**\n        @notice Get the total and pending collateral and debt amounts for a trove\n        @dev Used by the liquidation manager\n     */\n    function getEntireDebtAndColl(\n        address _borrower\n    ) public view returns (uint256 debt, uint256 coll, uint256 pendingDebtReward, uint256 pendingCollateralReward) {\n        Trove storage t = Troves[_borrower];\n        debt = t.debt;\n        coll = t.coll;\n\n        (pendingCollateralReward, pendingDebtReward) = getPendingCollAndDebtRewards(_borrower);\n        // Accrued trove interest for correct liquidation values. This assumes the index to be updated.\n        uint256 troveInterestIndex = t.activeInterestIndex;\n        if (troveInterestIndex > 0) {\n            (uint256 currentIndex, ) = _calculateInterestIndex();\n            debt = (debt * currentIndex) / troveInterestIndex;\n        }\n\n        debt = debt + pendingDebtReward;\n        coll = coll + pendingCollateralReward;\n    }\n\n    function getEntireSystemColl() public view returns (uint256) {\n        return totalActiveCollateral + defaultedCollateral;\n    }\n\n    function getEntireSystemDebt() public view returns (uint256) {\n        uint256 currentActiveDebt = totalActiveDebt;\n        (, uint256 interestFactor) = _calculateInterestIndex();\n        if (interestFactor > 0) {\n            uint256 activeInterests = Math.mulDiv(currentActiveDebt, interestFactor, INTEREST_PRECISION);\n            currentActiveDebt = currentActiveDebt + activeInterests;\n        }\n        return currentActiveDebt + defaultedDebt;\n    }\n\n    function getEntireSystemBalances() external returns (uint256, uint256, uint256) {\n        return (getEntireSystemColl(), getEntireSystemDebt(), fetchPrice());\n    }\n\n    // --- Helper functions ---\n\n    // Return the nominal collateral ratio (ICR) of a given Trove, without the price. Takes a trove's pending coll and debt rewards from redistributions into account.\n    function getNominalICR(address _borrower) public view returns (uint256) {\n        (uint256 currentCollateral, uint256 currentDebt) = getTroveCollAndDebt(_borrower);\n\n        uint256 NICR = PrismaMath._computeNominalCR(currentCollateral, currentDebt);\n        return NICR;\n    }\n\n    // Return the current collateral ratio (ICR) of a given Trove. Takes a trove's pending coll and debt rewards from redistributions into account.\n    function getCurrentICR(address _borrower, uint256 _price) public view returns (uint256) {\n        (uint256 currentCollateral, uint256 currentDebt) = getTroveCollAndDebt(_borrower);\n\n        uint256 ICR = PrismaMath._computeCR(currentCollateral, currentDebt, _price);\n        return ICR;\n    }\n\n    function getTotalActiveCollateral() public view returns (uint256) {\n        return totalActiveCollateral;\n    }\n\n    function getTotalActiveDebt() public view returns (uint256) {\n        uint256 currentActiveDebt = totalActiveDebt;\n        (, uint256 interestFactor) = _calculateInterestIndex();\n        if (interestFactor > 0) {\n            uint256 activeInterests = Math.mulDiv(currentActiveDebt, interestFactor, INTEREST_PRECISION);\n            currentActiveDebt = currentActiveDebt + activeInterests;\n        }\n        return currentActiveDebt;\n    }\n\n    // Get the borrower's pending accumulated collateral and debt rewards, earned by their stake\n    function getPendingCollAndDebtRewards(address _borrower) public view returns (uint256, uint256) {\n        RewardSnapshot memory snapshot = rewardSnapshots[_borrower];\n\n        uint256 coll = L_collateral - snapshot.collateral;\n        uint256 debt = L_debt - snapshot.debt;\n\n        if (coll + debt == 0 || Troves[_borrower].status != Status.active) return (0, 0);\n\n        uint256 stake = Troves[_borrower].stake;\n        return ((stake * coll) / DECIMAL_PRECISION, (stake * debt) / DECIMAL_PRECISION);\n    }\n\n    function hasPendingRewards(address _borrower) public view returns (bool) {\n        /*\n         * A Trove has pending rewards if its snapshot is less than the current rewards per-unit-staked sum:\n         * this indicates that rewards have occured since the snapshot was made, and the user therefore has\n         * pending rewards\n         */\n        if (Troves[_borrower].status != Status.active) {\n            return false;\n        }\n\n        return (rewardSnapshots[_borrower].collateral < L_collateral);\n    }\n\n    // --- Redemption fee functions ---\n\n    /*\n     * This function has two impacts on the baseRate state variable:\n     * 1) decays the baseRate based on time passed since last redemption or debt borrowing operation.\n     * then,\n     * 2) increases the baseRate based on the amount redeemed, as a proportion of total supply\n     */\n    function _updateBaseRateFromRedemption(\n        uint256 _collateralDrawn,\n        uint256 _price,\n        uint256 _totalDebtSupply\n    ) internal returns (uint256) {\n        uint256 decayedBaseRate = _calcDecayedBaseRate();\n\n        /* Convert the drawn collateral back to debt at face value rate (1 debt:1 USD), in order to get\n         * the fraction of total supply that was redeemed at face value. */\n        uint256 redeemedDebtFraction = (_collateralDrawn * _price) / _totalDebtSupply;\n\n        uint256 newBaseRate = decayedBaseRate + (redeemedDebtFraction / BETA);\n        newBaseRate = PrismaMath._min(newBaseRate, DECIMAL_PRECISION); // cap baseRate at a maximum of 100%\n\n        // Update the baseRate state variable\n        baseRate = newBaseRate;\n        emit BaseRateUpdated(newBaseRate);\n\n        _updateLastFeeOpTime();\n\n        return newBaseRate;\n    }\n\n    function getRedemptionRate() public view returns (uint256) {\n        return _calcRedemptionRate(baseRate);\n    }\n\n    function getRedemptionRateWithDecay() public view returns (uint256) {\n        return _calcRedemptionRate(_calcDecayedBaseRate());\n    }\n\n    function _calcRedemptionRate(uint256 _baseRate) internal view returns (uint256) {\n        return\n            PrismaMath._min(\n                redemptionFeeFloor + _baseRate,\n                maxRedemptionFee // cap at a maximum of 100%\n            );\n    }\n\n    function getRedemptionFeeWithDecay(uint256 _collateralDrawn) external view returns (uint256) {\n        return _calcRedemptionFee(getRedemptionRateWithDecay(), _collateralDrawn);\n    }\n\n    function _calcRedemptionFee(uint256 _redemptionRate, uint256 _collateralDrawn) internal pure returns (uint256) {\n        uint256 redemptionFee = (_redemptionRate * _collateralDrawn) / DECIMAL_PRECISION;\n        require(redemptionFee < _collateralDrawn, \"Fee exceeds returned collateral\");\n        return redemptionFee;\n    }\n\n    // --- Borrowing fee functions ---\n\n    function getBorrowingRate() public view returns (uint256) {\n        return _calcBorrowingRate(baseRate);\n    }\n\n    function getBorrowingRateWithDecay() public view returns (uint256) {\n        return _calcBorrowingRate(_calcDecayedBaseRate());\n    }\n\n    function _calcBorrowingRate(uint256 _baseRate) internal view returns (uint256) {\n        return PrismaMath._min(borrowingFeeFloor + _baseRate, maxBorrowingFee);\n    }\n\n    function getBorrowingFee(uint256 _debt) external view returns (uint256) {\n        return _calcBorrowingFee(getBorrowingRate(), _debt);\n    }\n\n    function getBorrowingFeeWithDecay(uint256 _debt) external view returns (uint256) {\n        return _calcBorrowingFee(getBorrowingRateWithDecay(), _debt);\n    }\n\n    function _calcBorrowingFee(uint256 _borrowingRate, uint256 _debt) internal pure returns (uint256) {\n        return (_borrowingRate * _debt) / DECIMAL_PRECISION;\n    }\n\n    // --- Internal fee functions ---\n\n    // Update the last fee operation time only if time passed >= decay interval. This prevents base rate griefing.\n    function _updateLastFeeOpTime() internal {\n        uint256 timePassed = block.timestamp - lastFeeOperationTime;\n\n        if (timePassed >= SECONDS_IN_ONE_MINUTE) {\n            lastFeeOperationTime = block.timestamp;\n            emit LastFeeOpTimeUpdated(block.timestamp);\n        }\n    }\n\n    function _calcDecayedBaseRate() internal view returns (uint256) {\n        uint256 minutesPassed = (block.timestamp - lastFeeOperationTime) / SECONDS_IN_ONE_MINUTE;\n        uint256 decayFactor = PrismaMath._decPow(minuteDecayFactor, minutesPassed);\n\n        return (baseRate * decayFactor) / DECIMAL_PRECISION;\n    }\n\n    // --- Redemption functions ---\n\n    /* Send _debtAmount debt to the system and redeem the corresponding amount of collateral from as many Troves as are needed to fill the redemption\n     * request.  Applies pending rewards to a Trove before reducing its debt and coll.\n     *\n     * Note that if _amount is very large, this function can run out of gas, specially if traversed troves are small. This can be easily avoided by\n     * splitting the total _amount in appropriate chunks and calling the function multiple times.\n     *\n     * Param `_maxIterations` can also be provided, so the loop through Troves is capped (if it’s zero, it will be ignored).This makes it easier to\n     * avoid OOG for the frontend, as only knowing approximately the average cost of an iteration is enough, without needing to know the “topology”\n     * of the trove list. It also avoids the need to set the cap in stone in the contract, nor doing gas calculations, as both gas price and opcode\n     * costs can vary.\n     *\n     * All Troves that are redeemed from -- with the likely exception of the last one -- will end up with no debt left, therefore they will be closed.\n     * If the last Trove does have some remaining debt, it has a finite ICR, and the reinsertion could be anywhere in the list, therefore it requires a hint.\n     * A frontend should use getRedemptionHints() to calculate what the ICR of this Trove will be after redemption, and pass a hint for its position\n     * in the sortedTroves list along with the ICR value that the hint was found for.\n     *\n     * If another transaction modifies the list between calling getRedemptionHints() and passing the hints to redeemCollateral(), it\n     * is very likely that the last (partially) redeemed Trove would end up with a different ICR than what the hint is for. In this case the\n     * redemption will stop after the last completely redeemed Trove and the sender will keep the remaining debt amount, which they can attempt\n     * to redeem later.\n     */\n    function redeemCollateral(\n        uint256 _debtAmount,\n        address _firstRedemptionHint,\n        address _upperPartialRedemptionHint,\n        address _lowerPartialRedemptionHint,\n        uint256 _partialRedemptionHintNICR,\n        uint256 _maxIterations,\n        uint256 _maxFeePercentage\n    ) external {\n        ISortedTroves _sortedTrovesCached = sortedTroves;\n        RedemptionTotals memory totals;\n\n        require(\n            _maxFeePercentage >= redemptionFeeFloor && _maxFeePercentage <= maxRedemptionFee,\n            \"Max fee 0.5% to 100%\"\n        );\n        require(block.timestamp >= systemDeploymentTime + bootstrapPeriod, \"BOOTSTRAP_PERIOD\");\n        totals.price = fetchPrice();\n        uint256 _MCR = MCR;\n        require(IBorrowerOperations(borrowerOperationsAddress).getTCR() >= _MCR, \"Cannot redeem when TCR < MCR\");\n        require(_debtAmount > 0, \"Amount must be greater than zero\");\n        require(debtToken.balanceOf(msg.sender) >= _debtAmount, \"Insufficient balance\");\n        _updateBalances();\n        totals.totalDebtSupplyAtStart = getEntireSystemDebt();\n\n        totals.remainingDebt = _debtAmount;\n        address currentBorrower;\n\n        if (_isValidFirstRedemptionHint(_sortedTrovesCached, _firstRedemptionHint, totals.price, _MCR)) {\n            currentBorrower = _firstRedemptionHint;\n        } else {\n            currentBorrower = _sortedTrovesCached.getLast();\n            // Find the first trove with ICR >= MCR\n            while (currentBorrower != address(0) && getCurrentICR(currentBorrower, totals.price) < _MCR) {\n                currentBorrower = _sortedTrovesCached.getPrev(currentBorrower);\n            }\n        }\n\n        // Loop through the Troves starting from the one with lowest collateral ratio until _amount of debt is exchanged for collateral\n        if (_maxIterations == 0 || _maxIterations > 100) {\n            _maxIterations = 100;\n        }\n        totals.trovesRedeemed = new ReedemedTrove[](_maxIterations);\n        totals.numberOfRedemptions = 0;\n        while (currentBorrower != address(0) && totals.remainingDebt > 0 && _maxIterations > 0) {\n            _maxIterations--;\n            // Save the address of the Trove preceding the current one, before potentially modifying the list\n            address nextUserToCheck = _sortedTrovesCached.getPrev(currentBorrower);\n\n            _applyPendingRewards(currentBorrower);\n            SingleRedemptionValues memory singleRedemption = _redeemCollateralFromTrove(\n                _sortedTrovesCached,\n                currentBorrower,\n                totals.remainingDebt,\n                totals.price,\n                _upperPartialRedemptionHint,\n                _lowerPartialRedemptionHint,\n                _partialRedemptionHintNICR\n            );\n            if (singleRedemption.cancelledPartial) break; // Partial redemption was cancelled (out-of-date hint, or new net debt < minimum), therefore we could not redeem from the last Trove\n\n            totals.trovesRedeemed[totals.numberOfRedemptions++] = ReedemedTrove(\n                currentBorrower,\n                singleRedemption.debtLot,\n                singleRedemption.collateralLot\n            );\n\n            totals.totalDebtToRedeem = totals.totalDebtToRedeem + singleRedemption.debtLot;\n            totals.totalCollateralDrawn = totals.totalCollateralDrawn + singleRedemption.collateralLot;\n\n            totals.remainingDebt = totals.remainingDebt - singleRedemption.debtLot;\n            currentBorrower = nextUserToCheck;\n        }\n        require(totals.totalCollateralDrawn > 0, \"Unable to redeem any amount\");\n\n        // Decay the baseRate due to time passed, and then increase it according to the size of this redemption.\n        // Use the saved total debt supply value, from before it was reduced by the redemption.\n        _updateBaseRateFromRedemption(totals.totalCollateralDrawn, totals.price, totals.totalDebtSupplyAtStart);\n\n        // Calculate the collateral fee\n        totals.collateralFee = sunsetting ? 0 : _calcRedemptionFee(getRedemptionRate(), totals.totalCollateralDrawn);\n\n        _requireUserAcceptsFee(totals.collateralFee, totals.totalCollateralDrawn, _maxFeePercentage);\n        uint256 userRebate = (redemptionFeesRebate * totals.collateralFee) / 10000;\n\n        uint256 _numberOfRedemptions = totals.numberOfRedemptions;\n        if (userRebate > 0) {\n            for (uint256 i; i < _numberOfRedemptions; ) {\n                ReedemedTrove memory refund = totals.trovesRedeemed[i];\n                surplusBalances[refund.account] += (userRebate * refund.collateralLot) / totals.totalCollateralDrawn;\n                unchecked {\n                    ++i;\n                }\n            }\n        }\n\n        if (redemptionFeesRebate < 10000) {\n            uint256 treasuryRebate = totals.collateralFee - userRebate;\n            _sendCollateral(PRISMA_CORE.feeReceiver(), treasuryRebate);\n        }\n        totals.collateralToSendToRedeemer = totals.totalCollateralDrawn - totals.collateralFee;\n\n        emit Redemption(_debtAmount, totals.totalDebtToRedeem, totals.totalCollateralDrawn, totals.collateralFee);\n\n        // Burn the total debt that is cancelled with debt, and send the redeemed collateral to msg.sender\n        debtToken.burn(msg.sender, totals.totalDebtToRedeem);\n        // Update Trove Manager debt, and send collateral to account\n        totalActiveDebt = totalActiveDebt - totals.totalDebtToRedeem;\n        _sendCollateral(msg.sender, totals.collateralToSendToRedeemer);\n        _resetState();\n        if (address(_redemptionsCallback) != address(0)) {\n            assembly {\n                // Load array pointer value at slot 9 in the struct\n                let trovesRedeemedLengthSlot := mload(add(totals, 0x120))\n                // Set array length in referenced location\n                mstore(trovesRedeemedLengthSlot, _numberOfRedemptions)\n            }\n            _redemptionsCallback.onRedemptions(totals.trovesRedeemed);\n        }\n    }\n\n    // Redeem as much collateral as possible from _borrower's Trove in exchange for debt up to _maxDebtAmount\n    function _redeemCollateralFromTrove(\n        ISortedTroves _sortedTrovesCached,\n        address _borrower,\n        uint256 _maxDebtAmount,\n        uint256 _price,\n        address _upperPartialRedemptionHint,\n        address _lowerPartialRedemptionHint,\n        uint256 _partialRedemptionHintNICR\n    ) internal returns (SingleRedemptionValues memory singleRedemption) {\n        Trove storage t = Troves[_borrower];\n        // Determine the remaining amount (lot) to be redeemed, capped by the entire debt of the Trove minus the liquidation reserve\n        singleRedemption.debtLot = PrismaMath._min(_maxDebtAmount, t.debt - DEBT_GAS_COMPENSATION);\n\n        // Get the CollateralLot of equivalent value in USD\n        singleRedemption.collateralLot = (singleRedemption.debtLot * DECIMAL_PRECISION) / _price;\n\n        // Decrease the debt and collateral of the current Trove according to the debt lot and corresponding collateral to send\n        uint256 newDebt = (t.debt) - singleRedemption.debtLot;\n        uint256 newColl = (t.coll) - singleRedemption.collateralLot;\n\n        if (newDebt == DEBT_GAS_COMPENSATION) {\n            // No debt left in the Trove (except for the liquidation reserve), therefore the trove gets closed\n            _removeStake(_borrower);\n            _closeTrove(_borrower, Status.closedByRedemption);\n            _redeemCloseTrove(_borrower, DEBT_GAS_COMPENSATION, newColl);\n            emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.redeemCollateral);\n        } else {\n            uint256 newNICR = PrismaMath._computeNominalCR(newColl, newDebt);\n            /*\n             * If the provided hint is out of date, we bail since trying to reinsert without a good hint will almost\n             * certainly result in running out of gas.\n             *\n             * If the resultant net debt of the partial is less than the minimum, net debt we bail.\n             */\n\n            {\n                // We check if the ICR hint is reasonable up to date, with continuous interest there might be slight differences (<1bps)\n                uint256 icrError = _partialRedemptionHintNICR > newNICR\n                    ? _partialRedemptionHintNICR - newNICR\n                    : newNICR - _partialRedemptionHintNICR;\n                if (\n                    icrError > 5e14 ||\n                    _getNetDebt(newDebt) < IBorrowerOperations(borrowerOperationsAddress).minNetDebt()\n                ) {\n                    singleRedemption.cancelledPartial = true;\n                    return singleRedemption;\n                }\n            }\n\n            _sortedTrovesCached.reInsert(_borrower, newNICR, _upperPartialRedemptionHint, _lowerPartialRedemptionHint);\n\n            t.debt = newDebt;\n            t.coll = newColl;\n            _updateStakeAndTotalStakes(t);\n\n            emit TroveUpdated(_borrower, newDebt, newColl, t.stake, TroveManagerOperation.redeemCollateral);\n        }\n\n        return singleRedemption;\n    }\n\n    /*\n     * Called when a full redemption occurs, and closes the trove.\n     * The redeemer swaps (debt - liquidation reserve) debt for (debt - liquidation reserve) worth of collateral, so the debt liquidation reserve left corresponds to the remaining debt.\n     * In order to close the trove, the debt liquidation reserve is burned, and the corresponding debt is removed.\n     * The debt recorded on the trove's struct is zero'd elswhere, in _closeTrove.\n     * Any surplus collateral left in the trove can be later claimed by the borrower.\n     */\n    function _redeemCloseTrove(address _borrower, uint256 _debt, uint256 _collateral) internal {\n        debtToken.burn(gasPoolAddress, _debt);\n        totalActiveDebt = totalActiveDebt - _debt;\n\n        surplusBalances[_borrower] += _collateral;\n        totalActiveCollateral -= _collateral;\n    }\n\n    function _isValidFirstRedemptionHint(\n        ISortedTroves _sortedTroves,\n        address _firstRedemptionHint,\n        uint256 _price,\n        uint256 _MCR\n    ) internal view returns (bool) {\n        if (\n            _firstRedemptionHint == address(0) ||\n            !_sortedTroves.contains(_firstRedemptionHint) ||\n            getCurrentICR(_firstRedemptionHint, _price) < _MCR\n        ) {\n            return false;\n        }\n\n        address nextTrove = _sortedTroves.getNext(_firstRedemptionHint);\n        return nextTrove == address(0) || getCurrentICR(nextTrove, _price) < _MCR;\n    }\n\n    /**\n     * Claim remaining collateral from a redemption or from a liquidation with ICR > MCR in Recovery Mode\n     */\n    function claimCollateral(address _receiver) external {\n        uint256 claimableColl = surplusBalances[msg.sender];\n        require(claimableColl > 0, \"No collateral available to claim\");\n\n        surplusBalances[msg.sender] = 0;\n\n        collateralToken.safeTransfer(_receiver, claimableColl);\n    }\n\n    // --- Reward Claim functions ---\n\n    function claimReward(address receiver) external returns (uint256) {\n        uint256 amount = _claimReward(msg.sender);\n\n        if (amount > 0) {\n            vault.transferAllocatedTokens(msg.sender, receiver, amount);\n        }\n        emit RewardClaimed(msg.sender, receiver, amount);\n        return amount;\n    }\n\n    function vaultClaimReward(address claimant, address) external returns (uint256) {\n        require(msg.sender == address(vault));\n\n        return _claimReward(claimant);\n    }\n\n    function _claimReward(address account) internal returns (uint256) {\n        require(emissionId.debt > 0, \"Rewards not active\");\n        // update active debt rewards\n        _applyPendingRewards(account);\n        uint256 amount = storedPendingReward[account];\n        if (amount > 0) storedPendingReward[account] = 0;\n\n        // add pending mint awards\n        uint256 mintAmount = _getPendingMintReward(account);\n        if (mintAmount > 0) {\n            amount += mintAmount;\n            delete accountLatestMint[account];\n        }\n\n        return amount;\n    }\n\n    function claimableReward(address account) external view returns (uint256) {\n        // previously calculated rewards\n        uint256 amount = storedPendingReward[account];\n\n        // pending active debt rewards\n        uint256 updated = periodFinish;\n        if (updated > block.timestamp) updated = block.timestamp;\n        uint256 duration = updated - lastUpdate;\n        uint256 integral = rewardIntegral;\n        if (duration > 0) {\n            uint256 supply = totalActiveDebt;\n            if (supply > 0) {\n                integral += (duration * rewardRate * 1e18) / supply;\n            }\n        }\n        uint256 integralFor = rewardIntegralFor[account];\n\n        if (integral > integralFor) {\n            amount += (Troves[account].debt * (integral - integralFor)) / 1e18;\n        }\n\n        // pending mint rewards\n        amount += _getPendingMintReward(account);\n\n        return amount;\n    }\n\n    function _getPendingMintReward(address account) internal view returns (uint256 amount) {\n        VolumeData memory data = accountLatestMint[account];\n        if (data.amount > 0) {\n            (uint256 week, uint256 day) = getWeekAndDay();\n            if (data.day != day || data.week != week) {\n                return (dailyMintReward[data.week] * data.amount) / totalMints[data.week][data.day];\n            }\n        }\n    }\n\n    function _updateIntegrals(address account, uint256 balance, uint256 supply) internal {\n        uint256 integral = _updateRewardIntegral(supply);\n        _updateIntegralForAccount(account, balance, integral);\n    }\n\n    function _updateIntegralForAccount(address account, uint256 balance, uint256 currentIntegral) internal {\n        uint256 integralFor = rewardIntegralFor[account];\n\n        if (currentIntegral > integralFor) {\n            storedPendingReward[account] += (balance * (currentIntegral - integralFor)) / 1e18;\n            rewardIntegralFor[account] = currentIntegral;\n        }\n    }\n\n    function _updateRewardIntegral(uint256 supply) internal returns (uint256 integral) {\n        uint256 _periodFinish = periodFinish;\n        uint256 updated = _periodFinish;\n        if (updated > block.timestamp) updated = block.timestamp;\n        uint256 duration = updated - lastUpdate;\n        integral = rewardIntegral;\n        if (duration > 0) {\n            lastUpdate = uint32(updated);\n            if (supply > 0) {\n                integral += (duration * rewardRate * 1e18) / supply;\n                rewardIntegral = integral;\n            }\n        }\n        _fetchRewards(_periodFinish);\n\n        return integral;\n    }\n\n    function _fetchRewards(uint256 _periodFinish) internal {\n        EmissionId memory id = emissionId;\n        if (id.debt == 0) return;\n        uint256 currentWeek = getWeek();\n        if (currentWeek < (_periodFinish - startTime) / 1 weeks) return;\n        uint256 previousWeek = (_periodFinish - startTime) / 1 weeks - 1;\n\n        // active debt rewards\n        uint256 amount = vault.allocateNewEmissions(id.debt);\n        if (block.timestamp < _periodFinish) {\n            uint256 remaining = _periodFinish - block.timestamp;\n            amount += remaining * rewardRate;\n        }\n        rewardRate = uint128(amount / REWARD_DURATION);\n        lastUpdate = uint32(block.timestamp);\n        periodFinish = uint32(block.timestamp + REWARD_DURATION);\n\n        // minting rewards\n        amount = vault.allocateNewEmissions(id.minting);\n        uint256 reward = dailyMintReward[previousWeek];\n        if (reward > 0) {\n            uint32[7] memory totals = totalMints[previousWeek];\n            for (uint256 i = 0; i < 7; i++) {\n                if (totals[i] == 0) {\n                    amount += reward;\n                }\n            }\n        }\n        dailyMintReward[currentWeek] = amount / 7;\n    }\n\n    // --- Trove Adjustment functions ---\n\n    function openTrove(\n        address _borrower,\n        uint256 _collateralAmount,\n        uint256 _compositeDebt,\n        uint256 NICR,\n        address _upperHint,\n        address _lowerHint,\n        bool _isRecoveryMode\n    ) external whenNotPaused returns (uint256 stake, uint256 arrayIndex) {\n        _requireCallerIsBO();\n        require(!sunsetting, \"Cannot open while sunsetting\");\n        uint256 supply = totalActiveDebt;\n\n        Trove storage t = Troves[_borrower];\n        require(t.status != Status.active, \"BorrowerOps: Trove is active\");\n        t.status = Status.active;\n        t.coll = _collateralAmount;\n        t.debt = _compositeDebt;\n        uint256 currentInterestIndex = _accrueActiveInterests();\n        t.activeInterestIndex = currentInterestIndex;\n        _updateTroveRewardSnapshots(_borrower);\n        stake = _updateStakeAndTotalStakes(t);\n        sortedTroves.insert(_borrower, NICR, _upperHint, _lowerHint);\n\n        TroveOwners.push(_borrower);\n        arrayIndex = TroveOwners.length - 1;\n        t.arrayIndex = uint128(arrayIndex);\n\n        _updateIntegrals(_borrower, 0, supply);\n        if (!_isRecoveryMode) _updateMintVolume(_borrower, _compositeDebt);\n\n        totalActiveCollateral = totalActiveCollateral + _collateralAmount;\n        uint256 _newTotalDebt = totalActiveDebt + _compositeDebt;\n        require(_newTotalDebt + defaultedDebt <= maxSystemDebt, \"Collateral debt limit reached\");\n        totalActiveDebt = _newTotalDebt;\n        emit TroveUpdated(_borrower, _compositeDebt, _collateralAmount, stake, TroveManagerOperation.open);\n    }\n\n    function updateTroveFromAdjustment(\n        bool _isRecoveryMode,\n        bool _isDebtIncrease,\n        uint256 _debtChange,\n        uint256 _netDebtChange,\n        bool _isCollIncrease,\n        uint256 _collChange,\n        address _upperHint,\n        address _lowerHint,\n        address _borrower,\n        address _receiver\n    ) external returns (uint256, uint256, uint256) {\n        _requireCallerIsBO();\n        if (_isCollIncrease || _isDebtIncrease) {\n            require(!paused, \"Collateral Paused\");\n            require(!sunsetting, \"Cannot increase while sunsetting\");\n        }\n\n        Trove storage t = Troves[_borrower];\n        require(t.status == Status.active, \"Trove closed or does not exist\");\n\n        uint256 newDebt = t.debt;\n        if (_debtChange > 0) {\n            if (_isDebtIncrease) {\n                newDebt = newDebt + _netDebtChange;\n                if (!_isRecoveryMode) _updateMintVolume(_borrower, _netDebtChange);\n                _increaseDebt(_receiver, _netDebtChange, _debtChange);\n            } else {\n                newDebt = newDebt - _netDebtChange;\n                _decreaseDebt(_receiver, _debtChange);\n            }\n            t.debt = newDebt;\n        }\n\n        uint256 newColl = t.coll;\n        if (_collChange > 0) {\n            if (_isCollIncrease) {\n                newColl = newColl + _collChange;\n                totalActiveCollateral = totalActiveCollateral + _collChange;\n                // trust that BorrowerOperations sent the collateral\n            } else {\n                newColl = newColl - _collChange;\n                _sendCollateral(_receiver, _collChange);\n            }\n            t.coll = newColl;\n        }\n\n        uint256 newNICR = PrismaMath._computeNominalCR(newColl, newDebt);\n        sortedTroves.reInsert(_borrower, newNICR, _upperHint, _lowerHint);\n        uint256 newStake = _updateStakeAndTotalStakes(t);\n        emit TroveUpdated(_borrower, newDebt, newColl, newStake, TroveManagerOperation.adjust);\n\n        return (newColl, newDebt, newStake);\n    }\n\n    function closeTrove(address _borrower, address _receiver, uint256 collAmount, uint256 debtAmount) external {\n        _requireCallerIsBO();\n        require(Troves[_borrower].status == Status.active, \"Trove closed or does not exist\");\n        _removeStake(_borrower);\n        _closeTrove(_borrower, Status.closedByOwner);\n        if (TroveOwners.length > 0) {\n            totalActiveDebt = totalActiveDebt - debtAmount;\n        } else {\n            // Account for dust discrepancies due to interest sampling\n            totalActiveDebt = 0;\n        }\n        _sendCollateral(_receiver, collAmount);\n        _resetState();\n        emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.close);\n    }\n\n    /**\n        @dev Only called from `closeTrove` because liquidating the final trove is blocked in\n             `LiquidationManager`. Many liquidation paths involve redistributing debt and\n             collateral to existing troves. If the collateral is being sunset, the final trove\n             must be closed by repaying the debt or via a redemption.\n     */\n    function _resetState() private {\n        if (TroveOwners.length == 0) {\n            activeInterestIndex = INTEREST_PRECISION;\n            lastActiveIndexUpdate = block.timestamp;\n            totalStakes = 0;\n            totalStakesSnapshot = 0;\n            totalCollateralSnapshot = 0;\n            L_collateral = 0;\n            L_debt = 0;\n            lastCollateralError_Redistribution = 0;\n            lastDebtError_Redistribution = 0;\n            totalActiveCollateral = 0;\n            totalActiveDebt = 0;\n            defaultedCollateral = 0;\n            defaultedDebt = 0;\n        }\n    }\n\n    function _closeTrove(address _borrower, Status closedStatus) internal {\n        uint256 TroveOwnersArrayLength = TroveOwners.length;\n\n        Trove storage t = Troves[_borrower];\n        t.status = closedStatus;\n        t.coll = 0;\n        t.debt = 0;\n        t.activeInterestIndex = 0;\n        ISortedTroves sortedTrovesCached = sortedTroves;\n        rewardSnapshots[_borrower].collateral = 0;\n        rewardSnapshots[_borrower].debt = 0;\n        if (TroveOwnersArrayLength > 1 && sortedTrovesCached.getSize() > 1) {\n            // remove trove owner from the TroveOwners array, not preserving array order\n            uint128 index = t.arrayIndex;\n            address addressToMove = TroveOwners[TroveOwnersArrayLength - 1];\n            TroveOwners[index] = addressToMove;\n            Troves[addressToMove].arrayIndex = index;\n            emit TroveIndexUpdated(addressToMove, index);\n        }\n\n        TroveOwners.pop();\n\n        sortedTrovesCached.remove(_borrower);\n        t.arrayIndex = 0;\n    }\n\n    function _updateMintVolume(address account, uint256 initialAmount) internal {\n        uint32 amount = uint32(initialAmount / VOLUME_MULTIPLIER);\n        (uint256 week, uint256 day) = getWeekAndDay();\n        totalMints[week][day] += amount;\n\n        VolumeData memory data = accountLatestMint[account];\n        if (data.day == day && data.week == week) {\n            // if the caller made a previous redemption today, we only increase their redeemed amount\n            accountLatestMint[account].amount = data.amount + amount;\n        } else {\n            if (data.amount > 0) {\n                // if the caller made a previous redemption on a different day,\n                // calculate the emissions earned for that redemption\n                uint256 pending = (dailyMintReward[data.week] * data.amount) / totalMints[data.week][data.day];\n                storedPendingReward[account] += pending;\n            }\n            accountLatestMint[account] = VolumeData({ week: uint32(week), day: uint32(day), amount: amount });\n        }\n    }\n\n    // Updates the baseRate state variable based on time elapsed since the last redemption or debt borrowing operation.\n    function decayBaseRateAndGetBorrowingFee(uint256 _debt) external returns (uint256) {\n        _requireCallerIsBO();\n        uint256 rate = _decayBaseRate();\n\n        return _calcBorrowingFee(_calcBorrowingRate(rate), _debt);\n    }\n\n    function _decayBaseRate() internal returns (uint256) {\n        uint256 decayedBaseRate = _calcDecayedBaseRate();\n\n        baseRate = decayedBaseRate;\n        emit BaseRateUpdated(decayedBaseRate);\n\n        _updateLastFeeOpTime();\n\n        return decayedBaseRate;\n    }\n\n    function applyPendingRewards(address _borrower) external returns (uint256 coll, uint256 debt) {\n        _requireCallerIsBO();\n        return _applyPendingRewards(_borrower);\n    }\n\n    // Add the borrowers's coll and debt rewards earned from redistributions, to their Trove\n    function _applyPendingRewards(address _borrower) internal returns (uint256 coll, uint256 debt) {\n        Trove storage t = Troves[_borrower];\n        if (t.status == Status.active) {\n            uint256 troveInterestIndex = t.activeInterestIndex;\n            uint256 supply = totalActiveDebt;\n            uint256 currentInterestIndex = _accrueActiveInterests();\n            debt = t.debt;\n            uint256 prevDebt = debt;\n            coll = t.coll;\n            // We accrued interests for this trove if not already updated\n            if (troveInterestIndex < currentInterestIndex) {\n                debt = (debt * currentInterestIndex) / troveInterestIndex;\n                t.activeInterestIndex = currentInterestIndex;\n            }\n\n            if (rewardSnapshots[_borrower].collateral < L_collateral) {\n                // Compute pending rewards\n                (uint256 pendingCollateralReward, uint256 pendingDebtReward) = getPendingCollAndDebtRewards(_borrower);\n\n                // Apply pending rewards to trove's state\n                coll = coll + pendingCollateralReward;\n                t.coll = coll;\n                debt = debt + pendingDebtReward;\n\n                _updateTroveRewardSnapshots(_borrower);\n\n                _movePendingTroveRewardsToActiveBalance(pendingDebtReward, pendingCollateralReward);\n            }\n            if (prevDebt != debt) {\n                t.debt = debt;\n            }\n            _updateIntegrals(_borrower, prevDebt, supply);\n        }\n        return (coll, debt);\n    }\n\n    function _updateTroveRewardSnapshots(address _borrower) internal {\n        uint256 L_collateralCached = L_collateral;\n        uint256 L_debtCached = L_debt;\n        rewardSnapshots[_borrower] = RewardSnapshot(L_collateralCached, L_debtCached);\n        emit TroveSnapshotsUpdated(L_collateralCached, L_debtCached);\n    }\n\n    // Remove borrower's stake from the totalStakes sum, and set their stake to 0\n    function _removeStake(address _borrower) internal {\n        uint256 stake = Troves[_borrower].stake;\n        totalStakes = totalStakes - stake;\n        Troves[_borrower].stake = 0;\n    }\n\n    // Update borrower's stake based on their latest collateral value\n    function _updateStakeAndTotalStakes(Trove storage t) internal returns (uint256) {\n        uint256 newStake = _computeNewStake(t.coll);\n        uint256 oldStake = t.stake;\n        t.stake = newStake;\n        uint256 newTotalStakes = totalStakes - oldStake + newStake;\n        totalStakes = newTotalStakes;\n        emit TotalStakesUpdated(newTotalStakes);\n\n        return newStake;\n    }\n\n    // Calculate a new stake based on the snapshots of the totalStakes and totalCollateral taken at the last liquidation\n    function _computeNewStake(uint256 _coll) internal view returns (uint256) {\n        uint256 stake;\n        uint256 totalCollateralSnapshotCached = totalCollateralSnapshot;\n        if (totalCollateralSnapshotCached == 0) {\n            stake = _coll;\n        } else {\n            /*\n             * The following assert() holds true because:\n             * - The system always contains >= 1 trove\n             * - When we close or liquidate a trove, we redistribute the pending rewards, so if all troves were closed/liquidated,\n             * rewards would’ve been emptied and totalCollateralSnapshot would be zero too.\n             */\n            uint256 totalStakesSnapshotCached = totalStakesSnapshot;\n            assert(totalStakesSnapshotCached > 0);\n            stake = (_coll * totalStakesSnapshotCached) / totalCollateralSnapshotCached;\n        }\n        return stake;\n    }\n\n    // --- Liquidation Functions ---\n\n    function closeTroveByLiquidation(address _borrower) external {\n        _requireCallerIsLM();\n        uint256 debtBefore = Troves[_borrower].debt;\n        _removeStake(_borrower);\n        _closeTrove(_borrower, Status.closedByLiquidation);\n        _updateIntegralForAccount(_borrower, debtBefore, rewardIntegral);\n        emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidate);\n    }\n\n    function movePendingTroveRewardsToActiveBalances(uint256 _debt, uint256 _collateral) external {\n        _requireCallerIsLM();\n        _movePendingTroveRewardsToActiveBalance(_debt, _collateral);\n    }\n\n    function _movePendingTroveRewardsToActiveBalance(uint256 _debt, uint256 _collateral) internal {\n        defaultedDebt -= _debt;\n        totalActiveDebt += _debt;\n        defaultedCollateral -= _collateral;\n        totalActiveCollateral += _collateral;\n    }\n\n    function addCollateralSurplus(address borrower, uint256 collSurplus) external {\n        _requireCallerIsLM();\n        surplusBalances[borrower] += collSurplus;\n    }\n\n    function finalizeLiquidation(\n        address _liquidator,\n        uint256 _debt,\n        uint256 _coll,\n        uint256 _collSurplus,\n        uint256 _debtGasComp,\n        uint256 _collGasComp\n    ) external {\n        _requireCallerIsLM();\n        // redistribute debt and collateral\n        _redistributeDebtAndColl(_debt, _coll);\n\n        uint256 _activeColl = totalActiveCollateral;\n        if (_collSurplus > 0) {\n            _activeColl -= _collSurplus;\n            totalActiveCollateral = _activeColl;\n        }\n\n        // update system snapshos\n        totalStakesSnapshot = totalStakes;\n        totalCollateralSnapshot = _activeColl - _collGasComp + defaultedCollateral;\n        emit SystemSnapshotsUpdated(totalStakesSnapshot, totalCollateralSnapshot);\n\n        // send gas compensation\n        debtToken.returnFromPool(gasPoolAddress, _liquidator, _debtGasComp);\n        _sendCollateral(_liquidator, _collGasComp);\n    }\n\n    function _redistributeDebtAndColl(uint256 _debt, uint256 _coll) internal {\n        if (_debt == 0) {\n            return;\n        }\n        /*\n         * Add distributed coll and debt rewards-per-unit-staked to the running totals. Division uses a \"feedback\"\n         * error correction, to keep the cumulative error low in the running totals L_collateral and L_debt:\n         *\n         * 1) Form numerators which compensate for the floor division errors that occurred the last time this\n         * function was called.\n         * 2) Calculate \"per-unit-staked\" ratios.\n         * 3) Multiply each ratio back by its denominator, to reveal the current floor division error.\n         * 4) Store these errors for use in the next correction when this function is called.\n         * 5) Note: static analysis tools complain about this \"division before multiplication\", however, it is intended.\n         */\n        uint256 collateralNumerator = (_coll * DECIMAL_PRECISION) + lastCollateralError_Redistribution;\n        uint256 debtNumerator = (_debt * DECIMAL_PRECISION) + lastDebtError_Redistribution;\n        uint256 totalStakesCached = totalStakes;\n        // Get the per-unit-staked terms\n        uint256 collateralRewardPerUnitStaked = collateralNumerator / totalStakesCached;\n        uint256 debtRewardPerUnitStaked = debtNumerator / totalStakesCached;\n\n        lastCollateralError_Redistribution = collateralNumerator - (collateralRewardPerUnitStaked * totalStakesCached);\n        lastDebtError_Redistribution = debtNumerator - (debtRewardPerUnitStaked * totalStakesCached);\n\n        // Add per-unit-staked terms to the running totals\n        uint256 new_L_collateral = L_collateral + collateralRewardPerUnitStaked;\n        uint256 new_L_debt = L_debt + debtRewardPerUnitStaked;\n        L_collateral = new_L_collateral;\n        L_debt = new_L_debt;\n\n        emit LTermsUpdated(new_L_collateral, new_L_debt);\n\n        totalActiveDebt -= _debt;\n        defaultedDebt += _debt;\n        defaultedCollateral += _coll;\n        totalActiveCollateral -= _coll;\n    }\n\n    // --- Trove property setters ---\n\n    function _sendCollateral(address _account, uint256 _amount) private {\n        if (_amount > 0) {\n            totalActiveCollateral = totalActiveCollateral - _amount;\n            emit CollateralSent(_account, _amount);\n\n            collateralToken.safeTransfer(_account, _amount);\n        }\n    }\n\n    function _increaseDebt(address account, uint256 netDebtAmount, uint256 debtAmount) internal {\n        uint256 _newTotalDebt = totalActiveDebt + netDebtAmount;\n        require(_newTotalDebt + defaultedDebt <= maxSystemDebt, \"Collateral debt limit reached\");\n        totalActiveDebt = _newTotalDebt;\n        debtToken.mint(account, debtAmount);\n    }\n\n    function decreaseDebtAndSendCollateral(address account, uint256 debt, uint256 coll) external {\n        _requireCallerIsLM();\n        _decreaseDebt(account, debt);\n        _sendCollateral(account, coll);\n    }\n\n    function _decreaseDebt(address account, uint256 amount) internal {\n        debtToken.burn(account, amount);\n        totalActiveDebt = totalActiveDebt - amount;\n    }\n\n    // --- Balances and interest ---\n\n    function updateBalances() external {\n        _requireCallerIsLM();\n        _updateBalances();\n    }\n\n    function _updateBalances() private {\n        _updateRewardIntegral(totalActiveDebt);\n        _accrueActiveInterests();\n    }\n\n    // This function must be called any time the debt or the interest changes\n    function _accrueActiveInterests() internal returns (uint256) {\n        (uint256 currentInterestIndex, uint256 interestFactor) = _calculateInterestIndex();\n        if (interestFactor > 0) {\n            uint256 currentDebt = totalActiveDebt;\n            uint256 activeInterests = Math.mulDiv(currentDebt, interestFactor, INTEREST_PRECISION);\n            totalActiveDebt = currentDebt + activeInterests;\n            interestPayable = interestPayable + activeInterests;\n            activeInterestIndex = currentInterestIndex;\n            lastActiveIndexUpdate = block.timestamp;\n        }\n        return currentInterestIndex;\n    }\n\n    function _calculateInterestIndex() internal view returns (uint256 currentInterestIndex, uint256 interestFactor) {\n        uint256 lastIndexUpdateCached = lastActiveIndexUpdate;\n        // Short circuit if we updated in the current block\n        if (lastIndexUpdateCached == block.timestamp) return (activeInterestIndex, 0);\n        uint256 currentInterest = interestRate;\n        currentInterestIndex = activeInterestIndex; // we need to return this if it's already up to date\n        if (currentInterest > 0) {\n            /*\n             * Calculate the interest accumulated and the new index:\n             * We compound the index and increase the debt accordingly\n             */\n            uint256 deltaT = block.timestamp - lastIndexUpdateCached;\n            interestFactor = deltaT * currentInterest;\n            currentInterestIndex =\n                currentInterestIndex +\n                Math.mulDiv(currentInterestIndex, interestFactor, INTEREST_PRECISION);\n        }\n    }\n\n    // --- Requires ---\n\n    function _requireCallerIsBO() internal view {\n        require(msg.sender == borrowerOperationsAddress, \"Caller not BO\");\n    }\n\n    function _requireCallerIsLM() internal view {\n        require(msg.sender == liquidationManager, \"Not Liquidation Manager\");\n    }\n}\n"
      },
      "contracts/dependencies/PrismaBase.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.19;\n\n/*\n * Base contract for TroveManager, BorrowerOperations and StabilityPool. Contains global system constants and\n * common functions.\n */\ncontract PrismaBase {\n    uint256 public constant DECIMAL_PRECISION = 1e18;\n\n    // Critical system collateral ratio. If the system's total collateral ratio (TCR) falls below the CCR, Recovery Mode is triggered.\n    uint256 public constant CCR = 1500000000000000000; // 150%\n\n    // Amount of debt to be locked in gas pool on opening troves\n    uint256 public immutable DEBT_GAS_COMPENSATION;\n\n    uint256 public constant PERCENT_DIVISOR = 200; // dividing by 200 yields 0.5%\n\n    constructor(uint256 _gasCompensation) {\n        DEBT_GAS_COMPENSATION = _gasCompensation;\n    }\n\n    // --- Gas compensation functions ---\n\n    // Returns the composite debt (drawn debt + gas compensation) of a trove, for the purpose of ICR calculation\n    function _getCompositeDebt(uint256 _debt) internal view returns (uint256) {\n        return _debt + DEBT_GAS_COMPENSATION;\n    }\n\n    function _getNetDebt(uint256 _debt) internal view returns (uint256) {\n        return _debt - DEBT_GAS_COMPENSATION;\n    }\n\n    // Return the amount of collateral to be drawn from a trove's collateral and sent as gas compensation.\n    function _getCollGasCompensation(uint256 _entireColl) internal pure returns (uint256) {\n        return _entireColl / PERCENT_DIVISOR;\n    }\n\n    function _requireUserAcceptsFee(uint256 _fee, uint256 _amount, uint256 _maxFeePercentage) internal pure {\n        uint256 feePercentage = (_fee * DECIMAL_PRECISION) / _amount;\n        require(feePercentage <= _maxFeePercentage, \"Fee exceeded provided maximum\");\n    }\n}\n"
      },
      "contracts/dependencies/PrismaMath.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.19;\n\nlibrary PrismaMath {\n    uint256 internal constant DECIMAL_PRECISION = 1e18;\n\n    /* Precision for Nominal ICR (independent of price). Rationale for the value:\n     *\n     * - Making it “too high” could lead to overflows.\n     * - Making it “too low” could lead to an ICR equal to zero, due to truncation from Solidity floor division.\n     *\n     * This value of 1e20 is chosen for safety: the NICR will only overflow for numerator > ~1e39,\n     * and will only truncate to 0 if the denominator is at least 1e20 times greater than the numerator.\n     *\n     */\n    uint256 internal constant NICR_PRECISION = 1e20;\n\n    function _min(uint256 _a, uint256 _b) internal pure returns (uint256) {\n        return (_a < _b) ? _a : _b;\n    }\n\n    function _max(uint256 _a, uint256 _b) internal pure returns (uint256) {\n        return (_a >= _b) ? _a : _b;\n    }\n\n    /*\n     * Multiply two decimal numbers and use normal rounding rules:\n     * -round product up if 19'th mantissa digit >= 5\n     * -round product down if 19'th mantissa digit < 5\n     *\n     * Used only inside the exponentiation, _decPow().\n     */\n    function decMul(uint256 x, uint256 y) internal pure returns (uint256 decProd) {\n        uint256 prod_xy = x * y;\n\n        decProd = (prod_xy + (DECIMAL_PRECISION / 2)) / DECIMAL_PRECISION;\n    }\n\n    /*\n     * _decPow: Exponentiation function for 18-digit decimal base, and integer exponent n.\n     *\n     * Uses the efficient \"exponentiation by squaring\" algorithm. O(log(n)) complexity.\n     *\n     * Called by two functions that represent time in units of minutes:\n     * 1) TroveManager._calcDecayedBaseRate\n     * 2) CommunityIssuance._getCumulativeIssuanceFraction\n     *\n     * The exponent is capped to avoid reverting due to overflow. The cap 525600000 equals\n     * \"minutes in 1000 years\": 60 * 24 * 365 * 1000\n     *\n     * If a period of > 1000 years is ever used as an exponent in either of the above functions, the result will be\n     * negligibly different from just passing the cap, since:\n     *\n     * In function 1), the decayed base rate will be 0 for 1000 years or > 1000 years\n     * In function 2), the difference in tokens issued at 1000 years and any time > 1000 years, will be negligible\n     */\n    function _decPow(uint256 _base, uint256 _minutes) internal pure returns (uint256) {\n        if (_minutes > 525600000) {\n            _minutes = 525600000;\n        } // cap to avoid overflow\n\n        if (_minutes == 0) {\n            return DECIMAL_PRECISION;\n        }\n\n        uint256 y = DECIMAL_PRECISION;\n        uint256 x = _base;\n        uint256 n = _minutes;\n\n        // Exponentiation-by-squaring\n        while (n > 1) {\n            if (n % 2 == 0) {\n                x = decMul(x, x);\n                n = n / 2;\n            } else {\n                // if (n % 2 != 0)\n                y = decMul(x, y);\n                x = decMul(x, x);\n                n = (n - 1) / 2;\n            }\n        }\n\n        return decMul(x, y);\n    }\n\n    function _getAbsoluteDifference(uint256 _a, uint256 _b) internal pure returns (uint256) {\n        return (_a >= _b) ? _a - _b : _b - _a;\n    }\n\n    function _computeNominalCR(uint256 _coll, uint256 _debt) internal pure returns (uint256) {\n        if (_debt > 0) {\n            return (_coll * NICR_PRECISION) / _debt;\n        }\n        // Return the maximal value for uint256 if the Trove has a debt of 0. Represents \"infinite\" CR.\n        else {\n            // if (_debt == 0)\n            return 2 ** 256 - 1;\n        }\n    }\n\n    function _computeCR(uint256 _coll, uint256 _debt, uint256 _price) internal pure returns (uint256) {\n        if (_debt > 0) {\n            uint256 newCollRatio = (_coll * _price) / _debt;\n\n            return newCollRatio;\n        }\n        // Return the maximal value for uint256 if the Trove has a debt of 0. Represents \"infinite\" CR.\n        else {\n            // if (_debt == 0)\n            return 2 ** 256 - 1;\n        }\n    }\n\n    function _computeCR(uint256 _coll, uint256 _debt) internal pure returns (uint256) {\n        if (_debt > 0) {\n            uint256 newCollRatio = (_coll) / _debt;\n\n            return newCollRatio;\n        }\n        // Return the maximal value for uint256 if the Trove has a debt of 0. Represents \"infinite\" CR.\n        else {\n            // if (_debt == 0)\n            return 2 ** 256 - 1;\n        }\n    }\n}\n"
      },
      "contracts/dependencies/PrismaOwnable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.19;\n\nimport \"../interfaces/IPrismaCore.sol\";\n\n/**\n    @title Prisma Ownable\n    @notice Contracts inheriting `PrismaOwnable` have the same owner as `PrismaCore`.\n            The ownership cannot be independently modified or renounced.\n */\ncontract PrismaOwnable {\n    IPrismaCore public immutable PRISMA_CORE;\n\n    constructor(address _prismaCore) {\n        PRISMA_CORE = IPrismaCore(_prismaCore);\n    }\n\n    modifier onlyOwner() {\n        require(msg.sender == PRISMA_CORE.owner(), \"Only owner\");\n        _;\n    }\n\n    function owner() public view returns (address) {\n        return PRISMA_CORE.owner();\n    }\n\n    function guardian() public view returns (address) {\n        return PRISMA_CORE.guardian();\n    }\n}\n"
      },
      "contracts/dependencies/SystemStart.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.19;\n\nimport \"../interfaces/IPrismaCore.sol\";\n\n/**\n    @title Prisma System Start Time\n    @dev Provides a unified `startTime` and `getWeek`, used for emissions.\n */\ncontract SystemStart {\n    uint256 immutable startTime;\n\n    constructor(address prismaCore) {\n        startTime = IPrismaCore(prismaCore).startTime();\n    }\n\n    function getWeek() public view returns (uint256 week) {\n        return (block.timestamp - startTime) / 1 weeks;\n    }\n}\n"
      },
      "contracts/interfaces/IBorrowerOperations.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\ninterface IBorrowerOperations {\n    struct Balances {\n        uint256[] collaterals;\n        uint256[] debts;\n        uint256[] prices;\n    }\n\n    event BorrowingFeePaid(address indexed borrower, uint256 amount);\n    event CollateralConfigured(address troveManager, address collateralToken);\n    event TroveCreated(address indexed _borrower, uint256 arrayIndex);\n    event TroveManagerRemoved(address troveManager);\n    event TroveUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 stake, uint8 operation);\n\n    function addColl(\n        address troveManager,\n        address account,\n        uint256 _collateralAmount,\n        address _upperHint,\n        address _lowerHint\n    ) external;\n\n    function adjustTrove(\n        address troveManager,\n        address account,\n        uint256 _maxFeePercentage,\n        uint256 _collDeposit,\n        uint256 _collWithdrawal,\n        uint256 _debtChange,\n        bool _isDebtIncrease,\n        address _upperHint,\n        address _lowerHint\n    ) external;\n\n    function closeTrove(address troveManager, address account) external;\n\n    function configureCollateral(address troveManager, address collateralToken) external;\n\n    function fetchBalances() external returns (Balances memory balances);\n\n    function getGlobalSystemBalances() external returns (uint256 totalPricedCollateral, uint256 totalDebt);\n\n    function getTCR() external returns (uint256 globalTotalCollateralRatio);\n\n    function openTrove(\n        address troveManager,\n        address account,\n        uint256 _maxFeePercentage,\n        uint256 _collateralAmount,\n        uint256 _debtAmount,\n        address _upperHint,\n        address _lowerHint\n    ) external;\n\n    function removeTroveManager(address troveManager) external;\n\n    function repayDebt(\n        address troveManager,\n        address account,\n        uint256 _debtAmount,\n        address _upperHint,\n        address _lowerHint\n    ) external;\n\n    function setDelegateApproval(address _delegate, bool _isApproved) external;\n\n    function setMinNetDebt(uint256 _minNetDebt) external;\n\n    function withdrawColl(\n        address troveManager,\n        address account,\n        uint256 _collWithdrawal,\n        address _upperHint,\n        address _lowerHint\n    ) external;\n\n    function withdrawDebt(\n        address troveManager,\n        address account,\n        uint256 _maxFeePercentage,\n        uint256 _debtAmount,\n        address _upperHint,\n        address _lowerHint\n    ) external;\n\n    function checkRecoveryMode(uint256 TCR) external pure returns (bool);\n\n    function CCR() external view returns (uint256);\n\n    function DEBT_GAS_COMPENSATION() external view returns (uint256);\n\n    function DECIMAL_PRECISION() external view returns (uint256);\n\n    function PERCENT_DIVISOR() external view returns (uint256);\n\n    function PRISMA_CORE() external view returns (address);\n\n    function _100pct() external view returns (uint256);\n\n    function debtToken() external view returns (address);\n\n    function factory() external view returns (address);\n\n    function getCompositeDebt(uint256 _debt) external view returns (uint256);\n\n    function guardian() external view returns (address);\n\n    function isApprovedDelegate(address owner, address caller) external view returns (bool isApproved);\n\n    function minNetDebt() external view returns (uint256);\n\n    function owner() external view returns (address);\n\n    function troveManagersData(address) external view returns (address collateralToken, uint16 index);\n}\n"
      },
      "contracts/interfaces/IDebtToken.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\ninterface IDebtToken {\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n    event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, bytes _reason);\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n    event ReceiveFromChain(uint16 indexed _srcChainId, address indexed _to, uint256 _amount);\n    event RetryMessageSuccess(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes32 _payloadHash);\n    event SendToChain(uint16 indexed _dstChainId, address indexed _from, bytes _toAddress, uint256 _amount);\n    event SetMinDstGas(uint16 _dstChainId, uint16 _type, uint256 _minDstGas);\n    event SetPrecrime(address precrime);\n    event SetTrustedRemote(uint16 _remoteChainId, bytes _path);\n    event SetTrustedRemoteAddress(uint16 _remoteChainId, bytes _remoteAddress);\n    event SetUseCustomAdapterParams(bool _useCustomAdapterParams);\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    function burn(address _account, uint256 _amount) external;\n\n    function burnWithGasCompensation(address _account, uint256 _amount) external returns (bool);\n\n    function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);\n\n    function enableTroveManager(address _troveManager) external;\n\n    function flashLoan(address receiver, address token, uint256 amount, bytes calldata data) external returns (bool);\n\n    function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external;\n\n    function increaseAllowance(address spender, uint256 addedValue) external returns (bool);\n\n    function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) external;\n\n    function mint(address _account, uint256 _amount) external;\n\n    function mintWithGasCompensation(address _account, uint256 _amount) external returns (bool);\n\n    function nonblockingLzReceive(\n        uint16 _srcChainId,\n        bytes calldata _srcAddress,\n        uint64 _nonce,\n        bytes calldata _payload\n    ) external;\n\n    function permit(\n        address owner,\n        address spender,\n        uint256 amount,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    function renounceOwnership() external;\n\n    function returnFromPool(address _poolAddress, address _receiver, uint256 _amount) external;\n\n    function sendToSP(address _sender, uint256 _amount) external;\n\n    function setConfig(uint16 _version, uint16 _chainId, uint256 _configType, bytes calldata _config) external;\n\n    function setMinDstGas(uint16 _dstChainId, uint16 _packetType, uint256 _minGas) external;\n\n    function setPayloadSizeLimit(uint16 _dstChainId, uint256 _size) external;\n\n    function setPrecrime(address _precrime) external;\n\n    function setReceiveVersion(uint16 _version) external;\n\n    function setSendVersion(uint16 _version) external;\n\n    function setTrustedRemote(uint16 _srcChainId, bytes calldata _path) external;\n\n    function setTrustedRemoteAddress(uint16 _remoteChainId, bytes calldata _remoteAddress) external;\n\n    function setUseCustomAdapterParams(bool _useCustomAdapterParams) external;\n\n    function transfer(address recipient, uint256 amount) external returns (bool);\n\n    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n    function transferOwnership(address newOwner) external;\n\n    function retryMessage(\n        uint16 _srcChainId,\n        bytes calldata _srcAddress,\n        uint64 _nonce,\n        bytes calldata _payload\n    ) external payable;\n\n    function sendFrom(\n        address _from,\n        uint16 _dstChainId,\n        bytes calldata _toAddress,\n        uint256 _amount,\n        address _refundAddress,\n        address _zroPaymentAddress,\n        bytes calldata _adapterParams\n    ) external payable;\n\n    function DEBT_GAS_COMPENSATION() external view returns (uint256);\n\n    function DEFAULT_PAYLOAD_SIZE_LIMIT() external view returns (uint256);\n\n    function FLASH_LOAN_FEE() external view returns (uint256);\n\n    function NO_EXTRA_GAS() external view returns (uint256);\n\n    function PT_SEND() external view returns (uint16);\n\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    function balanceOf(address account) external view returns (uint256);\n\n    function borrowerOperationsAddress() external view returns (address);\n\n    function circulatingSupply() external view returns (uint256);\n\n    function decimals() external view returns (uint8);\n\n    function domainSeparator() external view returns (bytes32);\n\n    function estimateSendFee(\n        uint16 _dstChainId,\n        bytes calldata _toAddress,\n        uint256 _amount,\n        bool _useZro,\n        bytes calldata _adapterParams\n    ) external view returns (uint256 nativeFee, uint256 zroFee);\n\n    function factory() external view returns (address);\n\n    function failedMessages(uint16, bytes calldata, uint64) external view returns (bytes32);\n\n    function flashFee(address token, uint256 amount) external view returns (uint256);\n\n    function gasPool() external view returns (address);\n\n    function getConfig(\n        uint16 _version,\n        uint16 _chainId,\n        address,\n        uint256 _configType\n    ) external view returns (bytes memory);\n\n    function getTrustedRemoteAddress(uint16 _remoteChainId) external view returns (bytes memory);\n\n    function isTrustedRemote(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool);\n\n    function lzEndpoint() external view returns (address);\n\n    function maxFlashLoan(address token) external view returns (uint256);\n\n    function minDstGasLookup(uint16, uint16) external view returns (uint256);\n\n    function name() external view returns (string memory);\n\n    function nonces(address owner) external view returns (uint256);\n\n    function owner() external view returns (address);\n\n    function payloadSizeLimitLookup(uint16) external view returns (uint256);\n\n    function permitTypeHash() external view returns (bytes32);\n\n    function precrime() external view returns (address);\n\n    function stabilityPoolAddress() external view returns (address);\n\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n\n    function symbol() external view returns (string memory);\n\n    function token() external view returns (address);\n\n    function totalSupply() external view returns (uint256);\n\n    function troveManager(address) external view returns (bool);\n\n    function trustedRemoteLookup(uint16) external view returns (bytes memory);\n\n    function useCustomAdapterParams() external view returns (bool);\n\n    function version() external view returns (string memory);\n}\n"
      },
      "contracts/interfaces/IPriceFeed.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\ninterface IPriceFeed {\n    event NewOracleRegistered(address token, address chainlinkAggregator, bool isEthIndexed);\n    event PriceFeedStatusUpdated(address token, address oracle, bool isWorking);\n    event PriceRecordUpdated(address indexed token, uint256 _price);\n\n    function fetchPrice(address _token) external returns (uint256);\n\n    function setOracle(\n        address _token,\n        address _chainlinkOracle,\n        bytes4 sharePriceSignature,\n        uint8 sharePriceDecimals,\n        bool _isEthIndexed\n    ) external;\n\n    function MAX_PRICE_DEVIATION_FROM_PREVIOUS_ROUND() external view returns (uint256);\n\n    function PRISMA_CORE() external view returns (address);\n\n    function RESPONSE_TIMEOUT() external view returns (uint256);\n\n    function TARGET_DIGITS() external view returns (uint256);\n\n    function guardian() external view returns (address);\n\n    function oracleRecords(\n        address\n    )\n        external\n        view\n        returns (\n            address chainLinkOracle,\n            uint8 decimals,\n            bytes4 sharePriceSignature,\n            uint8 sharePriceDecimals,\n            bool isFeedWorking,\n            bool isEthIndexed\n        );\n\n    function owner() external view returns (address);\n\n    function priceRecords(\n        address\n    ) external view returns (uint96 scaledPrice, uint32 timestamp, uint32 lastUpdated, uint80 roundId);\n}\n"
      },
      "contracts/interfaces/IPrismaCallbacks.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nstruct ReedemedTrove {\n    address account;\n    uint256 debtLot;\n    uint256 collateralLot;\n}\n\ninterface ITroveRedemptionsCallback {\n    /**\n     * @notice Function called after redemptions are executed in a Trove Manager\n     * @dev This functions should be called EXCLUSIVELY by a registered Trove Manger\n     * @param redemptions Values related to redeemed troves\n     */\n    function onRedemptions(ReedemedTrove[] memory redemptions) external returns (bool);\n}\n"
      },
      "contracts/interfaces/IPrismaCore.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\ninterface IPrismaCore {\n    event FeeReceiverSet(address feeReceiver);\n    event GuardianSet(address guardian);\n    event NewOwnerAccepted(address oldOwner, address owner);\n    event NewOwnerCommitted(address owner, address pendingOwner, uint256 deadline);\n    event NewOwnerRevoked(address owner, address revokedOwner);\n    event Paused();\n    event PriceFeedSet(address priceFeed);\n    event Unpaused();\n\n    function acceptTransferOwnership() external;\n\n    function commitTransferOwnership(address newOwner) external;\n\n    function revokeTransferOwnership() external;\n\n    function setFeeReceiver(address _feeReceiver) external;\n\n    function setGuardian(address _guardian) external;\n\n    function setPaused(bool _paused) external;\n\n    function setPriceFeed(address _priceFeed) external;\n\n    function OWNERSHIP_TRANSFER_DELAY() external view returns (uint256);\n\n    function feeReceiver() external view returns (address);\n\n    function guardian() external view returns (address);\n\n    function owner() external view returns (address);\n\n    function ownershipTransferDeadline() external view returns (uint256);\n\n    function paused() external view returns (bool);\n\n    function pendingOwner() external view returns (address);\n\n    function priceFeed() external view returns (address);\n\n    function startTime() external view returns (uint256);\n}\n"
      },
      "contracts/interfaces/ISortedTroves.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\ninterface ISortedTroves {\n    event NodeAdded(address _id, uint256 _NICR);\n    event NodeRemoved(address _id);\n\n    function insert(address _id, uint256 _NICR, address _prevId, address _nextId) external;\n\n    function reInsert(address _id, uint256 _newNICR, address _prevId, address _nextId) external;\n\n    function remove(address _id) external;\n\n    function setAddresses(address _troveManagerAddress) external;\n\n    function contains(address _id) external view returns (bool);\n\n    function data() external view returns (address head, address tail, uint256 size);\n\n    function findInsertPosition(\n        uint256 _NICR,\n        address _prevId,\n        address _nextId\n    ) external view returns (address, address);\n\n    function getFirst() external view returns (address);\n\n    function getLast() external view returns (address);\n\n    function getNext(address _id) external view returns (address);\n\n    function getPrev(address _id) external view returns (address);\n\n    function getSize() external view returns (uint256);\n\n    function isEmpty() external view returns (bool);\n\n    function troveManager() external view returns (address);\n\n    function validInsertPosition(uint256 _NICR, address _prevId, address _nextId) external view returns (bool);\n}\n"
      },
      "contracts/interfaces/IVault.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\ninterface IPrismaVault {\n    struct InitialAllowance {\n        address receiver;\n        uint256 amount;\n    }\n\n    event BoostCalculatorSet(address boostCalculator);\n    event BoostDelegationSet(address indexed boostDelegate, bool isEnabled, uint256 feePct, address callback);\n    event EmissionScheduleSet(address emissionScheduler);\n    event IncreasedAllocation(address indexed receiver, uint256 increasedAmount);\n    event NewReceiverRegistered(address receiver, uint256 id);\n    event ReceiverIsActiveStatusModified(uint256 indexed id, bool isActive);\n    event UnallocatedSupplyIncreased(uint256 increasedAmount, uint256 unallocatedTotal);\n    event UnallocatedSupplyReduced(uint256 reducedAmount, uint256 unallocatedTotal);\n\n    function allocateNewEmissions(uint256 id) external returns (uint256);\n\n    function batchClaimRewards(\n        address receiver,\n        address boostDelegate,\n        address[] calldata rewardContracts,\n        uint256 maxFeePct\n    ) external returns (bool);\n\n    function increaseUnallocatedSupply(uint256 amount) external returns (bool);\n\n    function registerReceiver(address receiver, uint256 count) external returns (bool);\n\n    function setBoostCalculator(address _boostCalculator) external returns (bool);\n\n    function setBoostDelegationParams(bool isEnabled, uint256 feePct, address callback) external returns (bool);\n\n    function setEmissionSchedule(address _emissionSchedule) external returns (bool);\n\n    function setInitialParameters(\n        address _emissionSchedule,\n        address _boostCalculator,\n        uint256 totalSupply,\n        uint64 initialLockWeeks,\n        uint128[] calldata _fixedInitialAmounts,\n        InitialAllowance[] calldata initialAllowances\n    ) external;\n\n    function setReceiverIsActive(uint256 id, bool isActive) external returns (bool);\n\n    function transferAllocatedTokens(address claimant, address receiver, uint256 amount) external returns (bool);\n\n    function transferTokens(address token, address receiver, uint256 amount) external returns (bool);\n\n    function PRISMA_CORE() external view returns (address);\n\n    function allocated(address) external view returns (uint256);\n\n    function boostCalculator() external view returns (address);\n\n    function boostDelegation(address) external view returns (bool isEnabled, uint16 feePct, address callback);\n\n    function claimableRewardAfterBoost(\n        address account,\n        address receiver,\n        address boostDelegate,\n        address rewardContract\n    ) external view returns (uint256 adjustedAmount, uint256 feeToDelegate);\n\n    function emissionSchedule() external view returns (address);\n\n    function getClaimableWithBoost(address claimant) external view returns (uint256 maxBoosted, uint256 boosted);\n\n    function getWeek() external view returns (uint256 week);\n\n    function guardian() external view returns (address);\n\n    function idToReceiver(uint256) external view returns (address account, bool isActive);\n\n    function lockWeeks() external view returns (uint64);\n\n    function locker() external view returns (address);\n\n    function owner() external view returns (address);\n\n    function claimableBoostDelegationFees(address claimant) external view returns (uint256 amount);\n\n    function prismaToken() external view returns (address);\n\n    function receiverUpdatedWeek(uint256) external view returns (uint16);\n\n    function totalUpdateWeek() external view returns (uint64);\n\n    function unallocatedTotal() external view returns (uint128);\n\n    function voter() external view returns (address);\n\n    function weeklyEmissions(uint256) external view returns (uint128);\n}\n"
      }
    }
  }}