AdaptiveCurveIrm

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/irm/adaptive-curve-irm/AdaptiveCurveIrm.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {IIrm} from "../../interfaces/IIrm.sol";
import {IAdaptiveCurveIrm} from "./interfaces/IAdaptiveCurveIrm.sol";
import {IProtocolConfig, IRMConfig, IRMConfigTyped} from "../../interfaces/IProtocolConfig.sol";
import {UtilsLib} from "./libraries/UtilsLib.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {ExpLib} from "./libraries/ExpLib.sol";
import {MathLib, WAD_INT as WAD} from "./libraries/MathLib.sol";
import {MarketParamsLib} from "../../libraries/MarketParamsLib.sol";
import {Id, MarketParams, Market, IMorphoCredit} from "../../interfaces/IMorpho.sol";
import {MathLib as MorphoMathLib} from "../../libraries/MathLib.sol";
import {Initializable} from "../../../lib/openzeppelin/contracts/proxy/utils/Initializable.sol";
import {IAaveMarket} from "./interfaces/IAaveMarket.sol";

/// @title AdaptiveCurveIrm
/// @author Morpho Labs
/// @custom:contact security@morpho.org
contract AdaptiveCurveIrm is IAdaptiveCurveIrm, Initializable {
    using MathLib for int256;
    using UtilsLib for int256;
    using MorphoMathLib for uint128;
    using MorphoMathLib for uint256;
    using MarketParamsLib for MarketParams;

    /* EVENTS */

    /// @notice Emitted when a borrow rate is updated.
    event BorrowRateUpdate(Id indexed id, uint256 avgBorrowRate, uint256 rateAtTarget);

    /* IMMUTABLES */

    /// @inheritdoc IAdaptiveCurveIrm
    address public immutable MORPHO;

    /// @notice The Aave V3 pool address for fetching reserve data
    address public immutable AAVE_POOL;

    /// @notice The underlying USDC asset address
    address public immutable USDC;

    /* STORAGE */

    /// @inheritdoc IAdaptiveCurveIrm
    mapping(Id => int256) public rateAtTarget;

    /// @notice Tracks Aave normalized index data per market for spread calculation
    /// @dev Packed into a single storage slot: 96 + 96 + 64 = 256 bits
    struct AaveIndexData {
        uint96 lastNormalizedDebt; // Last recorded Aave normalized variable debt (RAY)
        uint96 lastNormalizedIncome; // Last recorded Aave normalized income (RAY)
        uint64 lastUpdate; // Timestamp of last index update
    }

    /// @notice Aave index data per market ID
    mapping(Id => AaveIndexData) public aaveIndexData;

    /// @dev Storage gap for future upgrades (8 slots after adding 2 for mapping).
    uint256[8] private __gap;

    /* CONSTRUCTOR */

    /// @notice Constructor.
    /// @param morpho The address of Morpho.
    /// @param aavePool The address of the Aave V3 pool.
    /// @param usdc The address of the underlying USDC asset.
    constructor(address morpho, address aavePool, address usdc) {
        require(morpho != address(0), ErrorsLib.ZERO_ADDRESS);
        require(aavePool != address(0), ErrorsLib.ZERO_ADDRESS);
        require(usdc != address(0), ErrorsLib.ZERO_ADDRESS);

        MORPHO = morpho;
        AAVE_POOL = aavePool;
        USDC = usdc;
        _disableInitializers();
    }

    function initialize() external initializer {}

    /* BORROW RATES */

    /// @inheritdoc IIrm
    function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256) {
        (uint256 avgRate,) = _borrowRate(marketParams.id(), market);
        return avgRate;
    }

    /// @inheritdoc IIrm
    function borrowRate(MarketParams memory marketParams, Market memory market) public virtual returns (uint256) {
        require(msg.sender == MORPHO, ErrorsLib.NOT_MORPHO);

        Id id = marketParams.id();

        (uint256 avgRate, int256 endRateAtTarget) = _borrowRate(id, market);

        rateAtTarget[id] = endRateAtTarget;

        // Update Aave indices for next calculation
        _updateAaveIndices(id);

        // Safe "unchecked" cast because endRateAtTarget >= 0.
        emit BorrowRateUpdate(id, avgRate, uint256(endRateAtTarget));

        return avgRate;
    }

    /// @dev Returns avgRate and endRateAtTarget.
    /// @dev Assumes that the inputs `marketParams` and `id` match.
    function _borrowRate(Id id, Market memory market) internal view returns (uint256, int256) {
        (uint256 adaptiveCurveRate, int256 endRateAtTarget) = _calculateAdaptiveCurve(id, market);

        // Calculate and add the Aave spread
        uint256 aaveSpread = _calculateAaveSpread(id);

        return (adaptiveCurveRate + aaveSpread, endRateAtTarget);
    }

    /// @dev Calculates the adaptive curve rate portion.
    function _calculateAdaptiveCurve(Id id, Market memory market) internal view returns (uint256, int256) {
        IRMConfigTyped memory terms = _unpackIRMConfig();

        // Safe "unchecked" cast because the utilization is smaller than 1 (scaled by WAD).
        int256 utilization =
            int256(market.totalSupplyAssets > 0 ? market.totalBorrowAssets.wDivDown(market.totalSupplyAssets) : 0);

        int256 errNormFactor =
            utilization > terms.targetUtilization ? WAD - terms.targetUtilization : terms.targetUtilization;
        int256 err = (utilization - terms.targetUtilization).wDivToZero(errNormFactor);

        int256 startRateAtTarget = rateAtTarget[id];

        int256 avgRateAtTarget;
        int256 endRateAtTarget;

        if (startRateAtTarget == 0) {
            // First interaction.
            avgRateAtTarget = terms.initialRateAtTarget;
            endRateAtTarget = terms.initialRateAtTarget;
        } else {
            // The speed is assumed constant between two updates, but it is in fact not constant because of interest.
            // So the rate is always underestimated.
            int256 speed = terms.adjustmentSpeed.wMulToZero(err);
            // market.lastUpdate != 0 because it is not the first interaction with this market.
            // Safe "unchecked" cast because block.timestamp - market.lastUpdate <= block.timestamp <= type(int256).max.
            int256 elapsed = int256(block.timestamp - market.lastUpdate);
            int256 linearAdaptation = speed * elapsed;

            if (linearAdaptation == 0) {
                // If linearAdaptation == 0, avgRateAtTarget = endRateAtTarget = startRateAtTarget;
                avgRateAtTarget = startRateAtTarget;
                endRateAtTarget = startRateAtTarget;
            } else {
                // Formula of the average rate that should be returned to Morpho Blue:
                // avg = 1/T * ∫_0^T curve(startRateAtTarget*exp(speed*x), err) dx
                // The integral is approximated with the trapezoidal rule:
                // avg ~= 1/T * Σ_i=1^N [curve(f((i-1) * T/N), err) + curve(f(i * T/N), err)] / 2 * T/N
                // Where f(x) = startRateAtTarget*exp(speed*x)
                // avg ~= Σ_i=1^N [curve(f((i-1) * T/N), err) + curve(f(i * T/N), err)] / (2 * N)
                // As curve is linear in its first argument:
                // avg ~= curve([Σ_i=1^N [f((i-1) * T/N) + f(i * T/N)] / (2 * N), err)
                // avg ~= curve([(f(0) + f(T))/2 + Σ_i=1^(N-1) f(i * T/N)] / N, err)
                // avg ~= curve([(startRateAtTarget + endRateAtTarget)/2 + Σ_i=1^(N-1) f(i * T/N)] / N, err)
                // With N = 2:
                // avg ~= curve([(startRateAtTarget + endRateAtTarget)/2 + startRateAtTarget*exp(speed*T/2)] / 2, err)
                // avg ~= curve([startRateAtTarget + endRateAtTarget + 2*startRateAtTarget*exp(speed*T/2)] / 4, err)
                endRateAtTarget =
                    _newRateAtTarget(startRateAtTarget, linearAdaptation, terms.minRateAtTarget, terms.maxRateAtTarget);
                int256 midRateAtTarget = _newRateAtTarget(
                    startRateAtTarget, linearAdaptation / 2, terms.minRateAtTarget, terms.maxRateAtTarget
                );
                avgRateAtTarget = (startRateAtTarget + endRateAtTarget + 2 * midRateAtTarget) / 4;
            }
        }

        // Safe "unchecked" cast because avgRateAtTarget >= 0.
        return (uint256(_curve(avgRateAtTarget, err, terms.curveSteepness)), endRateAtTarget);
    }

    /// @dev Returns the rate for a given `_rateAtTarget` and an `err`.
    /// The formula of the curve is the following:
    /// r = ((1-1/C)*err + 1) * rateAtTarget if err < 0
    ///     ((C-1)*err + 1) * rateAtTarget else.
    function _curve(int256 _rateAtTarget, int256 err, int256 curveSteepness) private pure returns (int256) {
        // Non negative because 1 - 1/C >= 0, C - 1 >= 0.
        int256 coeff = err < 0 ? WAD - WAD.wDivToZero(curveSteepness) : curveSteepness - WAD;
        // Non negative if _rateAtTarget >= 0 because if err < 0, coeff <= 1.
        return (coeff.wMulToZero(err) + WAD).wMulToZero(_rateAtTarget);
    }

    /// @dev Returns the new rate at target, for a given `startRateAtTarget` and a given `linearAdaptation`.
    /// The formula is: max(min(startRateAtTarget * exp(linearAdaptation), maxRateAtTarget), minRateAtTarget).
    function _newRateAtTarget(
        int256 startRateAtTarget,
        int256 linearAdaptation,
        int256 minRateAtTarget,
        int256 maxRateAtTarget
    ) internal view virtual returns (int256) {
        // Non negative because MIN_RATE_AT_TARGET > 0.
        return startRateAtTarget.wMulToZero(ExpLib.wExp(linearAdaptation)).bound(minRateAtTarget, maxRateAtTarget);
    }

    /// @dev Unpacks IRMConfig into individual int256 values.
    /// @return terms The IRMConfigTyped struct.
    function _unpackIRMConfig() internal view returns (IRMConfigTyped memory) {
        IRMConfig memory terms = IProtocolConfig(IMorphoCredit(MORPHO).protocolConfig()).getIRMConfig();
        return IRMConfigTyped({
            curveSteepness: int256(terms.curveSteepness),
            adjustmentSpeed: int256(terms.adjustmentSpeed),
            targetUtilization: int256(terms.targetUtilization),
            initialRateAtTarget: int256(terms.initialRateAtTarget),
            minRateAtTarget: int256(terms.minRateAtTarget),
            maxRateAtTarget: int256(terms.maxRateAtTarget)
        });
    }

    /// @dev Calculates the Aave borrow-supply spread to be added to the adaptive curve rate.
    /// Uses time-weighted average rates from index growth to be manipulation-resistant.
    /// @param id The market ID
    /// @return The spread rate per second (scaled by WAD)
    function _calculateAaveSpread(Id id) internal view returns (uint256) {
        AaveIndexData memory lastData = aaveIndexData[id];

        // If never initialized, return 0 (will be initialized on first write)
        if (lastData.lastUpdate == 0) {
            return 0;
        }

        uint256 elapsed = block.timestamp - lastData.lastUpdate;

        // If no time has passed, no spread to calculate
        if (elapsed == 0) {
            return 0;
        }

        // Fetch current normalized indices (these are automatically up-to-date)
        uint256 currentNormalizedDebt = IAaveMarket(AAVE_POOL).getReserveNormalizedVariableDebt(USDC);
        uint256 currentNormalizedIncome = IAaveMarket(AAVE_POOL).getReserveNormalizedIncome(USDC);

        // Calculate rates from normalized index growth
        uint256 aaveBorrowRate =
            currentNormalizedDebt.wDivUp(lastData.lastNormalizedDebt).wInverseTaylorCompounded(elapsed);
        uint256 aaveSupplyRate =
            currentNormalizedIncome.wDivUp(lastData.lastNormalizedIncome).wInverseTaylorCompounded(elapsed);

        // The spread is the difference (can be 0 if rates are equal)
        return aaveBorrowRate > aaveSupplyRate ? aaveBorrowRate - aaveSupplyRate : 0;
    }

    /// @dev Updates the stored Aave normalized indices for the next spread calculation.
    /// @param id The market ID
    function _updateAaveIndices(Id id) internal {
        // Fetch current normalized indices (automatically includes all accrued interest)
        uint256 currentNormalizedDebt = IAaveMarket(AAVE_POOL).getReserveNormalizedVariableDebt(USDC);
        uint256 currentNormalizedIncome = IAaveMarket(AAVE_POOL).getReserveNormalizedIncome(USDC);

        AaveIndexData memory data = aaveIndexData[id];

        // Update normalized indices for next calculation
        // Safe to cast as Aave indices start at 1e27 and would take decades to overflow uint96
        data.lastNormalizedDebt = uint96(currentNormalizedDebt);
        data.lastNormalizedIncome = uint96(currentNormalizedIncome);
        data.lastUpdate = uint64(block.timestamp);

        aaveIndexData[id] = data;
    }
}
"
    },
    "src/interfaces/IIrm.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.18;

