diff --git a/src/core/contracts/interfaces/IDefaultInterestRateStrategy.sol b/src/core/contracts/interfaces/IDefaultInterestRateStrategy.sol deleted file mode 100644 index a9ae554b..00000000 --- a/src/core/contracts/interfaces/IDefaultInterestRateStrategy.sol +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import {IReserveInterestRateStrategy} from './IReserveInterestRateStrategy.sol'; -import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol'; - -/** - * @title IDefaultInterestRateStrategy - * @author Aave - * @notice Defines the basic interface of the DefaultReserveInterestRateStrategy - */ -interface IDefaultInterestRateStrategy is IReserveInterestRateStrategy { - /** - * @notice Returns the usage ratio at which the pool aims to obtain most competitive borrow rates. - * @return The optimal usage ratio, expressed in ray. - */ - function OPTIMAL_USAGE_RATIO() external view returns (uint256); - - /** - * @notice Returns the optimal stable to total debt ratio of the reserve. - * @return The optimal stable to total debt ratio, expressed in ray. - */ - function OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO() external view returns (uint256); - - /** - * @notice Returns the excess usage ratio above the optimal. - * @dev It's always equal to 1-optimal usage ratio (added as constant for gas optimizations) - * @return The max excess usage ratio, expressed in ray. - */ - function MAX_EXCESS_USAGE_RATIO() external view returns (uint256); - - /** - * @notice Returns the excess stable debt ratio above the optimal. - * @dev It's always equal to 1-optimal stable to total debt ratio (added as constant for gas optimizations) - * @return The max excess stable to total debt ratio, expressed in ray. - */ - function MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO() external view returns (uint256); - - /** - * @notice Returns the address of the PoolAddressesProvider - * @return The address of the PoolAddressesProvider contract - */ - function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider); - - /** - * @notice Returns the variable rate slope below optimal usage ratio - * @dev It's the variable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO - * @return The variable rate slope, expressed in ray - */ - function getVariableRateSlope1() external view returns (uint256); - - /** - * @notice Returns the variable rate slope above optimal usage ratio - * @dev It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO - * @return The variable rate slope, expressed in ray - */ - function getVariableRateSlope2() external view returns (uint256); - - /** - * @notice Returns the stable rate slope below optimal usage ratio - * @dev It's the stable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO - * @return The stable rate slope, expressed in ray - */ - function getStableRateSlope1() external view returns (uint256); - - /** - * @notice Returns the stable rate slope above optimal usage ratio - * @dev It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO - * @return The stable rate slope, expressed in ray - */ - function getStableRateSlope2() external view returns (uint256); - - /** - * @notice Returns the stable rate excess offset - * @dev It's an additional premium applied to the stable when stable debt > OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO - * @return The stable rate excess offset, expressed in ray - */ - function getStableRateExcessOffset() external view returns (uint256); - - /** - * @notice Returns the base stable borrow rate - * @return The base stable borrow rate, expressed in ray - */ - function getBaseStableBorrowRate() external view returns (uint256); - - /** - * @notice Returns the base variable borrow rate - * @return The base variable borrow rate, expressed in ray - */ - function getBaseVariableBorrowRate() external view returns (uint256); - - /** - * @notice Returns the maximum variable borrow rate - * @return The maximum variable borrow rate, expressed in ray - */ - function getMaxVariableBorrowRate() external view returns (uint256); -} diff --git a/src/core/contracts/interfaces/IDefaultInterestRateStrategyV2.sol b/src/core/contracts/interfaces/IDefaultInterestRateStrategyV2.sol new file mode 100644 index 00000000..c0e1afae --- /dev/null +++ b/src/core/contracts/interfaces/IDefaultInterestRateStrategyV2.sol @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import './IReserveInterestRateStrategy.sol'; +import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol'; + +/** + * @title IDefaultInterestRateStrategyV2 + * @author BGD Labs + * @notice Interface of the default interest rate strategy used by the Aave protocol + */ +interface IDefaultInterestRateStrategyV2 is IReserveInterestRateStrategy { + struct CalcInterestRatesLocalVars { + uint256 availableLiquidity; + uint256 totalDebt; + uint256 currentVariableBorrowRate; + uint256 currentLiquidityRate; + uint256 borrowUsageRatio; + uint256 supplyUsageRatio; + uint256 availableLiquidityPlusDebt; + } + + /** + * @notice Holds the interest rate data for a given reserve + * + * @dev Since values are in bps, they are multiplied by 1e23 in order to become rays with 27 decimals. This + * in turn means that the maximum supported interest rate is 4294967295 (2**32-1) bps or 42949672.95%. + * + * @param optimalUsageRatio The optimal usage ratio, in bps + * @param baseVariableBorrowRate The base variable borrow rate, in bps + * @param variableRateSlope1 The slope of the variable interest curve, before hitting the optimal ratio, in bps + * @param variableRateSlope2 The slope of the variable interest curve, after hitting the optimal ratio, in bps + */ + struct InterestRateData { + uint16 optimalUsageRatio; + uint32 baseVariableBorrowRate; + uint32 variableRateSlope1; + uint32 variableRateSlope2; + } + + /** + * @notice The interest rate data, where all values are in ray (fixed-point 27 decimal numbers) for a given reserve, + * used in in-memory calculations. + * + * @param optimalUsageRatio The optimal usage ratio + * @param baseVariableBorrowRate The base variable borrow rate + * @param variableRateSlope1 The slope of the variable interest curve, before hitting the optimal ratio + * @param variableRateSlope2 The slope of the variable interest curve, after hitting the optimal ratio + */ + struct InterestRateDataRay { + uint256 optimalUsageRatio; + uint256 baseVariableBorrowRate; + uint256 variableRateSlope1; + uint256 variableRateSlope2; + } + + /** + * @notice emitted when new interest rate data is set in a reserve + * + * @param reserve address of the reserve that has new interest rate data set + * @param optimalUsageRatio The optimal usage ratio, in bps + * @param baseVariableBorrowRate The base variable borrow rate, in bps + * @param variableRateSlope1 The slope of the variable interest curve, before hitting the optimal ratio, in bps + * @param variableRateSlope2 The slope of the variable interest curve, after hitting the optimal ratio, in bps + */ + event RateDataUpdate( + address indexed reserve, + uint256 optimalUsageRatio, + uint256 baseVariableBorrowRate, + uint256 variableRateSlope1, + uint256 variableRateSlope2 + ); + + /** + * @notice Returns the address of the PoolAddressesProvider + * @return The address of the PoolAddressesProvider contract + */ + function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider); + + /** + * @notice Returns the maximum value achievable for variable borrow rate, in bps + * @return The maximum rate + */ + function MAX_BORROW_RATE() external view returns (uint256); + + /** + * @notice Returns the minimum optimal point, in bps + * @return The optimal point + */ + function MIN_OPTIMAL_POINT() external view returns (uint256); + + /** + * @notice Returns the maximum optimal point, in bps + * @return The optimal point + */ + function MAX_OPTIMAL_POINT() external view returns (uint256); + + /** + * notice Returns the full InterestRateData object for the given reserve, in ray + * + * @param reserve The reserve to get the data of + * + * @return The InterestRateDataRay object for the given reserve + */ + function getInterestRateData(address reserve) external view returns (InterestRateDataRay memory); + + /** + * notice Returns the full InterestRateDataRay object for the given reserve, in bps + * + * @param reserve The reserve to get the data of + * + * @return The InterestRateData object for the given reserve + */ + function getInterestRateDataBps(address reserve) external view returns (InterestRateData memory); + + /** + * @notice Returns the optimal usage rate for the given reserve in ray + * + * @param reserve The reserve to get the optimal usage rate of + * + * @return The optimal usage rate is the level of borrow / collateral at which the borrow rate + */ + function getOptimalUsageRatio(address reserve) external view returns (uint256); + + /** + * @notice Returns the variable rate slope below optimal usage ratio in ray + * @dev It's the variable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO + * + * @param reserve The reserve to get the variable rate slope 1 of + * + * @return The variable rate slope + */ + function getVariableRateSlope1(address reserve) external view returns (uint256); + + /** + * @notice Returns the variable rate slope above optimal usage ratio in ray + * @dev It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO + * + * @param reserve The reserve to get the variable rate slope 2 of + * + * @return The variable rate slope + */ + function getVariableRateSlope2(address reserve) external view returns (uint256); + + /** + * @notice Returns the base variable borrow rate, in ray + * + * @param reserve The reserve to get the base variable borrow rate of + * + * @return The base variable borrow rate + */ + function getBaseVariableBorrowRate(address reserve) external view returns (uint256); + + /** + * @notice Returns the maximum variable borrow rate, in ray + * + * @param reserve The reserve to get the maximum variable borrow rate of + * + * @return The maximum variable borrow rate + */ + function getMaxVariableBorrowRate(address reserve) external view returns (uint256); + + /** + * @notice Sets interest rate data for an Aave rate strategy + * @param reserve The reserve to update + * @param rateData The reserve interest rate data to apply to the given reserve + * Being specific to this custom implementation, with custom struct type, + * overloading the function on the generic interface + */ + function setInterestRateParams(address reserve, InterestRateData calldata rateData) external; +} diff --git a/src/core/contracts/interfaces/IPool.sol b/src/core/contracts/interfaces/IPool.sol index 377fae85..f746d172 100644 --- a/src/core/contracts/interfaces/IPool.sol +++ b/src/core/contracts/interfaces/IPool.sol @@ -380,6 +380,14 @@ interface IPool { */ function swapBorrowRateMode(address asset, uint256 interestRateMode) external; + /** + * @notice Allows a borrower to swap his debt between stable and variable mode, + * @dev introduce in a flavor stable rate deprecation + * @param asset The address of the underlying asset borrowed + * @param user The address of the user to be swapped + */ + function swapToVariable(address asset, address user) external; + /** * @notice Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. * - Users can be rebalanced if the following conditions are satisfied: @@ -524,6 +532,22 @@ interface IPool { address rateStrategyAddress ) external; + /** + * @notice Accumulates interest to all indexes of the reserve + * @dev Only callable by the PoolConfigurator contract + * @dev To be used when required by the configurator, for example when updating interest rates strategy data + * @param asset The address of the underlying asset of the reserve + */ + function syncIndexesState(address asset) external; + + /** + * @notice Updates interest rates on the reserve data + * @dev Only callable by the PoolConfigurator contract + * @dev To be used when required by the configurator, for example when updating interest rates strategy data + * @param asset The address of the underlying asset of the reserve + */ + function syncRatesState(address asset) external; + /** * @notice Sets the configuration bitmap of the reserve as a whole * @dev Only callable by the PoolConfigurator contract @@ -579,7 +603,23 @@ interface IPool { * @param asset The address of the underlying asset of the reserve * @return The state and configuration data of the reserve */ - function getReserveData(address asset) external view returns (DataTypes.ReserveData memory); + function getReserveData(address asset) external view returns (DataTypes.ReserveDataLegacy memory); + + /** + * @notice Returns the state and configuration of the reserve, including extra data included with Aave v3.1 + * @param asset The address of the underlying asset of the reserve + * @return The state and configuration data of the reserve with virtual accounting + */ + function getReserveDataExtended( + address asset + ) external view returns (DataTypes.ReserveData memory); + + /** + * @notice Returns the virtual underlying balance of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The reserve virtual underlying balance + */ + function getVirtualUnderlyingBalance(address asset) external view returns (uint128); /** * @notice Validates and finalizes an aToken transfer @@ -684,6 +724,22 @@ interface IPool { */ function resetIsolationModeTotalDebt(address asset) external; + /** + * @notice Sets the liquidation grace period of the given asset + * @dev To enable a liquidation grace period, a timestamp in the future should be set, + * To disable a liquidation grace period, any timestamp in the past works, like 0 + * @param asset The address of the underlying asset to set the liquidationGracePeriod + * @param until Timestamp when the liquidation grace period will end + **/ + function setLiquidationGracePeriod(address asset, uint40 until) external; + + /** + * @notice Returns the liquidation grace period of the given asset + * @param asset The address of the underlying asset + * @return Timestamp when the liquidation grace period will end + **/ + function getLiquidationGracePeriod(address asset) external returns (uint40); + /** * @notice Returns the percentage of available liquidity that can be borrowed at once at stable rate * @return The percentage of available liquidity to borrow, expressed in bps @@ -741,4 +797,39 @@ interface IPool { * 0 if the action is executed directly by the user, without any middle-man */ function deposit(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external; + + /** + * @notice Gets the address of the external FlashLoanLogic + */ + function getFlashLoanLogic() external returns (address); + + /** + * @notice Gets the address of the external BorrowLogic + */ + function getBorrowLogic() external returns (address); + + /** + * @notice Gets the address of the external BridgeLogic + */ + function getBridgeLogic() external returns (address); + + /** + * @notice Gets the address of the external EModeLogic + */ + function getEModeLogic() external returns (address); + + /** + * @notice Gets the address of the external LiquidationLogic + */ + function getLiquidationLogic() external returns (address); + + /** + * @notice Gets the address of the external PoolLogic + */ + function getPoolLogic() external returns (address); + + /** + * @notice Gets the address of the external SupplyLogic + */ + function getSupplyLogic() external returns (address); } diff --git a/src/core/contracts/interfaces/IPoolConfigurator.sol b/src/core/contracts/interfaces/IPoolConfigurator.sol index 138ced4d..beab809a 100644 --- a/src/core/contracts/interfaces/IPoolConfigurator.sol +++ b/src/core/contracts/interfaces/IPoolConfigurator.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; import {ConfiguratorInputTypes} from '../protocol/libraries/types/ConfiguratorInputTypes.sol'; +import {IDefaultInterestRateStrategyV2} from './IDefaultInterestRateStrategyV2.sol'; /** * @title IPoolConfigurator @@ -39,6 +40,19 @@ interface IPoolConfigurator { */ event ReserveFlashLoaning(address indexed asset, bool enabled); + /** + * @dev Emitted when the ltv is set for the frozen asset. + * @param asset The address of the underlying asset of the reserve + * @param ltv The loan to value of the asset when used as collateral + */ + event PendingLtvChanged(address indexed asset, uint256 ltv); + + /** + * @dev Emitted when the asset is unfrozen and pending ltv is removed. + * @param asset The address of the underlying asset of the reserve + */ + event PendingLtvRemoved(address indexed asset); + /** * @dev Emitted when the collateralization risk parameters for the specified asset are updated. * @param asset The address of the underlying asset of the reserve @@ -123,6 +137,13 @@ interface IPoolConfigurator { */ event LiquidationProtocolFeeChanged(address indexed asset, uint256 oldFee, uint256 newFee); + /** + * @dev Emitted when the liquidation grace period is updated. + * @param asset The address of the underlying asset of the reserve + * @param gracePeriodUntil Timestamp until when liquidations will not be allowed post-unpause + */ + event LiquidationGracePeriodChanged(address indexed asset, uint40 gracePeriodUntil); + /** * @dev Emitted when the unbacked mint cap of a reserve is updated. * @param asset The address of the underlying asset of the reserve @@ -173,6 +194,13 @@ interface IPoolConfigurator { address newStrategy ); + /** + * @dev Emitted when the data of a reserve interest strategy contract is updated. + * @param asset The address of the underlying asset of the reserve + * @param data abi encoded data + */ + event ReserveInterestRateDataChanged(address indexed asset, address indexed strategy, bytes data); + /** * @dev Emitted when an aToken implementation is upgraded. * @param asset The address of the underlying asset of the reserve @@ -357,6 +385,19 @@ interface IPoolConfigurator { * swap interest rate, liquidate, atoken transfers). * @param asset The address of the underlying asset of the reserve * @param paused True if pausing the reserve, false if unpausing + * @param gracePeriod Count of seconds after unpause during which liquidations will not be available + * - Only applicable whenever unpausing (`paused` as false) + * - Passing 0 means no grace period + * - Capped to maximum MAX_GRACE_PERIOD + */ + function setReservePause(address asset, bool paused, uint40 gracePeriod) external; + + /** + * @notice Pauses a reserve. A paused reserve does not allow any interaction (supply, borrow, repay, + * swap interest rate, liquidate, atoken transfers). + * @dev Version with no grace period + * @param asset The address of the underlying asset of the reserve + * @param paused True if pausing the reserve, false if unpausing */ function setReservePause(address asset, bool paused) external; @@ -371,16 +412,39 @@ interface IPoolConfigurator { * @notice Sets the interest rate strategy of a reserve. * @param asset The address of the underlying asset of the reserve * @param newRateStrategyAddress The address of the new interest strategy contract + * @param rateData bytes-encoded rate data. In this format in order to allow the rate strategy contract + * to de-structure custom data */ function setReserveInterestRateStrategyAddress( address asset, - address newRateStrategyAddress + address newRateStrategyAddress, + bytes calldata rateData ) external; + /** + * @notice Sets interest rate data for a reserve + * @param asset The address of the underlying asset of the reserve + * @param rateData bytes-encoded rate data. In this format in order to allow the rate strategy contract + * to de-structure custom data + */ + function setReserveInterestRateData(address asset, bytes calldata rateData) external; + /** * @notice Pauses or unpauses all the protocol reserves. In the paused state all the protocol interactions * are suspended. * @param paused True if protocol needs to be paused, false otherwise + * @param gracePeriod Count of seconds after unpause during which liquidations will not be available + * - Only applicable whenever unpausing (`paused` as false) + * - Passing 0 means no grace period + * - Capped to maximum MAX_GRACE_PERIOD + */ + function setPoolPause(bool paused, uint40 gracePeriod) external; + + /** + * @notice Pauses or unpauses all the protocol reserves. In the paused state all the protocol interactions + * are suspended. + * @dev Version with no grace period + * @param paused True if protocol needs to be paused, false otherwise */ function setPoolPause(bool paused) external; @@ -483,4 +547,20 @@ interface IPoolConfigurator { * @param siloed The new siloed borrowing state */ function setSiloedBorrowing(address asset, bool siloed) external; + + /** + * @notice Gets pending ltv value and flag if it is set + * @param asset The new siloed borrowing state + */ + function getPendingLtv(address asset) external returns (uint256, bool); + + /** + * @notice Gets the address of the external ConfiguratorLogic + */ + function getConfiguratorLogic() external returns (address); + + /** + * @notice Gets the maximum liquidations grace period allowed, in seconds + */ + function MAX_GRACE_PERIOD() external returns (uint40); } diff --git a/src/core/contracts/interfaces/IReserveInterestRateStrategy.sol b/src/core/contracts/interfaces/IReserveInterestRateStrategy.sol index 03d021cd..cdd81565 100644 --- a/src/core/contracts/interfaces/IReserveInterestRateStrategy.sol +++ b/src/core/contracts/interfaces/IReserveInterestRateStrategy.sol @@ -5,16 +5,24 @@ import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; /** * @title IReserveInterestRateStrategy - * @author Aave - * @notice Interface for the calculation of the interest rates + * @author BGD Labs + * @notice Basic interface for any rate strategy used by the Aave protocol */ interface IReserveInterestRateStrategy { + /** + * @notice Sets interest rate data for an Aave rate strategy + * @param reserve The reserve to update + * @param rateData The abi encoded reserve interest rate data to apply to the given reserve + * Abstracted this way as rate strategies can be custom + */ + function setInterestRateParams(address reserve, bytes calldata rateData) external; + /** * @notice Calculates the interest rates depending on the reserve's state and configurations * @param params The parameters needed to calculate interest rates - * @return liquidityRate The liquidity rate expressed in rays - * @return stableBorrowRate The stable borrow rate expressed in rays - * @return variableBorrowRate The variable borrow rate expressed in rays + * @return liquidityRate The liquidity rate expressed in ray + * @return stableBorrowRate The stable borrow rate expressed in ray + * @return variableBorrowRate The variable borrow rate expressed in ray */ function calculateInterestRates( DataTypes.CalculateInterestRatesParams memory params diff --git a/src/core/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/src/core/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index ce3ba715..700b129a 100644 --- a/src/core/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/src/core/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -29,6 +29,7 @@ library ReserveConfiguration { uint256 internal constant EMODE_CATEGORY_MASK = 0xFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 internal constant UNBACKED_MINT_CAP_MASK = 0xFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 internal constant DEBT_CEILING_MASK = 0xF0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant VIRTUAL_ACC_ACTIVE = 0xEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed uint256 internal constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16; @@ -49,6 +50,7 @@ library ReserveConfiguration { uint256 internal constant EMODE_CATEGORY_START_BIT_POSITION = 168; uint256 internal constant UNBACKED_MINT_CAP_START_BIT_POSITION = 176; uint256 internal constant DEBT_CEILING_START_BIT_POSITION = 212; + uint256 internal constant VIRTUAL_ACC_START_BIT_POSITION = 252; uint256 internal constant MAX_VALID_LTV = 65535; uint256 internal constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535; @@ -544,6 +546,31 @@ library ReserveConfiguration { return (self.data & ~FLASHLOAN_ENABLED_MASK) != 0; } + /** + * @notice Sets the virtual account active/not state of the reserve + * @param self The reserve configuration + * @param active The active state + */ + function setVirtualAccActive( + DataTypes.ReserveConfigurationMap memory self, + bool active + ) internal pure { + self.data = + (self.data & VIRTUAL_ACC_ACTIVE) | + (uint256(active ? 1 : 0) << VIRTUAL_ACC_START_BIT_POSITION); + } + + /** + * @notice Gets the virtual account active/not state of the reserve + * @param self The reserve configuration + * @return The active state + */ + function getIsVirtualAccActive( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (bool) { + return (self.data & ~VIRTUAL_ACC_ACTIVE) != 0; + } + /** * @notice Gets the configuration flags of the reserve * @param self The reserve configuration diff --git a/src/core/contracts/protocol/libraries/helpers/Errors.sol b/src/core/contracts/protocol/libraries/helpers/Errors.sol index 16f78d72..2e4521b4 100644 --- a/src/core/contracts/protocol/libraries/helpers/Errors.sol +++ b/src/core/contracts/protocol/libraries/helpers/Errors.sol @@ -97,4 +97,11 @@ library Errors { string public constant SILOED_BORROWING_VIOLATION = '89'; // 'User is trying to borrow multiple assets including a siloed one' string public constant RESERVE_DEBT_NOT_ZERO = '90'; // the total debt of the reserve needs to be 0 string public constant FLASHLOAN_DISABLED = '91'; // FlashLoaning for this asset is disabled + string public constant INVALID_MAXRATE = '92'; // The expect maximum borrow rate is invalid + string public constant WITHDRAW_TO_ATOKEN = '93'; // Withdrawing to the aToken is not allowed + string public constant SUPPLY_TO_ATOKEN = '94'; // Supplying to the aToken is not allowed + string public constant SLOPE_2_MUST_BE_GTE_SLOPE_1 = '95'; // Variable interest rate slope 2 can not be lower than slope 1 + string public constant CALLER_NOT_RISK_OR_POOL_OR_EMERGENCY_ADMIN = '96'; // 'The caller of the function is not a risk, pool or emergency admin' + string public constant LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED = '97'; // 'Liquidation grace sentinel validation failed' + string public constant INVALID_GRACE_PERIOD = '98'; // Grace period above a valid range } diff --git a/src/core/contracts/protocol/libraries/logic/BorrowLogic.sol b/src/core/contracts/protocol/libraries/logic/BorrowLogic.sol index 7ac22d94..91ea0fa6 100644 --- a/src/core/contracts/protocol/libraries/logic/BorrowLogic.sol +++ b/src/core/contracts/protocol/libraries/logic/BorrowLogic.sol @@ -52,6 +52,7 @@ library BorrowLogic { DataTypes.InterestRateMode interestRateMode ); event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt); + event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); /** * @notice Implements the borrow feature. Borrowing allows users that provided collateral to draw liquidity from the @@ -70,7 +71,7 @@ library BorrowLogic { mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories, DataTypes.UserConfigurationMap storage userConfig, DataTypes.ExecuteBorrowParams memory params - ) public { + ) external { DataTypes.ReserveData storage reserve = reservesData[params.asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); @@ -142,7 +143,7 @@ library BorrowLogic { ); } - reserve.updateInterestRates( + reserve.updateInterestRatesAndVirtualBalance( reserveCache, params.asset, 0, @@ -224,7 +225,7 @@ library BorrowLogic { ).burn(params.onBehalfOf, paybackAmount, reserveCache.nextVariableBorrowIndex); } - reserve.updateInterestRates( + reserve.updateInterestRatesAndVirtualBalance( reserveCache, params.asset, params.useATokens ? 0 : paybackAmount, @@ -250,6 +251,11 @@ library BorrowLogic { paybackAmount, reserveCache.nextLiquidityIndex ); + // in case of aToken repayment the msg.sender must always repay on behalf of itself + if (IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender) == 0) { + userConfig.setUsingAsCollateral(reserve.id, false); + emit ReserveUsedAsCollateralDisabled(params.asset, msg.sender); + } } else { IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, paybackAmount); IAToken(reserveCache.aTokenAddress).handleRepayment( @@ -291,7 +297,7 @@ library BorrowLogic { (, reserveCache.nextTotalStableDebt, reserveCache.nextAvgStableBorrowRate) = stableDebtToken .mint(user, user, stableDebt, reserve.currentStableBorrowRate); - reserve.updateInterestRates(reserveCache, asset, 0, 0); + reserve.updateInterestRatesAndVirtualBalance(reserveCache, asset, 0, 0); emit RebalanceStableBorrowRate(asset, user); } @@ -308,16 +314,14 @@ library BorrowLogic { DataTypes.ReserveData storage reserve, DataTypes.UserConfigurationMap storage userConfig, address asset, + address user, DataTypes.InterestRateMode interestRateMode ) external { DataTypes.ReserveCache memory reserveCache = reserve.cache(); reserve.updateState(reserveCache); - (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt( - msg.sender, - reserveCache - ); + (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(user, reserveCache); ValidationLogic.validateSwapRateMode( reserve, @@ -331,23 +335,23 @@ library BorrowLogic { if (interestRateMode == DataTypes.InterestRateMode.STABLE) { (reserveCache.nextTotalStableDebt, reserveCache.nextAvgStableBorrowRate) = IStableDebtToken( reserveCache.stableDebtTokenAddress - ).burn(msg.sender, stableDebt); + ).burn(user, stableDebt); (, reserveCache.nextScaledVariableDebt) = IVariableDebtToken( reserveCache.variableDebtTokenAddress - ).mint(msg.sender, msg.sender, stableDebt, reserveCache.nextVariableBorrowIndex); + ).mint(user, user, stableDebt, reserveCache.nextVariableBorrowIndex); } else { reserveCache.nextScaledVariableDebt = IVariableDebtToken( reserveCache.variableDebtTokenAddress - ).burn(msg.sender, variableDebt, reserveCache.nextVariableBorrowIndex); + ).burn(user, variableDebt, reserveCache.nextVariableBorrowIndex); (, reserveCache.nextTotalStableDebt, reserveCache.nextAvgStableBorrowRate) = IStableDebtToken( reserveCache.stableDebtTokenAddress - ).mint(msg.sender, msg.sender, variableDebt, reserve.currentStableBorrowRate); + ).mint(user, user, variableDebt, reserve.currentStableBorrowRate); } - reserve.updateInterestRates(reserveCache, asset, 0, 0); + reserve.updateInterestRatesAndVirtualBalance(reserveCache, asset, 0, 0); - emit SwapBorrowRateMode(asset, msg.sender, interestRateMode); + emit SwapBorrowRateMode(asset, user, interestRateMode); } } diff --git a/src/core/contracts/protocol/libraries/logic/BridgeLogic.sol b/src/core/contracts/protocol/libraries/logic/BridgeLogic.sol index ef1f7f81..3b4d469b 100644 --- a/src/core/contracts/protocol/libraries/logic/BridgeLogic.sol +++ b/src/core/contracts/protocol/libraries/logic/BridgeLogic.sol @@ -63,7 +63,7 @@ library BridgeLogic { reserve.updateState(reserveCache); - ValidationLogic.validateSupply(reserveCache, reserve, amount); + ValidationLogic.validateSupply(reserveCache, reserve, amount, onBehalfOf); uint256 unbackedMintCap = reserveCache.reserveConfiguration.getUnbackedMintCap(); uint256 reserveDecimals = reserveCache.reserveConfiguration.getDecimals(); @@ -75,7 +75,7 @@ library BridgeLogic { Errors.UNBACKED_MINT_CAP_EXCEEDED ); - reserve.updateInterestRates(reserveCache, asset, 0, 0); + reserve.updateInterestRatesAndVirtualBalance(reserveCache, asset, 0, 0); bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint( msg.sender, @@ -139,7 +139,7 @@ library BridgeLogic { reserve.accruedToTreasury += feeToProtocol.rayDiv(reserveCache.nextLiquidityIndex).toUint128(); reserve.unbacked -= backingAmount.toUint128(); - reserve.updateInterestRates(reserveCache, asset, added, 0); + reserve.updateInterestRatesAndVirtualBalance(reserveCache, asset, added, 0); IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, added); diff --git a/src/core/contracts/protocol/libraries/logic/ConfiguratorLogic.sol b/src/core/contracts/protocol/libraries/logic/ConfiguratorLogic.sol index d9672d1e..39a448b4 100644 --- a/src/core/contracts/protocol/libraries/logic/ConfiguratorLogic.sol +++ b/src/core/contracts/protocol/libraries/logic/ConfiguratorLogic.sol @@ -5,6 +5,7 @@ import {IPool} from '../../../interfaces/IPool.sol'; import {IInitializableAToken} from '../../../interfaces/IInitializableAToken.sol'; import {IInitializableDebtToken} from '../../../interfaces/IInitializableDebtToken.sol'; import {InitializableImmutableAdminUpgradeabilityProxy} from '../aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol'; +import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol'; import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; import {DataTypes} from '../types/DataTypes.sol'; import {ConfiguratorInputTypes} from '../types/ConfiguratorInputTypes.sol'; @@ -50,7 +51,7 @@ library ConfiguratorLogic { function executeInitReserve( IPool pool, ConfiguratorInputTypes.InitReserveInput calldata input - ) public { + ) external { address aTokenProxyAddress = _initTokenWithProxy( input.aTokenImpl, abi.encodeWithSelector( @@ -109,9 +110,15 @@ library ConfiguratorLogic { currentConfig.setActive(true); currentConfig.setPaused(false); currentConfig.setFrozen(false); + currentConfig.setVirtualAccActive(input.useVirtualBalance); pool.setConfiguration(input.underlyingAsset, currentConfig); + IReserveInterestRateStrategy(input.interestRateStrategyAddress).setInterestRateParams( + input.underlyingAsset, + input.interestRateData + ); + emit ReserveInitialized( input.underlyingAsset, aTokenProxyAddress, @@ -130,8 +137,8 @@ library ConfiguratorLogic { function executeUpdateAToken( IPool cachedPool, ConfiguratorInputTypes.UpdateATokenInput calldata input - ) public { - DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); + ) external { + DataTypes.ReserveDataLegacy memory reserveData = cachedPool.getReserveData(input.asset); (, , , uint256 decimals, , ) = cachedPool.getConfiguration(input.asset).getParams(); @@ -161,8 +168,8 @@ library ConfiguratorLogic { function executeUpdateStableDebtToken( IPool cachedPool, ConfiguratorInputTypes.UpdateDebtTokenInput calldata input - ) public { - DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); + ) external { + DataTypes.ReserveDataLegacy memory reserveData = cachedPool.getReserveData(input.asset); (, , , uint256 decimals, , ) = cachedPool.getConfiguration(input.asset).getParams(); @@ -199,8 +206,8 @@ library ConfiguratorLogic { function executeUpdateVariableDebtToken( IPool cachedPool, ConfiguratorInputTypes.UpdateDebtTokenInput calldata input - ) public { - DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); + ) external { + DataTypes.ReserveDataLegacy memory reserveData = cachedPool.getReserveData(input.asset); (, , , uint256 decimals, , ) = cachedPool.getConfiguration(input.asset).getParams(); diff --git a/src/core/contracts/protocol/libraries/logic/FlashLoanLogic.sol b/src/core/contracts/protocol/libraries/logic/FlashLoanLogic.sol index bca89157..615b2fdd 100644 --- a/src/core/contracts/protocol/libraries/logic/FlashLoanLogic.sol +++ b/src/core/contracts/protocol/libraries/logic/FlashLoanLogic.sol @@ -96,6 +96,13 @@ library FlashLoanLogic { DataTypes.InterestRateMode.NONE ? vars.currentAmount.percentMul(vars.flashloanPremiumTotal) : 0; + + if (reservesData[params.assets[vars.i]].configuration.getIsVirtualAccActive()) { + reservesData[params.assets[vars.i]].virtualUnderlyingBalance -= vars + .currentAmount + .toUint128(); + } + IAToken(reservesData[params.assets[vars.i]].aTokenAddress).transferUnderlyingTo( params.receiverAddress, vars.currentAmount @@ -148,7 +155,8 @@ library FlashLoanLogic { interestRateMode: DataTypes.InterestRateMode(params.interestRateModes[vars.i]), referralCode: params.referralCode, releaseUnderlying: false, - maxStableRateBorrowSizePercent: IPool(params.pool).MAX_STABLE_RATE_BORROW_SIZE_PERCENT(), + maxStableRateBorrowSizePercent: IPool(params.pool) + .MAX_STABLE_RATE_BORROW_SIZE_PERCENT(), reservesCount: IPool(params.pool).getReservesCount(), oracle: IPoolAddressesProvider(params.addressesProvider).getPriceOracle(), userEModeCategory: IPool(params.pool).getUserEMode(params.onBehalfOf).toUint8(), @@ -188,10 +196,15 @@ library FlashLoanLogic { // is altered to (validation -> user payload -> cache -> updateState -> changeState -> updateRates) for flashloans. // This is done to protect against reentrance and rate manipulation within the user specified payload. - ValidationLogic.validateFlashloanSimple(reserve); + ValidationLogic.validateFlashloanSimple(reserve, params.amount); IFlashLoanSimpleReceiver receiver = IFlashLoanSimpleReceiver(params.receiverAddress); uint256 totalPremium = params.amount.percentMul(params.flashLoanPremiumTotal); + + if (reserve.configuration.getIsVirtualAccActive()) { + reserve.virtualUnderlyingBalance -= params.amount.toUint128(); + } + IAToken(reserve.aTokenAddress).transferUnderlyingTo(params.receiverAddress, params.amount); require( @@ -244,7 +257,7 @@ library FlashLoanLogic { .rayDiv(reserveCache.nextLiquidityIndex) .toUint128(); - reserve.updateInterestRates(reserveCache, params.asset, amountPlusPremium, 0); + reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, amountPlusPremium, 0); IERC20(params.asset).safeTransferFrom( params.receiverAddress, diff --git a/src/core/contracts/protocol/libraries/logic/LiquidationLogic.sol b/src/core/contracts/protocol/libraries/logic/LiquidationLogic.sol index 57f00fd4..158cd46f 100644 --- a/src/core/contracts/protocol/libraries/logic/LiquidationLogic.sol +++ b/src/core/contracts/protocol/libraries/logic/LiquidationLogic.sol @@ -130,6 +130,7 @@ library LiquidationLogic { ValidationLogic.validateLiquidationCall( userConfig, collateralReserve, + debtReserve, DataTypes.ValidateLiquidationCallParams({ debtReserveCache: vars.debtReserveCache, totalDebt: vars.userTotalDebt, @@ -178,7 +179,7 @@ library LiquidationLogic { _burnDebtTokens(params, vars); - debtReserve.updateInterestRates( + debtReserve.updateInterestRatesAndVirtualBalance( vars.debtReserveCache, params.debtAsset, vars.actualDebtToLiquidate, @@ -255,7 +256,7 @@ library LiquidationLogic { ) internal { DataTypes.ReserveCache memory collateralReserveCache = collateralReserve.cache(); collateralReserve.updateState(collateralReserveCache); - collateralReserve.updateInterestRates( + collateralReserve.updateInterestRatesAndVirtualBalance( collateralReserveCache, params.collateralAsset, 0, diff --git a/src/core/contracts/protocol/libraries/logic/PoolLogic.sol b/src/core/contracts/protocol/libraries/logic/PoolLogic.sol index 18b312c7..92138fe8 100644 --- a/src/core/contracts/protocol/libraries/logic/PoolLogic.sol +++ b/src/core/contracts/protocol/libraries/logic/PoolLogic.sol @@ -123,6 +123,20 @@ library PoolLogic { emit IsolationModeTotalDebtUpdated(asset, 0); } + /** + * @notice Sets the liquidation grace period of the asset + * @param reservesData The state of all the reserves + * @param asset The address of the underlying asset to set the liquidationGracePeriod + * @param until Timestamp when the liquidation grace period will end + */ + function executeSetLiquidationGracePeriod( + mapping(address => DataTypes.ReserveData) storage reservesData, + address asset, + uint40 until + ) external { + reservesData[asset].liquidationGracePeriodUntil = until; + } + /** * @notice Drop a reserve * @param reservesData The state of all the reserves diff --git a/src/core/contracts/protocol/libraries/logic/ReserveLogic.sol b/src/core/contracts/protocol/libraries/logic/ReserveLogic.sol index bc48163d..dedcfb61 100644 --- a/src/core/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/src/core/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -154,7 +154,7 @@ library ReserveLogic { reserve.interestRateStrategyAddress = interestRateStrategyAddress; } - struct UpdateInterestRatesLocalVars { + struct UpdateInterestRatesAndVirtualBalanceLocalVars { uint256 nextLiquidityRate; uint256 nextStableRate; uint256 nextVariableRate; @@ -169,14 +169,14 @@ library ReserveLogic { * @param liquidityAdded The amount of liquidity added to the protocol (supply or repay) in the previous action * @param liquidityTaken The amount of liquidity taken from the protocol (redeem or borrow) */ - function updateInterestRates( + function updateInterestRatesAndVirtualBalance( DataTypes.ReserveData storage reserve, DataTypes.ReserveCache memory reserveCache, address reserveAddress, uint256 liquidityAdded, uint256 liquidityTaken ) internal { - UpdateInterestRatesLocalVars memory vars; + UpdateInterestRatesAndVirtualBalanceLocalVars memory vars; vars.totalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul( reserveCache.nextVariableBorrowIndex @@ -196,7 +196,8 @@ library ReserveLogic { averageStableBorrowRate: reserveCache.nextAvgStableBorrowRate, reserveFactor: reserveCache.reserveFactor, reserve: reserveAddress, - aToken: reserveCache.aTokenAddress + usingVirtualBalance: reserve.configuration.getIsVirtualAccActive(), + virtualUnderlyingBalance: reserve.virtualUnderlyingBalance }) ); @@ -204,6 +205,16 @@ library ReserveLogic { reserve.currentStableBorrowRate = vars.nextStableRate.toUint128(); reserve.currentVariableBorrowRate = vars.nextVariableRate.toUint128(); + // Only affect virtual balance if the reserve uses it + if (reserve.configuration.getIsVirtualAccActive()) { + if (liquidityAdded > 0) { + reserve.virtualUnderlyingBalance += liquidityAdded.toUint128(); + } + if (liquidityTaken > 0) { + reserve.virtualUnderlyingBalance -= liquidityTaken.toUint128(); + } + } + emit ReserveDataUpdated( reserveAddress, vars.nextLiquidityRate, diff --git a/src/core/contracts/protocol/libraries/logic/SupplyLogic.sol b/src/core/contracts/protocol/libraries/logic/SupplyLogic.sol index 59cef41c..727886e7 100644 --- a/src/core/contracts/protocol/libraries/logic/SupplyLogic.sol +++ b/src/core/contracts/protocol/libraries/logic/SupplyLogic.sol @@ -60,9 +60,9 @@ library SupplyLogic { reserve.updateState(reserveCache); - ValidationLogic.validateSupply(reserveCache, reserve, params.amount); + ValidationLogic.validateSupply(reserveCache, reserve, params.amount, params.onBehalfOf); - reserve.updateInterestRates(reserveCache, params.asset, params.amount, 0); + reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, params.amount, 0); IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, params.amount); @@ -113,6 +113,8 @@ library SupplyLogic { DataTypes.ReserveData storage reserve = reservesData[params.asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); + require(params.to != reserveCache.aTokenAddress, Errors.WITHDRAW_TO_ATOKEN); + reserve.updateState(reserveCache); uint256 userBalance = IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul( @@ -127,7 +129,7 @@ library SupplyLogic { ValidationLogic.validateWithdraw(reserveCache, amountToWithdraw, userBalance); - reserve.updateInterestRates(reserveCache, params.asset, 0, amountToWithdraw); + reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, 0, amountToWithdraw); bool isCollateral = userConfig.isUsingAsCollateral(reserve.id); @@ -186,8 +188,9 @@ library SupplyLogic { ValidationLogic.validateTransfer(reserve); uint256 reserveId = reserve.id; + uint256 scaledAmount = params.amount.rayDiv(reserve.getNormalizedIncome()); - if (params.from != params.to && params.amount != 0) { + if (params.from != params.to && scaledAmount != 0) { DataTypes.UserConfigurationMap storage fromConfig = usersConfig[params.from]; if (fromConfig.isUsingAsCollateral(reserveId)) { diff --git a/src/core/contracts/protocol/libraries/logic/ValidationLogic.sol b/src/core/contracts/protocol/libraries/logic/ValidationLogic.sol index 75218f05..2486db31 100644 --- a/src/core/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/src/core/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -66,7 +66,8 @@ library ValidationLogic { function validateSupply( DataTypes.ReserveCache memory reserveCache, DataTypes.ReserveData storage reserve, - uint256 amount + uint256 amount, + address onBehalfOf ) internal view { require(amount != 0, Errors.INVALID_AMOUNT); @@ -76,6 +77,7 @@ library ValidationLogic { require(isActive, Errors.RESERVE_INACTIVE); require(!isPaused, Errors.RESERVE_PAUSED); require(!isFrozen, Errors.RESERVE_FROZEN); + require(onBehalfOf != reserveCache.aTokenAddress, Errors.SUPPLY_TO_ATOKEN); uint256 supplyCap = reserveCache.reserveConfiguration.getSupplyCap(); require( @@ -158,6 +160,11 @@ library ValidationLogic { require(!vars.isPaused, Errors.RESERVE_PAUSED); require(!vars.isFrozen, Errors.RESERVE_FROZEN); require(vars.borrowingEnabled, Errors.BORROWING_NOT_ENABLED); + require( + !params.reserveCache.reserveConfiguration.getIsVirtualAccActive() || + IERC20(params.reserveCache.aTokenAddress).totalSupply() >= params.amount, + Errors.INVALID_AMOUNT + ); require( params.priceOracleSentinel == address(0) || @@ -285,7 +292,7 @@ library ValidationLogic { Errors.COLLATERAL_SAME_AS_BORROWING_CURRENCY ); - vars.availableLiquidity = IERC20(params.asset).balanceOf(params.reserveCache.aTokenAddress); + vars.availableLiquidity = reservesData[params.asset].virtualUnderlyingBalance; //calculate the max available loan size in stable rate mode as a percentage of the //available liquidity @@ -361,12 +368,11 @@ library ValidationLogic { uint256 variableDebt, DataTypes.InterestRateMode currentRateMode ) internal view { - (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = reserveCache + (bool isActive, , , bool stableRateEnabled, bool isPaused) = reserveCache .reserveConfiguration .getFlags(); require(isActive, Errors.RESERVE_INACTIVE); require(!isPaused, Errors.RESERVE_PAUSED); - require(!isFrozen, Errors.RESERVE_FROZEN); if (currentRateMode == DataTypes.InterestRateMode.STABLE) { require(stableDebt != 0, Errors.NO_OUTSTANDING_STABLE_DEBT); @@ -424,7 +430,8 @@ library ValidationLogic { averageStableBorrowRate: 0, reserveFactor: reserveCache.reserveFactor, reserve: reserveAddress, - aToken: reserveCache.aTokenAddress + usingVirtualBalance: reserve.configuration.getIsVirtualAccActive(), + virtualUnderlyingBalance: reserve.virtualUnderlyingBalance }) ); @@ -464,7 +471,7 @@ library ValidationLogic { ) internal view { require(assets.length == amounts.length, Errors.INCONSISTENT_FLASHLOAN_PARAMS); for (uint256 i = 0; i < assets.length; i++) { - validateFlashloanSimple(reservesData[assets[i]]); + validateFlashloanSimple(reservesData[assets[i]], amounts[i]); } } @@ -472,11 +479,19 @@ library ValidationLogic { * @notice Validates a flashloan action. * @param reserve The state of the reserve */ - function validateFlashloanSimple(DataTypes.ReserveData storage reserve) internal view { + function validateFlashloanSimple( + DataTypes.ReserveData storage reserve, + uint256 amount + ) internal view { DataTypes.ReserveConfigurationMap memory configuration = reserve.configuration; require(!configuration.getPaused(), Errors.RESERVE_PAUSED); require(configuration.getActive(), Errors.RESERVE_INACTIVE); require(configuration.getFlashLoanEnabled(), Errors.FLASHLOAN_DISABLED); + require( + !configuration.getIsVirtualAccActive() || + IERC20(reserve.aTokenAddress).totalSupply() >= amount, + Errors.INVALID_AMOUNT + ); } struct ValidateLiquidationCallLocalVars { @@ -491,11 +506,13 @@ library ValidationLogic { * @notice Validates the liquidation action. * @param userConfig The user configuration mapping * @param collateralReserve The reserve data of the collateral + * @param debtReserve The reserve data of the debt * @param params Additional parameters needed for the validation */ function validateLiquidationCall( DataTypes.UserConfigurationMap storage userConfig, DataTypes.ReserveData storage collateralReserve, + DataTypes.ReserveData storage debtReserve, DataTypes.ValidateLiquidationCallParams memory params ) internal view { ValidateLiquidationCallLocalVars memory vars; @@ -519,6 +536,12 @@ library ValidationLogic { Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED ); + require( + collateralReserve.liquidationGracePeriodUntil < uint40(block.timestamp) && + debtReserve.liquidationGracePeriodUntil < uint40(block.timestamp), + Errors.LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED + ); + require( params.healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD, Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD diff --git a/src/core/contracts/protocol/libraries/types/ConfiguratorInputTypes.sol b/src/core/contracts/protocol/libraries/types/ConfiguratorInputTypes.sol index f3cf4f02..ad297064 100644 --- a/src/core/contracts/protocol/libraries/types/ConfiguratorInputTypes.sol +++ b/src/core/contracts/protocol/libraries/types/ConfiguratorInputTypes.sol @@ -7,6 +7,7 @@ library ConfiguratorInputTypes { address stableDebtTokenImpl; address variableDebtTokenImpl; uint8 underlyingAssetDecimals; + bool useVirtualBalance; address interestRateStrategyAddress; address underlyingAsset; address treasury; @@ -18,6 +19,7 @@ library ConfiguratorInputTypes { string stableDebtTokenName; string stableDebtTokenSymbol; bytes params; + bytes interestRateData; } struct UpdateATokenInput { diff --git a/src/core/contracts/protocol/libraries/types/DataTypes.sol b/src/core/contracts/protocol/libraries/types/DataTypes.sol index 1ef3d76c..22920a32 100644 --- a/src/core/contracts/protocol/libraries/types/DataTypes.sol +++ b/src/core/contracts/protocol/libraries/types/DataTypes.sol @@ -2,6 +2,43 @@ pragma solidity ^0.8.0; library DataTypes { + /** + * This exists specifically to maintain the `getReserveData()` interface, since the new, internal + * `ReserveData` struct includes the reserve's `virtualUnderlyingBalance`. + */ + struct ReserveDataLegacy { + //stores the reserve configuration + ReserveConfigurationMap configuration; + //the liquidity index. Expressed in ray + uint128 liquidityIndex; + //the current supply rate. Expressed in ray + uint128 currentLiquidityRate; + //variable borrow index. Expressed in ray + uint128 variableBorrowIndex; + //the current variable borrow rate. Expressed in ray + uint128 currentVariableBorrowRate; + //the current stable borrow rate. Expressed in ray + uint128 currentStableBorrowRate; + //timestamp of last update + uint40 lastUpdateTimestamp; + //the id of the reserve. Represents the position in the list of the active reserves + uint16 id; + //aToken address + address aTokenAddress; + //stableDebtToken address + address stableDebtTokenAddress; + //variableDebtToken address + address variableDebtTokenAddress; + //address of the interest rate strategy + address interestRateStrategyAddress; + //the current treasury balance, scaled + uint128 accruedToTreasury; + //the outstanding unbacked aTokens minted through the bridging feature + uint128 unbacked; + //the outstanding debt borrowed against this asset in isolation mode + uint128 isolationModeTotalDebt; + } + struct ReserveData { //stores the reserve configuration ReserveConfigurationMap configuration; @@ -19,6 +56,8 @@ library DataTypes { uint40 lastUpdateTimestamp; //the id of the reserve. Represents the position in the list of the active reserves uint16 id; + //timestamp in the future, until when liquidations are not allowed on the reserve + uint40 liquidationGracePeriodUntil; //aToken address address aTokenAddress; //stableDebtToken address @@ -33,6 +72,8 @@ library DataTypes { uint128 unbacked; //the outstanding debt borrowed against this asset in isolation mode uint128 isolationModeTotalDebt; + //the amount of underlying accounted for by the protocol + uint128 virtualUnderlyingBalance; } struct ReserveConfigurationMap { @@ -49,13 +90,14 @@ library DataTypes { //bit 62: siloed borrowing enabled //bit 63: flashloaning enabled //bit 64-79: reserve factor - //bit 80-115 borrow cap in whole tokens, borrowCap == 0 => no cap - //bit 116-151 supply cap in whole tokens, supplyCap == 0 => no cap - //bit 152-167 liquidation protocol fee - //bit 168-175 eMode category - //bit 176-211 unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled - //bit 212-251 debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals - //bit 252-255 unused + //bit 80-115: borrow cap in whole tokens, borrowCap == 0 => no cap + //bit 116-151: supply cap in whole tokens, supplyCap == 0 => no cap + //bit 152-167: liquidation protocol fee + //bit 168-175: eMode category + //bit 176-211: unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled + //bit 212-251: debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals + //bit 252: virtual accounting is enabled for the reserve + //bit 253-255 unused uint256 data; } @@ -255,7 +297,8 @@ library DataTypes { uint256 averageStableBorrowRate; uint256 reserveFactor; address reserve; - address aToken; + bool usingVirtualBalance; + uint256 virtualUnderlyingBalance; } struct InitReserveParams { diff --git a/src/core/contracts/protocol/pool/DefaultReserveInterestRateStrategy.sol b/src/core/contracts/protocol/pool/DefaultReserveInterestRateStrategy.sol deleted file mode 100644 index c07ad657..00000000 --- a/src/core/contracts/protocol/pool/DefaultReserveInterestRateStrategy.sol +++ /dev/null @@ -1,256 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; -import {WadRayMath} from '../libraries/math/WadRayMath.sol'; -import {PercentageMath} from '../libraries/math/PercentageMath.sol'; -import {DataTypes} from '../libraries/types/DataTypes.sol'; -import {Errors} from '../libraries/helpers/Errors.sol'; -import {IDefaultInterestRateStrategy} from '../../interfaces/IDefaultInterestRateStrategy.sol'; -import {IReserveInterestRateStrategy} from '../../interfaces/IReserveInterestRateStrategy.sol'; -import {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol'; - -/** - * @title DefaultReserveInterestRateStrategy contract - * @author Aave - * @notice Implements the calculation of the interest rates depending on the reserve state - * @dev The model of interest rate is based on 2 slopes, one before the `OPTIMAL_USAGE_RATIO` - * point of usage and another from that one to 100%. - * - An instance of this same contract, can't be used across different Aave markets, due to the caching - * of the PoolAddressesProvider - */ -contract DefaultReserveInterestRateStrategy is IDefaultInterestRateStrategy { - using WadRayMath for uint256; - using PercentageMath for uint256; - - /// @inheritdoc IDefaultInterestRateStrategy - uint256 public immutable OPTIMAL_USAGE_RATIO; - - /// @inheritdoc IDefaultInterestRateStrategy - uint256 public immutable OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO; - - /// @inheritdoc IDefaultInterestRateStrategy - uint256 public immutable MAX_EXCESS_USAGE_RATIO; - - /// @inheritdoc IDefaultInterestRateStrategy - uint256 public immutable MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO; - - IPoolAddressesProvider public immutable ADDRESSES_PROVIDER; - - // Base variable borrow rate when usage rate = 0. Expressed in ray - uint256 internal immutable _baseVariableBorrowRate; - - // Slope of the variable interest curve when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO. Expressed in ray - uint256 internal immutable _variableRateSlope1; - - // Slope of the variable interest curve when usage ratio > OPTIMAL_USAGE_RATIO. Expressed in ray - uint256 internal immutable _variableRateSlope2; - - // Slope of the stable interest curve when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO. Expressed in ray - uint256 internal immutable _stableRateSlope1; - - // Slope of the stable interest curve when usage ratio > OPTIMAL_USAGE_RATIO. Expressed in ray - uint256 internal immutable _stableRateSlope2; - - // Premium on top of `_variableRateSlope1` for base stable borrowing rate - uint256 internal immutable _baseStableRateOffset; - - // Additional premium applied to stable rate when stable debt surpass `OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO` - uint256 internal immutable _stableRateExcessOffset; - - /** - * @dev Constructor. - * @param provider The address of the PoolAddressesProvider contract - * @param optimalUsageRatio The optimal usage ratio - * @param baseVariableBorrowRate The base variable borrow rate - * @param variableRateSlope1 The variable rate slope below optimal usage ratio - * @param variableRateSlope2 The variable rate slope above optimal usage ratio - * @param stableRateSlope1 The stable rate slope below optimal usage ratio - * @param stableRateSlope2 The stable rate slope above optimal usage ratio - * @param baseStableRateOffset The premium on top of variable rate for base stable borrowing rate - * @param stableRateExcessOffset The premium on top of stable rate when there stable debt surpass the threshold - * @param optimalStableToTotalDebtRatio The optimal stable debt to total debt ratio of the reserve - */ - constructor( - IPoolAddressesProvider provider, - uint256 optimalUsageRatio, - uint256 baseVariableBorrowRate, - uint256 variableRateSlope1, - uint256 variableRateSlope2, - uint256 stableRateSlope1, - uint256 stableRateSlope2, - uint256 baseStableRateOffset, - uint256 stableRateExcessOffset, - uint256 optimalStableToTotalDebtRatio - ) { - require(WadRayMath.RAY >= optimalUsageRatio, Errors.INVALID_OPTIMAL_USAGE_RATIO); - require( - WadRayMath.RAY >= optimalStableToTotalDebtRatio, - Errors.INVALID_OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO - ); - OPTIMAL_USAGE_RATIO = optimalUsageRatio; - MAX_EXCESS_USAGE_RATIO = WadRayMath.RAY - optimalUsageRatio; - OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO = optimalStableToTotalDebtRatio; - MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO = WadRayMath.RAY - optimalStableToTotalDebtRatio; - ADDRESSES_PROVIDER = provider; - _baseVariableBorrowRate = baseVariableBorrowRate; - _variableRateSlope1 = variableRateSlope1; - _variableRateSlope2 = variableRateSlope2; - _stableRateSlope1 = stableRateSlope1; - _stableRateSlope2 = stableRateSlope2; - _baseStableRateOffset = baseStableRateOffset; - _stableRateExcessOffset = stableRateExcessOffset; - } - - /// @inheritdoc IDefaultInterestRateStrategy - function getVariableRateSlope1() external view returns (uint256) { - return _variableRateSlope1; - } - - /// @inheritdoc IDefaultInterestRateStrategy - function getVariableRateSlope2() external view returns (uint256) { - return _variableRateSlope2; - } - - /// @inheritdoc IDefaultInterestRateStrategy - function getStableRateSlope1() external view returns (uint256) { - return _stableRateSlope1; - } - - /// @inheritdoc IDefaultInterestRateStrategy - function getStableRateSlope2() external view returns (uint256) { - return _stableRateSlope2; - } - - /// @inheritdoc IDefaultInterestRateStrategy - function getStableRateExcessOffset() external view returns (uint256) { - return _stableRateExcessOffset; - } - - /// @inheritdoc IDefaultInterestRateStrategy - function getBaseStableBorrowRate() public view returns (uint256) { - return _variableRateSlope1 + _baseStableRateOffset; - } - - /// @inheritdoc IDefaultInterestRateStrategy - function getBaseVariableBorrowRate() external view override returns (uint256) { - return _baseVariableBorrowRate; - } - - /// @inheritdoc IDefaultInterestRateStrategy - function getMaxVariableBorrowRate() external view override returns (uint256) { - return _baseVariableBorrowRate + _variableRateSlope1 + _variableRateSlope2; - } - - struct CalcInterestRatesLocalVars { - uint256 availableLiquidity; - uint256 totalDebt; - uint256 currentVariableBorrowRate; - uint256 currentStableBorrowRate; - uint256 currentLiquidityRate; - uint256 borrowUsageRatio; - uint256 supplyUsageRatio; - uint256 stableToTotalDebtRatio; - uint256 availableLiquidityPlusDebt; - } - - /// @inheritdoc IReserveInterestRateStrategy - function calculateInterestRates( - DataTypes.CalculateInterestRatesParams memory params - ) public view override returns (uint256, uint256, uint256) { - CalcInterestRatesLocalVars memory vars; - - vars.totalDebt = params.totalStableDebt + params.totalVariableDebt; - - vars.currentLiquidityRate = 0; - vars.currentVariableBorrowRate = _baseVariableBorrowRate; - vars.currentStableBorrowRate = getBaseStableBorrowRate(); - - if (vars.totalDebt != 0) { - vars.stableToTotalDebtRatio = params.totalStableDebt.rayDiv(vars.totalDebt); - vars.availableLiquidity = - IERC20(params.reserve).balanceOf(params.aToken) + - params.liquidityAdded - - params.liquidityTaken; - - vars.availableLiquidityPlusDebt = vars.availableLiquidity + vars.totalDebt; - vars.borrowUsageRatio = vars.totalDebt.rayDiv(vars.availableLiquidityPlusDebt); - vars.supplyUsageRatio = vars.totalDebt.rayDiv( - vars.availableLiquidityPlusDebt + params.unbacked - ); - } - - if (vars.borrowUsageRatio > OPTIMAL_USAGE_RATIO) { - uint256 excessBorrowUsageRatio = (vars.borrowUsageRatio - OPTIMAL_USAGE_RATIO).rayDiv( - MAX_EXCESS_USAGE_RATIO - ); - - vars.currentStableBorrowRate += - _stableRateSlope1 + - _stableRateSlope2.rayMul(excessBorrowUsageRatio); - - vars.currentVariableBorrowRate += - _variableRateSlope1 + - _variableRateSlope2.rayMul(excessBorrowUsageRatio); - } else { - vars.currentStableBorrowRate += _stableRateSlope1.rayMul(vars.borrowUsageRatio).rayDiv( - OPTIMAL_USAGE_RATIO - ); - - vars.currentVariableBorrowRate += _variableRateSlope1.rayMul(vars.borrowUsageRatio).rayDiv( - OPTIMAL_USAGE_RATIO - ); - } - - if (vars.stableToTotalDebtRatio > OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO) { - uint256 excessStableDebtRatio = (vars.stableToTotalDebtRatio - - OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO).rayDiv(MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO); - vars.currentStableBorrowRate += _stableRateExcessOffset.rayMul(excessStableDebtRatio); - } - - vars.currentLiquidityRate = _getOverallBorrowRate( - params.totalStableDebt, - params.totalVariableDebt, - vars.currentVariableBorrowRate, - params.averageStableBorrowRate - ).rayMul(vars.supplyUsageRatio).percentMul( - PercentageMath.PERCENTAGE_FACTOR - params.reserveFactor - ); - - return ( - vars.currentLiquidityRate, - vars.currentStableBorrowRate, - vars.currentVariableBorrowRate - ); - } - - /** - * @dev Calculates the overall borrow rate as the weighted average between the total variable debt and total stable - * debt - * @param totalStableDebt The total borrowed from the reserve at a stable rate - * @param totalVariableDebt The total borrowed from the reserve at a variable rate - * @param currentVariableBorrowRate The current variable borrow rate of the reserve - * @param currentAverageStableBorrowRate The current weighted average of all the stable rate loans - * @return The weighted averaged borrow rate - */ - function _getOverallBorrowRate( - uint256 totalStableDebt, - uint256 totalVariableDebt, - uint256 currentVariableBorrowRate, - uint256 currentAverageStableBorrowRate - ) internal pure returns (uint256) { - uint256 totalDebt = totalStableDebt + totalVariableDebt; - - if (totalDebt == 0) return 0; - - uint256 weightedVariableRate = totalVariableDebt.wadToRay().rayMul(currentVariableBorrowRate); - - uint256 weightedStableRate = totalStableDebt.wadToRay().rayMul(currentAverageStableBorrowRate); - - uint256 overallBorrowRate = (weightedVariableRate + weightedStableRate).rayDiv( - totalDebt.wadToRay() - ); - - return overallBorrowRate; - } -} diff --git a/src/core/contracts/protocol/pool/DefaultReserveInterestRateStrategyV2.sol b/src/core/contracts/protocol/pool/DefaultReserveInterestRateStrategyV2.sol new file mode 100644 index 00000000..5620eef0 --- /dev/null +++ b/src/core/contracts/protocol/pool/DefaultReserveInterestRateStrategyV2.sol @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {WadRayMath} from '../libraries/math/WadRayMath.sol'; +import {PercentageMath} from '../libraries/math/PercentageMath.sol'; +import {DataTypes} from '../libraries/types/DataTypes.sol'; +import {Errors} from '../libraries/helpers/Errors.sol'; +import {IDefaultInterestRateStrategyV2} from '../../interfaces/IDefaultInterestRateStrategyV2.sol'; +import {IReserveInterestRateStrategy} from '../../interfaces/IReserveInterestRateStrategy.sol'; +import {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol'; + +/** + * @title DefaultReserveInterestRateStrategyV2 contract + * @author BGD Labs + * @notice Default interest rate strategy used by the Aave protocol + * @dev Strategies are pool-specific: each contract CAN'T be used across different Aave pools + * due to the caching of the PoolAddressesProvider and the usage of underlying addresses as + * index of the _interestRateData + */ +contract DefaultReserveInterestRateStrategyV2 is IDefaultInterestRateStrategyV2 { + using WadRayMath for uint256; + using PercentageMath for uint256; + + /// @inheritdoc IDefaultInterestRateStrategyV2 + IPoolAddressesProvider public immutable ADDRESSES_PROVIDER; + + /// @inheritdoc IDefaultInterestRateStrategyV2 + uint256 public constant MAX_BORROW_RATE = 1000_00; + + /// @inheritdoc IDefaultInterestRateStrategyV2 + uint256 public constant MIN_OPTIMAL_POINT = 1_00; + + /// @inheritdoc IDefaultInterestRateStrategyV2 + uint256 public constant MAX_OPTIMAL_POINT = 99_00; + + /// @dev Underlying asset listed on the Aave pool => rate data + mapping(address reserve => InterestRateData) internal _interestRateData; + + /** + * @param provider The address of the PoolAddressesProvider of the associated Aave pool + */ + constructor(address provider) { + require(provider != address(0), Errors.INVALID_ADDRESSES_PROVIDER); + ADDRESSES_PROVIDER = IPoolAddressesProvider(provider); + } + + modifier onlyPoolConfigurator() { + require( + msg.sender == ADDRESSES_PROVIDER.getPoolConfigurator(), + Errors.CALLER_NOT_POOL_CONFIGURATOR + ); + _; + } + + /// @inheritdoc IReserveInterestRateStrategy + function setInterestRateParams( + address reserve, + bytes calldata rateData + ) external onlyPoolConfigurator { + _setInterestRateParams(reserve, abi.decode(rateData, (InterestRateData))); + } + + /// @inheritdoc IDefaultInterestRateStrategyV2 + function setInterestRateParams( + address reserve, + InterestRateData calldata rateData + ) external onlyPoolConfigurator { + _setInterestRateParams(reserve, rateData); + } + + /// @inheritdoc IDefaultInterestRateStrategyV2 + function getInterestRateData(address reserve) external view returns (InterestRateDataRay memory) { + return _rayifyRateData(_interestRateData[reserve]); + } + + /// @inheritdoc IDefaultInterestRateStrategyV2 + function getInterestRateDataBps(address reserve) external view returns (InterestRateData memory) { + return _interestRateData[reserve]; + } + + /// @inheritdoc IDefaultInterestRateStrategyV2 + function getOptimalUsageRatio(address reserve) external view returns (uint256) { + return _bpsToRay(uint256(_interestRateData[reserve].optimalUsageRatio)); + } + + /// @inheritdoc IDefaultInterestRateStrategyV2 + function getVariableRateSlope1(address reserve) external view returns (uint256) { + return _bpsToRay(uint256(_interestRateData[reserve].variableRateSlope1)); + } + + /// @inheritdoc IDefaultInterestRateStrategyV2 + function getVariableRateSlope2(address reserve) external view returns (uint256) { + return _bpsToRay(uint256(_interestRateData[reserve].variableRateSlope2)); + } + + /// @inheritdoc IDefaultInterestRateStrategyV2 + function getBaseVariableBorrowRate(address reserve) external view override returns (uint256) { + return _bpsToRay(uint256(_interestRateData[reserve].baseVariableBorrowRate)); + } + + /// @inheritdoc IDefaultInterestRateStrategyV2 + function getMaxVariableBorrowRate(address reserve) external view override returns (uint256) { + return + _bpsToRay( + uint256( + _interestRateData[reserve].baseVariableBorrowRate + + _interestRateData[reserve].variableRateSlope1 + + _interestRateData[reserve].variableRateSlope2 + ) + ); + } + + /// @inheritdoc IReserveInterestRateStrategy + function calculateInterestRates( + DataTypes.CalculateInterestRatesParams memory params + ) external view virtual override returns (uint256, uint256, uint256) { + InterestRateDataRay memory rateData = _rayifyRateData(_interestRateData[params.reserve]); + + // @note This is a short circuit to allow mintable assets, which by definition cannot be supplied + // and thus do not use virtual underlying balances. + if (!params.usingVirtualBalance) { + return (0, 0, rateData.baseVariableBorrowRate); + } + + CalcInterestRatesLocalVars memory vars; + + vars.totalDebt = params.totalStableDebt + params.totalVariableDebt; + + vars.currentLiquidityRate = 0; + vars.currentVariableBorrowRate = rateData.baseVariableBorrowRate; + + if (vars.totalDebt != 0) { + vars.availableLiquidity = + params.virtualUnderlyingBalance + + params.liquidityAdded - + params.liquidityTaken; + + vars.availableLiquidityPlusDebt = vars.availableLiquidity + vars.totalDebt; + vars.borrowUsageRatio = vars.totalDebt.rayDiv(vars.availableLiquidityPlusDebt); + vars.supplyUsageRatio = vars.totalDebt.rayDiv( + vars.availableLiquidityPlusDebt + params.unbacked + ); + } else { + return (0, 0, vars.currentVariableBorrowRate); + } + + if (vars.borrowUsageRatio > rateData.optimalUsageRatio) { + uint256 excessBorrowUsageRatio = (vars.borrowUsageRatio - rateData.optimalUsageRatio).rayDiv( + WadRayMath.RAY - rateData.optimalUsageRatio + ); + + vars.currentVariableBorrowRate += + rateData.variableRateSlope1 + + rateData.variableRateSlope2.rayMul(excessBorrowUsageRatio); + } else { + vars.currentVariableBorrowRate += rateData + .variableRateSlope1 + .rayMul(vars.borrowUsageRatio) + .rayDiv(rateData.optimalUsageRatio); + } + + vars.currentLiquidityRate = _getOverallBorrowRate( + params.totalStableDebt, + params.totalVariableDebt, + vars.currentVariableBorrowRate, + params.averageStableBorrowRate + ).rayMul(vars.supplyUsageRatio).percentMul( + PercentageMath.PERCENTAGE_FACTOR - params.reserveFactor + ); + + return (vars.currentLiquidityRate, 0, vars.currentVariableBorrowRate); + } + + /** + * @dev Calculates the overall borrow rate as the weighted average between the total variable debt and total stable + * debt + * @param totalStableDebt The total borrowed from the reserve at a stable rate + * @param totalVariableDebt The total borrowed from the reserve at a variable rate + * @param currentVariableBorrowRate The current variable borrow rate of the reserve + * @param currentAverageStableBorrowRate The current weighted average of all the stable rate loans + * @return The weighted averaged borrow rate + */ + function _getOverallBorrowRate( + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 currentVariableBorrowRate, + uint256 currentAverageStableBorrowRate + ) internal pure returns (uint256) { + uint256 totalDebt = totalStableDebt + totalVariableDebt; + + uint256 weightedVariableRate = totalVariableDebt.wadToRay().rayMul(currentVariableBorrowRate); + + uint256 weightedStableRate = totalStableDebt.wadToRay().rayMul(currentAverageStableBorrowRate); + + uint256 overallBorrowRate = (weightedVariableRate + weightedStableRate).rayDiv( + totalDebt.wadToRay() + ); + + return overallBorrowRate; + } + + /** + * @dev Doing validations and data update for an asset + * @param reserve address of the underlying asset of the reserve + * @param rateData Encoded eserve interest rate data to apply + */ + function _setInterestRateParams(address reserve, InterestRateData memory rateData) internal { + require(reserve != address(0), Errors.ZERO_ADDRESS_NOT_VALID); + + require( + rateData.optimalUsageRatio <= MAX_OPTIMAL_POINT && + rateData.optimalUsageRatio >= MIN_OPTIMAL_POINT, + Errors.INVALID_OPTIMAL_USAGE_RATIO + ); + + require( + rateData.variableRateSlope1 <= rateData.variableRateSlope2, + Errors.SLOPE_2_MUST_BE_GTE_SLOPE_1 + ); + + // The maximum rate should not be above certain threshold + require( + uint256(rateData.baseVariableBorrowRate) + + uint256(rateData.variableRateSlope1) + + uint256(rateData.variableRateSlope2) <= + MAX_BORROW_RATE, + Errors.INVALID_MAXRATE + ); + + _interestRateData[reserve] = rateData; + emit RateDataUpdate( + reserve, + rateData.optimalUsageRatio, + rateData.baseVariableBorrowRate, + rateData.variableRateSlope1, + rateData.variableRateSlope2 + ); + } + + /** + * @dev Transforms an InterestRateData struct to an InterestRateDataRay struct by multiplying all values + * by 1e23, turning them into ray values + * + * @param data The InterestRateData struct to transform + * + * @return The resulting InterestRateDataRay struct + */ + function _rayifyRateData( + InterestRateData memory data + ) internal pure returns (InterestRateDataRay memory) { + return + InterestRateDataRay({ + optimalUsageRatio: _bpsToRay(uint256(data.optimalUsageRatio)), + baseVariableBorrowRate: _bpsToRay(uint256(data.baseVariableBorrowRate)), + variableRateSlope1: _bpsToRay(uint256(data.variableRateSlope1)), + variableRateSlope2: _bpsToRay(uint256(data.variableRateSlope2)) + }); + } + + // @dev helper function added here, as generally the protocol doesn't use bps + function _bpsToRay(uint256 n) internal pure returns (uint256) { + return n * 1e23; + } +} diff --git a/src/core/contracts/protocol/pool/L2Pool.sol b/src/core/contracts/protocol/pool/L2Pool.sol index 8206601d..801c20e8 100644 --- a/src/core/contracts/protocol/pool/L2Pool.sol +++ b/src/core/contracts/protocol/pool/L2Pool.sol @@ -12,15 +12,7 @@ import {CalldataLogic} from '../libraries/logic/CalldataLogic.sol'; * @notice Calldata optimized extension of the Pool contract allowing users to pass compact calldata representation * to reduce transaction costs on rollups. */ -contract L2Pool is Pool, IL2Pool { - /** - * @dev Constructor. - * @param provider The address of the PoolAddressesProvider contract - */ - constructor(IPoolAddressesProvider provider) Pool(provider) { - // Intentionally left blank - } - +abstract contract L2Pool is Pool, IL2Pool { /// @inheritdoc IL2Pool function supply(bytes32 args) external override { (address asset, uint256 amount, uint16 referralCode) = CalldataLogic.decodeSupplyParams( diff --git a/src/core/contracts/protocol/pool/Pool.sol b/src/core/contracts/protocol/pool/Pool.sol index ed763b49..4397323d 100644 --- a/src/core/contracts/protocol/pool/Pool.sol +++ b/src/core/contracts/protocol/pool/Pool.sol @@ -36,10 +36,9 @@ import {PoolStorage} from './PoolStorage.sol'; * @dev All admin functions are callable by the PoolConfigurator contract defined also in the * PoolAddressesProvider */ -contract Pool is VersionedInitializable, PoolStorage, IPool { +abstract contract Pool is VersionedInitializable, PoolStorage, IPool { using ReserveLogic for DataTypes.ReserveData; - uint256 public constant POOL_REVISION = 0x3; IPoolAddressesProvider public immutable ADDRESSES_PROVIDER; /** @@ -87,10 +86,6 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { ); } - function getRevision() internal pure virtual override returns (uint256) { - return POOL_REVISION; - } - /** * @dev Constructor. * @param provider The address of the PoolAddressesProvider contract @@ -106,10 +101,7 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { * @dev Caching the address of the PoolAddressesProvider in order to reduce gas consumption on subsequent operations * @param provider The address of the PoolAddressesProvider */ - function initialize(IPoolAddressesProvider provider) external virtual initializer { - require(provider == ADDRESSES_PROVIDER, Errors.INVALID_ADDRESSES_PROVIDER); - _maxStableRateBorrowSizePercent = 0.25e4; - } + function initialize(IPoolAddressesProvider provider) external virtual; /// @inheritdoc IPool function mintUnbacked( @@ -170,15 +162,17 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { bytes32 permitR, bytes32 permitS ) public virtual override { - IERC20WithPermit(asset).permit( - msg.sender, - address(this), - amount, - deadline, - permitV, - permitR, - permitS - ); + try + IERC20WithPermit(asset).permit( + msg.sender, + address(this), + amount, + deadline, + permitV, + permitR, + permitS + ) + {} catch {} SupplyLogic.executeSupply( _reserves, _reservesList, @@ -278,7 +272,7 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { bytes32 permitR, bytes32 permitS ) public virtual override returns (uint256) { - { + try IERC20WithPermit(asset).permit( msg.sender, address(this), @@ -287,8 +281,9 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { permitV, permitR, permitS - ); - } + ) + {} catch {} + { DataTypes.ExecuteRepayParams memory params = DataTypes.ExecuteRepayParams({ asset: asset, @@ -328,10 +323,22 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { _reserves[asset], _usersConfig[msg.sender], asset, + msg.sender, DataTypes.InterestRateMode(interestRateMode) ); } + /// @inheritdoc IPool + function swapToVariable(address asset, address user) public virtual override { + BorrowLogic.executeSwapBorrowRateMode( + _reserves[asset], + _usersConfig[user], + asset, + user, + DataTypes.InterestRateMode.STABLE + ); + } + /// @inheritdoc IPool function rebalanceStableBorrowRate(address asset, address user) public virtual override { BorrowLogic.executeRebalanceStableBorrowRate(_reserves[asset], asset, user); @@ -447,12 +454,44 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { } /// @inheritdoc IPool - function getReserveData( + function getReserveDataExtended( address asset - ) external view virtual override returns (DataTypes.ReserveData memory) { + ) external view returns (DataTypes.ReserveData memory) { return _reserves[asset]; } + /// @inheritdoc IPool + function getReserveData( + address asset + ) external view virtual override returns (DataTypes.ReserveDataLegacy memory) { + DataTypes.ReserveData memory reserve = _reserves[asset]; + DataTypes.ReserveDataLegacy memory res; + + res.configuration = reserve.configuration; + res.liquidityIndex = reserve.liquidityIndex; + res.currentLiquidityRate = reserve.currentLiquidityRate; + res.variableBorrowIndex = reserve.variableBorrowIndex; + res.currentVariableBorrowRate = reserve.currentVariableBorrowRate; + res.currentStableBorrowRate = reserve.currentStableBorrowRate; + res.lastUpdateTimestamp = reserve.lastUpdateTimestamp; + res.id = reserve.id; + res.aTokenAddress = reserve.aTokenAddress; + res.stableDebtTokenAddress = reserve.stableDebtTokenAddress; + res.variableDebtTokenAddress = reserve.variableDebtTokenAddress; + res.interestRateStrategyAddress = reserve.interestRateStrategyAddress; + res.accruedToTreasury = reserve.accruedToTreasury; + res.unbacked = reserve.unbacked; + res.isolationModeTotalDebt = reserve.isolationModeTotalDebt; + return res; + } + + /// @inheritdoc IPool + function getVirtualUnderlyingBalance( + address asset + ) external view virtual override returns (uint128) { + return _reserves[asset].virtualUnderlyingBalance; + } + /// @inheritdoc IPool function getUserAccountData( address user @@ -637,9 +676,26 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { ) external virtual override onlyPoolConfigurator { require(asset != address(0), Errors.ZERO_ADDRESS_NOT_VALID); require(_reserves[asset].id != 0 || _reservesList[0] == asset, Errors.ASSET_NOT_LISTED); + _reserves[asset].interestRateStrategyAddress = rateStrategyAddress; } + /// @inheritdoc IPool + function syncIndexesState(address asset) external virtual override onlyPoolConfigurator { + DataTypes.ReserveData storage reserve = _reserves[asset]; + DataTypes.ReserveCache memory reserveCache = reserve.cache(); + + reserve.updateState(reserveCache); + } + + /// @inheritdoc IPool + function syncRatesState(address asset) external virtual override onlyPoolConfigurator { + DataTypes.ReserveData storage reserve = _reserves[asset]; + DataTypes.ReserveCache memory reserveCache = reserve.cache(); + + ReserveLogic.updateInterestRatesAndVirtualBalance(reserve, reserveCache, asset, 0, 0); + } + /// @inheritdoc IPool function setConfiguration( address asset, @@ -711,6 +767,20 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { PoolLogic.executeResetIsolationModeTotalDebt(_reserves, asset); } + /// @inheritdoc IPool + function getLiquidationGracePeriod(address asset) external virtual override returns (uint40) { + return _reserves[asset].liquidationGracePeriodUntil; + } + + /// @inheritdoc IPool + function setLiquidationGracePeriod( + address asset, + uint40 until + ) external virtual override onlyPoolConfigurator { + require(_reserves[asset].id != 0 || _reservesList[0] == asset, Errors.ASSET_NOT_LISTED); + PoolLogic.executeSetLiquidationGracePeriod(_reserves, asset, until); + } + /// @inheritdoc IPool function rescueTokens( address token, @@ -740,4 +810,39 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { }) ); } + + /// @inheritdoc IPool + function getFlashLoanLogic() external pure returns (address) { + return address(FlashLoanLogic); + } + + /// @inheritdoc IPool + function getBorrowLogic() external pure returns (address) { + return address(BorrowLogic); + } + + /// @inheritdoc IPool + function getBridgeLogic() external pure returns (address) { + return address(BridgeLogic); + } + + /// @inheritdoc IPool + function getEModeLogic() external pure returns (address) { + return address(EModeLogic); + } + + /// @inheritdoc IPool + function getLiquidationLogic() external pure returns (address) { + return address(LiquidationLogic); + } + + /// @inheritdoc IPool + function getPoolLogic() external pure returns (address) { + return address(PoolLogic); + } + + /// @inheritdoc IPool + function getSupplyLogic() external pure returns (address) { + return address(SupplyLogic); + } } diff --git a/src/core/contracts/protocol/pool/PoolConfigurator.sol b/src/core/contracts/protocol/pool/PoolConfigurator.sol index 0863300b..e023dc6d 100644 --- a/src/core/contracts/protocol/pool/PoolConfigurator.sol +++ b/src/core/contracts/protocol/pool/PoolConfigurator.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.10; import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; import {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol'; +import {IDefaultInterestRateStrategyV2} from '../../interfaces/IDefaultInterestRateStrategyV2.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; import {PercentageMath} from '../libraries/math/PercentageMath.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; @@ -14,19 +15,25 @@ import {IPool} from '../../interfaces/IPool.sol'; import {IACLManager} from '../../interfaces/IACLManager.sol'; import {IPoolDataProvider} from '../../interfaces/IPoolDataProvider.sol'; import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; /** * @title PoolConfigurator * @author Aave * @dev Implements the configuration methods for the Aave protocol */ -contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { +abstract contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { using PercentageMath for uint256; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; IPoolAddressesProvider internal _addressesProvider; IPool internal _pool; + mapping(address => uint256) internal _pendingLtv; + mapping(address => bool) internal _isPendingLtvSet; + + uint40 public constant MAX_GRACE_PERIOD = 4 hours; + /** * @dev Only pool admin can call functions marked by this modifier. */ @@ -35,14 +42,6 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { _; } - /** - * @dev Only emergency admin can call functions marked by this modifier. - */ - modifier onlyEmergencyAdmin() { - _onlyEmergencyAdmin(); - _; - } - /** * @dev Only emergency or pool admin can call functions marked by this modifier. */ @@ -67,17 +66,15 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { _; } - uint256 public constant CONFIGURATOR_REVISION = 0x1; - - /// @inheritdoc VersionedInitializable - function getRevision() internal pure virtual override returns (uint256) { - return CONFIGURATOR_REVISION; + /** + * @dev Only risk, pool or emergency admin can call functions marked by this modifier. + */ + modifier onlyRiskOrPoolOrEmergencyAdmins() { + _onlyRiskOrPoolOrEmergencyAdmins(); + _; } - function initialize(IPoolAddressesProvider provider) public initializer { - _addressesProvider = provider; - _pool = IPool(_addressesProvider.getPool()); - } + function initialize(IPoolAddressesProvider provider) public virtual; /// @inheritdoc IPoolConfigurator function initReserves( @@ -85,7 +82,14 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { ) external override onlyAssetListingOrPoolAdmins { IPool cachedPool = _pool; for (uint256 i = 0; i < input.length; i++) { + require(IERC20Detailed(input[i].underlyingAsset).decimals() > 5, Errors.INVALID_DECIMALS); + ConfiguratorLogic.executeInitReserve(cachedPool, input[i]); + emit ReserveInterestRateDataChanged( + input[i].underlyingAsset, + input[i].interestRateStrategyAddress, + input[i].interestRateData + ); } } @@ -160,7 +164,15 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { _checkNoSuppliers(asset); } - currentConfig.setLtv(ltv); + if (currentConfig.getFrozen()) { + _pendingLtv[asset] = ltv; + _isPendingLtvSet[asset] = true; + + emit PendingLtvChanged(asset, ltv); + } else { + currentConfig.setLtv(ltv); + } + currentConfig.setLiquidationThreshold(liquidationThreshold); currentConfig.setLiquidationBonus(liquidationBonus); @@ -205,9 +217,29 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { } /// @inheritdoc IPoolConfigurator - function setReserveFreeze(address asset, bool freeze) external override onlyRiskOrPoolAdmins { + function setReserveFreeze( + address asset, + bool freeze + ) external override onlyRiskOrPoolOrEmergencyAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setFrozen(freeze); + + if (freeze) { + _pendingLtv[asset] = currentConfig.getLtv(); + _isPendingLtvSet[asset] = true; + currentConfig.setLtv(0); + + emit PendingLtvChanged(asset, currentConfig.getLtv()); + } else if (_isPendingLtvSet[asset]) { + uint256 ltv = _pendingLtv[asset]; + currentConfig.setLtv(ltv); + + delete _pendingLtv[asset]; + delete _isPendingLtvSet[asset]; + + emit PendingLtvRemoved(asset); + } + _pool.setConfiguration(asset, currentConfig); emit ReserveFrozen(asset, freeze); } @@ -224,24 +256,46 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { } /// @inheritdoc IPoolConfigurator - function setReservePause(address asset, bool paused) public override onlyEmergencyOrPoolAdmin { + function setReservePause( + address asset, + bool paused, + uint40 gracePeriod + ) public override onlyEmergencyOrPoolAdmin { + if (!paused && gracePeriod != 0) { + require(gracePeriod <= MAX_GRACE_PERIOD, Errors.INVALID_GRACE_PERIOD); + + uint40 until = uint40(block.timestamp) + gracePeriod; + _pool.setLiquidationGracePeriod(asset, until); + emit LiquidationGracePeriodChanged(asset, until); + } + DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setPaused(paused); _pool.setConfiguration(asset, currentConfig); emit ReservePaused(asset, paused); } + /// @inheritdoc IPoolConfigurator + function setReservePause(address asset, bool paused) external override onlyEmergencyOrPoolAdmin { + setReservePause(asset, paused, 0); + } + /// @inheritdoc IPoolConfigurator function setReserveFactor( address asset, uint256 newReserveFactor ) external override onlyRiskOrPoolAdmins { require(newReserveFactor <= PercentageMath.PERCENTAGE_FACTOR, Errors.INVALID_RESERVE_FACTOR); + + _pool.syncIndexesState(asset); + DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); uint256 oldReserveFactor = currentConfig.getReserveFactor(); currentConfig.setReserveFactor(newReserveFactor); _pool.setConfiguration(asset, currentConfig); emit ReserveFactorChanged(asset, oldReserveFactor, newReserveFactor); + + _pool.syncRatesState(asset); } /// @inheritdoc IPoolConfigurator @@ -252,7 +306,7 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); uint256 oldDebtCeiling = currentConfig.getDebtCeiling(); - if (oldDebtCeiling == 0) { + if (currentConfig.getLiquidationThreshold() != 0 && oldDebtCeiling == 0) { _checkNoSuppliers(asset); } currentConfig.setDebtCeiling(newDebtCeiling); @@ -407,28 +461,41 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { emit UnbackedMintCapChanged(asset, oldUnbackedMintCap, newUnbackedMintCap); } + /// @inheritdoc IPoolConfigurator + function setReserveInterestRateData( + address asset, + bytes calldata rateData + ) external onlyRiskOrPoolAdmins { + DataTypes.ReserveDataLegacy memory reserve = _pool.getReserveData(asset); + _updateInterestRateStrategy(asset, reserve, reserve.interestRateStrategyAddress, rateData); + } + /// @inheritdoc IPoolConfigurator function setReserveInterestRateStrategyAddress( address asset, - address newRateStrategyAddress + address rateStrategyAddress, + bytes calldata rateData ) external override onlyRiskOrPoolAdmins { - DataTypes.ReserveData memory reserve = _pool.getReserveData(asset); - address oldRateStrategyAddress = reserve.interestRateStrategyAddress; - _pool.setReserveInterestRateStrategyAddress(asset, newRateStrategyAddress); - emit ReserveInterestRateStrategyChanged(asset, oldRateStrategyAddress, newRateStrategyAddress); + DataTypes.ReserveDataLegacy memory reserve = _pool.getReserveData(asset); + _updateInterestRateStrategy(asset, reserve, rateStrategyAddress, rateData); } /// @inheritdoc IPoolConfigurator - function setPoolPause(bool paused) external override onlyEmergencyAdmin { + function setPoolPause(bool paused, uint40 gracePeriod) public override onlyEmergencyOrPoolAdmin { address[] memory reserves = _pool.getReservesList(); for (uint256 i = 0; i < reserves.length; i++) { if (reserves[i] != address(0)) { - setReservePause(reserves[i], paused); + setReservePause(reserves[i], paused, gracePeriod); } } } + /// @inheritdoc IPoolConfigurator + function setPoolPause(bool paused) external override onlyEmergencyOrPoolAdmin { + setPoolPause(paused, 0); + } + /// @inheritdoc IPoolConfigurator function updateBridgeProtocolFee(uint256 newBridgeProtocolFee) external override onlyPoolAdmin { require( @@ -469,8 +536,43 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { ); } + /// @inheritdoc IPoolConfigurator + function getPendingLtv(address asset) external view override returns (uint256, bool) { + return (_pendingLtv[asset], _isPendingLtvSet[asset]); + } + + /// @inheritdoc IPoolConfigurator + function getConfiguratorLogic() external pure returns (address) { + return address(ConfiguratorLogic); + } + + function _updateInterestRateStrategy( + address asset, + DataTypes.ReserveDataLegacy memory reserve, + address newRateStrategyAddress, + bytes calldata rateData + ) internal { + address oldRateStrategyAddress = reserve.interestRateStrategyAddress; + + _pool.syncIndexesState(asset); + + IDefaultInterestRateStrategyV2(newRateStrategyAddress).setInterestRateParams(asset, rateData); + emit ReserveInterestRateDataChanged(asset, newRateStrategyAddress, rateData); + + if (oldRateStrategyAddress != newRateStrategyAddress) { + _pool.setReserveInterestRateStrategyAddress(asset, newRateStrategyAddress); + emit ReserveInterestRateStrategyChanged( + asset, + oldRateStrategyAddress, + newRateStrategyAddress + ); + } + + _pool.syncRatesState(asset); + } + function _checkNoSuppliers(address asset) internal view { - DataTypes.ReserveData memory reserveData = _pool.getReserveData(asset); + DataTypes.ReserveDataLegacy memory reserveData = _pool.getReserveData(asset); uint256 totalSupplied = IPoolDataProvider(_addressesProvider.getPoolDataProvider()) .getATokenTotalSupply(asset); @@ -492,11 +594,6 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { require(aclManager.isPoolAdmin(msg.sender), Errors.CALLER_NOT_POOL_ADMIN); } - function _onlyEmergencyAdmin() internal view { - IACLManager aclManager = IACLManager(_addressesProvider.getACLManager()); - require(aclManager.isEmergencyAdmin(msg.sender), Errors.CALLER_NOT_EMERGENCY_ADMIN); - } - function _onlyPoolOrEmergencyAdmin() internal view { IACLManager aclManager = IACLManager(_addressesProvider.getACLManager()); require( @@ -520,4 +617,14 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator { Errors.CALLER_NOT_RISK_OR_POOL_ADMIN ); } + + function _onlyRiskOrPoolOrEmergencyAdmins() internal view { + IACLManager aclManager = IACLManager(_addressesProvider.getACLManager()); + require( + aclManager.isRiskAdmin(msg.sender) || + aclManager.isPoolAdmin(msg.sender) || + aclManager.isEmergencyAdmin(msg.sender), + Errors.CALLER_NOT_RISK_OR_POOL_OR_EMERGENCY_ADMIN + ); + } } diff --git a/src/core/instances/L2PoolInstance.sol b/src/core/instances/L2PoolInstance.sol new file mode 100644 index 00000000..80bd406f --- /dev/null +++ b/src/core/instances/L2PoolInstance.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {L2Pool} from '../contracts/protocol/pool/L2Pool.sol'; +import {IPoolAddressesProvider} from '../contracts/interfaces/IPoolAddressesProvider.sol'; +import {PoolInstance} from './PoolInstance.sol'; + +contract L2PoolInstance is L2Pool, PoolInstance { + constructor(IPoolAddressesProvider provider) PoolInstance(provider) {} +} diff --git a/src/core/instances/PoolConfiguratorInstance.sol b/src/core/instances/PoolConfiguratorInstance.sol new file mode 100644 index 00000000..03ae6105 --- /dev/null +++ b/src/core/instances/PoolConfiguratorInstance.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {PoolConfigurator, IPoolAddressesProvider, IPool, VersionedInitializable} from 'aave-v3-core/contracts/protocol/pool/PoolConfigurator.sol'; + +contract PoolConfiguratorInstance is PoolConfigurator { + uint256 public constant CONFIGURATOR_REVISION = 3; + + /// @inheritdoc VersionedInitializable + function getRevision() internal pure virtual override returns (uint256) { + return CONFIGURATOR_REVISION; + } + + function initialize(IPoolAddressesProvider provider) public virtual override initializer { + _addressesProvider = provider; + _pool = IPool(_addressesProvider.getPool()); + } +} diff --git a/src/core/instances/PoolInstance.sol b/src/core/instances/PoolInstance.sol new file mode 100644 index 00000000..0af51bbe --- /dev/null +++ b/src/core/instances/PoolInstance.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Pool} from '../contracts/protocol/pool/Pool.sol'; +import {IPoolAddressesProvider} from '../contracts/interfaces/IPoolAddressesProvider.sol'; +import {Errors} from '../contracts/protocol/libraries/helpers/Errors.sol'; + +contract PoolInstance is Pool { + uint256 public constant POOL_REVISION = 4; + + constructor(IPoolAddressesProvider provider) Pool(provider) {} + + /** + * @notice Initializes the Pool. + * @dev Function is invoked by the proxy contract when the Pool contract is added to the + * PoolAddressesProvider of the market. + * @dev Caching the address of the PoolAddressesProvider in order to reduce gas consumption on subsequent operations + * @param provider The address of the PoolAddressesProvider + */ + function initialize(IPoolAddressesProvider provider) external virtual override initializer { + require(provider == ADDRESSES_PROVIDER, Errors.INVALID_ADDRESSES_PROVIDER); + _maxStableRateBorrowSizePercent = 0.25e4; + } + + function getRevision() internal pure virtual override returns (uint256) { + return POOL_REVISION; + } +}