refactor: further refactored the cache layer and replaced reads/calls

This commit is contained in:
The3D 2021-06-03 21:28:21 +02:00
parent 6cdfd8e31b
commit 360e83fd05
6 changed files with 250 additions and 150 deletions

View File

@ -288,28 +288,44 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
reserve.updateState(cachedData); reserve.updateState(cachedData);
if (interestRateMode == DataTypes.InterestRateMode.STABLE) { if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt); IStableDebtToken(cachedData.stableDebtTokenAddress).burn(msg.sender, stableDebt);
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData
.oldTotalStableDebt
.sub(stableDebt);
IVariableDebtToken(cachedData.variableDebtTokenAddress).mint(
msg.sender, msg.sender,
msg.sender, msg.sender,
stableDebt, stableDebt,
reserve.variableBorrowIndex cachedData.newVariableBorrowIndex
);
cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.add(
stableDebt.rayDiv(cachedData.newVariableBorrowIndex)
); );
} else { } else {
IVariableDebtToken(reserve.variableDebtTokenAddress).burn( IVariableDebtToken(cachedData.variableDebtTokenAddress).burn(
msg.sender, msg.sender,
variableDebt, variableDebt,
reserve.variableBorrowIndex cachedData.newVariableBorrowIndex
); );
IStableDebtToken(reserve.stableDebtTokenAddress).mint( cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.sub(
variableDebt.rayDiv(cachedData.newVariableBorrowIndex)
);
IStableDebtToken(cachedData.stableDebtTokenAddress).mint(
msg.sender, msg.sender,
msg.sender, msg.sender,
variableDebt, variableDebt,
reserve.currentStableBorrowRate reserve.currentStableBorrowRate
); );
cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData
.oldTotalStableDebt
.add(stableDebt);
} }
reserve.updateInterestRates(asset, reserve.aTokenAddress, 0, 0); reserve.updateInterestRates(cachedData, asset, 0, 0);
emit Swap(asset, msg.sender, rateMode); emit Swap(asset, msg.sender, rateMode);
} }
@ -327,10 +343,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveData storage reserve = _reserves[asset];
CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve);
IERC20 stableDebtToken = IERC20(reserve.stableDebtTokenAddress); IERC20 stableDebtToken = IERC20(cachedData.stableDebtTokenAddress);
IERC20 variableDebtToken = IERC20(reserve.variableDebtTokenAddress); IERC20 variableDebtToken = IERC20(cachedData.variableDebtTokenAddress);
address aTokenAddress = reserve.aTokenAddress;
uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user); uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user);
ValidationLogic.validateRebalanceStableBorrowRate( ValidationLogic.validateRebalanceStableBorrowRate(
@ -338,7 +352,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
asset, asset,
stableDebtToken, stableDebtToken,
variableDebtToken, variableDebtToken,
aTokenAddress cachedData.aTokenAddress
); );
reserve.updateState(cachedData); reserve.updateState(cachedData);
@ -351,7 +365,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
reserve.currentStableBorrowRate reserve.currentStableBorrowRate
); );
reserve.updateInterestRates(asset, aTokenAddress, 0, 0); reserve.updateInterestRates(cachedData, asset, 0, 0);
emit RebalanceStableBorrowRate(asset, user); emit RebalanceStableBorrowRate(asset, user);
} }
@ -475,7 +489,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
ValidationLogic.validateFlashloan(assets, amounts, _reserves); ValidationLogic.validateFlashloan(assets, amounts, _reserves);
vars.receiver = IFlashLoanReceiver(receiverAddress); vars.receiver = IFlashLoanReceiver(receiverAddress);
for (vars.i = 0; vars.i < assets.length; vars.i++) { for (vars.i = 0; vars.i < assets.length; vars.i++) {
@ -508,8 +521,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
vars.currentPremium vars.currentPremium
); );
reserve.updateInterestRates( reserve.updateInterestRates(
cachedData,
vars.currentAsset, vars.currentAsset,
vars.currentATokenAddress,
vars.currentAmountPlusPremium, vars.currentAmountPlusPremium,
0 0
); );
@ -553,11 +566,11 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
function mintToTreasury(address[] calldata reserves) public { function mintToTreasury(address[] calldata reserves) public {
for (uint256 i = 0; i < reserves.length; i++) { for (uint256 i = 0; i < reserves.length; i++) {
address reserveAddress = reserves[i]; address reserveAddress = reserves[i];
DataTypes.ReserveData storage reserve = _reserves[reserveAddress]; DataTypes.ReserveData storage reserve = _reserves[reserveAddress];
// this cover both inactive reserves and invalid reserves since the flag will be 0 for both // this cover both inactive reserves and invalid reserves since the flag will be 0 for both
if(!reserve.configuration.getActive()){ if (!reserve.configuration.getActive()) {
continue; continue;
} }
@ -874,48 +887,49 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf];
CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve);
address oracle = _addressesProvider.getPriceOracle(); reserve.updateState(cachedData);
uint256 amountInETH =
IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div(
10**reserve.configuration.getDecimals()
);
ValidationLogic.validateBorrow( ValidationLogic.validateBorrow(
cachedData,
vars.asset, vars.asset,
reserve,
vars.onBehalfOf, vars.onBehalfOf,
vars.amount, vars.amount,
amountInETH,
vars.interestRateMode, vars.interestRateMode,
_maxStableRateBorrowSizePercent, _maxStableRateBorrowSizePercent,
_reserves, _reserves,
userConfig, userConfig,
_reservesList, _reservesList,
_reservesCount, _reservesCount,
oracle _addressesProvider.getPriceOracle()
); );
reserve.updateState(cachedData);
uint256 currentStableRate = 0; uint256 currentStableRate = 0;
bool isFirstBorrowing = false; bool isFirstBorrowing = false;
if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) { if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) {
currentStableRate = reserve.currentStableBorrowRate; currentStableRate = reserve.currentStableBorrowRate;
isFirstBorrowing = IStableDebtToken(reserve.stableDebtTokenAddress).mint( isFirstBorrowing = IStableDebtToken(cachedData.stableDebtTokenAddress).mint(
vars.user, vars.user,
vars.onBehalfOf, vars.onBehalfOf,
vars.amount, vars.amount,
currentStableRate currentStableRate
); );
cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData
.oldTotalStableDebt
.add(vars.amount);
} else { } else {
isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress).mint( isFirstBorrowing = IVariableDebtToken(cachedData.variableDebtTokenAddress).mint(
vars.user, vars.user,
vars.onBehalfOf, vars.onBehalfOf,
vars.amount, vars.amount,
reserve.variableBorrowIndex cachedData.newVariableBorrowIndex
);
cachedData.newScaledVariableDebt = cachedData.newScaledVariableDebt.add(
vars.amount.rayDiv(cachedData.newVariableBorrowIndex)
); );
} }
@ -924,14 +938,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
} }
reserve.updateInterestRates( reserve.updateInterestRates(
cachedData,
vars.asset, vars.asset,
vars.aTokenAddress,
0, 0,
vars.releaseUnderlying ? vars.amount : 0 vars.releaseUnderlying ? vars.amount : 0
); );
if (vars.releaseUnderlying) { if (vars.releaseUnderlying) {
IAToken(vars.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); IAToken(cachedData.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
} }
emit Borrow( emit Borrow(
@ -956,14 +970,16 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveData storage reserve = _reserves[asset];
CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve);
ValidationLogic.validateDeposit(reserve, amount);
reserve.updateState(cachedData); reserve.updateState(cachedData);
reserve.updateInterestRates(asset, cachedData.aTokenAddress, amount, 0);
ValidationLogic.validateDeposit(reserve, cachedData, amount);
reserve.updateInterestRates(cachedData, asset, amount, 0);
IERC20(asset).safeTransferFrom(msg.sender, cachedData.aTokenAddress, amount); IERC20(asset).safeTransferFrom(msg.sender, cachedData.aTokenAddress, amount);
bool isFirstDeposit = IAToken(cachedData.aTokenAddress).mint(onBehalfOf, amount, cachedData.newLiquidityIndex); bool isFirstDeposit =
IAToken(cachedData.aTokenAddress).mint(onBehalfOf, amount, cachedData.newLiquidityIndex);
if (isFirstDeposit) { if (isFirstDeposit) {
_usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true);
@ -982,11 +998,12 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender];
CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve);
address aToken = reserve.aTokenAddress;
reserve.updateState(cachedData); reserve.updateState(cachedData);
uint256 userBalance = IAToken(aToken).scaledBalanceOf(msg.sender).rayMul(cachedData.newLiquidityIndex); uint256 userBalance =
IAToken(cachedData.aTokenAddress).scaledBalanceOf(msg.sender).rayMul(
cachedData.newLiquidityIndex
);
uint256 amountToWithdraw = amount; uint256 amountToWithdraw = amount;
@ -996,9 +1013,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
ValidationLogic.validateWithdraw(reserve, amountToWithdraw, userBalance); ValidationLogic.validateWithdraw(reserve, amountToWithdraw, userBalance);
reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw); reserve.updateInterestRates(cachedData, asset, 0, amountToWithdraw);
IAToken(aToken).burn(msg.sender, to, amountToWithdraw, cachedData.newLiquidityIndex); IAToken(cachedData.aTokenAddress).burn(
msg.sender,
to,
amountToWithdraw,
cachedData.newLiquidityIndex
);
if (userConfig.isUsingAsCollateral(reserve.id)) { if (userConfig.isUsingAsCollateral(reserve.id)) {
if (userConfig.isBorrowingAny()) { if (userConfig.isBorrowingAny()) {
@ -1055,25 +1077,30 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
reserve.updateState(cachedData); reserve.updateState(cachedData);
if (interestRateMode == DataTypes.InterestRateMode.STABLE) { if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); IStableDebtToken(cachedData.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount);
cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData
.oldTotalStableDebt
.sub(paybackAmount);
} else { } else {
IVariableDebtToken(reserve.variableDebtTokenAddress).burn( IVariableDebtToken(cachedData.variableDebtTokenAddress).burn(
onBehalfOf, onBehalfOf,
paybackAmount, paybackAmount,
reserve.variableBorrowIndex cachedData.newVariableBorrowIndex
);
cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.sub(
paybackAmount.rayDiv(cachedData.newVariableBorrowIndex)
); );
} }
address aToken = reserve.aTokenAddress; reserve.updateInterestRates(cachedData, asset, paybackAmount, 0);
reserve.updateInterestRates(asset, aToken, paybackAmount, 0);
if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) { if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) {
_usersConfig[onBehalfOf].setBorrowing(reserve.id, false); _usersConfig[onBehalfOf].setBorrowing(reserve.id, false);
} }
IERC20(asset).safeTransferFrom(msg.sender, aToken, paybackAmount); IERC20(asset).safeTransferFrom(msg.sender, cachedData.aTokenAddress, paybackAmount);
IAToken(aToken).handleRepayment(msg.sender, paybackAmount); IAToken(cachedData.aTokenAddress).handleRepayment(msg.sender, paybackAmount);
emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); emit Repay(asset, onBehalfOf, msg.sender, paybackAmount);