import {MarketParams, Market} from "./IMorpho.sol";

/// @title IIrm
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
interface IIrm {
    /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
    /// @dev Assumes that `market` corresponds to `marketParams`.
    function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);

    /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any
    /// storage.
    /// @dev Assumes that `market` corresponds to `marketParams`.
    function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256);
}
"
    },
    "src/irm/adaptive-curve-irm/interfaces/IAdaptiveCurveIrm.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {IIrm} from "../../../interfaces/IIrm.sol";
import {Id} from "../../../interfaces/IMorpho.sol";

/// @title IAdaptiveCurveIrm
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Interface exposed by the AdaptiveCurveIrm.
interface IAdaptiveCurveIrm is IIrm {
    /// @notice Address of Morpho.
    function MORPHO() external view returns (address);
    /// @notice Rate at target utilization.
    /// @dev Tells the height of the curve.
    function rateAtTarget(Id id) external view returns (int256);
}
"
    },
    "src/interfaces/IProtocolConfig.sol": {
      "content": "// SPDX-License-Identifier: GPL-20later
pragma solidity ^0.8.18;

/// @notice Interface for the ProtocolConfig contract

// Struct to hold market parameters
struct MarketConfig {
    uint256 gracePeriod; // Duration of grace period after cycle end
    uint256 delinquencyPeriod; // Duration of delinquency period before default
    uint256 minBorrow; // Minimum outstanding loan balance to prevent dust
    uint256 irp; // Penalty rate per second for delinquent borrowers
}

// Struct to hold credit line parameters
struct CreditLineConfig {
    uint256 maxLTV;
    uint256 maxVV;
    uint256 maxCreditLine;
    uint256 minCreditLine;
    uint256 maxDRP;
}

// Struct to hold IRM parameters
struct IRMConfig {
    uint256 curveSteepness;
    uint256 adjustmentSpeed;
    uint256 targetUtilization;
    uint256 initialRateAtTarget;
    uint256 minRateAtTarget;
    uint256 maxRateAtTarget;
}

/// @notice Struct to hold IRM parameters with int256 types for internal calculations
struct IRMConfigTyped {
    int256 curveSteepness;
    int256 adjustmentSpeed;
    int256 targetUtilization;
    int256 initialRateAtTarget;
    int256 minRateAtTarget;
    int256 maxRateAtTarget;
}

interface IProtocolConfig {
    /// @dev Initialize the contract with the owner
    /// @param newOwner The address of the new owner
    function initialize(address newOwner) external;

    /// @dev Set a configuration value
    /// @param key The configuration key
    /// @param value The configuration value
    function setConfig(bytes32 key, uint256 value) external;

    // Credit Line getters
    /// @dev Get the credit line parameters
    /// @return The credit line parameters
    function getCreditLineConfig() external view returns (CreditLineConfig memory);

    // Market getters
    /// @dev Get the pause status
    /// @return The pause status value
    function getIsPaused() external view returns (uint256);

    /// @dev Get the maximum on credit
    /// @return The max on credit value
    function getMaxOnCredit() external view returns (uint256);

    /// @dev Get the market parameters
    /// @return The market parameters
    function getMarketConfig() external view returns (MarketConfig memory);

    /// @dev Get the cycle duration for payment cycles
    /// @return The cycle duration in seconds
    function getCycleDuration() external view returns (uint256);

    // IRM getters
    /// @dev Get the IRM parameters
    /// @return The IRM parameters
    function getIRMConfig() external view returns (IRMConfig memory);

    // USD3 & sUSD3 getters
    /// @dev Get the tranche ratio
    /// @return The tranche ratio value
    function getTrancheRatio() external view returns (uint256);

    /// @dev Get the tranche share variant
    /// @return The tranche share variant value
    function getTrancheShareVariant() external view returns (uint256);

    /// @dev Get the SUSD3 lock duration
    /// @return The SUSD3 lock duration value
    function getSusd3LockDuration() external view returns (uint256);

    /// @dev Get the SUSD3 cooldown period
    /// @return The SUSD3 cooldown period value
    function getSusd3CooldownPeriod() external view returns (uint256);

    /// @dev Get the USD3 commitment time
    /// @return The lock period in seconds
    function getUsd3CommitmentTime() external view returns (uint256);

    /// @dev Get the sUSD3 withdrawal window
    /// @return The withdrawal window duration in seconds after cooldown
    function getSusd3WithdrawalWindow() external view returns (uint256);

    /// @dev Get the USD3 supply cap
    /// @return The supply cap in asset units (0 means no cap)
    function getUsd3SupplyCap() external view returns (uint256);

    /// @dev Get configuration value by key
    /// @param key The configuration key
    /// @return The configuration value
    function config(bytes32 key) external view returns (uint256);
}
"
    },
    "src/irm/adaptive-curve-irm/libraries/UtilsLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title UtilsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library exposing helpers.
