From ed987ac46eb05ac439026cdb3f78cd6cf63ca693 Mon Sep 17 00:00:00 2001 From: The3D Date: Thu, 29 Apr 2021 19:55:52 +0200 Subject: [PATCH 1/7] refactor: removed balanceDecreaseAllowed --- .../protocol/lendingpool/LendingPool.sol | 44 ++++----- .../protocol/libraries/helpers/Errors.sol | 1 - .../protocol/libraries/logic/GenericLogic.sol | 91 +------------------ .../libraries/logic/ValidationLogic.sol | 80 +++------------- 4 files changed, 38 insertions(+), 178 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 8e38650c..3d2be8ce 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -156,16 +156,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage amountToWithdraw = userBalance; } - ValidationLogic.validateWithdraw( - asset, - amountToWithdraw, - userBalance, - _reserves, - _usersConfig[msg.sender], - _reservesList, - _reservesCount, - _addressesProvider.getPriceOracle() - ); + ValidationLogic.validateWithdraw(reserve, amountToWithdraw, userBalance); reserve.updateState(); @@ -178,6 +169,15 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage IAToken(aToken).burn(msg.sender, to, amountToWithdraw, reserve.liquidityIndex); + ValidationLogic.validateHealthFactor( + msg.sender, + _reserves, + _usersConfig[msg.sender], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + emit Withdraw(asset, msg.sender, to, amountToWithdraw); return amountToWithdraw; @@ -391,22 +391,22 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage { DataTypes.ReserveData storage reserve = _reserves[asset]; - ValidationLogic.validateSetUseReserveAsCollateral( - reserve, - asset, - useAsCollateral, - _reserves, - _usersConfig[msg.sender], - _reservesList, - _reservesCount, - _addressesProvider.getPriceOracle() - ); + ValidationLogic.validateSetUseReserveAsCollateral(reserve); _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); if (useAsCollateral) { emit ReserveUsedAsCollateralEnabled(asset, msg.sender); } else { + ValidationLogic.validateHealthFactor( + msg.sender, + _reserves, + _usersConfig[msg.sender], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + emit ReserveUsedAsCollateralDisabled(asset, msg.sender); } } @@ -713,7 +713,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } /** - * @dev Returns the fee on flash loans + * @dev Returns the fee on flash loans */ function FLASHLOAN_PREMIUM_TOTAL() public view returns (uint256) { return _flashLoanPremiumTotal; @@ -746,7 +746,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ) external override whenNotPaused { require(msg.sender == _reserves[asset].aTokenAddress, Errors.LP_CALLER_MUST_BE_AN_ATOKEN); - ValidationLogic.validateTransfer( + ValidationLogic.validateHealthFactor( from, _reserves, _usersConfig[from], diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 8756d797..9729379d 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -30,7 +30,6 @@ library Errors { string public constant VL_RESERVE_FROZEN = '3'; // 'Action cannot be performed because the reserve is frozen' string public constant VL_CURRENT_AVAILABLE_LIQUIDITY_NOT_ENOUGH = '4'; // 'The current liquidity is not enough' string public constant VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE = '5'; // 'User cannot withdraw more than the available balance' - string public constant VL_TRANSFER_NOT_ALLOWED = '6'; // 'Transfer cannot be allowed.' string public constant VL_BORROWING_NOT_ENABLED = '7'; // 'Borrowing is not enabled' string public constant VL_INVALID_INTEREST_RATE_MODE_SELECTED = '8'; // 'Invalid interest rate mode selected' string public constant VL_COLLATERAL_BALANCE_IS_0 = '9'; // 'The collateral balance is 0' diff --git a/contracts/protocol/libraries/logic/GenericLogic.sol b/contracts/protocol/libraries/logic/GenericLogic.sol index 5722dd2b..381fe804 100644 --- a/contracts/protocol/libraries/logic/GenericLogic.sol +++ b/contracts/protocol/libraries/logic/GenericLogic.sol @@ -28,94 +28,6 @@ library GenericLogic { uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether; - struct balanceDecreaseAllowedLocalVars { - uint256 decimals; - uint256 liquidationThreshold; - uint256 totalCollateralInETH; - uint256 totalDebtInETH; - uint256 avgLiquidationThreshold; - uint256 amountToDecreaseInETH; - uint256 collateralBalanceAfterDecrease; - uint256 liquidationThresholdAfterDecrease; - uint256 healthFactorAfterDecrease; - bool reserveUsageAsCollateralEnabled; - } - - /** - * @dev Checks if a specific balance decrease is allowed - * (i.e. doesn't bring the user borrow position health factor under HEALTH_FACTOR_LIQUIDATION_THRESHOLD) - * @param asset The address of the underlying asset of the reserve - * @param user The address of the user - * @param amount The amount to decrease - * @param reservesData The data of all the reserves - * @param userConfig The user configuration - * @param reserves The list of all the active reserves - * @param oracle The address of the oracle contract - * @return true if the decrease of the balance is allowed - **/ - function balanceDecreaseAllowed( - address asset, - address user, - uint256 amount, - mapping(address => DataTypes.ReserveData) storage reservesData, - DataTypes.UserConfigurationMap calldata userConfig, - mapping(uint256 => address) storage reserves, - uint256 reservesCount, - address oracle - ) external view returns (bool) { - if (!userConfig.isBorrowingAny() || !userConfig.isUsingAsCollateral(reservesData[asset].id)) { - return true; - } - - balanceDecreaseAllowedLocalVars memory vars; - - (, vars.liquidationThreshold, , vars.decimals, ) = reservesData[asset] - .configuration - .getParams(); - - if (vars.liquidationThreshold == 0) { - return true; - } - - ( - vars.totalCollateralInETH, - vars.totalDebtInETH, - , - vars.avgLiquidationThreshold, - - ) = calculateUserAccountData(user, reservesData, userConfig, reserves, reservesCount, oracle); - - if (vars.totalDebtInETH == 0) { - return true; - } - - vars.amountToDecreaseInETH = IPriceOracleGetter(oracle).getAssetPrice(asset).mul(amount).div( - 10**vars.decimals - ); - - vars.collateralBalanceAfterDecrease = vars.totalCollateralInETH.sub(vars.amountToDecreaseInETH); - - //if there is a borrow, there can't be 0 collateral - if (vars.collateralBalanceAfterDecrease == 0) { - return false; - } - - vars.liquidationThresholdAfterDecrease = vars - .totalCollateralInETH - .mul(vars.avgLiquidationThreshold) - .sub(vars.amountToDecreaseInETH.mul(vars.liquidationThreshold)) - .div(vars.collateralBalanceAfterDecrease); - - uint256 healthFactorAfterDecrease = - calculateHealthFactorFromBalances( - vars.collateralBalanceAfterDecrease, - vars.totalDebtInETH, - vars.liquidationThresholdAfterDecrease - ); - - return healthFactorAfterDecrease >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD; - } - struct CalculateUserAccountDataVars { uint256 assetPrice; uint256 assetUnit; @@ -220,8 +132,7 @@ library GenericLogic { IERC20(currentReserve.stableDebtTokenAddress).balanceOf(user) ); vars.userDebtETH = vars.assetPrice.mul(vars.userDebt).div(vars.assetUnit); - vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH); - + vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH); } } diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 080b792d..6c15a4e1 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -38,7 +38,7 @@ library ValidationLogic { * @param reserve The reserve object on which the user is depositing * @param amount The amount to be deposited */ - function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) external view { + function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) internal view { (bool isActive, bool isFrozen, , ) = reserve.configuration.getFlags(); require(amount != 0, Errors.VL_INVALID_AMOUNT); @@ -46,46 +46,23 @@ library ValidationLogic { require(!isFrozen, Errors.VL_RESERVE_FROZEN); } + /** * @dev Validates a withdraw action - * @param reserveAddress The address of the reserve + * @param reserve The reserve object * @param amount The amount to be withdrawn * @param userBalance The balance of the user - * @param reservesData The reserves state - * @param userConfig The user configuration - * @param reserves The addresses of the reserves - * @param reservesCount The number of reserves - * @param oracle The price oracle */ function validateWithdraw( - address reserveAddress, + DataTypes.ReserveData storage reserve, uint256 amount, - uint256 userBalance, - mapping(address => DataTypes.ReserveData) storage reservesData, - DataTypes.UserConfigurationMap storage userConfig, - mapping(uint256 => address) storage reserves, - uint256 reservesCount, - address oracle - ) external view { + uint256 userBalance + ) internal view { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); - (bool isActive, , , ) = reservesData[reserveAddress].configuration.getFlags(); + (bool isActive, , , ) = reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); - - require( - GenericLogic.balanceDecreaseAllowed( - reserveAddress, - msg.sender, - amount, - reservesData, - userConfig, - reserves, - reservesCount, - oracle - ), - Errors.VL_TRANSFER_NOT_ALLOWED - ); } struct ValidateBorrowLocalVars { @@ -130,7 +107,7 @@ library ValidationLogic { mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle - ) external view { + ) internal view { ValidateBorrowLocalVars memory vars; (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled) = reserve @@ -227,7 +204,7 @@ library ValidationLogic { address onBehalfOf, uint256 stableDebt, uint256 variableDebt - ) external view { + ) internal view { bool isActive = reserve.configuration.getActive(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); @@ -306,7 +283,7 @@ library ValidationLogic { IERC20 stableDebtToken, IERC20 variableDebtToken, address aTokenAddress - ) external view { + ) internal view { (bool isActive, , , ) = reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); @@ -335,40 +312,13 @@ library ValidationLogic { /** * @dev Validates the action of setting an asset as collateral * @param reserve The state of the reserve that the user is enabling or disabling as collateral - * @param reserveAddress The address of the reserve - * @param reservesData The data of all the reserves - * @param userConfig The state of the user for the specific reserve - * @param reserves The addresses of all the active reserves - * @param oracle The price oracle */ function validateSetUseReserveAsCollateral( - DataTypes.ReserveData storage reserve, - address reserveAddress, - bool useAsCollateral, - mapping(address => DataTypes.ReserveData) storage reservesData, - DataTypes.UserConfigurationMap storage userConfig, - mapping(uint256 => address) storage reserves, - uint256 reservesCount, - address oracle - ) external view { + DataTypes.ReserveData storage reserve + ) internal view { uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender); require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); - - require( - useAsCollateral || - GenericLogic.balanceDecreaseAllowed( - reserveAddress, - msg.sender, - underlyingBalance, - reservesData, - userConfig, - reserves, - reservesCount, - oracle - ), - Errors.VL_DEPOSIT_ALREADY_IN_USE - ); } /** @@ -436,14 +386,14 @@ library ValidationLogic { } /** - * @dev Validates an aToken transfer + * @dev Validates the health factor of a user * @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 * @param reserves The addresses of all the active reserves * @param oracle The price oracle */ - function validateTransfer( + function validateHealthFactor( address from, mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap storage userConfig, @@ -463,7 +413,7 @@ library ValidationLogic { require( healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, - Errors.VL_TRANSFER_NOT_ALLOWED + Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD ); } } From bce4c6b674bd4ca864165f12f6433a06b4035490 Mon Sep 17 00:00:00 2001 From: The3D Date: Thu, 29 Apr 2021 19:56:22 +0200 Subject: [PATCH 2/7] test: fixed tests to adapt the error messages --- helpers/types.ts | 1 - test-suites/test-aave/atoken-transfer.spec.ts | 6 +++--- test-suites/test-amm/atoken-transfer.spec.ts | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/helpers/types.ts b/helpers/types.ts index ea8826a0..8f6b19d2 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -111,7 +111,6 @@ export enum ProtocolErrors { VL_RESERVE_FROZEN = '3', // 'Action requires an unfrozen reserve' VL_CURRENT_AVAILABLE_LIQUIDITY_NOT_ENOUGH = '4', // 'The current liquidity is not enough' VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE = '5', // 'User cannot withdraw more than the available balance' - VL_TRANSFER_NOT_ALLOWED = '6', // 'Transfer cannot be allowed.' VL_BORROWING_NOT_ENABLED = '7', // 'Borrowing is not enabled' VL_INVALID_INTEREST_RATE_MODE_SELECTED = '8', // 'Invalid interest rate mode selected' VL_COLLATERAL_BALANCE_IS_0 = '9', // 'The collateral balance is 0' diff --git a/test-suites/test-aave/atoken-transfer.spec.ts b/test-suites/test-aave/atoken-transfer.spec.ts index c6451280..e5e13a36 100644 --- a/test-suites/test-aave/atoken-transfer.spec.ts +++ b/test-suites/test-aave/atoken-transfer.spec.ts @@ -12,7 +12,7 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => { const { INVALID_FROM_BALANCE_AFTER_TRANSFER, INVALID_TO_BALANCE_AFTER_TRANSFER, - VL_TRANSFER_NOT_ALLOWED, + VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD, } = ProtocolErrors; it('User 0 deposits 1000 DAI, transfers to user 1', async () => { @@ -81,8 +81,8 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => { await expect( aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer), - VL_TRANSFER_NOT_ALLOWED - ).to.be.revertedWith(VL_TRANSFER_NOT_ALLOWED); + VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD + ).to.be.revertedWith(VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD); }); it('User 1 tries to transfer a small amount of DAI used as collateral back to user 0', async () => { diff --git a/test-suites/test-amm/atoken-transfer.spec.ts b/test-suites/test-amm/atoken-transfer.spec.ts index 0290f941..f645e221 100644 --- a/test-suites/test-amm/atoken-transfer.spec.ts +++ b/test-suites/test-amm/atoken-transfer.spec.ts @@ -12,7 +12,7 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => { const { INVALID_FROM_BALANCE_AFTER_TRANSFER, INVALID_TO_BALANCE_AFTER_TRANSFER, - VL_TRANSFER_NOT_ALLOWED, + VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD, } = ProtocolErrors; it('User 0 deposits 1000 DAI, transfers to user 1', async () => { @@ -81,8 +81,8 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => { await expect( aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer), - VL_TRANSFER_NOT_ALLOWED - ).to.be.revertedWith(VL_TRANSFER_NOT_ALLOWED); + VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD + ).to.be.revertedWith(VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD); }); it('User 1 tries to transfer a small amount of DAI used as collateral back to user 0', async () => { From 2dac7fbd8026a609c2531b8e32cdf4b02d6b78e3 Mon Sep 17 00:00:00 2001 From: The3D Date: Mon, 10 May 2021 14:02:36 +0200 Subject: [PATCH 3/7] fix: fixed behavior in case the asset withdrawn was already being used as collateral --- .../protocol/lendingpool/LendingPool.sol | 30 +++++++++++-------- .../libraries/logic/ValidationLogic.sol | 4 +-- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index c204592b..681406fd 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -936,6 +936,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage address to ) internal returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; + DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; address aToken = reserve.aTokenAddress; @@ -953,21 +954,24 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw); - if (amountToWithdraw == userBalance) { - _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false); - emit ReserveUsedAsCollateralDisabled(asset, msg.sender); - } - IAToken(aToken).burn(msg.sender, to, amountToWithdraw, reserve.liquidityIndex); - ValidationLogic.validateHealthFactor( - msg.sender, - _reserves, - _usersConfig[msg.sender], - _reservesList, - _reservesCount, - _addressesProvider.getPriceOracle() - ); + if (userConfig.isUsingAsCollateral(reserve.id)) { + + ValidationLogic.validateHealthFactor( + msg.sender, + _reserves, + userConfig, + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + + if (amountToWithdraw == userBalance) { + userConfig.setUsingAsCollateral(reserve.id, false); + emit ReserveUsedAsCollateralDisabled(asset, msg.sender); + } + } emit Withdraw(asset, msg.sender, to, amountToWithdraw); diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 6c15a4e1..64a5f356 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -283,7 +283,7 @@ library ValidationLogic { IERC20 stableDebtToken, IERC20 variableDebtToken, address aTokenAddress - ) internal view { + ) external view { (bool isActive, , , ) = reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); @@ -315,7 +315,7 @@ library ValidationLogic { */ function validateSetUseReserveAsCollateral( DataTypes.ReserveData storage reserve - ) internal view { + ) external view { uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender); require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); From 2507a2220be4e51858aa5444d006abf62bd63b7c Mon Sep 17 00:00:00 2001 From: The3D Date: Thu, 13 May 2021 16:42:14 +0200 Subject: [PATCH 4/7] comments: fixed comment for validateHealthFactor() --- contracts/protocol/libraries/logic/ValidationLogic.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 64a5f356..32f2de4d 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -391,6 +391,7 @@ library ValidationLogic { * @param reservesData The state of all the reserves * @param userConfig The state of the user for the specific reserve * @param reserves The addresses of all the active reserves + * @param reservesCount The number of available reserves * @param oracle The price oracle */ function validateHealthFactor( From 235f2d80ec257bba203fb9b57cd3319add4f5c37 Mon Sep 17 00:00:00 2001 From: The3D Date: Thu, 13 May 2021 18:27:15 +0200 Subject: [PATCH 5/7] refactor: added validation on withdraw/finalizeTransfer - only check the HF if the user is using the asset as collateral or is borrowing --- .../protocol/lendingpool/LendingPool.sol | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 681406fd..5e0a32cf 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -720,20 +720,23 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ) external override whenNotPaused { require(msg.sender == _reserves[asset].aTokenAddress, Errors.LP_CALLER_MUST_BE_AN_ATOKEN); - ValidationLogic.validateHealthFactor( - from, - _reserves, - _usersConfig[from], - _reservesList, - _reservesCount, - _addressesProvider.getPriceOracle() - ); - uint256 reserveId = _reserves[asset].id; if (from != to) { + DataTypes.UserConfigurationMap storage fromConfig = _usersConfig[from]; + + if (fromConfig.isUsingAsCollateral(reserveId) && fromConfig.isBorrowingAny()) { + ValidationLogic.validateHealthFactor( + from, + _reserves, + _usersConfig[from], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + } + if (balanceFromBefore.sub(amount) == 0) { - DataTypes.UserConfigurationMap storage fromConfig = _usersConfig[from]; fromConfig.setUsingAsCollateral(reserveId, false); emit ReserveUsedAsCollateralDisabled(asset, from); } @@ -957,15 +960,16 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage IAToken(aToken).burn(msg.sender, to, amountToWithdraw, reserve.liquidityIndex); if (userConfig.isUsingAsCollateral(reserve.id)) { - - ValidationLogic.validateHealthFactor( - msg.sender, - _reserves, - userConfig, - _reservesList, - _reservesCount, - _addressesProvider.getPriceOracle() - ); + if (userConfig.isBorrowingAny()) { + ValidationLogic.validateHealthFactor( + msg.sender, + _reserves, + userConfig, + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + } if (amountToWithdraw == userBalance) { userConfig.setUsingAsCollateral(reserve.id, false); From 9d12200b8cb39086e03aef6f5cff4f160df263c9 Mon Sep 17 00:00:00 2001 From: The3D Date: Thu, 13 May 2021 18:44:47 +0200 Subject: [PATCH 6/7] refactor: replaced balanceOf() with scaledBalanceOf() --- contracts/protocol/lendingpool/LendingPool.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 5e0a32cf..11e727a4 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -943,7 +943,11 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage address aToken = reserve.aTokenAddress; - uint256 userBalance = IAToken(aToken).balanceOf(msg.sender); + reserve.updateState(); + + uint256 liquidityIndex = reserve.liquidityIndex; + + uint256 userBalance = IAToken(aToken).scaledBalanceOf(msg.sender).rayMul(liquidityIndex); uint256 amountToWithdraw = amount; @@ -953,11 +957,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ValidationLogic.validateWithdraw(reserve, amountToWithdraw, userBalance); - reserve.updateState(); - reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw); - IAToken(aToken).burn(msg.sender, to, amountToWithdraw, reserve.liquidityIndex); + IAToken(aToken).burn(msg.sender, to, amountToWithdraw, liquidityIndex); if (userConfig.isUsingAsCollateral(reserve.id)) { if (userConfig.isBorrowingAny()) { From 49db36726bb120473eb6f1016a363a1fd08569f6 Mon Sep 17 00:00:00 2001 From: The3D Date: Fri, 14 May 2021 20:00:37 +0200 Subject: [PATCH 7/7] fix: update behavior of finalizeTransfer() to proper emit the ReserveUsedAsCollateralDisabled event --- .../protocol/lendingpool/LendingPool.sol | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 11e727a4..f61826f0 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -725,20 +725,21 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (from != to) { DataTypes.UserConfigurationMap storage fromConfig = _usersConfig[from]; - if (fromConfig.isUsingAsCollateral(reserveId) && fromConfig.isBorrowingAny()) { - ValidationLogic.validateHealthFactor( - from, - _reserves, - _usersConfig[from], - _reservesList, - _reservesCount, - _addressesProvider.getPriceOracle() - ); - } - - if (balanceFromBefore.sub(amount) == 0) { - fromConfig.setUsingAsCollateral(reserveId, false); - emit ReserveUsedAsCollateralDisabled(asset, from); + if (fromConfig.isUsingAsCollateral(reserveId)) { + if (fromConfig.isBorrowingAny()) { + ValidationLogic.validateHealthFactor( + from, + _reserves, + _usersConfig[from], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + } + if (balanceFromBefore.sub(amount) == 0) { + fromConfig.setUsingAsCollateral(reserveId, false); + emit ReserveUsedAsCollateralDisabled(asset, from); + } } if (balanceToBefore == 0 && amount != 0) {