diff --git a/Makefile b/Makefile index fa1885ed..a13a5993 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ install: forge install contracts: - FOUNDRY_PROFILE=build FOUNDRY_TEST=/dev/null FOUNDRY_SCRIPT=/dev/null forge build --via-ir --extra-output-files irOptimized --sizes --force + FOUNDRY_TEST=/dev/null FOUNDRY_SCRIPT=/dev/null forge build --via-ir --extra-output-files irOptimized --sizes --force test-invariant: diff --git a/foundry.toml b/foundry.toml index 4599b737..70c49976 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,9 +6,6 @@ fs_permissions = [{ access = "read", path = "./config/"}] gas_limit = "18446744073709551615" # 2^64 - 1, to remove the gas limit evm_version = "shanghai" -[profile.build] -evm_version = "paris" - [fuzz] runs = 32 diff --git a/src/MorphoInternal.sol b/src/MorphoInternal.sol index 727c836b..790f2919 100644 --- a/src/MorphoInternal.sol +++ b/src/MorphoInternal.sol @@ -27,7 +27,7 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet import {DataTypes} from "@aave-v3-origin/protocol/libraries/types/DataTypes.sol"; import {UserConfiguration} from "@aave-v3-origin/protocol/libraries/configuration/UserConfiguration.sol"; import {ReserveConfiguration} from "@aave-v3-origin/protocol/libraries/configuration/ReserveConfiguration.sol"; -import {ReserveConfigurationLegacy} from "./libraries/ReserveConfigurationLegacy.sol"; +import {EModeConfiguration} from "@aave-v3-origin/protocol/libraries/configuration/EModeConfiguration.sol"; import {MorphoStorage} from "./MorphoStorage.sol"; @@ -52,7 +52,7 @@ abstract contract MorphoInternal is MorphoStorage { using UserConfiguration for DataTypes.UserConfigurationMap; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - using ReserveConfigurationLegacy for DataTypes.ReserveConfigurationMap; + using EModeConfiguration for uint128; /* INTERNAL */ @@ -92,7 +92,7 @@ abstract contract MorphoInternal is MorphoStorage { market.underlying = underlying; market.aToken = reserve.aTokenAddress; market.variableDebtToken = reserve.variableDebtTokenAddress; - market.stableDebtToken = reserve.stableDebtTokenAddress; + // Note that reserve.stableDebtTokenAddress is deprecated in v3.2.0. _marketsCreated.push(underlying); @@ -231,7 +231,9 @@ abstract contract MorphoInternal is MorphoStorage { function _liquidityData(address user) internal view returns (Types.LiquidityData memory liquidityData) { Types.LiquidityVars memory vars; - if (_eModeCategoryId != 0) vars.eModeCategory = _pool.getEModeCategoryData(_eModeCategoryId); + if (_eModeCategoryId != 0) { + vars.eModeCollateralConfig = _pool.getEModeCategoryCollateralConfig(_eModeCategoryId); + } vars.oracle = IAaveOracle(_addressesProvider.getPriceOracle()); vars.user = user; @@ -307,8 +309,7 @@ abstract contract MorphoInternal is MorphoStorage { /// @return debtValue The debt value of `vars.user` on the `underlying` market. function _debt(address underlying, Types.LiquidityVars memory vars) internal view returns (uint256 debtValue) { DataTypes.ReserveConfigurationMap memory config = _pool.getConfiguration(underlying); - (, uint256 underlyingPrice, uint256 underlyingUnit) = - _assetData(underlying, vars.oracle, config, vars.eModeCategory.priceSource); + (uint256 underlyingPrice, uint256 underlyingUnit) = _assetData(underlying, vars.oracle, config); Types.Indexes256 memory indexes = _computeIndexes(underlying); debtValue = @@ -329,18 +330,16 @@ abstract contract MorphoInternal is MorphoStorage { { DataTypes.ReserveConfigurationMap memory config = _pool.getConfiguration(underlying); - bool isInEMode; - (isInEMode, underlyingPrice, underlyingUnit) = - _assetData(underlying, vars.oracle, config, vars.eModeCategory.priceSource); + (underlyingPrice, underlyingUnit) = _assetData(underlying, vars.oracle, config); // If the LTV is 0 on Aave V3, the asset cannot be used as collateral to borrow upon a breaking withdraw. // In response, Morpho disables the asset as collateral and sets its liquidation threshold // to 0 and the governance should warn users to repay their debt. if (config.getLtv() == 0) return (underlyingPrice, 0, 0, underlyingUnit); - if (isInEMode) { - ltv = vars.eModeCategory.ltv; - liquidationThreshold = vars.eModeCategory.liquidationThreshold; + if (_hasTailoredParametersInEmode(underlying)) { + ltv = vars.eModeCollateralConfig.ltv; + liquidationThreshold = vars.eModeCollateralConfig.liquidationThreshold; } else { ltv = config.getLtv(); liquidationThreshold = config.getLiquidationThreshold(); @@ -489,33 +488,37 @@ abstract contract MorphoInternal is MorphoStorage { return liquidityData.debt > 0 ? liquidityData.maxDebt.wadDiv(liquidityData.debt) : type(uint256).max; } - /// @dev Returns data relative to the given asset and its configuration, according to a given oracle. - /// @return Whether the given asset is part of Morpho's e-mode category. - /// @return The asset's price or the price of the given e-mode price source if the asset is in the e-mode category, according to the given oracle. + /// @dev Returns data relative to the given asset, according to a given oracle. + /// @return The asset's price according to the given oracle. /// @return The asset's unit. - function _assetData( - address asset, - IAaveOracle oracle, - DataTypes.ReserveConfigurationMap memory config, - address priceSource - ) internal view returns (bool, uint256, uint256) { + function _assetData(address asset, IAaveOracle oracle, DataTypes.ReserveConfigurationMap memory config) + internal + view + returns (uint256, uint256) + { uint256 assetUnit; unchecked { assetUnit = 10 ** config.getDecimals(); } - bool isInEMode = _isInEModeCategory(config); - if (isInEMode && priceSource != address(0)) { - uint256 eModePrice = oracle.getAssetPrice(priceSource); + return (oracle.getAssetPrice(asset), assetUnit); + } - if (eModePrice != 0) return (isInEMode, eModePrice, assetUnit); - } + /// @dev Returns whether the underlying asset is enabled as an e-mode collateral on the specific Morpho e-mode. + function _hasTailoredParametersInEmode(address underlying) internal view returns (bool) { + if (_eModeCategoryId == 0) return false; - return (isInEMode, oracle.getAssetPrice(asset), assetUnit); + uint256 reserveIndex = _pool.getReserveData(underlying).id; + uint128 bitmap = _pool.getEModeCategoryCollateralBitmap(_eModeCategoryId); + return bitmap.isReserveEnabledOnBitmap(reserveIndex); } - /// @dev Returns whether Morpho is in an e-mode category and the given asset configuration is in the same e-mode category. - function _isInEModeCategory(DataTypes.ReserveConfigurationMap memory config) internal view returns (bool) { - return _eModeCategoryId != 0 && config.getEModeCategory() == _eModeCategoryId; + /// @dev Returns whether the underlying asset is borrowable in the specific Morpho e-mode. + function _isBorrowableInEMode(address underlying) internal view returns (bool) { + if (_eModeCategoryId == 0) return true; + + uint256 reserveIndex = _pool.getReserveData(underlying).id; + uint128 bitmap = _pool.getEModeCategoryBorrowableBitmap(_eModeCategoryId); + return bitmap.isReserveEnabledOnBitmap(reserveIndex); } } diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index 21f9c1e4..a99056fb 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -23,7 +23,6 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet import {DataTypes} from "@aave-v3-origin/protocol/libraries/types/DataTypes.sol"; import {UserConfiguration} from "@aave-v3-origin/protocol/libraries/configuration/UserConfiguration.sol"; import {ReserveConfiguration} from "@aave-v3-origin/protocol/libraries/configuration/ReserveConfiguration.sol"; -import {ReserveConfigurationLegacy} from "./libraries/ReserveConfigurationLegacy.sol"; import {ERC20} from "@solmate/tokens/ERC20.sol"; @@ -48,7 +47,6 @@ abstract contract PositionsManagerInternal is MatchingEngine { using UserConfiguration for DataTypes.UserConfigurationMap; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - using ReserveConfigurationLegacy for DataTypes.ReserveConfigurationMap; /// @dev Validates the manager's permission. function _validatePermission(address delegator, address manager) internal view { @@ -118,7 +116,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { revert Errors.SentinelBorrowNotEnabled(); } - if (_eModeCategoryId != 0 && _eModeCategoryId != config.getEModeCategory()) { + if (!_isBorrowableInEMode(underlying)) { revert Errors.InconsistentEMode(); } @@ -127,8 +125,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { uint256 trueP2PBorrow = market.trueP2PBorrow(indexes); uint256 borrowCap = config.getBorrowCap() * (10 ** config.getDecimals()); - uint256 poolDebt = - ERC20(market.variableDebtToken).totalSupply() + ERC20(market.stableDebtToken).totalSupply(); + uint256 poolDebt = ERC20(market.variableDebtToken).totalSupply(); if (amount + trueP2PBorrow + poolDebt > borrowCap) revert Errors.ExceedsBorrowCap(); } @@ -599,21 +596,19 @@ abstract contract PositionsManagerInternal is MatchingEngine { ) internal view returns (uint256 amountToRepay, uint256 amountToSeize) { Types.AmountToSeizeVars memory vars; - DataTypes.EModeCategoryLegacy memory eModeCategory; - if (_eModeCategoryId != 0) eModeCategory = _pool.getEModeCategoryData(_eModeCategoryId); + DataTypes.CollateralConfig memory eModeCollateralConfig; + if (_eModeCategoryId != 0) eModeCollateralConfig = _pool.getEModeCategoryCollateralConfig(_eModeCategoryId); - bool collateralIsInEMode; IAaveOracle oracle = IAaveOracle(_addressesProvider.getPriceOracle()); DataTypes.ReserveConfigurationMap memory borrowedConfig = _pool.getConfiguration(underlyingBorrowed); DataTypes.ReserveConfigurationMap memory collateralConfig = _pool.getConfiguration(underlyingCollateral); - (, vars.borrowedPrice, vars.borrowedTokenUnit) = - _assetData(underlyingBorrowed, oracle, borrowedConfig, eModeCategory.priceSource); - (collateralIsInEMode, vars.collateralPrice, vars.collateralTokenUnit) = - _assetData(underlyingCollateral, oracle, collateralConfig, eModeCategory.priceSource); + (vars.borrowedPrice, vars.borrowedTokenUnit) = _assetData(underlyingBorrowed, oracle, borrowedConfig); + (vars.collateralPrice, vars.collateralTokenUnit) = _assetData(underlyingCollateral, oracle, collateralConfig); - vars.liquidationBonus = - collateralIsInEMode ? eModeCategory.liquidationBonus : collateralConfig.getLiquidationBonus(); + vars.liquidationBonus = _hasTailoredParametersInEmode(underlyingCollateral) + ? eModeCollateralConfig.liquidationBonus + : collateralConfig.getLiquidationBonus(); amountToRepay = maxToRepay; amountToSeize = ( diff --git a/src/interfaces/aave/IStableDebtTokenLegacy.sol b/src/interfaces/aave/IStableDebtTokenLegacy.sol deleted file mode 100644 index 34faf6a9..00000000 --- a/src/interfaces/aave/IStableDebtTokenLegacy.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.0; - -interface IStableDebtTokenLegacy { - function getSupplyData() external view returns (uint256, uint256, uint256, uint40); -} diff --git a/src/libraries/ReserveConfigurationLegacy.sol b/src/libraries/ReserveConfigurationLegacy.sol deleted file mode 100644 index aa9909ec..00000000 --- a/src/libraries/ReserveConfigurationLegacy.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.17; - -import {Errors} from "@aave-v3-origin/protocol/libraries/helpers/Errors.sol"; -import {DataTypes} from "@aave-v3-origin/protocol/libraries/types/DataTypes.sol"; - -/// @title ReserveConfigurationLegacy -/// @author Morpho Labs -/// @custom:contact security@morpho.xyz -/// @notice Library used to ease AaveV3's legacy reserve configuration calculations. -library ReserveConfigurationLegacy { - uint256 internal constant EMODE_CATEGORY_START_BIT_POSITION = 168; - uint256 internal constant EMODE_CATEGORY_MASK = 0xFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; - uint256 internal constant MAX_VALID_EMODE_CATEGORY = 255; - - function getEModeCategory(DataTypes.ReserveConfigurationMap memory self) internal pure returns (uint256) { - return (self.data & ~EMODE_CATEGORY_MASK) >> EMODE_CATEGORY_START_BIT_POSITION; - } - - function setEModeCategory(DataTypes.ReserveConfigurationMap memory self, uint256 category) internal pure { - require(category <= MAX_VALID_EMODE_CATEGORY, Errors.INVALID_EMODE_CATEGORY); - - self.data = (self.data & EMODE_CATEGORY_MASK) | (category << EMODE_CATEGORY_START_BIT_POSITION); - } -} diff --git a/src/libraries/ReserveDataLib.sol b/src/libraries/ReserveDataLib.sol index c7b89dee..6e5c5e58 100644 --- a/src/libraries/ReserveDataLib.sol +++ b/src/libraries/ReserveDataLib.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.17; -import {IStableDebtTokenLegacy} from "src/interfaces/aave/IStableDebtTokenLegacy.sol"; import {IVariableDebtToken} from "@aave-v3-origin/interfaces/IVariableDebtToken.sol"; import {Types} from "./Types.sol"; @@ -36,24 +35,12 @@ library ReserveDataLib { uint256 reserveFactor = reserve.configuration.getReserveFactor(); if (reserveFactor == 0) return reserve.accruedToTreasury; - ( - uint256 currPrincipalStableDebt, - uint256 currTotalStableDebt, - uint256 currAvgStableBorrowRate, - uint40 stableDebtLastUpdateTimestamp - ) = IStableDebtTokenLegacy(reserve.stableDebtTokenAddress).getSupplyData(); uint256 scaledTotalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply(); uint256 currTotalVariableDebt = scaledTotalVariableDebt.rayMul(indexes.borrow.poolIndex); uint256 prevTotalVariableDebt = scaledTotalVariableDebt.rayMul(reserve.variableBorrowIndex); - uint256 prevTotalStableDebt = currPrincipalStableDebt.rayMul( - MathUtils.calculateCompoundedInterest( - currAvgStableBorrowRate, stableDebtLastUpdateTimestamp, reserve.lastUpdateTimestamp - ) - ); - - uint256 accruedTotalDebt = - currTotalVariableDebt + currTotalStableDebt - prevTotalVariableDebt - prevTotalStableDebt; + + uint256 accruedTotalDebt = currTotalVariableDebt - prevTotalVariableDebt; if (accruedTotalDebt == 0) return reserve.accruedToTreasury; uint256 newAccruedToTreasury = accruedTotalDebt.percentMul(reserveFactor).rayDiv(indexes.supply.poolIndex); diff --git a/src/libraries/Types.sol b/src/libraries/Types.sol index 28184378..c562e7df 100644 --- a/src/libraries/Types.sol +++ b/src/libraries/Types.sol @@ -83,7 +83,7 @@ library Types { // SLOT 8 address aToken; // 160 bits // SLOT 9 - address stableDebtToken; // 160 bits + address _deprecated_stableDebtToken; // 160 bits (deprecated: will be empty for new markets) // SLOT 10 uint256 idleSupply; // 256 bits } @@ -169,7 +169,7 @@ library Types { struct LiquidityVars { address user; // The user address. IAaveOracle oracle; // The oracle used by Aave. - DataTypes.EModeCategoryLegacy eModeCategory; // The data related to the eMode category (could be empty if not in any e-mode). + DataTypes.CollateralConfig eModeCollateralConfig; // The data related to the eMode category (could be empty if not in any e-mode). } /// @notice Variables used during a borrow or withdraw. diff --git a/test/helpers/ForkTest.sol b/test/helpers/ForkTest.sol index e001bb0a..0dd70870 100644 --- a/test/helpers/ForkTest.sol +++ b/test/helpers/ForkTest.sol @@ -18,7 +18,6 @@ import {MathUtils} from "@aave-v3-origin/protocol/libraries/math/MathUtils.sol"; import {DataTypes} from "@aave-v3-origin/protocol/libraries/types/DataTypes.sol"; import {Errors as AaveErrors} from "@aave-v3-origin/protocol/libraries/helpers/Errors.sol"; import {ReserveConfiguration} from "@aave-v3-origin/protocol/libraries/configuration/ReserveConfiguration.sol"; -import {ReserveConfigurationLegacy} from "src/libraries/ReserveConfigurationLegacy.sol"; import {PermitHash} from "@permit2/libraries/PermitHash.sol"; import {IAllowanceTransfer, AllowanceTransfer} from "@permit2/AllowanceTransfer.sol"; @@ -38,7 +37,6 @@ contract ForkTest is BaseTest, Configured { using ReserveDataLib for DataTypes.ReserveDataLegacy; using ReserveDataTestLib for DataTypes.ReserveDataLegacy; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - using ReserveConfigurationLegacy for DataTypes.ReserveConfigurationMap; /* CONSTANTS */ @@ -221,36 +219,23 @@ contract ForkTest is BaseTest, Configured { return reserve.totalSupplyToCap(poolSupplyIndex, poolBorrowIndex); } - /// @dev Computes the valid lower bound for ltv and lt for a given CategoryEModeId, conditions required by Aave's code. - /// https://github.com/aave/aave-v3-core/blob/94e571f3a7465201881a59555314cd550ccfda57/contracts/protocol/pool/PoolConfigurator.sol#L369-L376 - function _getLtvLt(address underlying, uint8 eModeCategoryId) - internal - view - returns (uint256 ltvBound, uint256 ltBound, uint256 ltvConfig, uint256 ltConfig) - { - address[] memory reserves = pool.getReservesList(); - for (uint256 i = 0; i < reserves.length; ++i) { - DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(reserves[i]); - if (eModeCategoryId == currentConfig.getEModeCategory() || underlying == reserves[i]) { - ltvBound = uint16(Math.max(ltvBound, currentConfig.getLtv())); - - ltBound = uint16(Math.max(ltBound, currentConfig.getLiquidationThreshold())); - - if (underlying == reserves[i]) { - ltvConfig = uint16(currentConfig.getLtv()); - ltConfig = uint16(currentConfig.getLiquidationThreshold()); - } - } - } + function _getLtvLt(address underlying) internal view returns (uint256 ltvConfig, uint256 ltConfig) { + DataTypes.ReserveConfigurationMap memory config = pool.getConfiguration(underlying); + ltvConfig = config.getLtv(); + ltConfig = config.getLiquidationThreshold(); } function _setEModeCategoryAsset( - DataTypes.EModeCategoryLegacy memory eModeCategory, + DataTypes.CollateralConfig memory eModeCollateralConfig, address underlying, uint8 eModeCategoryId ) internal { poolAdmin.setEModeCategory( - eModeCategoryId, eModeCategory.ltv, eModeCategory.liquidationThreshold, eModeCategory.liquidationBonus, "" + eModeCategoryId, + eModeCollateralConfig.ltv, + eModeCollateralConfig.liquidationThreshold, + eModeCollateralConfig.liquidationBonus, + "" ); poolAdmin.setAssetBorrowableInEMode(underlying, eModeCategoryId, true); poolAdmin.setAssetCollateralInEMode(underlying, eModeCategoryId, true); diff --git a/test/helpers/IntegrationTest.sol b/test/helpers/IntegrationTest.sol index e73d8bf2..e1dde07d 100644 --- a/test/helpers/IntegrationTest.sol +++ b/test/helpers/IntegrationTest.sol @@ -27,7 +27,6 @@ contract IntegrationTest is ForkTest { using PercentageMath for uint256; using ReserveDataTestLib for DataTypes.ReserveData; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - using ReserveConfigurationLegacy for DataTypes.ReserveConfigurationMap; using TestMarketLib for TestMarket; uint256 internal constant INITIAL_BALANCE = 10_000_000_000 ether; @@ -129,9 +128,9 @@ contract IntegrationTest is ForkTest { reserve = pool.getReserveData(underlying); market.underlying = underlying; + market.reserveIndex = reserve.id; market.aToken = reserve.aTokenAddress; market.variableDebtToken = reserve.variableDebtTokenAddress; - market.stableDebtToken = reserve.stableDebtTokenAddress; market.symbol = ERC20(underlying).symbol(); market.reserveFactor = reserveFactor; market.p2pIndexCursor = p2pIndexCursor; @@ -148,16 +147,15 @@ contract IntegrationTest is ForkTest { market.supplyCap = type(uint256).max; market.borrowCap = type(uint256).max; - market.eModeCategoryId = uint8(reserve.configuration.getEModeCategory()); - market.eModeCategory = pool.getEModeCategoryData(market.eModeCategoryId); + market.eModeCollateralConfig = pool.getEModeCategoryCollateralConfig(eModeCategoryId); + market.eModeCollateralBitmap = pool.getEModeCategoryCollateralBitmap(eModeCategoryId); + market.eModeBorrowableBitmap = pool.getEModeCategoryBorrowableBitmap(eModeCategoryId); - market.isInEMode = eModeCategoryId == 0 || eModeCategoryId == market.eModeCategoryId; market.isCollateral = market.getLt(eModeCategoryId) > 0 && reserve.configuration.getDebtCeiling() == 0; market.isBorrowable = reserve.configuration.getBorrowingEnabled() && !reserve.configuration.getSiloedBorrowing(); vm.label(reserve.aTokenAddress, string.concat("a", market.symbol)); vm.label(reserve.variableDebtTokenAddress, string.concat("vd", market.symbol)); - vm.label(reserve.stableDebtTokenAddress, string.concat("sd", market.symbol)); } function _createTestMarket(address underlying, uint16 reserveFactor, uint16 p2pIndexCursor) internal virtual { @@ -177,7 +175,7 @@ contract IntegrationTest is ForkTest { } if (market.isBorrowable) { - if (market.isInEMode) borrowableInEModeUnderlyings.push(underlying); + if (market.getIsBorrowableInEMode(eModeCategoryId)) borrowableInEModeUnderlyings.push(underlying); else borrowableNotInEModeUnderlyings.push(underlying); } } @@ -494,7 +492,6 @@ contract IntegrationTest is ForkTest { vm.assume(output != market.underlying); vm.assume(output != market.aToken); vm.assume(output != market.variableDebtToken); - vm.assume(output != market.stableDebtToken); } } diff --git a/test/helpers/ProductionTest.sol b/test/helpers/ProductionTest.sol index 54c6ea90..74a31a03 100644 --- a/test/helpers/ProductionTest.sol +++ b/test/helpers/ProductionTest.sol @@ -5,7 +5,6 @@ import "./IntegrationTest.sol"; contract ProductionTest is IntegrationTest { using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - using ReserveConfigurationLegacy for DataTypes.ReserveConfigurationMap; using TestMarketLib for TestMarket; using ConfigLib for Config; @@ -44,12 +43,13 @@ contract ProductionTest is IntegrationTest { function _createTestMarket(address underlying, uint16, uint16) internal override { TestMarket storage market = testMarkets[underlying]; Types.Market memory morphoMarket = morpho.market(underlying); - DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(underlying); + DataTypes.ReserveDataLegacy memory reserveData = pool.getReserveData(underlying); + DataTypes.ReserveConfigurationMap memory configuration = reserveData.configuration; market.underlying = underlying; + market.reserveIndex = reserveData.id; market.aToken = morphoMarket.aToken; market.variableDebtToken = morphoMarket.variableDebtToken; - market.stableDebtToken = morphoMarket.stableDebtToken; market.symbol = ERC20(underlying).symbol(); market.reserveFactor = morphoMarket.reserveFactor; market.p2pIndexCursor = morphoMarket.p2pIndexCursor; @@ -66,24 +66,23 @@ contract ProductionTest is IntegrationTest { market.supplyCap = type(uint256).max; market.borrowCap = type(uint256).max; - market.eModeCategoryId = uint8(configuration.getEModeCategory()); - market.eModeCategory = pool.getEModeCategoryData(market.eModeCategoryId); + market.eModeCollateralConfig = pool.getEModeCategoryCollateralConfig(eModeCategoryId); + market.eModeCollateralBitmap = pool.getEModeCategoryCollateralBitmap(eModeCategoryId); + market.eModeBorrowableBitmap = pool.getEModeCategoryBorrowableBitmap(eModeCategoryId); - market.isInEMode = eModeCategoryId == 0 || eModeCategoryId == market.eModeCategoryId; market.isCollateral = market.getLt(eModeCategoryId) > 0 && configuration.getDebtCeiling() == 0 && morphoMarket.isCollateral; market.isBorrowable = configuration.getBorrowingEnabled() && !configuration.getSiloedBorrowing(); vm.label(morphoMarket.aToken, string.concat("a", market.symbol)); vm.label(morphoMarket.variableDebtToken, string.concat("vd", market.symbol)); - vm.label(morphoMarket.stableDebtToken, string.concat("sd", market.symbol)); if (market.isCollateral) { collateralUnderlyings.push(underlying); } if (market.isBorrowable) { - if (market.isInEMode) borrowableInEModeUnderlyings.push(underlying); + if (market.getIsBorrowableInEMode(eModeCategoryId)) borrowableInEModeUnderlyings.push(underlying); else borrowableNotInEModeUnderlyings.push(underlying); } } diff --git a/test/helpers/ReserveDataTestLib.sol b/test/helpers/ReserveDataTestLib.sol index 67a1ccd7..071762e2 100644 --- a/test/helpers/ReserveDataTestLib.sol +++ b/test/helpers/ReserveDataTestLib.sol @@ -23,9 +23,9 @@ library ReserveDataTestLib { return ERC20(reserve.aTokenAddress).totalSupply(); } - /// @dev Returns the quantity currently borrowed (with variable & stable rates) on the market on AaveV3. + /// @dev Returns the quantity currently borrowed (with variable only, stable being deprecated) on the market on AaveV3. function totalBorrow(DataTypes.ReserveDataLegacy memory reserve) internal view returns (uint256) { - return totalVariableBorrow(reserve) + totalStableBorrow(reserve); + return totalVariableBorrow(reserve); } /// @dev Returns the quantity currently borrowed with variable rate from the market on AaveV3. @@ -33,11 +33,6 @@ library ReserveDataTestLib { return ERC20(reserve.variableDebtTokenAddress).totalSupply(); } - /// @dev Returns the quantity currently borrowed with stable rate from the market on AaveV3. - function totalStableBorrow(DataTypes.ReserveDataLegacy memory reserve) internal view returns (uint256) { - return ERC20(reserve.stableDebtTokenAddress).totalSupply(); - } - /// @dev Returns the quantity currently supplied on behalf of the user, on the market on AaveV3. function supplyOf(DataTypes.ReserveDataLegacy memory reserve, address user) internal view returns (uint256) { return ERC20(reserve.aTokenAddress).balanceOf(user); @@ -52,11 +47,6 @@ library ReserveDataTestLib { return ERC20(reserve.variableDebtTokenAddress).balanceOf(user); } - /// @dev Returns the quantity currently borrowed on behalf of the user, with stable rate, on the market on AaveV3. - function stableBorrowOf(DataTypes.ReserveDataLegacy memory reserve, address user) internal view returns (uint256) { - return ERC20(reserve.stableDebtTokenAddress).balanceOf(user); - } - /// @dev Returns the total supply used towards the supply cap. function totalSupplyToCap( DataTypes.ReserveDataLegacy memory reserve, diff --git a/test/helpers/TestMarketLib.sol b/test/helpers/TestMarketLib.sol index 99cecdf0..caa9f99b 100644 --- a/test/helpers/TestMarketLib.sol +++ b/test/helpers/TestMarketLib.sol @@ -6,6 +6,7 @@ import {Constants} from "src/libraries/Constants.sol"; import {Math} from "@morpho-utils/math/Math.sol"; import {PercentageMath} from "@morpho-utils/math/PercentageMath.sol"; import {DataTypes} from "@aave-v3-origin/protocol/libraries/types/DataTypes.sol"; +import {EModeConfiguration} from "@aave-v3-origin/protocol/libraries/configuration/EModeConfiguration.sol"; import {collateralValue, rawCollateralValue} from "test/helpers/Utils.sol"; import {ERC20} from "@solmate/tokens/ERC20.sol"; @@ -13,10 +14,10 @@ import {ERC20} from "@solmate/tokens/ERC20.sol"; struct TestMarket { address aToken; address variableDebtToken; - address stableDebtToken; address underlying; string symbol; uint256 decimals; + uint256 reserveIndex; // uint256 ltv; uint256 lt; @@ -31,10 +32,10 @@ struct TestMarket { uint256 minAmount; uint256 maxAmount; // - uint8 eModeCategoryId; - DataTypes.EModeCategoryLegacy eModeCategory; + DataTypes.CollateralConfig eModeCollateralConfig; + uint128 eModeBorrowableBitmap; + uint128 eModeCollateralBitmap; // - bool isInEMode; bool isCollateral; bool isBorrowable; } @@ -42,6 +43,7 @@ struct TestMarket { library TestMarketLib { using Math for uint256; using PercentageMath for uint256; + using EModeConfiguration for uint128; /// @dev Returns the quantity that can be borrowed/withdrawn from the market. function liquidity(TestMarket storage market) internal view returns (uint256) { @@ -88,9 +90,25 @@ library TestMarketLib { (amount * baseMarket.price * 10 ** quoteMarket.decimals) / (quoteMarket.price * 10 ** baseMarket.decimals); } + function getHasTailoredParametersInEMode(TestMarket storage market, uint8 eModeCategoryId) + internal + view + returns (bool) + { + if (eModeCategoryId == 0) return false; + + return market.eModeCollateralBitmap.isReserveEnabledOnBitmap(market.reserveIndex); + } + + function getIsBorrowableInEMode(TestMarket storage market, uint8 eModeCategoryId) internal view returns (bool) { + if (eModeCategoryId == 0) return true; + + return market.eModeBorrowableBitmap.isReserveEnabledOnBitmap(market.reserveIndex); + } + function getLtv(TestMarket storage collateralMarket, uint8 eModeCategoryId) internal view returns (uint256) { - return eModeCategoryId != 0 && eModeCategoryId == collateralMarket.eModeCategoryId - ? collateralMarket.eModeCategory.ltv + return getHasTailoredParametersInEMode(collateralMarket, eModeCategoryId) + ? collateralMarket.eModeCollateralConfig.ltv : collateralMarket.ltv; } @@ -98,8 +116,8 @@ library TestMarketLib { uint256 ltv = getLtv(collateralMarket, eModeCategoryId); if (ltv == 0) return 0; - return eModeCategoryId != 0 && eModeCategoryId == collateralMarket.eModeCategoryId - ? collateralMarket.eModeCategory.liquidationThreshold + return getHasTailoredParametersInEMode(collateralMarket, eModeCategoryId) + ? collateralMarket.eModeCollateralConfig.liquidationThreshold : collateralMarket.lt; } diff --git a/test/integration/TestIntegrationEModeNative.sol b/test/integration/TestIntegrationEModeNative.sol index 44896ad4..c315496b 100644 --- a/test/integration/TestIntegrationEModeNative.sol +++ b/test/integration/TestIntegrationEModeNative.sol @@ -5,19 +5,28 @@ import {IWETHGateway} from "src/interfaces/extensions/IWETHGateway.sol"; import {WETHGateway} from "src/extensions/WETHGateway.sol"; +import {EModeConfiguration} from "@aave-v3-origin/protocol/libraries/configuration/EModeConfiguration.sol"; + import "test/helpers/IntegrationTest.sol"; contract TestIntegrationEModeNative is IntegrationTest { using Math for uint256; using PercentageMath for uint256; using TestMarketLib for TestMarket; - using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - using ReserveConfigurationLegacy for DataTypes.ReserveConfigurationMap; + using EModeConfiguration for uint128; function setUp() public virtual override { - DataTypes.ReserveConfigurationMap memory lsdConfig = pool.getConfiguration(stNative); - - eModeCategoryId = uint8(lsdConfig.getEModeCategory()); + // Guess the eModeCategoryId for LSD to be 1. + eModeCategoryId = 1; + + // Verify the guess that eModeCategoryId for LSD is 1. + uint256 stNativeIndex = pool.getReserveData(stNative).id; + DataTypes.CollateralConfig memory collateralConfig = pool.getEModeCategoryCollateralConfig(eModeCategoryId); + require(collateralConfig.liquidationThreshold != 0, "not activated e-mode"); + require( + pool.getEModeCategoryCollateralBitmap(eModeCategoryId).isReserveEnabledOnBitmap(stNativeIndex), + "wrong e-mode category" + ); super.setUp(); } @@ -78,7 +87,7 @@ contract TestIntegrationEModeNative is IntegrationTest { borrowed = bound( borrowed, wNativeMarket.borrowable(collateralMarket, rawCollateral, 0).percentAdd(20), - wNativeMarket.borrowable(collateralMarket, rawCollateral, collateralMarket.eModeCategoryId).percentAdd(20) + wNativeMarket.borrowable(collateralMarket, rawCollateral, eModeCategoryId).percentAdd(20) ); user.approve(collateralMarket.underlying, rawCollateral); diff --git a/test/integration/TestIntegrationMorphoGetters.sol b/test/integration/TestIntegrationMorphoGetters.sol index c4176969..6a9c252f 100644 --- a/test/integration/TestIntegrationMorphoGetters.sol +++ b/test/integration/TestIntegrationMorphoGetters.sol @@ -72,7 +72,7 @@ contract TestIntegrationMorphoGetters is IntegrationTest { user.approve(market.underlying, supplied); user.supply(market.underlying, supplied); - if (market.isBorrowable && market.isInEMode) { + if (market.isBorrowable && market.getIsBorrowableInEMode(eModeCategoryId)) { _borrowWithoutCollateral( address(user), market, supplied, address(user), address(user), DEFAULT_MAX_ITERATIONS ); diff --git a/test/integration/TestIntegrationMorphoSetters.sol b/test/integration/TestIntegrationMorphoSetters.sol index f64b42dc..55d5a304 100644 --- a/test/integration/TestIntegrationMorphoSetters.sol +++ b/test/integration/TestIntegrationMorphoSetters.sol @@ -120,7 +120,7 @@ contract TestIntegrationMorphoSetters is IntegrationTest { assertEq(market.reserveFactor, reserveFactor, "reserve factor"); assertEq(market.p2pIndexCursor, p2pIndexCursor, "p2p index cursor"); assertEq(market.aToken, reserveData.aTokenAddress, "aToken"); - assertEq(market.stableDebtToken, reserveData.stableDebtTokenAddress, "stable debt token"); + assertEq(market._deprecated_stableDebtToken, address(0), "stable debt token"); assertEq(market.idleSupply, 0, "idle supply"); assertEq(ERC20(link).allowance(address(morpho), address(pool)), type(uint256).max); diff --git a/test/integration/TestIntegrationUpgrade.sol b/test/integration/TestIntegrationUpgrade.sol index c3b3a4d3..a81253ed 100644 --- a/test/integration/TestIntegrationUpgrade.sol +++ b/test/integration/TestIntegrationUpgrade.sol @@ -230,7 +230,11 @@ contract TestIntegrationUpgrade is IntegrationTest { assertEq(s1.market.reserveFactor, s2.market.reserveFactor, "market.reserveFactor"); assertEq(s1.market.p2pIndexCursor, s2.market.p2pIndexCursor, "market.p2pIndexCursor"); assertEq(s1.market.aToken, s2.market.aToken, "market.aToken"); - assertEq(s1.market.stableDebtToken, s2.market.stableDebtToken, "market.stableDebtToken"); + assertEq( + s1.market._deprecated_stableDebtToken, + s2.market._deprecated_stableDebtToken, + "market._deprecated_stableDebtToken" + ); assertEq(s1.market.idleSupply, s2.market.idleSupply, "market.idleSupply"); for (uint256 i; i < s1.marketsCreated.length; i++) { diff --git a/test/internal/TestInternalEMode.sol b/test/internal/TestInternalEMode.sol index 5176e9bd..efbc8371 100644 --- a/test/internal/TestInternalEMode.sol +++ b/test/internal/TestInternalEMode.sol @@ -27,11 +27,8 @@ contract TestInternalEMode is InternalTest, PositionsManagerInternal { using ConfigLib for Config; using SafeTransferLib for ERC20; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - using ReserveConfigurationLegacy for DataTypes.ReserveConfigurationMap; struct AssetData { - uint256 underlyingPrice; - uint256 underlyingPriceEMode; uint16 ltvEMode; uint16 ltEMode; } @@ -56,37 +53,33 @@ contract TestInternalEMode is InternalTest, PositionsManagerInternal { _pool.supplyToPool(wNative, 1 ether, _pool.getReserveNormalizedIncome(wNative)); } - function testLtvLiquidationThresholdPriceSourceEMode(uint256 seed, AssetData memory assetData) public { + function testLtvLiquidationThresholdEMode(uint256 seed, uint256 underlyingPrice, AssetData memory assetData) + public + { address underlying = _randomUnderlying(seed); - (uint256 ltvBound, uint256 ltBound, uint256 ltvConfig, uint256 ltConfig) = - _getLtvLt(underlying, _eModeCategoryId); + (uint256 ltvConfig, uint256 ltConfig) = _getLtvLt(underlying); - assetData.ltEMode = uint16(bound(assetData.ltEMode, ltBound + 1, type(uint16).max)); - assetData.ltvEMode = uint16(bound(assetData.ltvEMode, ltvBound + 1, assetData.ltEMode)); + assetData.ltEMode = uint16(bound(assetData.ltEMode, 0, type(uint16).max)); + assetData.ltvEMode = uint16(bound(assetData.ltvEMode, 0, assetData.ltEMode)); uint16 liquidationBonus = uint16(PercentageMath.PERCENTAGE_FACTOR + 1); - assetData.underlyingPrice = bound(assetData.underlyingPrice, 0, type(uint96).max - 1); - assetData.underlyingPriceEMode = bound(assetData.underlyingPriceEMode, 0, type(uint96).max); + underlyingPrice = bound(underlyingPrice, 0, type(uint96).max - 1); vm.assume(uint256(assetData.ltEMode).percentMul(uint256(liquidationBonus)) <= PercentageMath.PERCENTAGE_FACTOR); - vm.assume(assetData.underlyingPrice != assetData.underlyingPriceEMode); - DataTypes.EModeCategoryLegacy memory eModeCategory = DataTypes.EModeCategoryLegacy({ + DataTypes.CollateralConfig memory eModeCollateralConfig = DataTypes.CollateralConfig({ ltv: assetData.ltvEMode, liquidationThreshold: assetData.ltEMode, - liquidationBonus: liquidationBonus, - priceSource: address(1), - label: "" + liquidationBonus: liquidationBonus }); if (_eModeCategoryId != 0) { - _setEModeCategoryAsset(eModeCategory, underlying, _eModeCategoryId); + _setEModeCategoryAsset(eModeCollateralConfig, underlying, _eModeCategoryId); } - oracle.setAssetPrice(address(1), assetData.underlyingPriceEMode); - oracle.setAssetPrice(underlying, assetData.underlyingPrice); + oracle.setAssetPrice(underlying, underlyingPrice); Types.LiquidityVars memory vars; vars.oracle = oracle; vars.user = address(this); - vars.eModeCategory = eModeCategory; + vars.eModeCollateralConfig = eModeCollateralConfig; (uint256 assetPrice, uint256 ltv, uint256 lt,) = _assetLiquidityData(underlying, vars); assertEq( @@ -99,151 +92,43 @@ contract TestInternalEMode is InternalTest, PositionsManagerInternal { _eModeCategoryId != 0 && ltvConfig != 0 ? assetData.ltEMode : ltConfig, "Liquidation Threshold E-Mode" ); - assertEq( - assetPrice, - _eModeCategoryId != 0 && assetData.underlyingPriceEMode != 0 - ? assetData.underlyingPriceEMode - : assetData.underlyingPrice, - "Underlying Price E-Mode" - ); + assertEq(assetPrice, underlyingPrice, "Underlying Price"); } - function testIsInEModeCategory(uint256 seed, uint8 eModeCategoryId, uint16 lt, uint16 ltv, uint16 liquidationBonus) - public - { + function testIsInEModeCollateralSameCategory( + uint256 seed, + uint8 eModeCategoryId, + uint16 lt, + uint16 ltv, + uint16 liquidationBonus + ) public { address underlying = _randomUnderlying(seed); eModeCategoryId = uint8(bound(uint256(eModeCategoryId), 1, type(uint8).max)); - (uint256 ltvBound, uint256 ltBound,,) = _getLtvLt(underlying, eModeCategoryId); - address priceSourceEMode = address(1); - ltv = uint16(bound(ltv, ltvBound + 1, PercentageMath.PERCENTAGE_FACTOR - 1)); - lt = uint16(bound(lt, Math.max(ltv + 1, ltBound + 1), PercentageMath.PERCENTAGE_FACTOR)); + ltv = uint16(bound(ltv, 1, PercentageMath.PERCENTAGE_FACTOR - 1)); + lt = uint16(bound(lt, ltv, PercentageMath.PERCENTAGE_FACTOR)); liquidationBonus = uint16(bound(liquidationBonus, PercentageMath.PERCENTAGE_FACTOR + 1, type(uint16).max)); vm.assume(uint256(lt).percentMul(liquidationBonus) <= PercentageMath.PERCENTAGE_FACTOR); - DataTypes.EModeCategoryLegacy memory eModeCategory = DataTypes.EModeCategoryLegacy({ - ltv: ltv, - liquidationThreshold: lt, - liquidationBonus: liquidationBonus, - priceSource: priceSourceEMode, - label: "" - }); - - _setEModeCategoryAsset(eModeCategory, underlying, eModeCategoryId); - - DataTypes.ReserveConfigurationMap memory config = _pool.getConfiguration(underlying); - - bool expectedIsInEMode = _eModeCategoryId == eModeCategoryId && _eModeCategoryId != 0; - bool isInEMode = _isInEModeCategory(config); - - assertEq(isInEMode, expectedIsInEMode, "Wrong E-Mode"); - } - - function testAssetDataEMode( - address underlying, - address priceSourceEMode, - uint256 underlyingPriceEMode, - uint256 underlyingPrice, - uint8 eModeCategoryId - ) public { - eModeCategoryId = uint8(bound(eModeCategoryId, 1, type(uint8).max)); - priceSourceEMode = _boundAddressNotZero(priceSourceEMode); - vm.assume(underlying != priceSourceEMode); - underlyingPriceEMode = bound(underlyingPriceEMode, 1, type(uint256).max); - underlyingPrice = bound(underlyingPrice, 0, type(uint256).max); - - oracle.setAssetPrice(underlying, underlyingPrice); - oracle.setAssetPrice(priceSourceEMode, underlyingPriceEMode); - - DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(underlying); - - _eModeCategoryId = eModeCategoryId; - configuration.setEModeCategory(eModeCategoryId); - - (bool isInEMode, uint256 price, uint256 assetUnit) = - _assetData(underlying, oracle, configuration, priceSourceEMode); - - assertEq(isInEMode, true, "isInEMode"); - assertEq(price, underlyingPriceEMode, "price != expected price"); - assertEq(assetUnit, 10 ** configuration.getDecimals(), "assetUnit"); - } - - function testAssetDataEModeWithPriceSourceZero( - address underlying, - uint256 underlyingPrice, - uint256 underlyingPriceEMode, - uint8 eModeCategoryId - ) public { - eModeCategoryId = uint8(bound(eModeCategoryId, 1, type(uint8).max)); - underlying = _boundAddressNotZero(underlying); - underlyingPriceEMode = bound(underlyingPriceEMode, 1, type(uint256).max); - underlyingPrice = bound(underlyingPrice, 0, type(uint256).max); - - oracle.setAssetPrice(underlying, underlyingPrice); - oracle.setAssetPrice(address(0), underlyingPriceEMode); - - DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(underlying); - - _eModeCategoryId = eModeCategoryId; - configuration.setEModeCategory(eModeCategoryId); - - (bool isInEMode, uint256 price, uint256 assetUnit) = _assetData(underlying, oracle, configuration, address(0)); - - assertEq(isInEMode, true, "isInEMode"); - assertEq(price, underlyingPrice, "price != expected price"); - assertEq(assetUnit, 10 ** configuration.getDecimals(), "assetUnit"); - } - - function testAssetDataNonEMode( - address underlying, - address priceSourceEMode, - uint256 underlyingPriceEMode, - uint256 underlyingPrice, - uint8 eModeCategoryId - ) public { - priceSourceEMode = _boundAddressNotZero(priceSourceEMode); - vm.assume(underlying != priceSourceEMode); - underlyingPriceEMode = bound(underlyingPriceEMode, 1, type(uint256).max); - underlyingPrice = bound(underlyingPrice, 0, type(uint256).max); + DataTypes.CollateralConfig memory eModeCollateralConfig = + DataTypes.CollateralConfig({ltv: ltv, liquidationThreshold: lt, liquidationBonus: liquidationBonus}); + _setEModeCategoryAsset(eModeCollateralConfig, underlying, eModeCategoryId); - oracle.setAssetPrice(underlying, underlyingPrice); - oracle.setAssetPrice(priceSourceEMode, underlyingPriceEMode); - - DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(underlying); - configuration.setEModeCategory(eModeCategoryId); + bool isInEModeCollateral = _hasTailoredParametersInEmode(underlying); + bool sameCategory = _eModeCategoryId == eModeCategoryId; - (bool isInEMode, uint256 price, uint256 assetUnit) = - _assetData(underlying, oracle, configuration, priceSourceEMode); - - assertEq(isInEMode, false, "isInEMode"); - assertEq(price, underlyingPrice, "price != expected price"); - assertEq(assetUnit, 10 ** configuration.getDecimals(), "assetUnit"); + assert(!isInEModeCollateral || sameCategory); } - function testAssetDataEModeWithEModePriceZero( - address underlying, - address priceSourceEMode, - uint256 underlyingPrice, - uint8 eModeCategoryId - ) public { - eModeCategoryId = uint8(bound(eModeCategoryId, 1, type(uint8).max)); - priceSourceEMode = _boundAddressNotZero(priceSourceEMode); - vm.assume(underlying != priceSourceEMode); + function testAssetDataEMode(address underlying, uint256 underlyingPrice) public { underlyingPrice = bound(underlyingPrice, 0, type(uint256).max); - oracle.setAssetPrice(underlying, underlyingPrice); - oracle.setAssetPrice(priceSourceEMode, 0); DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(underlying); - _eModeCategoryId = eModeCategoryId; - configuration.setEModeCategory(eModeCategoryId); - - (bool isInEMode, uint256 price, uint256 assetUnit) = - _assetData(underlying, oracle, configuration, priceSourceEMode); + (uint256 price, uint256 assetUnit) = _assetData(underlying, oracle, configuration); - assertEq(isInEMode, true, "isInEMode"); assertEq(price, underlyingPrice, "price != expected price"); assertEq(assetUnit, 10 ** configuration.getDecimals(), "assetUnit"); } @@ -251,33 +136,30 @@ contract TestInternalEMode is InternalTest, PositionsManagerInternal { function testShouldNotAuthorizeBorrowInconsistentEmode( uint8 eModeCategoryId, Types.Indexes256 memory indexes, - DataTypes.EModeCategoryLegacy memory eModeCategory + DataTypes.CollateralConfig memory eModeCollateralConfig ) public { eModeCategoryId = uint8(bound(uint256(eModeCategoryId), 1, type(uint8).max)); - (uint256 ltvBound, uint256 ltBound,,) = _getLtvLt(dai, eModeCategoryId); - eModeCategory.ltv = uint16(bound(eModeCategory.ltv, ltvBound + 1, PercentageMath.PERCENTAGE_FACTOR - 1)); - eModeCategory.liquidationThreshold = uint16( + eModeCollateralConfig.ltv = uint16(bound(eModeCollateralConfig.ltv, 1, PercentageMath.PERCENTAGE_FACTOR - 1)); + eModeCollateralConfig.liquidationThreshold = uint16( bound( - eModeCategory.liquidationThreshold, - Math.max(eModeCategory.ltv + 1, ltBound + 1), - PercentageMath.PERCENTAGE_FACTOR + eModeCollateralConfig.liquidationThreshold, eModeCollateralConfig.ltv, PercentageMath.PERCENTAGE_FACTOR ) ); - eModeCategory.liquidationBonus = uint16( + eModeCollateralConfig.liquidationBonus = uint16( bound( - eModeCategory.liquidationBonus, + eModeCollateralConfig.liquidationBonus, PercentageMath.PERCENTAGE_FACTOR + 1, 2 * PercentageMath.PERCENTAGE_FACTOR ) ); vm.assume( - uint256(eModeCategory.liquidationThreshold).percentMul(eModeCategory.liquidationBonus) + uint256(eModeCollateralConfig.liquidationThreshold).percentMul(eModeCollateralConfig.liquidationBonus) <= PercentageMath.PERCENTAGE_FACTOR ); - _setEModeCategoryAsset(eModeCategory, dai, eModeCategoryId); + _setEModeCategoryAsset(eModeCollateralConfig, dai, eModeCategoryId); indexes.supply.poolIndex = bound(indexes.supply.poolIndex, 0, type(uint96).max); indexes.supply.p2pIndex = bound(indexes.supply.p2pIndex, indexes.supply.poolIndex, type(uint96).max); diff --git a/test/internal/TestInternalMorphoInternal.sol b/test/internal/TestInternalMorphoInternal.sol index eb182675..b46baddb 100644 --- a/test/internal/TestInternalMorphoInternal.sol +++ b/test/internal/TestInternalMorphoInternal.sol @@ -379,10 +379,10 @@ contract TestInternalMorphoInternal is InternalTest { } function testAssetLiquidityData() public view { - DataTypes.EModeCategoryLegacy memory eModeCategory = _pool.getEModeCategoryData(0); + DataTypes.CollateralConfig memory eModeCollateralConfig = _pool.getEModeCategoryCollateralConfig(0); (uint256 poolLtv, uint256 poolLt,, uint256 poolDecimals,) = _pool.getConfiguration(dai).getParams(); - Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCategory); + Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCollateralConfig); (uint256 price, uint256 ltv, uint256 lt, uint256 units) = _assetLiquidityData(dai, vars); assertGt(price, 0, "price not gt 0"); @@ -401,8 +401,8 @@ contract TestInternalMorphoInternal is InternalTest { _marketBalances[dai].collateral[address(1)] = amount.rayDivUp(_market[dai].indexes.supply.poolIndex); - DataTypes.EModeCategoryLegacy memory eModeCategory = _pool.getEModeCategoryData(0); - Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCategory); + DataTypes.CollateralConfig memory eModeCollateralConfig = _pool.getEModeCategoryCollateralConfig(0); + Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCollateralConfig); (uint256 borrowable, uint256 maxDebt) = _collateralData(dai, vars); assertEq(borrowable, 0, "borrowable != 0"); @@ -415,8 +415,8 @@ contract TestInternalMorphoInternal is InternalTest { _market[dai].isCollateral = true; _marketBalances[dai].collateral[address(1)] = amount.rayDivUp(_market[dai].indexes.supply.poolIndex); - DataTypes.EModeCategoryLegacy memory eModeCategory = _pool.getEModeCategoryData(0); - Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCategory); + DataTypes.CollateralConfig memory eModeCollateralConfig = _pool.getEModeCategoryCollateralConfig(0); + Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCollateralConfig); (uint256 borrowable, uint256 maxDebt) = _collateralData(dai, vars); @@ -443,8 +443,8 @@ contract TestInternalMorphoInternal is InternalTest { true ); - DataTypes.EModeCategoryLegacy memory eModeCategory = _pool.getEModeCategoryData(0); - Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCategory); + DataTypes.CollateralConfig memory eModeCollateralConfig = _pool.getEModeCategoryCollateralConfig(0); + Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCollateralConfig); Types.Indexes256 memory indexes = _computeIndexes(dai); @@ -466,8 +466,8 @@ contract TestInternalMorphoInternal is InternalTest { _userCollaterals[address(1)].add(wbtc); _userCollaterals[address(1)].add(usdc); - DataTypes.EModeCategoryLegacy memory eModeCategory = _pool.getEModeCategoryData(0); - Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCategory); + DataTypes.CollateralConfig memory eModeCollateralConfig = _pool.getEModeCategoryCollateralConfig(0); + Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCollateralConfig); (uint256 borrowable, uint256 maxDebt) = _totalCollateralData(vars); @@ -513,8 +513,8 @@ contract TestInternalMorphoInternal is InternalTest { _userBorrows[address(1)].add(wbtc); _userBorrows[address(1)].add(usdc); - DataTypes.EModeCategoryLegacy memory eModeCategory = _pool.getEModeCategoryData(0); - Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCategory); + DataTypes.CollateralConfig memory eModeCollateralConfig = _pool.getEModeCategoryCollateralConfig(0); + Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCollateralConfig); uint256 debt = _totalDebt(vars); uint256[3] memory debtSingles = [_debt(dai, vars), _debt(wbtc, vars), _debt(usdc, vars)]; @@ -560,8 +560,8 @@ contract TestInternalMorphoInternal is InternalTest { _userBorrows[address(1)].add(usdc); Types.LiquidityData memory liquidityData = _liquidityData(address(1)); - DataTypes.EModeCategoryLegacy memory eModeCategory = _pool.getEModeCategoryData(0); - Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCategory); + DataTypes.CollateralConfig memory eModeCollateralConfig = _pool.getEModeCategoryCollateralConfig(0); + Types.LiquidityVars memory vars = Types.LiquidityVars(address(1), oracle, eModeCollateralConfig); (uint256 borrowable, uint256 maxDebt) = _totalCollateralData(vars); uint256 debt = _totalDebt(vars); diff --git a/test/internal/TestInternalPositionsManagerInternal.sol b/test/internal/TestInternalPositionsManagerInternal.sol index 4d3a14fe..afa6d212 100644 --- a/test/internal/TestInternalPositionsManagerInternal.sol +++ b/test/internal/TestInternalPositionsManagerInternal.sol @@ -393,18 +393,16 @@ contract TestInternalPositionsManagerInternal is InternalTest, PositionsManagerI DataTypes.ReserveConfigurationMap memory borrowConfig = _pool.getConfiguration(wbtc); DataTypes.ReserveConfigurationMap memory collateralConfig = _pool.getConfiguration(dai); - DataTypes.EModeCategoryLegacy memory eModeCategory = _pool.getEModeCategoryData(_eModeCategoryId); + DataTypes.CollateralConfig memory eModeCollateralConfig = + _pool.getEModeCategoryCollateralConfig(_eModeCategoryId); (,,, vars.borrowedTokenUnit,) = borrowConfig.getParams(); (,, vars.liquidationBonus, vars.collateralTokenUnit,) = collateralConfig.getParams(); - bool isInCollateralEMode; - (, vars.borrowedPrice, vars.borrowedTokenUnit) = - _assetData(wbtc, oracle, borrowConfig, eModeCategory.priceSource); - (isInCollateralEMode, vars.collateralPrice, vars.collateralTokenUnit) = - _assetData(dai, oracle, collateralConfig, eModeCategory.priceSource); + (vars.borrowedPrice, vars.borrowedTokenUnit) = _assetData(wbtc, oracle, borrowConfig); + (vars.collateralPrice, vars.collateralTokenUnit) = _assetData(dai, oracle, collateralConfig); - if (isInCollateralEMode) vars.liquidationBonus = eModeCategory.liquidationBonus; + if (_hasTailoredParametersInEmode(dai)) vars.liquidationBonus = eModeCollateralConfig.liquidationBonus; TestSeizeVars memory expected; TestSeizeVars memory actual; diff --git a/test/unit/TestUnitMarketLibIdle.sol b/test/unit/TestUnitMarketLibIdle.sol index 0151d0db..c039927f 100644 --- a/test/unit/TestUnitMarketLibIdle.sol +++ b/test/unit/TestUnitMarketLibIdle.sol @@ -6,7 +6,7 @@ import {MarketLib} from "src/libraries/MarketLib.sol"; import "test/helpers/ForkTest.sol"; contract TestUnitMarketLibIdle is ForkTest { - using ReserveDataLib for DataTypes.ReserveDataLegacy; + using ReserveDataLib for DataTypes.ReserveData; using ReserveDataTestLib for DataTypes.ReserveDataLegacy; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using MarketLib for Types.Market;