library UtilsLib {
    /// @dev Bounds `x` between `low` and `high`.
    /// @dev Assumes that `low` <= `high`. If it is not the case it returns `low`.
    function bound(int256 x, int256 low, int256 high) internal pure returns (int256 z) {
        assembly {
            // z = min(x, high).
            z := xor(x, mul(xor(x, high), slt(high, x)))
            // z = max(z, low).
            z := xor(z, mul(xor(z, low), sgt(low, z)))
        }
    }
}
"
    },
    "src/irm/adaptive-curve-irm/libraries/ErrorsLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title ErrorsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library exposing error messages.
library ErrorsLib {
    /// @dev Thrown when passing the zero address.
    string internal constant ZERO_ADDRESS = "zero address";

    /// @dev Thrown when the caller is not Morpho.
    string internal constant NOT_MORPHO = "not Morpho";
}
"
    },
    "src/irm/adaptive-curve-irm/libraries/ExpLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {WAD_INT} from "./MathLib.sol";

/// @title ExpLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to approximate the exponential function.
library ExpLib {
    /// @dev ln(2).
    int256 internal constant LN_2_INT = 0.693147180559945309 ether;

    /// @dev ln(1e-18).
    int256 internal constant LN_WEI_INT = -41.446531673892822312 ether;

    /// @dev Above this bound, `wExp` is clipped to avoid overflowing when multiplied with 1 ether.
    /// @dev This upper bound corresponds to: ln(type(int256).max / 1e36) (scaled by WAD, floored).
    int256 internal constant WEXP_UPPER_BOUND = 93.859467695000404319 ether;

    /// @dev The value of wExp(`WEXP_UPPER_BOUND`).
    int256 internal constant WEXP_UPPER_VALUE = 57716089161558943949701069502944508345128.422502756744429568 ether;

    /// @dev Returns an approximation of exp.
    function wExp(int256 x) internal pure returns (int256) {
        unchecked {
            // If x < ln(1e-18) then exp(x) < 1e-18 so it is rounded to zero.
            if (x < LN_WEI_INT) return 0;
            // `wExp` is clipped to avoid overflowing when multiplied with 1 ether.
            if (x >= WEXP_UPPER_BOUND) return WEXP_UPPER_VALUE;

            // Decompose x as x = q * ln(2) + r with q an integer and -ln(2)/2 <= r <= ln(2)/2.
            // q = x / ln(2) rounded half toward zero.
            int256 roundingAdjustment = (x < 0) ? -(LN_2_INT / 2) : (LN_2_INT / 2);
            // Safe unchecked because x is bounded.
            int256 q = (x + roundingAdjustment) / LN_2_INT;
            // Safe unchecked because |q * ln(2) - x| <= ln(2)/2.
            int256 r = x - q * LN_2_INT;

            // Compute e^r with a 2nd-order Taylor polynomial.
            // Safe unchecked because |r| < 1e18.
            int256 expR = WAD_INT + r + (r * r) / WAD_INT / 2;

            // Return e^x = 2^q * e^r.
            if (q >= 0) return expR << uint256(q);
            else return expR >> uint256(-q);
        }
    }
}
"
    },
    "src/irm/adaptive-curve-irm/libraries/MathLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {WAD} from "../../../libraries/MathLib.sol";

