diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index e99428cc..1c5f4208 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -26,7 +26,6 @@ import {ReserveConfiguration} from '../libraries/configuration/ReserveConfigurat import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; import {LendingPoolStorage} from './LendingPoolStorage.sol'; -import {CachingHelper} from '../libraries/helpers/CachingHelper.sol'; /** * @title LendingPool contract @@ -270,7 +269,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage **/ function swapBorrowRateMode(address asset, uint256 rateMode) external override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve); @@ -278,53 +277,54 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ValidationLogic.validateSwapRateMode( reserve, + reserveCache, _usersConfig[msg.sender], stableDebt, variableDebt, interestRateMode ); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); if (interestRateMode == DataTypes.InterestRateMode.STABLE) { - IStableDebtToken(cachedData.stableDebtTokenAddress).burn(msg.sender, stableDebt); + IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(msg.sender, stableDebt); - cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache .oldTotalStableDebt .sub(stableDebt); - IVariableDebtToken(cachedData.variableDebtTokenAddress).mint( + IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( msg.sender, msg.sender, stableDebt, - cachedData.newVariableBorrowIndex + reserveCache.newVariableBorrowIndex ); - cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.add( - stableDebt.rayDiv(cachedData.newVariableBorrowIndex) + reserveCache.newScaledVariableDebt = reserveCache.oldScaledVariableDebt.add( + stableDebt.rayDiv(reserveCache.newVariableBorrowIndex) ); } else { - IVariableDebtToken(cachedData.variableDebtTokenAddress).burn( + IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( msg.sender, variableDebt, - cachedData.newVariableBorrowIndex + reserveCache.newVariableBorrowIndex ); - cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.sub( - variableDebt.rayDiv(cachedData.newVariableBorrowIndex) + reserveCache.newScaledVariableDebt = reserveCache.oldScaledVariableDebt.sub( + variableDebt.rayDiv(reserveCache.newVariableBorrowIndex) ); - IStableDebtToken(cachedData.stableDebtTokenAddress).mint( + IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( msg.sender, msg.sender, variableDebt, reserve.currentStableBorrowRate ); - cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache .oldTotalStableDebt .add(stableDebt); } - reserve.updateInterestRates(cachedData, asset, 0, 0); + reserve.updateInterestRates(reserveCache, asset, 0, 0); emit Swap(asset, msg.sender, rateMode); } @@ -340,21 +340,22 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage **/ function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - IERC20 stableDebtToken = IERC20(cachedData.stableDebtTokenAddress); - IERC20 variableDebtToken = IERC20(cachedData.variableDebtTokenAddress); + IERC20 stableDebtToken = IERC20(reserveCache.stableDebtTokenAddress); + IERC20 variableDebtToken = IERC20(reserveCache.variableDebtTokenAddress); uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user); ValidationLogic.validateRebalanceStableBorrowRate( reserve, + reserveCache, asset, stableDebtToken, variableDebtToken, - cachedData.aTokenAddress + reserveCache.aTokenAddress ); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt); IStableDebtToken(address(stableDebtToken)).mint( @@ -364,7 +365,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.currentStableBorrowRate ); - reserve.updateInterestRates(cachedData, asset, 0, 0); + reserve.updateInterestRates(reserveCache, asset, 0, 0); emit RebalanceStableBorrowRate(asset, user); } @@ -380,8 +381,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - ValidationLogic.validateSetUseReserveAsCollateral(reserve); + ValidationLogic.validateSetUseReserveAsCollateral(reserve, reserveCache); _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); @@ -512,15 +514,15 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) { DataTypes.ReserveData storage reserve = _reserves[vars.currentAsset]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); reserve.cumulateToLiquidityIndex( IERC20(vars.currentATokenAddress).totalSupply(), vars.currentPremium ); reserve.updateInterestRates( - cachedData, + reserveCache, vars.currentAsset, vars.currentAmountPlusPremium, 0 @@ -894,12 +896,12 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function _executeBorrow(ExecuteBorrowParams memory vars) internal { DataTypes.ReserveData storage reserve = _reserves[vars.asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); ValidationLogic.validateBorrow( - cachedData, + reserveCache, vars.asset, vars.onBehalfOf, vars.amount, @@ -918,27 +920,27 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) { currentStableRate = reserve.currentStableBorrowRate; - isFirstBorrowing = IStableDebtToken(cachedData.stableDebtTokenAddress).mint( + isFirstBorrowing = IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( vars.user, vars.onBehalfOf, vars.amount, currentStableRate ); - cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache .oldTotalStableDebt .add(vars.amount); } else { - isFirstBorrowing = IVariableDebtToken(cachedData.variableDebtTokenAddress).mint( + isFirstBorrowing = IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( vars.user, vars.onBehalfOf, vars.amount, - cachedData.newVariableBorrowIndex + reserveCache.newVariableBorrowIndex ); - cachedData.newScaledVariableDebt = cachedData.newScaledVariableDebt.add( - vars.amount.rayDiv(cachedData.newVariableBorrowIndex) + reserveCache.newScaledVariableDebt = reserveCache.newScaledVariableDebt.add( + vars.amount.rayDiv(reserveCache.newVariableBorrowIndex) ); } @@ -947,14 +949,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } reserve.updateInterestRates( - cachedData, + reserveCache, vars.asset, 0, vars.releaseUnderlying ? vars.amount : 0 ); if (vars.releaseUnderlying) { - IAToken(cachedData.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); + IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); } emit Borrow( @@ -977,18 +979,18 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage uint16 referralCode ) internal { DataTypes.ReserveData storage reserve = _reserves[asset]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); - ValidationLogic.validateDeposit(reserve, cachedData, amount); + ValidationLogic.validateDeposit(reserve, reserveCache, amount); - reserve.updateInterestRates(cachedData, asset, amount, 0); + reserve.updateInterestRates(reserveCache, asset, amount, 0); - IERC20(asset).safeTransferFrom(msg.sender, cachedData.aTokenAddress, amount); + IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, amount); bool isFirstDeposit = - IAToken(cachedData.aTokenAddress).mint(onBehalfOf, amount, cachedData.newLiquidityIndex); + IAToken(reserveCache.aTokenAddress).mint(onBehalfOf, amount, reserveCache.newLiquidityIndex); if (isFirstDeposit) { _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); @@ -1005,13 +1007,13 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ) internal returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); uint256 userBalance = - IAToken(cachedData.aTokenAddress).scaledBalanceOf(msg.sender).rayMul( - cachedData.newLiquidityIndex + IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul( + reserveCache.newLiquidityIndex ); uint256 amountToWithdraw = amount; @@ -1020,15 +1022,15 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage amountToWithdraw = userBalance; } - ValidationLogic.validateWithdraw(reserve, amountToWithdraw, userBalance); + ValidationLogic.validateWithdraw(reserve, reserveCache, amountToWithdraw, userBalance); - reserve.updateInterestRates(cachedData, asset, 0, amountToWithdraw); + reserve.updateInterestRates(reserveCache, asset, 0, amountToWithdraw); - IAToken(cachedData.aTokenAddress).burn( + IAToken(reserveCache.aTokenAddress).burn( msg.sender, to, amountToWithdraw, - cachedData.newLiquidityIndex + reserveCache.newLiquidityIndex ); if (userConfig.isUsingAsCollateral(reserve.id)) { @@ -1061,7 +1063,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage address onBehalfOf ) internal returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve); @@ -1069,6 +1071,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ValidationLogic.validateRepay( reserve, + reserveCache, amount, interestRateMode, onBehalfOf, @@ -1083,33 +1086,33 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage paybackAmount = amount; } - reserve.updateState(cachedData); + reserve.updateState(reserveCache); if (interestRateMode == DataTypes.InterestRateMode.STABLE) { - IStableDebtToken(cachedData.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); - cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); + reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache .oldTotalStableDebt .sub(paybackAmount); } else { - IVariableDebtToken(cachedData.variableDebtTokenAddress).burn( + IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( onBehalfOf, paybackAmount, - cachedData.newVariableBorrowIndex + reserveCache.newVariableBorrowIndex ); - cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.sub( - paybackAmount.rayDiv(cachedData.newVariableBorrowIndex) + reserveCache.newScaledVariableDebt = reserveCache.oldScaledVariableDebt.sub( + paybackAmount.rayDiv(reserveCache.newVariableBorrowIndex) ); } - reserve.updateInterestRates(cachedData, asset, paybackAmount, 0); + reserve.updateInterestRates(reserveCache, asset, paybackAmount, 0); if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) { _usersConfig[onBehalfOf].setBorrowing(reserve.id, false); } - IERC20(asset).safeTransferFrom(msg.sender, cachedData.aTokenAddress, paybackAmount); + IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, paybackAmount); - IAToken(cachedData.aTokenAddress).handleRepayment(msg.sender, paybackAmount); + IAToken(reserveCache.aTokenAddress).handleRepayment(msg.sender, paybackAmount); emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); diff --git a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol index a92045c3..2f6b0bcb 100644 --- a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol +++ b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol @@ -18,7 +18,6 @@ import {Errors} from '../libraries/helpers/Errors.sol'; import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; import {LendingPoolStorage} from './LendingPoolStorage.sol'; -import {CachingHelper} from '../libraries/helpers/CachingHelper.sol'; /** * @title LendingPoolCollateralManager contract @@ -101,11 +100,16 @@ contract LendingPoolCollateralManager is _addressesProvider.getPriceOracle() ); + DataTypes.ReserveCache memory debtReserveCache = debtReserve.cache(); + DataTypes.ReserveCache memory collateralReserveCache = collateralReserve.cache(); + (vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve); (vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall( collateralReserve, debtReserve, + debtReserveCache, + collateralReserveCache, userConfig, vars.healthFactor, vars.userStableDebt, @@ -161,44 +165,42 @@ contract LendingPoolCollateralManager is } } - CachingHelper.CachedData memory debtReserveCachedData = CachingHelper.fetchData(debtReserve); - - debtReserve.updateState(debtReserveCachedData); + debtReserve.updateState(debtReserveCache); if (vars.userVariableDebt >= vars.actualDebtToLiquidate) { - IVariableDebtToken(debtReserveCachedData.variableDebtTokenAddress).burn( + IVariableDebtToken(debtReserveCache.variableDebtTokenAddress).burn( user, vars.actualDebtToLiquidate, - debtReserveCachedData.newVariableBorrowIndex + debtReserveCache.newVariableBorrowIndex ); - debtReserveCachedData.newScaledVariableDebt = debtReserveCachedData.oldScaledVariableDebt.sub( - vars.actualDebtToLiquidate.rayDiv(debtReserveCachedData.newVariableBorrowIndex) + debtReserveCache.newScaledVariableDebt = debtReserveCache.oldScaledVariableDebt.sub( + vars.actualDebtToLiquidate.rayDiv(debtReserveCache.newVariableBorrowIndex) ); } else { // If the user doesn't have variable debt, no need to try to burn variable debt tokens if (vars.userVariableDebt > 0) { - IVariableDebtToken(debtReserveCachedData.variableDebtTokenAddress).burn( + IVariableDebtToken(debtReserveCache.variableDebtTokenAddress).burn( user, vars.userVariableDebt, - debtReserveCachedData.newVariableBorrowIndex + debtReserveCache.newVariableBorrowIndex ); - debtReserveCachedData.newScaledVariableDebt = debtReserveCachedData + debtReserveCache.newScaledVariableDebt = debtReserveCache .oldScaledVariableDebt - .sub(vars.userVariableDebt.rayDiv(debtReserveCachedData.newVariableBorrowIndex)); + .sub(vars.userVariableDebt.rayDiv(debtReserveCache.newVariableBorrowIndex)); } - IStableDebtToken(debtReserveCachedData.stableDebtTokenAddress).burn( + IStableDebtToken(debtReserveCache.stableDebtTokenAddress).burn( user, vars.actualDebtToLiquidate.sub(vars.userVariableDebt) ); - debtReserveCachedData.newPrincipalStableDebt = debtReserveCachedData - .newTotalStableDebt = debtReserveCachedData.oldTotalStableDebt.sub( + debtReserveCache.newPrincipalStableDebt = debtReserveCache + .newTotalStableDebt = debtReserveCache.oldTotalStableDebt.sub( vars.actualDebtToLiquidate.sub(vars.userVariableDebt) ); } debtReserve.updateInterestRates( - debtReserveCachedData, + debtReserveCache, debtAsset, vars.actualDebtToLiquidate, 0 @@ -214,12 +216,9 @@ contract LendingPoolCollateralManager is emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender); } } else { - CachingHelper.CachedData memory collateralReserveCachedData = - CachingHelper.fetchData(collateralReserve); - - collateralReserve.updateState(collateralReserveCachedData); + collateralReserve.updateState(collateralReserveCache); collateralReserve.updateInterestRates( - collateralReserveCachedData, + collateralReserveCache, collateralAsset, 0, vars.maxCollateralToLiquidate diff --git a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index 389f5091..012e4cbd 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -104,6 +104,18 @@ library ReserveConfiguration { return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; } + /** + * @dev Gets the liquidation threshold of the reserve + * @param self The reserve configuration + * @return The liquidation threshold + **/ + function getLiquidationThresholdMemory(DataTypes.ReserveConfigurationMap memory self) + internal + view + returns (uint256) + { + return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; + } /** * @dev Sets the liquidation bonus of the reserve * @param self The reserve configuration diff --git a/contracts/protocol/libraries/helpers/CachingHelper.sol b/contracts/protocol/libraries/helpers/CachingHelper.sol deleted file mode 100644 index 06908ee9..00000000 --- a/contracts/protocol/libraries/helpers/CachingHelper.sol +++ /dev/null @@ -1,73 +0,0 @@ -pragma solidity 0.6.12; - -pragma experimental ABIEncoderV2; - -import {DataTypes} from '../types/DataTypes.sol'; -import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; -import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; - -library CachingHelper { - struct CachedData { - uint256 oldScaledVariableDebt; - uint256 oldTotalVariableDebt; - uint256 newScaledVariableDebt; - uint256 newTotalVariableDebt; - uint256 oldPrincipalStableDebt; - uint256 oldAvgStableBorrowRate; - uint256 oldTotalStableDebt; - uint256 newPrincipalStableDebt; - uint256 newAvgStableBorrowRate; - uint256 newTotalStableDebt; - uint256 oldLiquidityIndex; - uint256 newLiquidityIndex; - uint256 oldVariableBorrowIndex; - uint256 newVariableBorrowIndex; - uint256 oldLiquidityRate; - uint256 oldVariableBorrowRate; - DataTypes.ReserveConfigurationMap reserveConfiguration; - address aTokenAddress; - address stableDebtTokenAddress; - address variableDebtTokenAddress; - uint40 reserveLastUpdateTimestamp; - uint40 stableDebtLastUpdateTimestamp; - } - - function fetchData(DataTypes.ReserveData storage reserveData) - internal - view - returns (CachingHelper.CachedData memory) - { - CachedData memory cachedData; - - cachedData.reserveConfiguration = reserveData.configuration; - cachedData.oldLiquidityIndex = reserveData.liquidityIndex; - cachedData.oldVariableBorrowIndex = reserveData.variableBorrowIndex; - cachedData.oldLiquidityRate = reserveData.currentLiquidityRate; - cachedData.oldVariableBorrowRate = reserveData.currentVariableBorrowRate; - - cachedData.aTokenAddress = reserveData.aTokenAddress; - cachedData.stableDebtTokenAddress = reserveData.stableDebtTokenAddress; - cachedData.variableDebtTokenAddress = reserveData.variableDebtTokenAddress; - - cachedData.reserveLastUpdateTimestamp = reserveData.lastUpdateTimestamp; - - cachedData.oldScaledVariableDebt = cachedData.newScaledVariableDebt = IVariableDebtToken( - cachedData - .variableDebtTokenAddress - ) - .scaledTotalSupply(); - - ( - cachedData.oldPrincipalStableDebt, - cachedData.oldTotalStableDebt, - cachedData.oldAvgStableBorrowRate, - cachedData.stableDebtLastUpdateTimestamp - ) = IStableDebtToken(cachedData.stableDebtTokenAddress).getSupplyData(); - - cachedData.newPrincipalStableDebt = cachedData.oldPrincipalStableDebt; - cachedData.newTotalStableDebt = cachedData.oldTotalStableDebt; - cachedData.newAvgStableBorrowRate = cachedData.oldAvgStableBorrowRate; - - return cachedData; - } -} diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index ac5ff510..2db7fe34 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -14,7 +14,6 @@ import {WadRayMath} from '../math/WadRayMath.sol'; import {PercentageMath} from '../math/PercentageMath.sol'; import {Errors} from '../helpers/Errors.sol'; import {DataTypes} from '../types/DataTypes.sol'; -import {CachingHelper} from '../helpers/CachingHelper.sol'; /** * @title ReserveLogic library @@ -110,11 +109,10 @@ library ReserveLogic { **/ function updateState( DataTypes.ReserveData storage reserve, - CachingHelper.CachedData memory cachedData + DataTypes.ReserveCache memory reserveCache ) internal { - _updateIndexes(reserve, cachedData); - - _accrueToTreasury(reserve, cachedData); + _updateIndexes(reserve, reserveCache); + _accrueToTreasury(reserve, reserveCache); } /** @@ -181,20 +179,20 @@ library ReserveLogic { **/ function updateInterestRates( DataTypes.ReserveData storage reserve, - CachingHelper.CachedData memory cachedData, + DataTypes.ReserveCache memory reserveCache, address reserveAddress, uint256 liquidityAdded, uint256 liquidityTaken ) internal { UpdateInterestRatesLocalVars memory vars; - if (cachedData.oldTotalStableDebt != cachedData.newTotalStableDebt) { - cachedData.newAvgStableBorrowRate = IStableDebtToken(cachedData.stableDebtTokenAddress) + if (reserveCache.oldTotalStableDebt != reserveCache.newTotalStableDebt) { + reserveCache.newAvgStableBorrowRate = IStableDebtToken(reserveCache.stableDebtTokenAddress) .getAverageStableRate(); } - cachedData.newTotalVariableDebt = cachedData.newScaledVariableDebt.rayMul( - cachedData.newVariableBorrowIndex + reserveCache.newTotalVariableDebt = reserveCache.newScaledVariableDebt.rayMul( + reserveCache.newVariableBorrowIndex ); ( @@ -203,13 +201,13 @@ library ReserveLogic { vars.newVariableRate ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates( reserveAddress, - cachedData.aTokenAddress, + reserveCache.aTokenAddress, liquidityAdded, liquidityTaken, - cachedData.newTotalStableDebt, - cachedData.newTotalVariableDebt, - cachedData.newAvgStableBorrowRate, - cachedData.reserveConfiguration.getReserveFactorMemory() + reserveCache.newTotalStableDebt, + reserveCache.newTotalVariableDebt, + reserveCache.newAvgStableBorrowRate, + reserveCache.reserveConfiguration.getReserveFactorMemory() ); require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW); require(vars.newStableRate <= type(uint128).max, Errors.RL_STABLE_BORROW_RATE_OVERFLOW); @@ -224,8 +222,8 @@ library ReserveLogic { vars.newLiquidityRate, vars.newStableRate, vars.newVariableRate, - cachedData.newLiquidityIndex, - cachedData.newVariableBorrowIndex + reserveCache.newLiquidityIndex, + reserveCache.newVariableBorrowIndex ); } @@ -247,43 +245,45 @@ library ReserveLogic { * @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the * specific asset. * @param reserve The reserve reserve to be updated - * @param cachedData The caching layer for the reserve data + * @param reserveCache The caching layer for the reserve data **/ function _accrueToTreasury( DataTypes.ReserveData storage reserve, - CachingHelper.CachedData memory cachedData + DataTypes.ReserveCache memory reserveCache ) internal { MintToTreasuryLocalVars memory vars; - vars.reserveFactor = cachedData.reserveConfiguration.getReserveFactorMemory(); + vars.reserveFactor = reserveCache.reserveConfiguration.getReserveFactorMemory(); if (vars.reserveFactor == 0) { return; } //calculate the last principal variable debt - vars.previousVariableDebt = cachedData.oldScaledVariableDebt.rayMul( - cachedData.oldVariableBorrowIndex + vars.previousVariableDebt = reserveCache.oldScaledVariableDebt.rayMul( + reserveCache.oldVariableBorrowIndex ); //calculate the new total supply after accumulation of the index - vars.currentVariableDebt = cachedData.oldScaledVariableDebt.rayMul( - cachedData.newVariableBorrowIndex + vars.currentVariableDebt = reserveCache.oldScaledVariableDebt.rayMul( + reserveCache.newVariableBorrowIndex ); //calculate the stable debt until the last timestamp update vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest( - cachedData.oldAvgStableBorrowRate, - cachedData.stableDebtLastUpdateTimestamp, - cachedData.reserveLastUpdateTimestamp + reserveCache.oldAvgStableBorrowRate, + reserveCache.stableDebtLastUpdateTimestamp, + reserveCache.reserveLastUpdateTimestamp ); - vars.previousStableDebt = cachedData.oldPrincipalStableDebt.rayMul(vars.cumulatedStableInterest); + vars.previousStableDebt = reserveCache.oldPrincipalStableDebt.rayMul( + vars.cumulatedStableInterest + ); //debt accrued is the sum of the current debt minus the sum of the debt at the last update vars.totalDebtAccrued = vars .currentVariableDebt - .add(cachedData.oldTotalStableDebt) + .add(reserveCache.oldTotalStableDebt) .sub(vars.previousVariableDebt) .sub(vars.previousStableDebt); @@ -291,7 +291,7 @@ library ReserveLogic { if (vars.amountToMint != 0) { reserve.accruedToTreasury = reserve.accruedToTreasury.add( - vars.amountToMint.rayDiv(cachedData.newLiquidityIndex) + vars.amountToMint.rayDiv(reserveCache.newLiquidityIndex) ); } } @@ -299,52 +299,90 @@ library ReserveLogic { /** * @dev Updates the reserve indexes and the timestamp of the update * @param reserve The reserve reserve to be updated - * @param cachedData The cache layer holding the cached protocol data + * @param reserveCache The cache layer holding the cached protocol data **/ function _updateIndexes( DataTypes.ReserveData storage reserve, - CachingHelper.CachedData memory cachedData + DataTypes.ReserveCache memory reserveCache ) internal { - cachedData.newLiquidityIndex = cachedData.oldLiquidityIndex; - cachedData.newVariableBorrowIndex = cachedData.oldVariableBorrowIndex; + reserveCache.newLiquidityIndex = reserveCache.oldLiquidityIndex; + reserveCache.newVariableBorrowIndex = reserveCache.oldVariableBorrowIndex; //only cumulating if there is any income being produced - if (cachedData.oldLiquidityRate > 0) { + if (reserveCache.oldLiquidityRate > 0) { uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest( - cachedData.oldLiquidityRate, - cachedData.reserveLastUpdateTimestamp + reserveCache.oldLiquidityRate, + reserveCache.reserveLastUpdateTimestamp ); - cachedData.newLiquidityIndex = cumulatedLiquidityInterest.rayMul( - cachedData.oldLiquidityIndex + reserveCache.newLiquidityIndex = cumulatedLiquidityInterest.rayMul( + reserveCache.oldLiquidityIndex ); require( - cachedData.newLiquidityIndex <= type(uint128).max, + reserveCache.newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW ); - - reserve.liquidityIndex = uint128(cachedData.newLiquidityIndex); + reserve.liquidityIndex = uint128(reserveCache.newLiquidityIndex); //as the liquidity rate might come only from stable rate loans, we need to ensure //that there is actual variable debt before accumulating - if (cachedData.oldScaledVariableDebt != 0) { + if (reserveCache.oldScaledVariableDebt != 0) { uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest( - cachedData.oldVariableBorrowRate, - cachedData.reserveLastUpdateTimestamp + reserveCache.oldVariableBorrowRate, + reserveCache.reserveLastUpdateTimestamp ); - cachedData.newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul( - cachedData.oldVariableBorrowIndex + reserveCache.newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul( + reserveCache.oldVariableBorrowIndex ); require( - cachedData.newVariableBorrowIndex <= type(uint128).max, + reserveCache.newVariableBorrowIndex <= type(uint128).max, Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW ); - reserve.variableBorrowIndex = uint128(cachedData.newVariableBorrowIndex); + reserve.variableBorrowIndex = uint128(reserveCache.newVariableBorrowIndex); } } //solium-disable-next-line reserve.lastUpdateTimestamp = uint40(block.timestamp); } + + function cache(DataTypes.ReserveData storage reserve) + internal + view + returns (DataTypes.ReserveCache memory) + { + DataTypes.ReserveCache memory reserveCache; + + reserveCache.reserveConfiguration = reserve.configuration; + reserveCache.oldLiquidityIndex = reserve.liquidityIndex; + reserveCache.oldVariableBorrowIndex = reserve.variableBorrowIndex; + reserveCache.oldLiquidityRate = reserve.currentLiquidityRate; + reserveCache.oldVariableBorrowRate = reserve.currentVariableBorrowRate; + + reserveCache.aTokenAddress = reserve.aTokenAddress; + reserveCache.stableDebtTokenAddress = reserve.stableDebtTokenAddress; + reserveCache.variableDebtTokenAddress = reserve.variableDebtTokenAddress; + + reserveCache.reserveLastUpdateTimestamp = reserve.lastUpdateTimestamp; + + reserveCache.oldScaledVariableDebt = reserveCache.newScaledVariableDebt = IVariableDebtToken( + reserveCache + .variableDebtTokenAddress + ) + .scaledTotalSupply(); + + ( + reserveCache.oldPrincipalStableDebt, + reserveCache.oldTotalStableDebt, + reserveCache.oldAvgStableBorrowRate, + reserveCache.stableDebtLastUpdateTimestamp + ) = IStableDebtToken(reserveCache.stableDebtTokenAddress).getSupplyData(); + + reserveCache.newPrincipalStableDebt = reserveCache.oldPrincipalStableDebt; + reserveCache.newTotalStableDebt = reserveCache.oldTotalStableDebt; + reserveCache.newAvgStableBorrowRate = reserveCache.oldAvgStableBorrowRate; + + return reserveCache; + } } diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 94cc3248..43629f1f 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -12,7 +12,6 @@ import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20. import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; import {UserConfiguration} from '../configuration/UserConfiguration.sol'; import {Errors} from '../helpers/Errors.sol'; -import {CachingHelper} from '../helpers/CachingHelper.sol'; import {Helpers} from '../helpers/Helpers.sol'; import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol'; import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; @@ -45,13 +44,13 @@ library ValidationLogic { */ function validateDeposit( DataTypes.ReserveData storage reserve, - CachingHelper.CachedData memory cachedData, + DataTypes.ReserveCache memory reserveCache, uint256 amount ) internal view { (bool isActive, bool isFrozen, , , bool isPaused) = - cachedData.reserveConfiguration.getFlagsMemory(); - (, , , uint256 reserveDecimals, ) = cachedData.reserveConfiguration.getParamsMemory(); - uint256 supplyCap = cachedData.reserveConfiguration.getSupplyCapMemory(); + reserveCache.reserveConfiguration.getFlagsMemory(); + (, , , uint256 reserveDecimals, ) = reserveCache.reserveConfiguration.getParamsMemory(); + uint256 supplyCap = reserveCache.reserveConfiguration.getSupplyCapMemory(); require(amount != 0, Errors.VL_INVALID_AMOUNT); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); @@ -59,9 +58,9 @@ library ValidationLogic { require(!isFrozen, Errors.VL_RESERVE_FROZEN); require( supplyCap == 0 || - IAToken(cachedData.aTokenAddress) + IAToken(reserveCache.aTokenAddress) .scaledTotalSupply() - .rayMul(cachedData.newLiquidityIndex) + .rayMul(reserveCache.newLiquidityIndex) .add(amount) .div(10**reserveDecimals) < supplyCap, @@ -77,13 +76,14 @@ library ValidationLogic { */ function validateWithdraw( DataTypes.ReserveData storage reserve, + DataTypes.ReserveCache memory reserveCache, uint256 amount, uint256 userBalance ) external view { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); - (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); + (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); } @@ -122,7 +122,7 @@ library ValidationLogic { */ function validateBorrow( - CachingHelper.CachedData memory cachedData, + DataTypes.ReserveCache memory reserveCache, address asset, address userAddress, uint256 amount, @@ -136,7 +136,7 @@ library ValidationLogic { ) external view { ValidateBorrowLocalVars memory vars; - (, , , vars.reserveDecimals, ) = cachedData.reserveConfiguration.getParamsMemory(); + (, , , vars.reserveDecimals, ) = reserveCache.reserveConfiguration.getParamsMemory(); ( vars.isActive, @@ -144,7 +144,7 @@ library ValidationLogic { vars.borrowingEnabled, vars.stableRateBorrowingEnabled, vars.isPaused - ) = cachedData.reserveConfiguration.getFlagsMemory(); + ) = reserveCache.reserveConfiguration.getFlagsMemory(); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!vars.isPaused, Errors.VL_RESERVE_PAUSED); @@ -160,15 +160,15 @@ library ValidationLogic { Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED ); - vars.borrowCap = cachedData.reserveConfiguration.getBorrowCapMemory(); + vars.borrowCap = reserveCache.reserveConfiguration.getBorrowCapMemory(); if (vars.borrowCap > 0) { { - vars.totalSupplyVariableDebt = cachedData.oldScaledVariableDebt.rayMul( - cachedData.newVariableBorrowIndex + vars.totalSupplyVariableDebt = reserveCache.oldScaledVariableDebt.rayMul( + reserveCache.newVariableBorrowIndex ); - vars.totalDebt = cachedData.oldTotalStableDebt.add(vars.totalSupplyVariableDebt).add( + vars.totalDebt = reserveCache.oldTotalStableDebt.add(vars.totalSupplyVariableDebt).add( amount ); require( @@ -228,12 +228,12 @@ library ValidationLogic { require( !userConfig.isUsingAsCollateral(reservesData[asset].id) || - cachedData.reserveConfiguration.getLtvMemory() == 0 || - amount > IERC20(cachedData.aTokenAddress).balanceOf(userAddress), + reserveCache.reserveConfiguration.getLtvMemory() == 0 || + amount > IERC20(reserveCache.aTokenAddress).balanceOf(userAddress), Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY ); - vars.availableLiquidity = IERC20(asset).balanceOf(cachedData.aTokenAddress); + vars.availableLiquidity = IERC20(asset).balanceOf(reserveCache.aTokenAddress); //calculate the max available loan size in stable rate mode as a percentage of the //available liquidity @@ -253,13 +253,14 @@ library ValidationLogic { */ function validateRepay( DataTypes.ReserveData storage reserve, + DataTypes.ReserveCache memory reserveCache, uint256 amountSent, DataTypes.InterestRateMode rateMode, address onBehalfOf, uint256 stableDebt, uint256 variableDebt ) external view { - (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); + (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -289,13 +290,14 @@ library ValidationLogic { */ function validateSwapRateMode( DataTypes.ReserveData storage reserve, + DataTypes.ReserveCache memory reserveCache, DataTypes.UserConfigurationMap storage userConfig, uint256 stableDebt, uint256 variableDebt, DataTypes.InterestRateMode currentRateMode ) external view { (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = - reserve.configuration.getFlags(); + reserveCache.reserveConfiguration.getFlagsMemory(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -316,8 +318,8 @@ library ValidationLogic { require( !userConfig.isUsingAsCollateral(reserve.id) || - reserve.configuration.getLtv() == 0 || - stableDebt.add(variableDebt) > IERC20(reserve.aTokenAddress).balanceOf(msg.sender), + reserveCache.reserveConfiguration.getLtvMemory() == 0 || + stableDebt.add(variableDebt) > IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender), Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY ); } else { @@ -335,12 +337,13 @@ library ValidationLogic { */ function validateRebalanceStableBorrowRate( DataTypes.ReserveData storage reserve, + DataTypes.ReserveCache memory reserveCache, address reserveAddress, IERC20 stableDebtToken, IERC20 variableDebtToken, address aTokenAddress ) external view { - (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); + (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -354,7 +357,7 @@ library ValidationLogic { //if the liquidity rate is below REBALANCE_UP_THRESHOLD of the max variable APR at 95% usage, //then we allow rebalancing of the stable rate positions. - uint256 currentLiquidityRate = reserve.currentLiquidityRate; + uint256 currentLiquidityRate = reserveCache.oldLiquidityRate; uint256 maxVariableBorrowRate = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).getMaxVariableBorrowRate(); @@ -370,10 +373,14 @@ 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 */ - function validateSetUseReserveAsCollateral(DataTypes.ReserveData storage reserve) external view { - uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender); - bool isPaused = reserve.configuration.getPaused(); + function validateSetUseReserveAsCollateral( + DataTypes.ReserveData storage reserve, + DataTypes.ReserveCache memory reserveCache + ) external view { + uint256 underlyingBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender); + (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); @@ -407,20 +414,26 @@ library ValidationLogic { function validateLiquidationCall( DataTypes.ReserveData storage collateralReserve, DataTypes.ReserveData storage principalReserve, + DataTypes.ReserveCache memory collateralReserveCache, + DataTypes.ReserveCache memory principalReserveCache, DataTypes.UserConfigurationMap storage userConfig, uint256 userHealthFactor, uint256 userStableDebt, uint256 userVariableDebt ) internal view returns (uint256, string memory) { - if ( - !collateralReserve.configuration.getActive() || !principalReserve.configuration.getActive() - ) { + (bool collateralReserveActive, , , , bool collateralReservePaused) = + collateralReserveCache.reserveConfiguration.getFlagsMemory(); + + (bool principalReserveActive, , , , bool principalReservePaused) = + collateralReserveCache.reserveConfiguration.getFlagsMemory(); + + if (!collateralReserveActive || !principalReserveActive) { return ( uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE), Errors.VL_NO_ACTIVE_RESERVE ); } - if (collateralReserve.configuration.getPaused() || principalReserve.configuration.getPaused()) { + if (collateralReservePaused || principalReservePaused) { return (uint256(Errors.CollateralManagerErrors.PAUSED_RESERVE), Errors.VL_RESERVE_PAUSED); } @@ -432,7 +445,7 @@ library ValidationLogic { } bool isCollateralEnabled = - collateralReserve.configuration.getLiquidationThreshold() > 0 && + collateralReserveCache.reserveConfiguration.getLiquidationThresholdMemory() > 0 && userConfig.isUsingAsCollateral(collateralReserve.id); //if collateral isn't enabled as collateral by user, it cannot be liquidated diff --git a/contracts/protocol/libraries/types/DataTypes.sol b/contracts/protocol/libraries/types/DataTypes.sol index 5daa68b1..fd05fa89 100644 --- a/contracts/protocol/libraries/types/DataTypes.sol +++ b/contracts/protocol/libraries/types/DataTypes.sol @@ -51,4 +51,29 @@ library DataTypes { } enum InterestRateMode {NONE, STABLE, VARIABLE} + + struct ReserveCache { + uint256 oldScaledVariableDebt; + uint256 oldTotalVariableDebt; + uint256 newScaledVariableDebt; + uint256 newTotalVariableDebt; + uint256 oldPrincipalStableDebt; + uint256 oldAvgStableBorrowRate; + uint256 oldTotalStableDebt; + uint256 newPrincipalStableDebt; + uint256 newAvgStableBorrowRate; + uint256 newTotalStableDebt; + uint256 oldLiquidityIndex; + uint256 newLiquidityIndex; + uint256 oldVariableBorrowIndex; + uint256 newVariableBorrowIndex; + uint256 oldLiquidityRate; + uint256 oldVariableBorrowRate; + DataTypes.ReserveConfigurationMap reserveConfiguration; + address aTokenAddress; + address stableDebtTokenAddress; + address variableDebtTokenAddress; + uint40 reserveLastUpdateTimestamp; + uint40 stableDebtLastUpdateTimestamp; + } } diff --git a/package-lock.json b/package-lock.json index ad482fe2..7f3c76fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11142,14 +11142,6 @@ "requires": { "min-document": "^2.19.0", "process": "^0.11.10" - }, - "dependencies": { - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", - "dev": true - } } }, "got": { @@ -12794,6 +12786,12 @@ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",