feat: initial cache layer implementation

This commit is contained in:
The3D 2021-06-03 14:10:50 +02:00
parent 729194d587
commit 6cdfd8e31b
4 changed files with 127 additions and 69 deletions

View File

@ -26,6 +26,7 @@ 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,6 +271,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);
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve);
@ -283,7 +285,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
interestRateMode
);
reserve.updateState();
reserve.updateState(cachedData);
if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt);
@ -323,6 +325,7 @@ 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);
IERC20 stableDebtToken = IERC20(reserve.stableDebtTokenAddress);
IERC20 variableDebtToken = IERC20(reserve.variableDebtTokenAddress);
@ -338,7 +341,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
aTokenAddress
);
reserve.updateState();
reserve.updateState(cachedData);
IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt);
IStableDebtToken(address(stableDebtToken)).mint(
@ -435,6 +438,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
uint256 currentPremium;
uint256 currentAmountPlusPremium;
address debtToken;
address[] aTokenAddresses;
uint256[] premiums;
}
/**
@ -465,40 +470,44 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
) external override whenNotPaused {
FlashLoanLocalVars memory vars;
vars.aTokenAddresses = new address[](assets.length);
vars.premiums = new uint256[](assets.length);
ValidationLogic.validateFlashloan(assets, amounts, _reserves);
address[] memory aTokenAddresses = new address[](assets.length);
uint256[] memory premiums = new uint256[](assets.length);
vars.receiver = IFlashLoanReceiver(receiverAddress);
for (vars.i = 0; vars.i < assets.length; vars.i++) {
aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress;
vars.aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress;
premiums[vars.i] = amounts[vars.i].mul(_flashLoanPremiumTotal).div(10000);
vars.premiums[vars.i] = amounts[vars.i].mul(_flashLoanPremiumTotal).div(10000);
IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]);
IAToken(vars.aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]);
}
require(
vars.receiver.executeOperation(assets, amounts, premiums, msg.sender, params),
vars.receiver.executeOperation(assets, amounts, vars.premiums, msg.sender, params),
Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN
);
for (vars.i = 0; vars.i < assets.length; vars.i++) {
vars.currentAsset = assets[vars.i];
vars.currentAmount = amounts[vars.i];
vars.currentPremium = premiums[vars.i];
vars.currentATokenAddress = aTokenAddresses[vars.i];
vars.currentPremium = vars.premiums[vars.i];
vars.currentATokenAddress = vars.aTokenAddresses[vars.i];
vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium);
if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) {
_reserves[vars.currentAsset].updateState();
_reserves[vars.currentAsset].cumulateToLiquidityIndex(
DataTypes.ReserveData storage reserve = _reserves[vars.currentAsset];
CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve);
reserve.updateState(cachedData);
reserve.cumulateToLiquidityIndex(
IERC20(vars.currentATokenAddress).totalSupply(),
vars.currentPremium
);
_reserves[vars.currentAsset].updateInterestRates(
reserve.updateInterestRates(
vars.currentAsset,
vars.currentATokenAddress,
vars.currentAmountPlusPremium,
@ -863,6 +872,7 @@ 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);
address oracle = _addressesProvider.getPriceOracle();
@ -886,7 +896,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
oracle
);
reserve.updateState();
reserve.updateState(cachedData);
uint256 currentStableRate = 0;
@ -944,17 +954,16 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
uint16 referralCode
) internal {
DataTypes.ReserveData storage reserve = _reserves[asset];
CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve);
ValidationLogic.validateDeposit(reserve, amount);
address aToken = reserve.aTokenAddress;
reserve.updateState(cachedData);
reserve.updateInterestRates(asset, cachedData.aTokenAddress, amount, 0);
reserve.updateState();
reserve.updateInterestRates(asset, aToken, amount, 0);
IERC20(asset).safeTransferFrom(msg.sender, cachedData.aTokenAddress, amount);
IERC20(asset).safeTransferFrom(msg.sender, aToken, amount);
bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex);
bool isFirstDeposit = IAToken(cachedData.aTokenAddress).mint(onBehalfOf, amount, cachedData.newLiquidityIndex);
if (isFirstDeposit) {
_usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true);
@ -971,14 +980,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);
address aToken = reserve.aTokenAddress;
reserve.updateState();
reserve.updateState(cachedData);
uint256 liquidityIndex = reserve.liquidityIndex;
uint256 userBalance = IAToken(aToken).scaledBalanceOf(msg.sender).rayMul(liquidityIndex);
uint256 userBalance = IAToken(aToken).scaledBalanceOf(msg.sender).rayMul(cachedData.newLiquidityIndex);
uint256 amountToWithdraw = amount;
@ -990,7 +998,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw);
IAToken(aToken).burn(msg.sender, to, amountToWithdraw, liquidityIndex);
IAToken(aToken).burn(msg.sender, to, amountToWithdraw, cachedData.newLiquidityIndex);
if (userConfig.isUsingAsCollateral(reserve.id)) {
if (userConfig.isBorrowingAny()) {
@ -1022,6 +1030,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);
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve);
@ -1043,7 +1052,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
paybackAmount = amount;
}
reserve.updateState();
reserve.updateState(cachedData);
if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount);

View File

@ -18,6 +18,7 @@ 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
@ -160,7 +161,9 @@ contract LendingPoolCollateralManager is
}
}
debtReserve.updateState();
CachingHelper.CachedData memory debtReserveCachedData = CachingHelper.fetchData(debtReserve);
debtReserve.updateState(debtReserveCachedData);
if (vars.userVariableDebt >= vars.actualDebtToLiquidate) {
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
@ -200,7 +203,9 @@ contract LendingPoolCollateralManager is
emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender);
}
} else {
collateralReserve.updateState();
CachingHelper.CachedData memory collateralReserveCachedData = CachingHelper.fetchData(collateralReserve);
collateralReserve.updateState(collateralReserveCachedData);
collateralReserve.updateInterestRates(
collateralAsset,
address(vars.collateralAtoken),

View File

@ -0,0 +1,59 @@
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import {DataTypes} from '../types/DataTypes.sol';
import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
library CachingHelper {
struct CachedData {
uint256 oldScaledVariableDebt;
uint256 oldTotalVariableDebt;
uint256 newSscaledVariableDebt;
uint256 newTtotalVariableDebt;
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;
}
function fetchData(DataTypes.ReserveData storage reserveData)
internal
view
returns (CachingHelper.CachedData memory)
{
CachedData memory cachedData;
cachedData.oldLiquidityIndex = reserveData.liquidityIndex;
cachedData.oldVariableBorrowIndex = reserveData.variableBorrowIndex;
cachedData.aTokenAddress = reserveData.aTokenAddress;
cachedData.stableDebtTokenAddress = reserveData.stableDebtTokenAddress;
cachedData.variableDebtTokenAddress = reserveData.variableDebtTokenAddress;
cachedData.reserveConfiguration = reserveData.configuration;
cachedData.oldLiquidityRate = reserveData.currentLiquidityRate;
cachedData.oldVariableBorrowRate = reserveData.currentVariableBorrowRate;
cachedData.reserveLastUpdateTimestamp = reserveData.lastUpdateTimestamp;
cachedData.oldScaledVariableDebt = IVariableDebtToken(cachedData.variableDebtTokenAddress)
.scaledTotalSupply();
return cachedData;
}
}

View File

@ -14,6 +14,7 @@ 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
@ -107,29 +108,22 @@ library ReserveLogic {
* @dev Updates the liquidity cumulative index and the variable borrow index.
* @param reserve the reserve object
**/
function updateState(DataTypes.ReserveData storage reserve) internal {
uint256 scaledVariableDebt =
IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply();
uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex;
uint256 previousLiquidityIndex = reserve.liquidityIndex;
uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp;
function updateState(DataTypes.ReserveData storage reserve, CachingHelper.CachedData memory cachedData) internal {
(uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) =
_updateIndexes(
reserve,
scaledVariableDebt,
previousLiquidityIndex,
previousVariableBorrowIndex,
lastUpdatedTimestamp
cachedData
);
_accrueToTreasury(
reserve,
scaledVariableDebt,
previousVariableBorrowIndex,
newLiquidityIndex,
newVariableBorrowIndex,
lastUpdatedTimestamp
cachedData.oldScaledVariableDebt,
cachedData.oldVariableBorrowIndex,
cachedData.newLiquidityIndex,
cachedData.newVariableBorrowIndex,
cachedData.reserveLastUpdateTimestamp
);
}
@ -209,9 +203,7 @@ library ReserveLogic {
(vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress)
.getTotalSupplyAndAvgRate();
//calculates the total variable debt locally using the scaled total supply instead
//of totalSupply(), as it's noticeably cheaper. Also, the index has been
//updated by the previous updateState() call
vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress)
.scaledTotalSupply()
.rayMul(reserve.variableBorrowIndex);
@ -327,47 +319,40 @@ library ReserveLogic {
/**
* @dev Updates the reserve indexes and the timestamp of the update
* @param reserve The reserve reserve to be updated
* @param scaledVariableDebt The scaled variable debt
* @param liquidityIndex The last stored liquidity index
* @param variableBorrowIndex The last stored variable borrow index
* @param cachedData The cache layer holding the cached protocol data
**/
function _updateIndexes(
DataTypes.ReserveData storage reserve,
uint256 scaledVariableDebt,
uint256 liquidityIndex,
uint256 variableBorrowIndex,
uint40 timestamp
) internal returns (uint256, uint256) {
uint256 currentLiquidityRate = reserve.currentLiquidityRate;
CachingHelper.CachedData memory cachedData
) internal {
uint256 newLiquidityIndex = liquidityIndex;
uint256 newVariableBorrowIndex = variableBorrowIndex;
cachedData.newLiquidityIndex = cachedData.oldLiquidityIndex;
cachedData.newVariableBorrowIndex = cachedData.oldVariableBorrowIndex;
//only cumulating if there is any income being produced
if (currentLiquidityRate > 0) {
if (cachedData.oldLiquidityRate > 0) {
uint256 cumulatedLiquidityInterest =
MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp);
newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex);
require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW);
MathUtils.calculateLinearInterest(cachedData.oldLiquidityRate, cachedData.reserveLastUpdateTimestamp);
cachedData.newLiquidityIndex = cumulatedLiquidityInterest.rayMul(cachedData.oldLiquidityIndex);
require( cachedData.newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW);
reserve.liquidityIndex = uint128(newLiquidityIndex);
reserve.liquidityIndex = uint128(cachedData.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 (scaledVariableDebt != 0) {
if (cachedData.oldScaledVariableDebt != 0) {
uint256 cumulatedVariableBorrowInterest =
MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp);
newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex);
MathUtils.calculateCompoundedInterest(cachedData.oldVariableBorrowRate, cachedData.reserveLastUpdateTimestamp);
cachedData.newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(cachedData.oldVariableBorrowIndex);
require(
newVariableBorrowIndex <= type(uint128).max,
cachedData.newVariableBorrowIndex <= type(uint128).max,
Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW
);
reserve.variableBorrowIndex = uint128(newVariableBorrowIndex);
reserve.variableBorrowIndex = uint128(cachedData.newVariableBorrowIndex);
}
}
//solium-disable-next-line
reserve.lastUpdateTimestamp = uint40(block.timestamp);
return (newLiquidityIndex, newVariableBorrowIndex);
}
}