int256 constant WAD_INT = int256(WAD);

/// @title MathLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to manage fixed-point arithmetic on signed integers.
library MathLib {
    /// @dev Returns the multiplication of `x` by `y` (in WAD) rounded towards 0.
    function wMulToZero(int256 x, int256 y) internal pure returns (int256) {
        return (x * y) / WAD_INT;
    }

    /// @dev Returns the division of `x` by `y` (in WAD) rounded towards 0.
    function wDivToZero(int256 x, int256 y) internal pure returns (int256) {
        return (x * WAD_INT) / y;
    }
}
"
    },
    "src/libraries/MarketParamsLib.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {Id, MarketParams} from "../interfaces/IMorpho.sol";

/// @title MarketParamsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to convert a market to its id.
library MarketParamsLib {
    /// @notice The length of the data used to compute the id of a market.
    /// @dev The length is 6 * 32 because `MarketParams` has 6 variables of 32 bytes each.
    uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 6 * 32;

    /// @notice Returns the id of the market `marketParams`.
    function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) {
        assembly ("memory-safe") {
            marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH)
        }
    }
}
"
    },
    "src/interfaces/IMorpho.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.18;

type Id is bytes32;

struct MarketParams {
    address loanToken;
    address collateralToken;
    address oracle;
    address irm;
    uint256 lltv;
    address creditLine;
}