View File

@ -169,26 +169,37 @@ contract LendingPoolCollateralManager is
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn( IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
user, user,
vars.actualDebtToLiquidate, vars.actualDebtToLiquidate,
debtReserve.variableBorrowIndex debtReserveCachedData.newVariableBorrowIndex
);
debtReserveCachedData.newScaledVariableDebt = debtReserveCachedData.oldScaledVariableDebt.sub(
vars.actualDebtToLiquidate.rayDiv(debtReserveCachedData.newVariableBorrowIndex)
); );
} else { } else {
// If the user doesn't have variable debt, no need to try to burn variable debt tokens // If the user doesn't have variable debt, no need to try to burn variable debt tokens
if (vars.userVariableDebt > 0) { if (vars.userVariableDebt > 0) {
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn( IVariableDebtToken(debtReserveCachedData.variableDebtTokenAddress).burn(
user, user,
vars.userVariableDebt, vars.userVariableDebt,
debtReserve.variableBorrowIndex debtReserveCachedData.newVariableBorrowIndex
); );
debtReserveCachedData.newScaledVariableDebt = debtReserveCachedData
.oldScaledVariableDebt
.sub(vars.userVariableDebt.rayDiv(debtReserveCachedData.newVariableBorrowIndex));
} }
IStableDebtToken(debtReserve.stableDebtTokenAddress).burn( IStableDebtToken(debtReserveCachedData.stableDebtTokenAddress).burn(
user, user,
vars.actualDebtToLiquidate.sub(vars.userVariableDebt) vars.actualDebtToLiquidate.sub(vars.userVariableDebt)
); );
debtReserveCachedData.newPrincipalStableDebt = debtReserveCachedData
.newTotalStableDebt = debtReserveCachedData.oldTotalStableDebt.sub(
vars.actualDebtToLiquidate.sub(vars.userVariableDebt)
);
} }
debtReserve.updateInterestRates( debtReserve.updateInterestRates(
debtReserveCachedData,
debtAsset, debtAsset,
debtReserve.aTokenAddress,
vars.actualDebtToLiquidate, vars.actualDebtToLiquidate,
0 0
); );
@ -203,12 +214,13 @@ contract LendingPoolCollateralManager is
emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender); emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender);
} }
} else { } else {
CachingHelper.CachedData memory collateralReserveCachedData = CachingHelper.fetchData(collateralReserve); CachingHelper.CachedData memory collateralReserveCachedData =
CachingHelper.fetchData(collateralReserve);
collateralReserve.updateState(collateralReserveCachedData); collateralReserve.updateState(collateralReserveCachedData);
collateralReserve.updateInterestRates( collateralReserve.updateInterestRates(
collateralReserveCachedData,
collateralAsset, collateralAsset,
address(vars.collateralAtoken),
0, 0,
vars.maxCollateralToLiquidate vars.maxCollateralToLiquidate
); );

View File

@ -65,6 +65,16 @@ library ReserveConfiguration {
return self.data & ~LTV_MASK; return self.data & ~LTV_MASK;
} }
/**
* @dev Gets the Loan to Value of the reserve
* @param self The reserve configuration
* @return The loan to value
**/
function getLtvMemory(DataTypes.ReserveConfigurationMap memory self) internal view returns (uint256) {
return self.data & ~LTV_MASK;
}
/** /**
* @dev Sets the liquidation threshold of the reserve * @dev Sets the liquidation threshold of the reserve
* @param self The reserve configuration * @param self The reserve configuration
@ -150,6 +160,20 @@ library ReserveConfiguration {
return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION; return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION;
} }
/**
* @dev Gets the decimals of the underlying asset of the reserve
* @param self The reserve configuration
* @return The decimals of the asset
**/
function getDecimalsMemory(DataTypes.ReserveConfigurationMap memory self)
internal
view
returns (uint256)
{
return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION;
}
/** /**
* @dev Sets the active state of the reserve * @dev Sets the active state of the reserve
* @param self The reserve configuration * @param self The reserve configuration
@ -293,6 +317,19 @@ library ReserveConfiguration {
return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION;
} }
/**
* @dev Gets the reserve factor of the reserve
* @param self The reserve configuration
* @return The reserve factor
**/
function getReserveFactorMemory(DataTypes.ReserveConfigurationMap memory self)
internal
view
returns (uint256)
{
return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION;
}
/** /**
* @dev Sets the borrow cap of the reserve * @dev Sets the borrow cap of the reserve
* @param self The reserve configuration * @param self The reserve configuration

View File

@ -4,13 +4,14 @@ pragma experimental ABIEncoderV2;
import {DataTypes} from '../types/DataTypes.sol'; import {DataTypes} from '../types/DataTypes.sol';
import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol';
library CachingHelper { library CachingHelper {
struct CachedData { struct CachedData {
uint256 oldScaledVariableDebt; uint256 oldScaledVariableDebt;
uint256 oldTotalVariableDebt; uint256 oldTotalVariableDebt;
uint256 newSscaledVariableDebt; uint256 newScaledVariableDebt;
uint256 newTtotalVariableDebt; uint256 newTotalVariableDebt;
uint256 oldPrincipalStableDebt; uint256 oldPrincipalStableDebt;
uint256 oldAvgStableBorrowRate; uint256 oldAvgStableBorrowRate;
uint256 oldTotalStableDebt; uint256 oldTotalStableDebt;
@ -28,6 +29,7 @@ library CachingHelper {
address stableDebtTokenAddress; address stableDebtTokenAddress;
address variableDebtTokenAddress; address variableDebtTokenAddress;
uint40 reserveLastUpdateTimestamp; uint40 reserveLastUpdateTimestamp;
uint40 stableDebtLastUpdateTimestamp;
} }
function fetchData(DataTypes.ReserveData storage reserveData) function fetchData(DataTypes.ReserveData storage reserveData)
@ -51,9 +53,23 @@ library CachingHelper {
cachedData.reserveLastUpdateTimestamp = reserveData.lastUpdateTimestamp; cachedData.reserveLastUpdateTimestamp = reserveData.lastUpdateTimestamp;
cachedData.oldScaledVariableDebt = IVariableDebtToken(cachedData.variableDebtTokenAddress) cachedData.oldScaledVariableDebt = cachedData.newScaledVariableDebt = IVariableDebtToken(
cachedData
.variableDebtTokenAddress
)
.scaledTotalSupply(); .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; return cachedData;
} }
} }

View File

@ -108,23 +108,13 @@ library ReserveLogic {
* @dev Updates the liquidity cumulative index and the variable borrow index. * @dev Updates the liquidity cumulative index and the variable borrow index.
* @param reserve the reserve object * @param reserve the reserve object
**/ **/
function updateState(DataTypes.ReserveData storage reserve, CachingHelper.CachedData memory cachedData) internal { function updateState(
DataTypes.ReserveData storage reserve,
CachingHelper.CachedData memory cachedData
) internal {
_updateIndexes(reserve, cachedData);
_updateIndexes( _accrueToTreasury(reserve, cachedData);
reserve,
cachedData
);
_accrueToTreasury(
reserve,
cachedData.oldScaledVariableDebt,
cachedData.oldVariableBorrowIndex,
cachedData.newLiquidityIndex,
cachedData.newVariableBorrowIndex,
cachedData.reserveLastUpdateTimestamp
);
} }
/** /**
@ -191,22 +181,21 @@ library ReserveLogic {
**/ **/
function updateInterestRates( function updateInterestRates(
DataTypes.ReserveData storage reserve, DataTypes.ReserveData storage reserve,
CachingHelper.CachedData memory cachedData,
address reserveAddress, address reserveAddress,
address aTokenAddress,
uint256 liquidityAdded, uint256 liquidityAdded,
uint256 liquidityTaken uint256 liquidityTaken
) internal { ) internal {
UpdateInterestRatesLocalVars memory vars; UpdateInterestRatesLocalVars memory vars;
vars.stableDebtTokenAddress = reserve.stableDebtTokenAddress; if (cachedData.oldTotalStableDebt != cachedData.newTotalStableDebt) {
cachedData.newAvgStableBorrowRate = IStableDebtToken(cachedData.stableDebtTokenAddress)
.getAverageStableRate();
}
(vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress) cachedData.newTotalVariableDebt = cachedData.newScaledVariableDebt.rayMul(
.getTotalSupplyAndAvgRate(); cachedData.newVariableBorrowIndex
);
vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress)
.scaledTotalSupply()
.rayMul(reserve.variableBorrowIndex);
( (
vars.newLiquidityRate, vars.newLiquidityRate,
@ -214,13 +203,13 @@ library ReserveLogic {
vars.newVariableRate vars.newVariableRate
) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates( ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates(
reserveAddress, reserveAddress,
aTokenAddress, cachedData.aTokenAddress,
liquidityAdded, liquidityAdded,
liquidityTaken, liquidityTaken,
vars.totalStableDebt, cachedData.newTotalStableDebt,
vars.totalVariableDebt, cachedData.newTotalVariableDebt,
vars.avgStableRate, cachedData.newAvgStableBorrowRate,
reserve.configuration.getReserveFactor() cachedData.reserveConfiguration.getReserveFactorMemory()
); );
require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW); require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW);
require(vars.newStableRate <= type(uint128).max, Errors.RL_STABLE_BORROW_RATE_OVERFLOW); require(vars.newStableRate <= type(uint128).max, Errors.RL_STABLE_BORROW_RATE_OVERFLOW);
@ -235,8 +224,8 @@ library ReserveLogic {
vars.newLiquidityRate, vars.newLiquidityRate,
vars.newStableRate, vars.newStableRate,
vars.newVariableRate, vars.newVariableRate,
reserve.liquidityIndex, cachedData.newLiquidityIndex,
reserve.variableBorrowIndex cachedData.newVariableBorrowIndex
); );
} }
@ -258,46 +247,35 @@ library ReserveLogic {
* @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the * @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the
* specific asset. * specific asset.
* @param reserve The reserve reserve to be updated * @param reserve The reserve reserve to be updated
* @param scaledVariableDebt The current scaled total variable debt * @param cachedData The caching layer for the reserve data
* @param previousVariableBorrowIndex The variable borrow index before the last accumulation of the interest
* @param newLiquidityIndex The new liquidity index
* @param newVariableBorrowIndex The variable borrow index after the last accumulation of the interest
**/ **/
function _accrueToTreasury( function _accrueToTreasury(
DataTypes.ReserveData storage reserve, DataTypes.ReserveData storage reserve,
uint256 scaledVariableDebt, CachingHelper.CachedData memory cachedData
uint256 previousVariableBorrowIndex,
uint256 newLiquidityIndex,
uint256 newVariableBorrowIndex,
uint40 timestamp
) internal { ) internal {
MintToTreasuryLocalVars memory vars; MintToTreasuryLocalVars memory vars;
vars.reserveFactor = reserve.configuration.getReserveFactor(); vars.reserveFactor = cachedData.reserveConfiguration.getReserveFactorMemory();
if (vars.reserveFactor == 0) { if (vars.reserveFactor == 0) {
return; return;
} }
//fetching the principal, total stable debt and the avg stable rate
(
vars.principalStableDebt,
vars.currentStableDebt,
vars.avgStableRate,
vars.stableSupplyUpdatedTimestamp
) = IStableDebtToken(reserve.stableDebtTokenAddress).getSupplyData();
//calculate the last principal variable debt //calculate the last principal variable debt
vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex); vars.previousVariableDebt = cachedData.oldScaledVariableDebt.rayMul(
cachedData.oldVariableBorrowIndex
);
//calculate the new total supply after accumulation of the index //calculate the new total supply after accumulation of the index
vars.currentVariableDebt = scaledVariableDebt.rayMul(newVariableBorrowIndex); vars.currentVariableDebt = cachedData.oldScaledVariableDebt.rayMul(
cachedData.newVariableBorrowIndex
);
//calculate the stable debt until the last timestamp update //calculate the stable debt until the last timestamp update
vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest( vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest(
vars.avgStableRate, vars.avgStableRate,
vars.stableSupplyUpdatedTimestamp, vars.stableSupplyUpdatedTimestamp,
timestamp cachedData.reserveLastUpdateTimestamp
); );
vars.previousStableDebt = vars.principalStableDebt.rayMul(vars.cumulatedStableInterest); vars.previousStableDebt = vars.principalStableDebt.rayMul(vars.cumulatedStableInterest);
@ -312,7 +290,9 @@ library ReserveLogic {
vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor); vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor);
if (vars.amountToMint != 0) { if (vars.amountToMint != 0) {
reserve.accruedToTreasury = reserve.accruedToTreasury.add(vars.amountToMint.rayDiv(newLiquidityIndex)); reserve.accruedToTreasury = reserve.accruedToTreasury.add(
vars.amountToMint.rayDiv(cachedData.newLiquidityIndex)
);
} }
} }
@ -325,16 +305,23 @@ library ReserveLogic {
DataTypes.ReserveData storage reserve, DataTypes.ReserveData storage reserve,
CachingHelper.CachedData memory cachedData CachingHelper.CachedData memory cachedData
) internal { ) internal {
cachedData.newLiquidityIndex = cachedData.oldLiquidityIndex; cachedData.newLiquidityIndex = cachedData.oldLiquidityIndex;
cachedData.newVariableBorrowIndex = cachedData.oldVariableBorrowIndex; cachedData.newVariableBorrowIndex = cachedData.oldVariableBorrowIndex;
//only cumulating if there is any income being produced //only cumulating if there is any income being produced
if (cachedData.oldLiquidityRate > 0) { if (cachedData.oldLiquidityRate > 0) {
uint256 cumulatedLiquidityInterest = uint256 cumulatedLiquidityInterest =
MathUtils.calculateLinearInterest(cachedData.oldLiquidityRate, cachedData.reserveLastUpdateTimestamp); MathUtils.calculateLinearInterest(
cachedData.newLiquidityIndex = cumulatedLiquidityInterest.rayMul(cachedData.oldLiquidityIndex); cachedData.oldLiquidityRate,
require( cachedData.newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); cachedData.reserveLastUpdateTimestamp
);
cachedData.newLiquidityIndex = cumulatedLiquidityInterest.rayMul(
cachedData.oldLiquidityIndex
);
require(
cachedData.newLiquidityIndex <= type(uint128).max,
Errors.RL_LIQUIDITY_INDEX_OVERFLOW
);
reserve.liquidityIndex = uint128(cachedData.newLiquidityIndex); reserve.liquidityIndex = uint128(cachedData.newLiquidityIndex);
@ -342,8 +329,13 @@ library ReserveLogic {
//that there is actual variable debt before accumulating //that there is actual variable debt before accumulating
if (cachedData.oldScaledVariableDebt != 0) { if (cachedData.oldScaledVariableDebt != 0) {
uint256 cumulatedVariableBorrowInterest = uint256 cumulatedVariableBorrowInterest =
MathUtils.calculateCompoundedInterest(cachedData.oldVariableBorrowRate, cachedData.reserveLastUpdateTimestamp); MathUtils.calculateCompoundedInterest(
cachedData.newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(cachedData.oldVariableBorrowIndex); cachedData.oldVariableBorrowRate,
cachedData.reserveLastUpdateTimestamp
);
cachedData.newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(
cachedData.oldVariableBorrowIndex
);
require( require(
cachedData.newVariableBorrowIndex <= type(uint128).max, cachedData.newVariableBorrowIndex <= type(uint128).max,
Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW

View File

@ -12,11 +12,14 @@ import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.
import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
import {UserConfiguration} from '../configuration/UserConfiguration.sol'; import {UserConfiguration} from '../configuration/UserConfiguration.sol';
import {Errors} from '../helpers/Errors.sol'; import {Errors} from '../helpers/Errors.sol';
import {CachingHelper} from '../helpers/CachingHelper.sol';
import {Helpers} from '../helpers/Helpers.sol'; import {Helpers} from '../helpers/Helpers.sol';
import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol'; import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol';
import {IAToken} from '../../../interfaces/IAToken.sol';
import {DataTypes} from '../types/DataTypes.sol'; import {DataTypes} from '../types/DataTypes.sol';
import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
/** /**
* @title ReserveLogic library * @title ReserveLogic library
@ -40,11 +43,15 @@ library ValidationLogic {
* @param reserve The reserve object on which the user is depositing * @param reserve The reserve object on which the user is depositing
* @param amount The amount to be deposited * @param amount The amount to be deposited
*/ */
function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) internal view { function validateDeposit(
DataTypes.ReserveConfigurationMap memory reserveConfiguration = reserve.configuration; DataTypes.ReserveData storage reserve,
(bool isActive, bool isFrozen, , , bool isPaused) = reserveConfiguration.getFlagsMemory(); CachingHelper.CachedData memory cachedData,
(, , , uint256 reserveDecimals, ) = reserveConfiguration.getParamsMemory(); uint256 amount
uint256 supplyCap = reserveConfiguration.getSupplyCapMemory(); ) internal view {
(bool isActive, bool isFrozen, , , bool isPaused) =
cachedData.reserveConfiguration.getFlagsMemory();
(, , , uint256 reserveDecimals, ) = cachedData.reserveConfiguration.getParamsMemory();
uint256 supplyCap = cachedData.reserveConfiguration.getSupplyCapMemory();
require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount != 0, Errors.VL_INVALID_AMOUNT);
require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
@ -52,7 +59,11 @@ library ValidationLogic {
require(!isFrozen, Errors.VL_RESERVE_FROZEN); require(!isFrozen, Errors.VL_RESERVE_FROZEN);
require( require(
supplyCap == 0 || supplyCap == 0 ||
IERC20(reserve.aTokenAddress).totalSupply().add(amount).div(10**reserveDecimals) < IAToken(cachedData.aTokenAddress)
.scaledTotalSupply()
.rayMul(cachedData.newLiquidityIndex)
.add(amount)
.div(10**reserveDecimals) <
supplyCap, supplyCap,
Errors.VL_SUPPLY_CAP_EXCEEDED Errors.VL_SUPPLY_CAP_EXCEEDED
); );
@ -85,10 +96,11 @@ library ValidationLogic {
uint256 userBorrowBalanceETH; uint256 userBorrowBalanceETH;
uint256 availableLiquidity; uint256 availableLiquidity;
uint256 healthFactor; uint256 healthFactor;
uint256 totalSupplyStableDebt; uint256 totalDebt;
uint256 totalSupplyVariableDebt; uint256 totalSupplyVariableDebt;
uint256 reserveDecimals; uint256 reserveDecimals;
uint256 borrowCap; uint256 borrowCap;
uint256 amountInETH;
bool isActive; bool isActive;
bool isFrozen; bool isFrozen;
bool isPaused; bool isPaused;
@ -99,10 +111,8 @@ library ValidationLogic {
/** /**
* @dev Validates a borrow action * @dev Validates a borrow action
* @param asset The address of the asset to borrow * @param asset The address of the asset to borrow
* @param reserve The reserve state from which the user is borrowing
* @param userAddress The address of the user * @param userAddress The address of the user
* @param amount The amount to be borrowed * @param amount The amount to be borrowed
* @param amountInETH The amount to be borrowed, in ETH
* @param interestRateMode The interest rate mode at which the user is borrowing * @param interestRateMode The interest rate mode at which the user is borrowing
* @param maxStableLoanPercent The max amount of the liquidity that can be borrowed at stable rate, in percentage * @param maxStableLoanPercent The max amount of the liquidity that can be borrowed at stable rate, in percentage
* @param reservesData The state of all the reserves * @param reservesData The state of all the reserves
@ -112,11 +122,10 @@ library ValidationLogic {
*/ */
function validateBorrow( function validateBorrow(
CachingHelper.CachedData memory cachedData,
address asset, address asset,
DataTypes.ReserveData storage reserve,
address userAddress, address userAddress,
uint256 amount, uint256 amount,
uint256 amountInETH,
uint256 interestRateMode, uint256 interestRateMode,
uint256 maxStableLoanPercent, uint256 maxStableLoanPercent,
mapping(address => DataTypes.ReserveData) storage reservesData, mapping(address => DataTypes.ReserveData) storage reservesData,
@ -127,8 +136,7 @@ library ValidationLogic {
) external view { ) external view {
ValidateBorrowLocalVars memory vars; ValidateBorrowLocalVars memory vars;
DataTypes.ReserveConfigurationMap memory reserveConfiguration = reserve.configuration; (, , , vars.reserveDecimals, ) = cachedData.reserveConfiguration.getParamsMemory();
(, , , vars.reserveDecimals, ) = reserveConfiguration.getParamsMemory();
( (
vars.isActive, vars.isActive,
@ -136,7 +144,7 @@ library ValidationLogic {
vars.borrowingEnabled, vars.borrowingEnabled,
vars.stableRateBorrowingEnabled, vars.stableRateBorrowingEnabled,
vars.isPaused vars.isPaused
) = reserveConfiguration.getFlagsMemory(); ) = cachedData.reserveConfiguration.getFlagsMemory();
require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE);
require(!vars.isPaused, Errors.VL_RESERVE_PAUSED); require(!vars.isPaused, Errors.VL_RESERVE_PAUSED);
@ -152,18 +160,23 @@ library ValidationLogic {
Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED
); );
vars.totalSupplyStableDebt = IERC20(reserve.stableDebtTokenAddress).totalSupply(); vars.borrowCap = cachedData.reserveConfiguration.getBorrowCapMemory();
vars.borrowCap = reserveConfiguration.getBorrowCapMemory();
vars.totalSupplyVariableDebt = IERC20(reserve.variableDebtTokenAddress).totalSupply();
require( if (vars.borrowCap > 0) {
vars.borrowCap == 0 || {
vars.totalSupplyStableDebt.add(vars.totalSupplyVariableDebt).add(amount).div( vars.totalSupplyVariableDebt = cachedData.oldScaledVariableDebt.rayMul(
10**vars.reserveDecimals cachedData.newVariableBorrowIndex
) < );
vars.borrowCap,
Errors.VL_BORROW_CAP_EXCEEDED vars.totalDebt = cachedData.oldTotalStableDebt.add(vars.totalSupplyVariableDebt).add(
); amount
);
require(
vars.totalDebt.div(10**vars.reserveDecimals) < vars.borrowCap,
Errors.VL_BORROW_CAP_EXCEEDED
);
}
}
( (
vars.userCollateralBalanceETH, vars.userCollateralBalanceETH,
@ -187,8 +200,11 @@ library ValidationLogic {
Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
); );
vars.amountInETH = IPriceOracleGetter(oracle).getAssetPrice(asset);
vars.amountInETH = vars.amountInETH.mul(amount).div(10**vars.reserveDecimals);
//add the current already borrowed amount to the amount requested to calculate the total collateral needed. //add the current already borrowed amount to the amount requested to calculate the total collateral needed.
vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(amountInETH).percentDiv( vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(vars.amountInETH).percentDiv(
vars.currentLtv vars.currentLtv
); //LTV is calculated in percentage ); //LTV is calculated in percentage
@ -211,13 +227,13 @@ library ValidationLogic {
require(vars.stableRateBorrowingEnabled, Errors.VL_STABLE_BORROWING_NOT_ENABLED); require(vars.stableRateBorrowingEnabled, Errors.VL_STABLE_BORROWING_NOT_ENABLED);
require( require(
!userConfig.isUsingAsCollateral(reserve.id) || !userConfig.isUsingAsCollateral(reservesData[asset].id) ||
reserve.configuration.getLtv() == 0 || cachedData.reserveConfiguration.getLtvMemory() == 0 ||
amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress), amount > IERC20(cachedData.aTokenAddress).balanceOf(userAddress),
Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY
); );
vars.availableLiquidity = IERC20(asset).balanceOf(reserve.aTokenAddress); vars.availableLiquidity = IERC20(asset).balanceOf(cachedData.aTokenAddress);
//calculate the max available loan size in stable rate mode as a percentage of the //calculate the max available loan size in stable rate mode as a percentage of the
//available liquidity //available liquidity