From 99aefbb3567b54369eed98ec2734cfe2c72f15b1 Mon Sep 17 00:00:00 2001 From: The3D Date: Mon, 27 Sep 2021 15:14:56 +0200 Subject: [PATCH 1/7] feat: added configuration flag for isolation mode --- contracts/misc/AaveProtocolDataProvider.sol | 4 +-- .../helpers/MockReserveConfiguration.sol | 1 + .../configuration/ReserveConfiguration.sol | 27 ++++++++++++++++++- .../libraries/logic/ValidationLogic.sol | 18 ++++++------- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/contracts/misc/AaveProtocolDataProvider.sol b/contracts/misc/AaveProtocolDataProvider.sol index 9c0e34664..e15f25eec 100644 --- a/contracts/misc/AaveProtocolDataProvider.sol +++ b/contracts/misc/AaveProtocolDataProvider.sol @@ -114,7 +114,7 @@ contract AaveProtocolDataProvider { (ltv, liquidationThreshold, liquidationBonus, decimals, reserveFactor, ) = configuration .getParams(); - (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled, ) = configuration.getFlags(); + (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled, , ) = configuration.getFlags(); usageAsCollateralEnabled = liquidationThreshold > 0; } @@ -150,7 +150,7 @@ contract AaveProtocolDataProvider { * @return isPaused True if the pool is paused, false otherwise **/ function getPaused(address asset) external view returns (bool isPaused) { - (, , , , isPaused) = IPool(ADDRESSES_PROVIDER.getPool()).getConfiguration(asset).getFlags(); + (, , , , isPaused, ) = IPool(ADDRESSES_PROVIDER.getPool()).getConfiguration(asset).getFlags(); } /** diff --git a/contracts/mocks/helpers/MockReserveConfiguration.sol b/contracts/mocks/helpers/MockReserveConfiguration.sol index fd93baf29..9cecf9ce1 100644 --- a/contracts/mocks/helpers/MockReserveConfiguration.sol +++ b/contracts/mocks/helpers/MockReserveConfiguration.sol @@ -127,6 +127,7 @@ contract MockReserveConfiguration { bool, bool, bool, + bool, bool ) { diff --git a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index 848207229..8a5b12567 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -19,6 +19,7 @@ library ReserveConfiguration { uint256 constant BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore uint256 constant STABLE_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore uint256 constant PAUSED_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant ISOLATION_MODE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant BORROW_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant SUPPLY_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore @@ -34,7 +35,8 @@ library ReserveConfiguration { uint256 constant BORROWING_ENABLED_START_BIT_POSITION = 58; uint256 constant STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59; uint256 constant IS_PAUSED_START_BIT_POSITION = 60; - /// @dev bits 61 62 63 unused yet + uint256 constant ISOLATION_MODE_START_BIT_POSITION = 61; + /// @dev bits 62 63 unused yet uint256 constant RESERVE_FACTOR_START_BIT_POSITION = 64; uint256 constant BORROW_CAP_START_BIT_POSITION = 80; uint256 constant SUPPLY_CAP_START_BIT_POSITION = 116; @@ -216,6 +218,26 @@ library ReserveConfiguration { return (self.data & ~PAUSED_MASK) != 0; } + /** + * @notice Sets the isolation mode for the reserve + * @param self The reserve configuration + * @param isolation True if the reserve should be in isolation mode, false otherwise + **/ + function setIsolationMode(DataTypes.ReserveConfigurationMap memory self, bool isolation) internal pure { + self.data = + (self.data & ISOLATION_MODE_MASK) | + (uint256(isolation ? 1 : 0) << ISOLATION_MODE_START_BIT_POSITION); + } + + /** + * @notice Gets the state of the isolation mode for the reserve + * @param self The reserve configuration + * @return True if the reserve is in isolation mode, false otherwise + **/ + function getIsolationMode(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) { + return (self.data & ~ISOLATION_MODE_MASK) != 0; + } + /** * @notice Enables or disables borrowing on the reserve * @param self The reserve configuration @@ -421,6 +443,7 @@ library ReserveConfiguration { * @return The state flag representing borrowing enabled * @return The state flag representing stabelRateBorrowing enabled * @return The state flag representing paused + * @return The state flag representing isolation mode **/ function getFlags(DataTypes.ReserveConfigurationMap memory self) internal @@ -430,6 +453,7 @@ library ReserveConfiguration { bool, bool, bool, + bool, bool ) { @@ -440,6 +464,7 @@ library ReserveConfiguration { (dataLocal & ~FROZEN_MASK) != 0, (dataLocal & ~BORROWING_MASK) != 0, (dataLocal & ~STABLE_BORROWING_MASK) != 0, + (dataLocal & ~PAUSED_MASK) != 0, (dataLocal & ~PAUSED_MASK) != 0 ); } diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 106384c90..fbce729d0 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -46,7 +46,7 @@ library ValidationLogic { internal view { - (bool isActive, bool isFrozen, , , bool isPaused) = reserveCache + (bool isActive, bool isFrozen, , , bool isPaused, ) = reserveCache .reserveConfiguration .getFlags(); (, , , uint256 reserveDecimals, , ) = reserveCache.reserveConfiguration.getParams(); @@ -81,7 +81,7 @@ library ValidationLogic { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); - (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlags(); + (bool isActive, , , , bool isPaused,) = reserveCache.reserveConfiguration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); } @@ -129,7 +129,7 @@ library ValidationLogic { vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled, - vars.isPaused + vars.isPaused, ) = params.reserveCache.reserveConfiguration.getFlags(); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); @@ -270,7 +270,7 @@ library ValidationLogic { uint256 stableDebt, uint256 variableDebt ) internal view { - (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlags(); + (bool isActive, , , , bool isPaused, ) = reserveCache.reserveConfiguration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -321,7 +321,7 @@ library ValidationLogic { uint256 variableDebt, DataTypes.InterestRateMode currentRateMode ) internal view { - (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = reserveCache + (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused, ) = reserveCache .reserveConfiguration .getFlags(); @@ -370,7 +370,7 @@ library ValidationLogic { IERC20 variableDebtToken, address aTokenAddress ) internal view { - (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlags(); + (bool isActive, , , , bool isPaused, ) = reserveCache.reserveConfiguration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -406,7 +406,7 @@ library ValidationLogic { DataTypes.ReserveCache memory reserveCache, uint256 userBalance ) internal pure { - (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlags(); + (bool isActive, , , , bool isPaused, ) = reserveCache.reserveConfiguration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -458,11 +458,11 @@ library ValidationLogic { ) internal view { ValidateLiquidationCallLocalVars memory vars; - (vars.collateralReserveActive, , , , vars.collateralReservePaused) = collateralReserve + (vars.collateralReserveActive, , , , vars.collateralReservePaused, ) = collateralReserve .configuration .getFlags(); - (vars.principalReserveActive, , , , vars.principalReservePaused) = params + (vars.principalReserveActive, , , , vars.principalReservePaused, ) = params .debtReserveCache .reserveConfiguration .getFlags(); From 6fd1a5f300e376059a2f008f46b07794209d849b Mon Sep 17 00:00:00 2001 From: The3D Date: Tue, 28 Sep 2021 23:24:58 +0200 Subject: [PATCH 2/7] feat: changed reserve configuration --- .../configuration/ReserveConfiguration.sol | 56 +++++++++++-------- .../configuration/UserConfiguration.sol | 34 ++++++++++- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index 4cb78ff31..a859b8cc3 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -19,13 +19,13 @@ library ReserveConfiguration { uint256 constant BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore uint256 constant STABLE_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore uint256 constant PAUSED_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFF; // prettier-ignore - uint256 constant ISOLATION_MODE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant BORROW_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant SUPPLY_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant LIQUIDATION_PROTOCOL_FEE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant EMODE_CATEGORY_MASK = 0xFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant UNBACKED_MINT_CAP_MASK = 0xFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant ISOLATION_DEBT_CEILING_MASK = 0xFFF00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed uint256 constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16; @@ -36,14 +36,14 @@ library ReserveConfiguration { uint256 constant BORROWING_ENABLED_START_BIT_POSITION = 58; uint256 constant STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59; uint256 constant IS_PAUSED_START_BIT_POSITION = 60; - uint256 constant ISOLATION_MODE_START_BIT_POSITION = 61; - /// @dev bits 62 63 unused yet + /// @dev bits 61 62 63 unused yet uint256 constant RESERVE_FACTOR_START_BIT_POSITION = 64; uint256 constant BORROW_CAP_START_BIT_POSITION = 80; uint256 constant SUPPLY_CAP_START_BIT_POSITION = 116; uint256 constant LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION = 152; uint256 constant EMODE_CATEGORY_START_BIT_POSITION = 168; uint256 constant UNBACKED_MINT_CAP_START_BIT_POSITION = 176; + uint256 constant ISOLATION_DEBT_CEILING_START_BIT_POSITION = 212; uint256 constant MAX_VALID_LTV = 65535; uint256 constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535; @@ -55,6 +55,7 @@ library ReserveConfiguration { uint256 constant MAX_VALID_LIQUIDATION_PROTOCOL_FEE = 10000; uint256 constant MAX_VALID_EMODE_CATEGORY = 255; uint256 constant MAX_VALID_UNBACKED_MINT_CAP = 68719476735; + uint256 constant MAX_VALID_ISOLATION_DEBT_CEILING = 4294967296; /** * @notice Sets the Loan to Value of the reserve @@ -221,26 +222,6 @@ library ReserveConfiguration { return (self.data & ~PAUSED_MASK) != 0; } - /** - * @notice Sets the isolation mode for the reserve - * @param self The reserve configuration - * @param isolation True if the reserve should be in isolation mode, false otherwise - **/ - function setIsolationMode(DataTypes.ReserveConfigurationMap memory self, bool isolation) internal pure { - self.data = - (self.data & ISOLATION_MODE_MASK) | - (uint256(isolation ? 1 : 0) << ISOLATION_MODE_START_BIT_POSITION); - } - - /** - * @notice Gets the state of the isolation mode for the reserve - * @param self The reserve configuration - * @return True if the reserve is in isolation mode, false otherwise - **/ - function getIsolationMode(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) { - return (self.data & ~ISOLATION_MODE_MASK) != 0; - } - /** * @notice Enables or disables borrowing on the reserve * @param self The reserve configuration @@ -378,6 +359,35 @@ library ReserveConfiguration { return (self.data & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION; } + /** + * @notice Sets the debt ceiling in isolation mode for the asset + * @param self The reserve configuration + * @param ceiling The maximum debt ceiling for the asset + **/ + function setIsolationDebtCeiling(DataTypes.ReserveConfigurationMap memory self, uint256 ceiling) + internal + pure + { + require(ceiling <= MAX_VALID_ISOLATION_DEBT_CEILING, Errors.RC_INVALID_ISOLATION_DEBT_CEILING); + + self.data = + (self.data & ISOLATION_DEBT_CEILING_MASK) | + (ceiling << ISOLATION_DEBT_CEILING_START_BIT_POSITION); + } + + /** + * @notice Gets the debt ceiling for the asset if the asset is in isolation mode + * @param self The reserve configuration + * @return The debt ceiling (0 = isolation mode disabled) + **/ + function getIsolationDebtCeiling(DataTypes.ReserveConfigurationMap memory self) + internal + pure + returns (uint256) + { + return (self.data & ~ISOLATION_DEBT_CEILING_MASK) >> ISOLATION_DEBT_CEILING_START_BIT_POSITION; + } + /** * @notice Sets the liquidation protocol fee of the reserve * @param self The reserve configuration diff --git a/contracts/protocol/libraries/configuration/UserConfiguration.sol b/contracts/protocol/libraries/configuration/UserConfiguration.sol index ccc1c4ede..99ec68216 100644 --- a/contracts/protocol/libraries/configuration/UserConfiguration.sol +++ b/contracts/protocol/libraries/configuration/UserConfiguration.sol @@ -12,6 +12,8 @@ import {DataTypes} from '../types/DataTypes.sol'; library UserConfiguration { uint256 internal constant BORROWING_MASK = 0x5555555555555555555555555555555555555555555555555555555555555555; + uint256 internal constant COLLATERAL_MASK = + 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA; /** * @notice Sets if the user is borrowing the reserve identified by reserveIndex @@ -102,7 +104,35 @@ library UserConfiguration { } /** - * @notice Validate a user has been borrowing from any reserve + * @notice Checks if a user has been supplying only one reserve as collateral + * @dev this uses a simple trick - if a number is a power of two (only one bit set) then n & (n - 1) == 0 + * @param self The configuration object + * @return True if the user has been supplying as collateral one reserve, false otherwise + **/ + function isUsingAsCollateralOne(DataTypes.UserConfigurationMap memory self) + internal + pure + returns (bool) + { + uint256 collateralData = self.data & COLLATERAL_MASK; + return collateralData & (collateralData - 1) == 0; + } + + /** + * @notice Checks if a user has been supplying any reserve as collateral + * @param self The configuration object + * @return True if the user has been supplying as collateral any reserve, false otherwise + **/ + function isUsingAsCollateralAny(DataTypes.UserConfigurationMap memory self) + internal + pure + returns (bool) + { + return self.data & COLLATERAL_MASK != 0; + } + + /** + * @notice Checks if a user has been borrowing from any reserve * @param self The configuration object * @return True if the user has been borrowing any reserve, false otherwise **/ @@ -111,7 +141,7 @@ library UserConfiguration { } /** - * @notice Validate a user has not been using any reserve + * @notice Checks if a user has not been using any reserve for borrowing or supply * @param self The configuration object * @return True if the user has been borrowing any reserve, false otherwise **/ From d4ddeb078e0c5bb0fc6dcf42251382834f638e8b Mon Sep 17 00:00:00 2001 From: The3D Date: Tue, 28 Sep 2021 23:34:48 +0200 Subject: [PATCH 3/7] fix: removed redundant flag in the getFlags function --- contracts/misc/AaveProtocolDataProvider.sol | 4 ++-- .../mocks/helpers/MockReserveConfiguration.sol | 1 - .../configuration/ReserveConfiguration.sol | 3 --- .../protocol/libraries/helpers/Errors.sol | 1 + .../libraries/logic/ValidationLogic.sol | 18 +++++++++--------- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/contracts/misc/AaveProtocolDataProvider.sol b/contracts/misc/AaveProtocolDataProvider.sol index 3175c8857..a859270a8 100644 --- a/contracts/misc/AaveProtocolDataProvider.sol +++ b/contracts/misc/AaveProtocolDataProvider.sol @@ -116,7 +116,7 @@ contract AaveProtocolDataProvider { (ltv, liquidationThreshold, liquidationBonus, decimals, reserveFactor, ) = configuration .getParams(); - (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled, , ) = configuration.getFlags(); + (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled, ) = configuration.getFlags(); usageAsCollateralEnabled = liquidationThreshold > 0; } @@ -152,7 +152,7 @@ contract AaveProtocolDataProvider { * @return isPaused True if the pool is paused, false otherwise **/ function getPaused(address asset) external view returns (bool isPaused) { - (, , , , isPaused, ) = IPool(ADDRESSES_PROVIDER.getPool()).getConfiguration(asset).getFlags(); + (, , , , isPaused) = IPool(ADDRESSES_PROVIDER.getPool()).getConfiguration(asset).getFlags(); } /** diff --git a/contracts/mocks/helpers/MockReserveConfiguration.sol b/contracts/mocks/helpers/MockReserveConfiguration.sol index c34619b49..c7577f3e4 100644 --- a/contracts/mocks/helpers/MockReserveConfiguration.sol +++ b/contracts/mocks/helpers/MockReserveConfiguration.sol @@ -137,7 +137,6 @@ contract MockReserveConfiguration { bool, bool, bool, - bool, bool ) { diff --git a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index a859b8cc3..564b0da97 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -485,7 +485,6 @@ library ReserveConfiguration { * @return The state flag representing borrowing enabled * @return The state flag representing stabelRateBorrowing enabled * @return The state flag representing paused - * @return The state flag representing isolation mode **/ function getFlags(DataTypes.ReserveConfigurationMap memory self) internal @@ -495,7 +494,6 @@ library ReserveConfiguration { bool, bool, bool, - bool, bool ) { @@ -506,7 +504,6 @@ library ReserveConfiguration { (dataLocal & ~FROZEN_MASK) != 0, (dataLocal & ~BORROWING_MASK) != 0, (dataLocal & ~STABLE_BORROWING_MASK) != 0, - (dataLocal & ~PAUSED_MASK) != 0, (dataLocal & ~PAUSED_MASK) != 0 ); } diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 6e2cdcc42..d338b67fa 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -114,4 +114,5 @@ library Errors { string public constant RC_INVALID_UNBACKED_MINT_CAP = '103'; string public constant VL_UNBACKED_MINT_CAP_EXCEEDED = '104'; string public constant VL_PRICE_ORACLE_SENTINEL_CHECK_FAILED = '105'; + string public constant RC_INVALID_ISOLATION_DEBT_CEILING = '106'; } diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 4215faf44..82de6d1ce 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -48,7 +48,7 @@ library ValidationLogic { internal view { - (bool isActive, bool isFrozen, , , bool isPaused, ) = reserveCache + (bool isActive, bool isFrozen, , , bool isPaused) = reserveCache .reserveConfiguration .getFlags(); @@ -84,7 +84,7 @@ library ValidationLogic { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); - (bool isActive, , , , bool isPaused,) = reserveCache.reserveConfiguration.getFlags(); + (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); } @@ -132,7 +132,7 @@ library ValidationLogic { vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled, - vars.isPaused, + vars.isPaused ) = params.reserveCache.reserveConfiguration.getFlags(); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); @@ -279,7 +279,7 @@ library ValidationLogic { uint256 stableDebt, uint256 variableDebt ) internal view { - (bool isActive, , , , bool isPaused, ) = reserveCache.reserveConfiguration.getFlags(); + (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -330,7 +330,7 @@ library ValidationLogic { uint256 variableDebt, DataTypes.InterestRateMode currentRateMode ) internal view { - (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused, ) = reserveCache + (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = reserveCache .reserveConfiguration .getFlags(); @@ -379,7 +379,7 @@ library ValidationLogic { IERC20 variableDebtToken, address aTokenAddress ) internal view { - (bool isActive, , , , bool isPaused, ) = reserveCache.reserveConfiguration.getFlags(); + (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -415,7 +415,7 @@ library ValidationLogic { DataTypes.ReserveCache memory reserveCache, uint256 userBalance ) internal pure { - (bool isActive, , , , bool isPaused, ) = reserveCache.reserveConfiguration.getFlags(); + (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -467,11 +467,11 @@ library ValidationLogic { ) internal view { ValidateLiquidationCallLocalVars memory vars; - (vars.collateralReserveActive, , , , vars.collateralReservePaused, ) = collateralReserve + (vars.collateralReserveActive, , , , vars.collateralReservePaused) = collateralReserve .configuration .getFlags(); - (vars.principalReserveActive, , , , vars.principalReservePaused, ) = params + (vars.principalReserveActive, , , , vars.principalReservePaused) = params .debtReserveCache .reserveConfiguration .getFlags(); From 14358a7941c5f30d9ccd76b22cfd04da2fa778aa Mon Sep 17 00:00:00 2001 From: The3D Date: Wed, 29 Sep 2021 12:07:41 +0200 Subject: [PATCH 4/7] refactor: changed debt ceiling configuration name --- .../configuration/ReserveConfiguration.sol | 18 ++++++++---------- .../protocol/libraries/helpers/Errors.sol | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index 564b0da97..e48fc9309 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -25,7 +25,7 @@ library ReserveConfiguration { uint256 constant LIQUIDATION_PROTOCOL_FEE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant EMODE_CATEGORY_MASK = 0xFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant UNBACKED_MINT_CAP_MASK = 0xFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore - uint256 constant ISOLATION_DEBT_CEILING_MASK = 0xFFF00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant DEBT_CEILING_MASK = 0xFFF00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed uint256 constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16; @@ -43,7 +43,7 @@ library ReserveConfiguration { uint256 constant LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION = 152; uint256 constant EMODE_CATEGORY_START_BIT_POSITION = 168; uint256 constant UNBACKED_MINT_CAP_START_BIT_POSITION = 176; - uint256 constant ISOLATION_DEBT_CEILING_START_BIT_POSITION = 212; + uint256 constant DEBT_CEILING_START_BIT_POSITION = 212; uint256 constant MAX_VALID_LTV = 65535; uint256 constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535; @@ -55,7 +55,7 @@ library ReserveConfiguration { uint256 constant MAX_VALID_LIQUIDATION_PROTOCOL_FEE = 10000; uint256 constant MAX_VALID_EMODE_CATEGORY = 255; uint256 constant MAX_VALID_UNBACKED_MINT_CAP = 68719476735; - uint256 constant MAX_VALID_ISOLATION_DEBT_CEILING = 4294967296; + uint256 constant MAX_VALID_DEBT_CEILING = 4294967296; /** * @notice Sets the Loan to Value of the reserve @@ -364,15 +364,13 @@ library ReserveConfiguration { * @param self The reserve configuration * @param ceiling The maximum debt ceiling for the asset **/ - function setIsolationDebtCeiling(DataTypes.ReserveConfigurationMap memory self, uint256 ceiling) + function setDebtCeiling(DataTypes.ReserveConfigurationMap memory self, uint256 ceiling) internal pure { - require(ceiling <= MAX_VALID_ISOLATION_DEBT_CEILING, Errors.RC_INVALID_ISOLATION_DEBT_CEILING); + require(ceiling <= MAX_VALID_DEBT_CEILING, Errors.RC_INVALID_DEBT_CEILING); - self.data = - (self.data & ISOLATION_DEBT_CEILING_MASK) | - (ceiling << ISOLATION_DEBT_CEILING_START_BIT_POSITION); + self.data = (self.data & DEBT_CEILING_MASK) | (ceiling << DEBT_CEILING_START_BIT_POSITION); } /** @@ -380,12 +378,12 @@ library ReserveConfiguration { * @param self The reserve configuration * @return The debt ceiling (0 = isolation mode disabled) **/ - function getIsolationDebtCeiling(DataTypes.ReserveConfigurationMap memory self) + function getDebtCeiling(DataTypes.ReserveConfigurationMap memory self) internal pure returns (uint256) { - return (self.data & ~ISOLATION_DEBT_CEILING_MASK) >> ISOLATION_DEBT_CEILING_START_BIT_POSITION; + return (self.data & ~DEBT_CEILING_MASK) >> DEBT_CEILING_START_BIT_POSITION; } /** diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index d338b67fa..cf0878bfc 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -114,5 +114,5 @@ library Errors { string public constant RC_INVALID_UNBACKED_MINT_CAP = '103'; string public constant VL_UNBACKED_MINT_CAP_EXCEEDED = '104'; string public constant VL_PRICE_ORACLE_SENTINEL_CHECK_FAILED = '105'; - string public constant RC_INVALID_ISOLATION_DEBT_CEILING = '106'; + string public constant RC_INVALID_DEBT_CEILING = '106'; } From 20853a3c381f03e4f7c34d9a32e30fd20140793f Mon Sep 17 00:00:00 2001 From: The3D Date: Wed, 29 Sep 2021 17:08:00 +0200 Subject: [PATCH 5/7] feat: implemented isInIsolationMode --- .../configuration/UserConfiguration.sol | 38 +++++++++++++++++++ .../protocol/libraries/logic/SupplyLogic.sol | 18 ++++++--- contracts/protocol/pool/Pool.sol | 3 ++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/contracts/protocol/libraries/configuration/UserConfiguration.sol b/contracts/protocol/libraries/configuration/UserConfiguration.sol index 99ec68216..91e81aee1 100644 --- a/contracts/protocol/libraries/configuration/UserConfiguration.sol +++ b/contracts/protocol/libraries/configuration/UserConfiguration.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.7; import {Errors} from '../helpers/Errors.sol'; import {DataTypes} from '../types/DataTypes.sol'; +import {ReserveConfiguration} from './ReserveConfiguration.sol'; /** * @title UserConfiguration library @@ -10,6 +11,8 @@ import {DataTypes} from '../types/DataTypes.sol'; * @notice Implements the bitmap logic to handle the user configuration */ library UserConfiguration { + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + uint256 internal constant BORROWING_MASK = 0x5555555555555555555555555555555555555555555555555555555555555555; uint256 internal constant COLLATERAL_MASK = @@ -148,4 +151,39 @@ library UserConfiguration { function isEmpty(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) { return self.data == 0; } + + function isInIsolationMode( + DataTypes.UserConfigurationMap memory self, + mapping(address => DataTypes.ReserveData) storage reservesData, + mapping(uint256 => address) storage reservesList + ) internal view returns (bool) { + if (!isUsingAsCollateralAny(self)) { + return false; + } + if (isUsingAsCollateralOne(self)) { + uint256 assetId = _getFirstAssetAsCollateralId(self); + + address assetAddress = reservesList[assetId]; + uint256 ceiling = reservesData[assetAddress].configuration.getDebtCeiling(); + return ceiling > 0; + } + return false; + } + + function _getFirstAssetAsCollateralId(DataTypes.UserConfigurationMap memory self) + internal + view + returns (uint256) + { + unchecked { + uint256 collateralData = self.data & COLLATERAL_MASK; + uint256 firstCollateralPosition = collateralData & ~(collateralData - 1); + uint256 id; + + while ((firstCollateralPosition >>= 2) > 0) { + id += 2; + } + return id / 2; + } + } } diff --git a/contracts/protocol/libraries/logic/SupplyLogic.sol b/contracts/protocol/libraries/logic/SupplyLogic.sol index d2a68cf77..f07a9d49d 100644 --- a/contracts/protocol/libraries/logic/SupplyLogic.sol +++ b/contracts/protocol/libraries/logic/SupplyLogic.sol @@ -45,6 +45,7 @@ library SupplyLogic { function executeSupply( mapping(address => DataTypes.ReserveData) storage reserves, + mapping(uint256 => address) storage reservesList, DataTypes.UserConfigurationMap storage userConfig, DataTypes.ExecuteSupplyParams memory params ) external { @@ -66,8 +67,10 @@ library SupplyLogic { ); if (isFirstSupply) { - userConfig.setUsingAsCollateral(reserve.id, true); - emit ReserveUsedAsCollateralEnabled(params.asset, params.onBehalfOf); + if (!userConfig.isInIsolationMode(reserves, reservesList)) { + userConfig.setUsingAsCollateral(reserve.id, true); + emit ReserveUsedAsCollateralEnabled(params.asset, params.onBehalfOf); + } } emit Supply(params.asset, msg.sender, params.onBehalfOf, params.amount, params.referralCode); @@ -168,8 +171,10 @@ library SupplyLogic { if (params.balanceToBefore == 0 && params.amount != 0) { DataTypes.UserConfigurationMap storage toConfig = usersConfig[params.to]; - toConfig.setUsingAsCollateral(reserveId, true); - emit ReserveUsedAsCollateralEnabled(params.asset, params.to); + if (!toConfig.isInIsolationMode(reserves, reservesList)) { + toConfig.setUsingAsCollateral(reserveId, true); + emit ReserveUsedAsCollateralEnabled(params.asset, params.to); + } } } } @@ -192,11 +197,12 @@ library SupplyLogic { ValidationLogic.validateSetUseReserveAsCollateral(reserveCache, userBalance); - userConfig.setUsingAsCollateral(reserve.id, useAsCollateral); - if (useAsCollateral) { + require(!userConfig.isInIsolationMode(reserves, reservesList)); + userConfig.setUsingAsCollateral(reserve.id, true); emit ReserveUsedAsCollateralEnabled(asset, msg.sender); } else { + userConfig.setUsingAsCollateral(reserve.id, false); ValidationLogic.validateHFAndLtv( reserves, reservesList, diff --git a/contracts/protocol/pool/Pool.sol b/contracts/protocol/pool/Pool.sol index d172508ad..8ba85f0e6 100644 --- a/contracts/protocol/pool/Pool.sol +++ b/contracts/protocol/pool/Pool.sol @@ -128,6 +128,7 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { ) external override { SupplyLogic.executeSupply( _reserves, + _reservesList, _usersConfig[onBehalfOf], DataTypes.ExecuteSupplyParams(asset, amount, onBehalfOf, referralCode) ); @@ -155,6 +156,7 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { ); SupplyLogic.executeSupply( _reserves, + _reservesList, _usersConfig[onBehalfOf], DataTypes.ExecuteSupplyParams(asset, amount, onBehalfOf, referralCode) ); @@ -679,6 +681,7 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { ) external override { SupplyLogic.executeSupply( _reserves, + _reservesList, _usersConfig[onBehalfOf], DataTypes.ExecuteSupplyParams(asset, amount, onBehalfOf, referralCode) ); From 1e72d0bcf5dd4a4facefa716f095fc3d97c02f22 Mon Sep 17 00:00:00 2001 From: The3D Date: Wed, 29 Sep 2021 18:59:03 +0200 Subject: [PATCH 6/7] feat: finalize implementation --- contracts/interfaces/IPoolConfigurator.sol | 13 ++++++ .../configuration/UserConfiguration.sol | 22 ++++++--- .../protocol/libraries/helpers/Errors.sol | 4 ++ .../protocol/libraries/logic/BorrowLogic.sol | 35 ++++++++++++++- .../protocol/libraries/logic/SupplyLogic.sol | 10 +++-- .../libraries/logic/ValidationLogic.sol | 22 +++++++++ .../protocol/libraries/types/DataTypes.sol | 4 ++ contracts/protocol/pool/Pool.sol | 45 +++++++++++++------ contracts/protocol/pool/PoolConfigurator.sol | 15 +++++++ hardhat.config.ts | 2 +- 10 files changed, 147 insertions(+), 25 deletions(-) diff --git a/contracts/interfaces/IPoolConfigurator.sol b/contracts/interfaces/IPoolConfigurator.sol index 9d1bf6ca3..8c4f137b1 100644 --- a/contracts/interfaces/IPoolConfigurator.sol +++ b/contracts/interfaces/IPoolConfigurator.sol @@ -215,6 +215,13 @@ interface IPoolConfigurator { address indexed implementation ); + /** + * @notice Emitted when the debt ceiling of an asset is set + * @param asset The address of the underlying asset of the reserve + * @param ceiling The new debt ceiling + **/ + event DebtCeilingChanged(address indexed asset, uint256 ceiling); + /** * @notice Emitted when a new risk admin is registered * @param admin The newly registered admin @@ -439,4 +446,10 @@ interface IPoolConfigurator { * @param flashloanPremiumToProtocol The part of the premium sent to protocol */ function updateFlashloanPremiumToProtocol(uint256 flashloanPremiumToProtocol) external; + + /** + * @notice Sets the debt ceiling for an asset + * @param ceiling The new debt ceiling + */ + function setDebtCeiling(address asset, uint256 ceiling) external; } diff --git a/contracts/protocol/libraries/configuration/UserConfiguration.sol b/contracts/protocol/libraries/configuration/UserConfiguration.sol index 91e81aee1..f1932dfb6 100644 --- a/contracts/protocol/libraries/configuration/UserConfiguration.sol +++ b/contracts/protocol/libraries/configuration/UserConfiguration.sol @@ -152,27 +152,37 @@ library UserConfiguration { return self.data == 0; } - function isInIsolationMode( + function getIsolationModeState( DataTypes.UserConfigurationMap memory self, mapping(address => DataTypes.ReserveData) storage reservesData, mapping(uint256 => address) storage reservesList - ) internal view returns (bool) { + ) + internal + view + returns ( + bool, + address, + uint256 + ) + { if (!isUsingAsCollateralAny(self)) { - return false; + return (false, address(0), 0); } if (isUsingAsCollateralOne(self)) { uint256 assetId = _getFirstAssetAsCollateralId(self); address assetAddress = reservesList[assetId]; uint256 ceiling = reservesData[assetAddress].configuration.getDebtCeiling(); - return ceiling > 0; + if (ceiling > 0) { + return (true, assetAddress, ceiling); + } } - return false; + return (false, address(0), 0); } function _getFirstAssetAsCollateralId(DataTypes.UserConfigurationMap memory self) internal - view + pure returns (uint256) { unchecked { diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index cf0878bfc..320db5a5c 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -115,4 +115,8 @@ library Errors { string public constant VL_UNBACKED_MINT_CAP_EXCEEDED = '104'; string public constant VL_PRICE_ORACLE_SENTINEL_CHECK_FAILED = '105'; string public constant RC_INVALID_DEBT_CEILING = '106'; + string public constant PC_INVALID_DEBT_CEILING_ASSET_ALREADY_SUPPLIED = '107'; + string public constant VL_INVALID_ISOLATION_MODE_BORROW_CATEGORY = '108'; + string public constant VL_DEBT_CEILING_CROSSED = '109'; + string public constant SL_USER_IN_ISOLATION_MODE = '110'; } diff --git a/contracts/protocol/libraries/logic/BorrowLogic.sol b/contracts/protocol/libraries/logic/BorrowLogic.sol index a3f8d8be1..8e5cdfb21 100644 --- a/contracts/protocol/libraries/logic/BorrowLogic.sol +++ b/contracts/protocol/libraries/logic/BorrowLogic.sol @@ -69,6 +69,12 @@ library BorrowLogic { reserve.updateState(reserveCache); + ( + bool isolationModeActive, + address isolationModeCollateralAddress, + uint256 isolationModeDebtCeiling + ) = userConfig.getIsolationModeState(reserves, reservesList); + ValidationLogic.validateBorrow( reserves, reservesList, @@ -84,7 +90,10 @@ library BorrowLogic { params.reservesCount, params.oracle, params.userEModeCategory, - params.priceOracleSentinel + params.priceOracleSentinel, + isolationModeActive, + isolationModeCollateralAddress, + isolationModeDebtCeiling ) ); @@ -114,6 +123,12 @@ library BorrowLogic { userConfig.setBorrowing(reserve.id, true); } + if (isolationModeActive) { + reserves[isolationModeCollateralAddress].isolationModeTotalDebt += Helpers.castUint128( + params.amount + ); + } + reserve.updateInterestRates( reserveCache, params.asset, @@ -139,6 +154,8 @@ library BorrowLogic { } function executeRepay( + mapping(address => DataTypes.ReserveData) storage reserves, + mapping(uint256 => address) storage reservesList, DataTypes.ReserveData storage reserve, DataTypes.UserConfigurationMap storage userConfig, DataTypes.ExecuteRepayParams memory params @@ -185,6 +202,22 @@ library BorrowLogic { userConfig.setBorrowing(reserve.id, false); } + (bool isolationModeActive, address isolationModeCollateralAddress, ) = userConfig + .getIsolationModeState(reserves, reservesList); + + if (isolationModeActive) { + uint128 isolationModeTotalDebt = reserves[isolationModeCollateralAddress] + .isolationModeTotalDebt; + // since the debt ceiling does not take into account the interest accrued, it might happen that amount repaid > debt in isolation mode + if (isolationModeTotalDebt < paybackAmount) { + reserves[isolationModeCollateralAddress].isolationModeTotalDebt = 0; + } else { + reserves[isolationModeCollateralAddress].isolationModeTotalDebt = + isolationModeTotalDebt - + Helpers.castUint128(paybackAmount); + } + } + if (params.useATokens) { IAToken(reserveCache.aTokenAddress).burn( msg.sender, diff --git a/contracts/protocol/libraries/logic/SupplyLogic.sol b/contracts/protocol/libraries/logic/SupplyLogic.sol index f07a9d49d..267c985ac 100644 --- a/contracts/protocol/libraries/logic/SupplyLogic.sol +++ b/contracts/protocol/libraries/logic/SupplyLogic.sol @@ -67,7 +67,8 @@ library SupplyLogic { ); if (isFirstSupply) { - if (!userConfig.isInIsolationMode(reserves, reservesList)) { + (bool isolationModeActive, , ) = userConfig.getIsolationModeState(reserves, reservesList); + if (!isolationModeActive) { userConfig.setUsingAsCollateral(reserve.id, true); emit ReserveUsedAsCollateralEnabled(params.asset, params.onBehalfOf); } @@ -171,7 +172,8 @@ library SupplyLogic { if (params.balanceToBefore == 0 && params.amount != 0) { DataTypes.UserConfigurationMap storage toConfig = usersConfig[params.to]; - if (!toConfig.isInIsolationMode(reserves, reservesList)) { + (bool isolationModeActive, , ) = toConfig.getIsolationModeState(reserves, reservesList); + if (!isolationModeActive) { toConfig.setUsingAsCollateral(reserveId, true); emit ReserveUsedAsCollateralEnabled(params.asset, params.to); } @@ -198,7 +200,9 @@ library SupplyLogic { ValidationLogic.validateSetUseReserveAsCollateral(reserveCache, userBalance); if (useAsCollateral) { - require(!userConfig.isInIsolationMode(reserves, reservesList)); + (bool isolationModeActive, , ) = userConfig.getIsolationModeState(reserves, reservesList); + require(!isolationModeActive, Errors.SL_USER_IN_ISOLATION_MODE); + userConfig.setUsingAsCollateral(reserve.id, true); emit ReserveUsedAsCollateralEnabled(asset, msg.sender); } else { diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 82de6d1ce..2b2399f49 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -39,6 +39,11 @@ library ValidationLogic { uint256 public constant REBALANCE_UP_USAGE_RATIO_THRESHOLD = 0.95 * 1e27; //usage ratio of 95% uint256 public constant MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 0.95 * 1e18; + // for borrowings in isolation mode, we give for granted that the eMode category for stablecoins is the category with id 1. + // this MUST be kept into account when configuring the stablecoins eMode category, otherwise users depositing asset in isolation + // mode will NOT be able to borrow. + uint256 public constant DEFAULT_ISOLATION_MODE_BORROW_CATEGORY = 1; + /** * @notice Validates a supply action * @param reserveCache The cached data of the reserve @@ -176,6 +181,23 @@ library ValidationLogic { } } + if (params.isolationModeActive) { + // check that the asset being borrowed belongs to the stablecoin category AND + // the total exposure is no bigger than the collateral debt ceiling + require( + params.reserveCache.reserveConfiguration.getEModeCategory() == + DEFAULT_ISOLATION_MODE_BORROW_CATEGORY, + Errors.VL_INVALID_ISOLATION_MODE_BORROW_CATEGORY + ); + + require( + reservesData[params.isolationModeCollateralAddress].isolationModeTotalDebt + + params.amount <= + params.isolationModeDebtCeiling, + Errors.VL_DEBT_CEILING_CROSSED + ); + } + if (params.userEModeCategory != 0) { require( params.reserveCache.reserveConfiguration.getEModeCategory() == params.userEModeCategory, diff --git a/contracts/protocol/libraries/types/DataTypes.sol b/contracts/protocol/libraries/types/DataTypes.sol index 2a428ff9f..18fb5c600 100644 --- a/contracts/protocol/libraries/types/DataTypes.sol +++ b/contracts/protocol/libraries/types/DataTypes.sol @@ -29,6 +29,7 @@ library DataTypes { uint128 accruedToTreasury; //the quickwithdraw balance waiting for underlying to be backed uint128 unbacked; + uint128 isolationModeTotalDebt; } struct ReserveConfigurationMap { @@ -204,6 +205,9 @@ library DataTypes { address oracle; uint8 userEModeCategory; address priceOracleSentinel; + bool isolationModeActive; + address isolationModeCollateralAddress; + uint256 isolationModeDebtCeiling; } struct ValidateLiquidationCallParams { diff --git a/contracts/protocol/pool/Pool.sol b/contracts/protocol/pool/Pool.sol index 8ba85f0e6..d14fdbf36 100644 --- a/contracts/protocol/pool/Pool.sol +++ b/contracts/protocol/pool/Pool.sol @@ -224,6 +224,8 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { ) external override returns (uint256) { return BorrowLogic.executeRepay( + _reserves, + _reservesList, _reserves[asset], _usersConfig[onBehalfOf], DataTypes.ExecuteRepayParams(asset, amount, rateMode, onBehalfOf, false) @@ -241,21 +243,34 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { bytes32 permitR, bytes32 permitS ) external override returns (uint256) { - IERC20WithPermit(asset).permit( - msg.sender, - address(this), - amount, - deadline, - permitV, - permitR, - permitS - ); - return - BorrowLogic.executeRepay( - _reserves[asset], - _usersConfig[onBehalfOf], - DataTypes.ExecuteRepayParams(asset, amount, rateMode, onBehalfOf, false) + { + IERC20WithPermit(asset).permit( + msg.sender, + address(this), + amount, + deadline, + permitV, + permitR, + permitS ); + } + { + DataTypes.ExecuteRepayParams memory params = DataTypes.ExecuteRepayParams( + asset, + amount, + rateMode, + onBehalfOf, + false + ); + return + BorrowLogic.executeRepay( + _reserves, + _reservesList, + _reserves[asset], + _usersConfig[onBehalfOf], + params + ); + } } /// @inheritdoc IPool @@ -267,6 +282,8 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { ) external override returns (uint256) { return BorrowLogic.executeRepay( + _reserves, + _reservesList, _reserves[asset], _usersConfig[onBehalfOf], DataTypes.ExecuteRepayParams(asset, amount, rateMode, onBehalfOf, true) diff --git a/contracts/protocol/pool/PoolConfigurator.sol b/contracts/protocol/pool/PoolConfigurator.sol index e21b3616f..af5b860ff 100644 --- a/contracts/protocol/pool/PoolConfigurator.sol +++ b/contracts/protocol/pool/PoolConfigurator.sol @@ -256,6 +256,21 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { emit ReserveFactorChanged(asset, reserveFactor); } + /// @inheritdoc IPoolConfigurator + function setDebtCeiling(address asset, uint256 ceiling) external override onlyRiskOrPoolAdmins { + DataTypes.ReserveData memory reserveData = _pool.getReserveData(asset); + uint256 aTokenSupply = IERC20Detailed(reserveData.aTokenAddress).totalSupply(); + + if (ceiling > 0) { + require(aTokenSupply == 0, Errors.PC_INVALID_DEBT_CEILING_ASSET_ALREADY_SUPPLIED); + } + + DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); + currentConfig.setDebtCeiling(ceiling); + _pool.setConfiguration(asset, currentConfig.data); + emit DebtCeilingChanged(asset, ceiling); + } + /// @inheritdoc IPoolConfigurator function setBorrowCap(address asset, uint256 borrowCap) external override onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); diff --git a/hardhat.config.ts b/hardhat.config.ts index 1fa2e8e62..af3084953 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -36,7 +36,7 @@ const hardhatConfig: HardhatUserConfig = { settings: { optimizer: { enabled: true, - runs: 20000, + runs: 5000, }, evmVersion: 'london', }, From 68bf644803a1d89ac5f650b2031a1896bf7fd5e0 Mon Sep 17 00:00:00 2001 From: The3D Date: Wed, 29 Sep 2021 19:20:53 +0200 Subject: [PATCH 7/7] fix: fixed the calculation for isolationModeTotalDebt --- contracts/protocol/libraries/logic/BorrowLogic.sol | 6 ++++-- hardhat.config.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/contracts/protocol/libraries/logic/BorrowLogic.sol b/contracts/protocol/libraries/logic/BorrowLogic.sol index 8e5cdfb21..8aeb148b4 100644 --- a/contracts/protocol/libraries/logic/BorrowLogic.sol +++ b/contracts/protocol/libraries/logic/BorrowLogic.sol @@ -8,6 +8,7 @@ import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; import {IAToken} from '../../../interfaces/IAToken.sol'; import {IFlashLoanReceiver} from '../../../flashloan/interfaces/IFlashLoanReceiver.sol'; import {UserConfiguration} from '../configuration/UserConfiguration.sol'; +import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; import {Helpers} from '../helpers/Helpers.sol'; import {Errors} from '../helpers/Errors.sol'; import {WadRayMath} from '../math/WadRayMath.sol'; @@ -26,6 +27,7 @@ library BorrowLogic { using ReserveLogic for DataTypes.ReserveData; using SafeERC20 for IERC20; using UserConfiguration for DataTypes.UserConfigurationMap; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using WadRayMath for uint256; using PercentageMath for uint256; @@ -125,7 +127,7 @@ library BorrowLogic { if (isolationModeActive) { reserves[isolationModeCollateralAddress].isolationModeTotalDebt += Helpers.castUint128( - params.amount + params.amount / 10**reserveCache.reserveConfiguration.getDecimals() ); } @@ -214,7 +216,7 @@ library BorrowLogic { } else { reserves[isolationModeCollateralAddress].isolationModeTotalDebt = isolationModeTotalDebt - - Helpers.castUint128(paybackAmount); + Helpers.castUint128(paybackAmount / 10**reserveCache.reserveConfiguration.getDecimals()); } } diff --git a/hardhat.config.ts b/hardhat.config.ts index af3084953..af3afd2be 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -36,7 +36,7 @@ const hardhatConfig: HardhatUserConfig = { settings: { optimizer: { enabled: true, - runs: 5000, + runs: 4000, }, evmVersion: 'london', },