/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
struct Position {
    uint256 supplyShares;
    uint128 borrowShares;
    uint128 collateral;
}

/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
/// interest accrual.
/// @dev Warning: `totalMarkdownAmount` may be stale as markdowns are only updated when borrowers are touched.
struct Market {
    uint128 totalSupplyAssets;
    uint128 totalSupplyShares;
    uint128 totalBorrowAssets;
    uint128 totalBorrowShares;
    uint128 lastUpdate;
    uint128 fee;
    uint128 totalMarkdownAmount; // Running tally of all borrower markdowns
}

/// @notice Per-borrower premium tracking
/// @param lastAccrualTime Timestamp of the last premium accrual for this borrower
/// @param rate Current risk premium rate per second (scaled by WAD)
/// @param borrowAssetsAtLastAccrual Snapshot of borrow position at last premium accrual
struct BorrowerPremium {
    uint128 lastAccrualTime;
    uint128 rate;
    uint128 borrowAssetsAtLastAccrual;
}

/// @notice Repayment tracking structures
enum RepaymentStatus {
    Current,
    GracePeriod,
    Delinquent,
    Default
}

struct PaymentCycle {
    uint256 endDate;
}

struct RepaymentObligation {
    uint128 paymentCycleId;
    uint128 amountDue;
    uint128 endingBalance;
}

/// @notice Markdown state for tracking defaulted debt value reduction
/// @param lastCalculatedMarkdown Last calculated markdown amount
struct MarkdownState {
    uint128 lastCalculatedMarkdown;
}

struct Authorization {
    address authorizer;
    address authorized;
    bool isAuthorized;
    uint256 nonce;
    uint256 deadline;
}

struct Signature {
    uint8 v;
    bytes32 r;
    bytes32 s;
}

