Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove deprecated features #888

Open
wants to merge 12 commits into
base: refactor/aave-v3-origin
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,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:
Expand Down
3 changes: 0 additions & 3 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
62 changes: 32 additions & 30 deletions src/MorphoInternal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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 */

Expand Down Expand Up @@ -92,7 +92,6 @@ abstract contract MorphoInternal is MorphoStorage {
market.underlying = underlying;
market.aToken = reserve.aTokenAddress;
market.variableDebtToken = reserve.variableDebtTokenAddress;
market.stableDebtToken = reserve.stableDebtTokenAddress;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe not necessary to do this change ?


_marketsCreated.push(underlying);

Expand Down Expand Up @@ -231,7 +230,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;

Expand Down Expand Up @@ -307,8 +308,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 =
Expand All @@ -329,18 +329,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 (_isCollateralInEMode(underlying)) {
ltv = vars.eModeCollateralConfig.ltv;
liquidationThreshold = vars.eModeCollateralConfig.liquidationThreshold;
} else {
ltv = config.getLtv();
liquidationThreshold = config.getLiquidationThreshold();
Expand Down Expand Up @@ -489,33 +487,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 _isCollateralInEMode(address underlying) internal view returns (bool) {
if (_eModeCategoryId == 0) return false;

return (isInEMode, oracle.getAssetPrice(asset), assetUnit);
uint256 reserveIndex = _pool.getReserveData(underlying).id;
Copy link
Contributor Author

@QGarchery QGarchery Oct 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this call costs a lot of gas, and isn't very ergonomic. Another approach would be to do this call upfront, and use the values from it in sub-functions (in the same way that the config parameter is passed around).
The solution proposed in this PR is meant to minimize changes though, but a refactoring PR could be made on top

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);
}
}
23 changes: 9 additions & 14 deletions src/PositionsManagerInternal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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 {
Expand Down Expand Up @@ -118,7 +116,7 @@ abstract contract PositionsManagerInternal is MatchingEngine {
revert Errors.SentinelBorrowNotEnabled();
}

if (_eModeCategoryId != 0 && _eModeCategoryId != config.getEModeCategory()) {
if (!_isBorrowableInEMode(underlying)) {
revert Errors.InconsistentEMode();
}

Expand All @@ -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();
}
Expand Down Expand Up @@ -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 = _isCollateralInEMode(underlyingCollateral)
? eModeCollateralConfig.liquidationBonus
: collateralConfig.getLiquidationBonus();

amountToRepay = maxToRepay;
amountToSeize = (
Expand Down
6 changes: 0 additions & 6 deletions src/interfaces/aave/IStableDebtTokenLegacy.sol

This file was deleted.

25 changes: 0 additions & 25 deletions src/libraries/ReserveConfigurationLegacy.sol

This file was deleted.

17 changes: 2 additions & 15 deletions src/libraries/ReserveDataLib.sol
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/libraries/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ library Types {
// SLOT 8
address aToken; // 160 bits
// SLOT 9
address stableDebtToken; // 160 bits
address _deprecated_stableDebtToken; // 160 bits
// SLOT 10
uint256 idleSupply; // 256 bits
}
Expand Down Expand Up @@ -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.
Expand Down
35 changes: 10 additions & 25 deletions test/helpers/ForkTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;

/* STRUCTS */

Expand Down Expand Up @@ -231,36 +229,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
Comment on lines -234 to -235
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not longer a restriction on Aave

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 = uint16(config.getLtv());
ltConfig = uint16(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);
Expand Down
Loading
Loading