diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index 0526a4f2..092a8d6a 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -350,7 +350,7 @@ interface ILendingPoolConfigurator { function unfreezeReserve(address asset) external; /** - * @dev Pauses/Unpauses a reserve. A paused reserve allow now user moves such as deposit, borrow, repay, swap interestrate, liquidate + * @dev Pauses a reserve. A paused reserve does not allow any interaction (deposit, borrow, repay, swap interestrate, liquidate, atoken transfers) * @param asset The address of the underlying asset of the reserve * @param val true = pausing, false = unpausing **/ diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 4be3b282..9a6295c8 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -269,7 +269,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); - ValidationLogic.validateSetUseReserveAsCollateral(reserveCache); + uint256 userBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender); + + ValidationLogic.validateSetUseReserveAsCollateral(reserveCache, userBalance); _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); @@ -278,6 +280,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } else { ValidationLogic.validateHFAndExposureCap( asset, + userBalance, msg.sender, _reserves, _usersConfig[msg.sender], @@ -614,6 +617,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (fromConfig.isBorrowingAny()) { ValidationLogic.validateHFAndExposureCap( asset, + amount, from, _reserves, _usersConfig[from], @@ -852,6 +856,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (userConfig.isBorrowingAny()) { ValidationLogic.validateHFAndExposureCap( asset, + 0, msg.sender, _reserves, userConfig, diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 4affa47a..4ecc8b2d 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -407,14 +407,14 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } /// @inheritdoc ILendingPoolConfigurator - function setReservePause(address asset, bool val) public override onlyEmergencyOrPoolAdmin { + function setReservePause(address asset, bool paused) public override onlyEmergencyOrPoolAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); - currentConfig.setPaused(val); + currentConfig.setPaused(paused); _pool.setConfiguration(asset, currentConfig.data); - if (val) { + if (paused) { emit ReservePaused(asset); } else { emit ReserveUnpaused(asset); @@ -484,11 +484,13 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } /// @inheritdoc ILendingPoolConfigurator - function setPoolPause(bool val) external override onlyEmergencyAdmin { + function setPoolPause(bool paused) external override onlyEmergencyAdmin { address[] memory reserves = _pool.getReservesList(); for (uint256 i = 0; i < reserves.length; i++) { - setReservePause(reserves[i], val); + if (reserves[i] != address(0)) { //might happen is a reserve was dropped + setReservePause(reserves[i], paused); + } } } diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 42fee42a..10b7a4f5 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -186,6 +186,7 @@ library ValidationLogic { vars.currentLtv, vars.currentLiquidationThreshold, vars.healthFactor, + ) = GenericLogic.calculateUserAccountData( userAddress, reservesData, @@ -387,17 +388,16 @@ library ValidationLogic { * @dev Validates the action of setting an asset as collateral * @param reserveCache The cached data of the reserve */ - function validateSetUseReserveAsCollateral(DataTypes.ReserveCache memory reserveCache) - external - view - { - uint256 underlyingBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender); + function validateSetUseReserveAsCollateral( + DataTypes.ReserveCache memory reserveCache, + uint256 userBalance + ) external pure { (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); - require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); + require(userBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); } /** @@ -507,8 +507,19 @@ library ValidationLogic { return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.LPCM_NO_ERRORS); } + struct validateHFAndExposureCapLocalVars { + uint256 healthFactor; + uint256 ltv; + uint256 uncappedLtv; + uint256 exposureCap; + uint256 reserveDecimals; + uint256 totalSupplyAtoken; + } + /** * @dev Validates the health factor of a user and the exposure cap for the asset being withdrawn + * @param asset The asset for which the exposure cap will be validated + * @param expCapOffset The offset to consider on the total atoken supply of asset when validating the exposure cap * @param from The user from which the aTokens are being transferred * @param reservesData The state of all the reserves * @param userConfig The state of the user for the specific reserve @@ -517,7 +528,8 @@ library ValidationLogic { * @param oracle The price oracle */ function validateHFAndExposureCap( - address collateral, + address asset, + uint256 expCapOffset, address from, mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap storage userConfig, @@ -525,30 +537,31 @@ library ValidationLogic { uint256 reservesCount, address oracle ) external view { - DataTypes.ReserveData memory reserve = reservesData[collateral]; - (, , uint256 ltv, uint256 liquidationThreshold, uint256 healthFactor, uint256 uncappedLtv) = - GenericLogic.calculateUserAccountData( - from, - reservesData, - userConfig, - reserves, - reservesCount, - oracle - ); - + validateHFAndExposureCapLocalVars memory vars; + DataTypes.ReserveData memory reserve = reservesData[asset]; + (, , vars.ltv, , vars.healthFactor, vars.uncappedLtv) = GenericLogic.calculateUserAccountData( + from, + reservesData, + userConfig, + reserves, + reservesCount, + oracle + ); require( - healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, + vars.healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD ); - uint256 exposureCap = reserve.configuration.getExposureCapMemory(); + vars.exposureCap = reserve.configuration.getExposureCapMemory(); - if (exposureCap != 0) { - if (ltv < uncappedLtv) { - uint256 totalSupplyAtoken = IERC20(reserve.aTokenAddress).totalSupply(); - (, , , uint256 reserveDecimals, ) = reserve.configuration.getParamsMemory(); - bool isAssetCapped = totalSupplyAtoken.div(10**reserveDecimals) >= exposureCap; + if (vars.exposureCap != 0) { + if (vars.ltv < vars.uncappedLtv) { + vars.totalSupplyAtoken = IERC20(reserve.aTokenAddress).totalSupply(); + (, , , vars.reserveDecimals, ) = reserve.configuration.getParamsMemory(); + bool isAssetCapped = + vars.totalSupplyAtoken.sub(expCapOffset).div(10**vars.reserveDecimals) >= + vars.exposureCap; require(isAssetCapped, Errors.VL_COLLATERAL_EXPOSURE_CAP_EXCEEDED); } } diff --git a/test-suites/test-aave/mint-to-treasury.spec.ts b/test-suites/test-aave/mint-to-treasury.spec.ts index 1ed83274..42cfc50d 100644 --- a/test-suites/test-aave/mint-to-treasury.spec.ts +++ b/test-suites/test-aave/mint-to-treasury.spec.ts @@ -61,8 +61,6 @@ makeSuite('Mint to treasury', (testEnv: TestEnv) => { const { accruedToTreasury } = await pool.getReserveData(dai.address); - console.log("Accrued to treasury ", accruedToTreasury.toString()); - expect(accruedToTreasury.toString()).to.be.bignumber.almostEqual( expectedAccruedToTreasury, 'Invalid amount accrued to the treasury'