/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoBase {
    /// @notice The EIP-712 domain separator.
    /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on chains sharing the
    /// same chain id and on forks because the domain separator would be the same.
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice The owner of the contract.
    /// @dev It has the power to change the owner.
    /// @dev It has the power to set fees on markets and set the fee recipient.
    /// @dev It has the power to enable but not disable IRMs and LLTVs.
    function owner() external view returns (address);

    /// @notice The fee recipient of all markets.
    /// @dev The recipient receives the fees of a given market through a supply position on that market.
    function feeRecipient() external view returns (address);

    /// @notice Whether the `irm` is enabled.
    function isIrmEnabled(address irm) external view returns (bool);

    /// @notice Whether the `lltv` is enabled.
    function isLltvEnabled(uint256 lltv) external view returns (bool);

    /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
    function nonce(address authorizer) external view returns (uint256);

    /// @notice Sets `newOwner` as `owner` of the contract.
    /// @dev Warning: No two-step transfer ownership.
    /// @dev Warning: The owner can be set to the zero address.
    function setOwner(address newOwner) external;

    /// @notice Enables `irm` as a possible IRM for market creation.
    /// @dev Warning: It is not possible to disable an IRM.
    function enableIrm(address irm) external;

    /// @notice Enables `lltv` as a possible LLTV for market creation.
    /// @dev Warning: It is not possible to disable a LLTV.
    function enableLltv(uint256 lltv) external;

    /// @notice Sets the `newFee` for the given market `marketParams`.
    /// @param newFee The new fee, scaled by WAD.
    /// @dev Warning: The recipient can be the zero address.
    function setFee(MarketParams memory marketParams, uint256 newFee) external;

    /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
    /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
    /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
    /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
    function setFeeRecipient(address newFeeRecipient) external;

    /// @notice Creates the market `marketParams`.
    /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
    /// Morpho behaves as expected:
    /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
    /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
    /// burn functions are not supported.
    /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
    /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
    /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
    /// - The IRM should not re-enter Morpho.
    /// - The oracle should return a price with the correct scaling.
    /// @dev Here is a list of assumptions on the market's dependencies which, if broken, could break Morpho's liveness
    /// properties (funds could get stuck):
    /// - The token should not revert on `transfer` and `transferFrom` if balances and approvals are right.
    /// - The amount of assets supplied and borrowed should not go above ~1e35 (otherwise the computation of
    /// `toSharesUp` and `toSharesDown` can overflow).
    /// - The IRM should not revert on `borrowRate`.
    /// - The IRM should not return a very high borrow rate (otherwise the computation of `interest` in
    /// `_accrueInterest` can overflow).
    /// - The oracle should not revert `price`.
    /// - The oracle should not return a very high price (otherwise the computation of `maxBorrow` in `_isHealthy` or of
    /// `assetsRepaid` in `liquidate` can overflow).
    /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
    /// the point where `totalBorrowShares` is very large and borrowing overflows.
    function createMarket(MarketParams memory marketParams) external;

    /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoSupply` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
    /// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
    /// amount of shares is given for full compatibility and precision.
    /// @dev Supplying a large amount can revert for overflow.
    /// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
    /// Consider using the `assets` parameter to avoid this.
    /// @param marketParams The market to supply assets to.
    /// @param assets The amount of assets to supply.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased supply position.
    /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
    /// @return assetsSupplied The amount of assets supplied.
    /// @return sharesSupplied The amount of shares minted.
    function supply(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsSupplied, uint256 sharesSupplied);

    /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
    /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
    /// conversion roundings between shares and assets.
    /// @param marketParams The market to withdraw assets from.
    /// @param assets The amount of assets to withdraw.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the supply position.
    /// @param receiver The address that will receive the withdrawn assets.
    /// @return assetsWithdrawn The amount of assets withdrawn.
    /// @return sharesWithdrawn The amount of shares burned.
    function withdraw(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);

    /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
    /// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
    /// given for full compatibility and precision.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Borrowing a large amount can revert for overflow.
    /// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
    /// Consider using the `assets` parameter to avoid this.
    /// @param marketParams The market to borrow assets from.
    /// @param assets The amount of assets to borrow.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased borrow position.
    /// @param receiver The address that will receive the borrowed assets.
    /// @return assetsBorrowed The amount of assets borrowed.
    /// @return sharesBorrowed The amount of shares minted.
    function borrow(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);

    /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoRepay` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
    /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
    /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
    /// roundings between shares and assets.
    /// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
    /// @param marketParams The market to repay assets to.
    /// @param assets The amount of assets to repay.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the debt position.
    /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
    /// @return assetsRepaid The amount of assets repaid.
    /// @return sharesRepaid The amount of shares burned.
    function repay(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsRepaid, uint256 sharesRepaid);

    /// @notice Accrues interest for the given market `marketParams`.
    function accrueInterest(MarketParams memory marketParams) external;

    /// @notice Returns the data stored on the different `slots`.
    function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
}

/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoStaticTyping is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user)
        external
        view
        returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
    /// accrual.
    function market(Id id)
        external
        view
        returns (
            uint128 totalSupplyAssets,
            uint128 totalSupplyShares,
            uint128 totalBorrowAssets,
            uint128 totalBorrowShares,
            uint128 lastUpdate,
            uint128 fee,
            uint128 totalMarkdownAmount
        );

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id)
        external
        view
        returns (
            address loanToken,
            address collateralToken,
            address oracle,
            address irm,
            uint256 lltv,
            address creditLine
        );
}

/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user) external view returns (Position memory p);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
    /// interest accrual.
    function market(Id id) external view returns (Market memory m);

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id) external view returns (MarketParams memory);
}

/// @title IMorphoCredit
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorphoCredit {
    /// @notice The helper of the contract.
    function helper() external view returns (address);

    /// @notice The usd3 contract
    function usd3() external view returns (address);

    /// @notice The protocol config of the contract.
    function protocolConfig() external view returns (address);

    /// @notice Sets `helper` as `helper` of the contract.
    /// @param newHelper The new helper address
    function setHelper(address newHelper) external;

    /// @notice Sets `usd3` as `usd3` of the contract.
    /// @param newUsd3 The new usd3 address
    function setUsd3(address newUsd3) external;

    /// @notice Sets the credit line and premium rate for a borrower
    /// @param id The market ID
    /// @param borrower The borrower address
    /// @param credit The credit line amount
    /// @param drp The drp per second in WAD
    function setCreditLine(Id id, address borrower, uint256 credit, uint128 drp) external;

    /// @notice Returns the premium data for a specific borrower in a market
    /// @param id The market ID
    /// @param borrower The borrower address
    /// @return lastAccrualTime Timestamp of the last premium accrual
    /// @return rate Current risk premium rate per second (scaled by WAD)
    /// @return borrowAssetsAtLastAccrual Snapshot of borrow position at last premium accrual
    function borrowerPremium(Id id, address borrower)
        external
        view
        returns (uint128 lastAccrualTime, uint128 rate, uint128 borrowAssetsAtLastAccrual);

    /// @notice Batch accrue premiums for multiple borrowers
    /// @param id Market ID
    /// @param borrowers Array of borrower addresses
    /// @dev Gas usage scales linearly with array size. Callers should manage batch sizes based on block gas limits.
    function accruePremiumsForBorrowers(Id id, address[] calldata borrowers) external;

    /// @notice Close a payment cycle and post obligations for multiple borrowers
    /// @param id Market ID
    /// @param endDate Cycle end date
    /// @param borrowers Array of borrower addresses
    /// @param repaymentBps Array of repayment basis points (e.g., 500 = 5%)
    /// @param endingBalances Array of ending balances for penalty calculations
    function closeCycleAndPostObligations(
        Id id,
        uint256 endDate,
        address[] calldata borrowers,
        uint256[] calldata repaymentBps,
        uint256[] calldata endingBalances
    ) external;

    /// @notice Add obligations to the latest payment cycle
    /// @param id Market ID
    /// @param borrowers Array of borrower addresses
    /// @param repaymentBps Array of repayment basis points (e.g., 500 = 5%)
    /// @param endingBalances Array of ending balances
    function addObligationsToLatestCycle(
        Id id,
        address[] calldata borrowers,
        uint256[] calldata repaymentBps,
        uint256[] calldata endingBalances
    ) external;

    /// @notice Get repayment obligation for a borrower
    /// @param id Market ID
    /// @param borrower Borrower address
    /// @return cycleId The payment cycle ID
    /// @return amountDue The amount due
    /// @return endingBalance The ending balance for penalty calculations
    function repaymentObligation(Id id, address borrower)
        external
        view
        returns (uint128 cycleId, uint128 amountDue, uint128 endingBalance);

    /// @notice Get payment cycle end date
    /// @param id Market ID
    /// @param cycleId Cycle ID
    /// @return endDate The cycle end date
    function paymentCycle(Id id, uint256 cycleId) external view returns (uint256 endDate);

    /// @notice Settle a borrower's account by writing off all remaining debt
    /// @dev Only callable by credit line contract
    /// @dev Should be called after any partial repayments have been made
    /// @param marketParams The market parameters
    /// @param borrower The borrower whose account to settle
    /// @return writtenOffAssets Amount of assets written off
    /// @return writtenOffShares Amount of shares written off
    function settleAccount(MarketParams memory marketParams, address borrower)
        external
        returns (uint256 writtenOffAssets, uint256 writtenOffShares);

    /// @notice Get markdown state for a borrower
    /// @param id Market ID
    /// @param borrower Borrower address
    /// @return lastCalculatedMarkdown Last calculated markdown amount
    function markdownState(Id id, address borrower) external view returns (uint128 lastCalculatedMarkdown);
}
"
    },
    "src/libraries/MathLib.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

uint256 constant WAD = 1e18;

/// @title MathLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to manage fixed-point arithmetic.
library MathLib {
    /// @dev Returns (`x` * `y`) / `WAD` rounded down.
    function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD);
    }

    /// @dev Returns (`x` * `WAD`) / `y` rounded down.
    function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y);
    }

    /// @dev Returns (`x` * `WAD`) / `y` rounded up.
    function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y);
    }

    /// @dev Returns (`x` * `y`) / `d` rounded down.
    function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
        return (x * y) / d;
    }

    /// @dev Returns (`x` * `y`) / `d` rounded up.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
        return (x * y + (d - 1)) / d;
    }

    /// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a
    /// continuous compound interest rate.
    function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
        uint256 firstTerm = x * n;
        uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD);
        uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD);

        return firstTerm + secondTerm + thirdTerm;
    }

    /// @dev Computes the inverse of wTaylorCompounded, finding the rate that produces the given growth factor.
    /// Uses a 3-term Taylor series approximation of ln(x) to solve for rate in the compound interest formula.
    /// Formula: rate = ln(x) / n ≈ [(x-1) - (x-1)²/2 + (x-1)³/3] / n
    ///
    /// Accuracy notes:
    /// - The Taylor approximation of ln(x) is most accurate for x close to 1
    /// - At growth factor x = 1.69*WAD (69% growth), approximation error < 2%
    /// - At growth factor x = 2*WAD (100% growth, where ln(2) ≈ 0.69), approximation error < 5%
    /// - Accuracy decreases for larger growth factors; not recommended for x > 2.5*WAD (150% growth)
    ///
    /// Example: If debt grew from 1000 to 1105 over 1 year (10.5% growth):
    /// - x = 1.105*WAD (growth factor)
    /// - n = 365 days (time period)
    /// - Returns ~10% APR as rate per second
    ///
    /// @param x The growth factor scaled by WAD (e.g., 1.1*WAD for 10% growth). Must be >= WAD.
    /// @param n The time period over which the growth occurred (in seconds)
    /// @return The continuously compounded rate per second that would produce this growth (scaled by WAD)
    function wInverseTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
        require(x >= WAD, "ln undefined");

        uint256 firstTerm = x - WAD;
        uint256 secondTerm = wMulDown(firstTerm, firstTerm);
        uint256 thirdTerm = wMulDown(secondTerm, firstTerm);

        uint256 series = firstTerm - secondTerm / 2 + thirdTerm / 3;

        return series / n;
    }
}
"
    },
    "lib/openzeppelin/contracts/proxy/utils/Initializable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reinitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
     *
     * NOTE: Consider following the ERC-7201 formula to derive storage locations.
     */
    function _initializableStorageSlot() internal pure virtual returns (bytes32) {
        return INITIALIZABLE_STORAGE;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        bytes32 slot = _initializableStorageSlot();
        assembly {
            $.slot := slot
        }
    }
}
"
    },
    "src/irm/adaptive-curve-irm/interfaces/IAaveMarket.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

struct ReserveConfigurationMap {
    //bit 0-15: LTV
    //bit 16-31: Liq. threshold
    //bit 32-47: Liq. bonus
    //bit 48-55: Decimals
    //bit 56: reserve is active
    //bit 57: reserve is frozen
    //bit 58: borrowing is enabled
    //bit 59: DEPRECATED: stable rate borrowing enabled
    //bit 60: asset is paused
    //bit 61: borrowing in isolation mode is enabled
    //bit 62: siloed borrowing enabled
    //bit 63: flashloaning enabled
    //bit 64-79: reserve factor
    //bit 80-115: borrow cap in whole tokens, borrowCap == 0 => no cap
    //bit 116-151: supply cap in whole tokens, supplyCap == 0 => no cap
    //bit 152-167: liquidation protocol fee
    //bit 168-175: DEPRECATED: eMode category
    //bit 176-211: unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
    //bit 212-251: debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
    //bit 252: virtual accounting is enabled for the reserve
    //bit 253-255 unused
    uint256 data;
}

/**
 * This exists specifically to maintain the `getReserveData()` interface, since the new, internal
 * `ReserveData` struct includes the reserve's `virtualUnderlyingBalance`.
 */
struct ReserveDataLegacy {
    //stores the reserve configuration
    ReserveConfigurationMap configuration;
    //the liquidity index. Expressed in ray
    uint128 liquidityIndex;
    //the current supply rate. Expressed in ray
    uint128 currentLiquidityRate;
    //variable borrow index. Expressed in ray
    uint128 variableBorrowIndex;
    //the current variable borrow rate. Expressed in ray
    uint128 currentVariableBorrowRate;
    // DEPRECATED on v3.2.0
    uint128 currentStableBorrowRate;
    //timestamp of last update
    uint40 lastUpdateTimestamp;
    //the id of the reserve. Represents the position in the list of the active reserves
    uint16 id;
    //aToken address
    address aTokenAddress;
    // DEPRECATED on v3.2.0
    address stableDebtTokenAddress;
    //variableDebtToken address
    address variableDebtTokenAddress;
    //address of the interest rate strategy
    address interestRateStrategyAddress;
    //the current treasury balance, scaled
    uint128 accruedToTreasury;
    //the outstanding unbacked aTokens minted through the bridging feature
    uint128 unbacked;
    //the outstanding debt borrowed against this asset in isolation mode
    uint128 isolationModeTotalDebt;
}

/// @title IAaveMarket
/// @author Morpho Labs
/// @custom:contact security@morpho.org
interface IAaveMarket {
    /// @notice Aave market reserve data
    function getReserveData(address reserve) external view returns (ReserveDataLegacy memory);

    /// @notice Returns the normalized income of the reserve
    /// @param asset The address of the underlying asset of the reserve
    /// @return The reserve's normalized income
    function getReserveNormalizedIncome(address asset) external view returns (uint256);

    /// @notice Returns the normalized variable debt per unit of asset
    /// @param asset The address of the underlying asset of the reserve
    /// @return The reserve normalized variable debt
    function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);
}
"
    }
  },
  "settings": {
    "remappings": [
      "forge-std/=lib/forge-std/src/",
      "halmos-cheatcodes/=lib/halmos-cheatcodes/src/",
      "@tokenized-strategy/=lib/tokenized-strategy/src/",
      "@periphery/=lib/tokenized-strategy-periphery/src/",
      "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
      "@openzeppelin/=lib/tokenized-strategy-periphery/lib/openzeppelin-contracts/",
      "@yearn-vaults/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/contracts/",
      "ds-test/=lib/forge-std/lib/ds-test/src/",
      "erc4626-tests/=lib/tokenized-strategy/lib/erc4626-tests/",
      "openzeppelin-contracts/=lib/tokenized-strategy/lib/openzeppelin-contracts/",
      "openzeppelin/=lib/openzeppelin/",
      "tokenized-strategy-periphery/=lib/tokenized-strategy-periphery/",
      "tokenized-strategy/=lib/tokenized-strategy/",
      "yearn-vaults-v3/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 999999
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "none",
      "appendCBOR": false
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "shanghai",
    "viaIR": true
  }
}}

Tags:
Multisig, Liquidity, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xdced1a6264a598d3fc07a4b502d1e06a0d210627|verified:true|block:23628176|tx:0x9edf6d3acca63b0e187d6c185f315e74dfc99f191364fbee9ab2be66d7721b62|first_check:1761228195

Submitted on: 2025-10-23 16:03:18

Comments

Log in to comment.

No comments yet.