Merge pull request #141 from aave/feat/gas-optimization-5

2.5 - Feat/gas optimization 5
This commit is contained in:
The-3D 2021-06-15 13:22:07 +02:00 committed by GitHub
commit 4b12f1916c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 513 additions and 304 deletions

View File

@ -49,6 +49,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
using WadRayMath for uint256; using WadRayMath for uint256;
using PercentageMath for uint256; using PercentageMath for uint256;
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
using ReserveLogic for DataTypes.ReserveCache;
uint256 public constant LENDINGPOOL_REVISION = 0x2; uint256 public constant LENDINGPOOL_REVISION = 0x2;
@ -190,7 +191,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
uint16 referralCode, uint16 referralCode,
address onBehalfOf address onBehalfOf
) external override whenNotPaused { ) external override whenNotPaused {
DataTypes.ReserveData storage reserve = _reserves[asset];
_executeBorrow( _executeBorrow(
ExecuteBorrowParams( ExecuteBorrowParams(
asset, asset,
@ -198,7 +198,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
onBehalfOf, onBehalfOf,
amount, amount,
interestRateMode, interestRateMode,
reserve.aTokenAddress,
referralCode, referralCode,
true true
) )
@ -271,6 +270,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
**/ **/
function swapBorrowRateMode(address asset, uint256 rateMode) external override whenNotPaused { function swapBorrowRateMode(address asset, uint256 rateMode) external override whenNotPaused {
DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveData storage reserve = _reserves[asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve); (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve);
@ -278,37 +278,40 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
ValidationLogic.validateSwapRateMode( ValidationLogic.validateSwapRateMode(
reserve, reserve,
reserveCache,
_usersConfig[msg.sender], _usersConfig[msg.sender],
stableDebt, stableDebt,
variableDebt, variableDebt,
interestRateMode interestRateMode
); );
reserve.updateState(); reserve.updateState(reserveCache);
if (interestRateMode == DataTypes.InterestRateMode.STABLE) { if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt); IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(msg.sender, stableDebt);
IVariableDebtToken(reserve.variableDebtTokenAddress).mint( IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint(
msg.sender, msg.sender,
msg.sender, msg.sender,
stableDebt, stableDebt,
reserve.variableBorrowIndex reserveCache.nextVariableBorrowIndex
); );
reserveCache.refreshDebt(0, stableDebt, stableDebt, 0);
} else { } else {
IVariableDebtToken(reserve.variableDebtTokenAddress).burn( IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn(
msg.sender, msg.sender,
variableDebt, variableDebt,
reserve.variableBorrowIndex reserveCache.nextVariableBorrowIndex
); );
IStableDebtToken(reserve.stableDebtTokenAddress).mint( IStableDebtToken(reserveCache.stableDebtTokenAddress).mint(
msg.sender, msg.sender,
msg.sender, msg.sender,
variableDebt, variableDebt,
reserve.currentStableBorrowRate reserve.currentStableBorrowRate
); );
reserveCache.refreshDebt(variableDebt, 0, 0, variableDebt);
} }
reserve.updateInterestRates(asset, reserve.aTokenAddress, 0, 0); reserve.updateInterestRates(reserveCache, asset, 0, 0);
emit Swap(asset, msg.sender, rateMode); emit Swap(asset, msg.sender, rateMode);
} }
@ -324,22 +327,22 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
**/ **/
function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused { function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused {
DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveData storage reserve = _reserves[asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
IERC20 stableDebtToken = IERC20(reserve.stableDebtTokenAddress); IERC20 stableDebtToken = IERC20(reserveCache.stableDebtTokenAddress);
IERC20 variableDebtToken = IERC20(reserve.variableDebtTokenAddress); IERC20 variableDebtToken = IERC20(reserveCache.variableDebtTokenAddress);
address aTokenAddress = reserve.aTokenAddress;
uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user); uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user);
ValidationLogic.validateRebalanceStableBorrowRate( ValidationLogic.validateRebalanceStableBorrowRate(
reserve, reserve,
reserveCache,
asset, asset,
stableDebtToken, stableDebtToken,
variableDebtToken, variableDebtToken,
aTokenAddress reserveCache.aTokenAddress
); );
reserve.updateState(); reserve.updateState(reserveCache);
IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt); IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt);
IStableDebtToken(address(stableDebtToken)).mint( IStableDebtToken(address(stableDebtToken)).mint(
@ -349,7 +352,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
reserve.currentStableBorrowRate reserve.currentStableBorrowRate
); );
reserve.updateInterestRates(asset, aTokenAddress, 0, 0); reserveCache.refreshDebt(stableDebt, stableDebt, 0, 0);
reserve.updateInterestRates(reserveCache, asset, 0, 0);
emit RebalanceStableBorrowRate(asset, user); emit RebalanceStableBorrowRate(asset, user);
} }
@ -365,8 +370,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
whenNotPaused whenNotPaused
{ {
DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveData storage reserve = _reserves[asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
ValidationLogic.validateSetUseReserveAsCollateral(reserve); ValidationLogic.validateSetUseReserveAsCollateral(reserveCache);
_usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral);
@ -437,6 +443,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
uint256 currentPremiumToProtocol; uint256 currentPremiumToProtocol;
uint256 currentAmountPlusPremium; uint256 currentAmountPlusPremium;
address debtToken; address debtToken;
address[] aTokenAddresses;
uint256[] totalPremiums;
uint256 flashloanPremiumTotal; uint256 flashloanPremiumTotal;
uint256 flashloanPremiumToProtocol; uint256 flashloanPremiumToProtocol;
} }
@ -469,10 +477,11 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
) external override whenNotPaused { ) external override whenNotPaused {
FlashLoanLocalVars memory vars; FlashLoanLocalVars memory vars;
vars.aTokenAddresses = new address[](assets.length);
vars.totalPremiums = new uint256[](assets.length);
ValidationLogic.validateFlashloan(assets, amounts, _reserves); ValidationLogic.validateFlashloan(assets, amounts, _reserves);
address[] memory aTokenAddresses = new address[](assets.length);
uint256[] memory totalPremiums = new uint256[](assets.length);
vars.receiver = IFlashLoanReceiver(receiverAddress); vars.receiver = IFlashLoanReceiver(receiverAddress);
(vars.flashloanPremiumTotal, vars.flashloanPremiumToProtocol) = _authorizedFlashBorrowers[ (vars.flashloanPremiumTotal, vars.flashloanPremiumToProtocol) = _authorizedFlashBorrowers[
msg.sender msg.sender
@ -481,38 +490,39 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
: (_flashLoanPremiumTotal, _flashLoanPremiumToProtocol); : (_flashLoanPremiumTotal, _flashLoanPremiumToProtocol);
for (vars.i = 0; vars.i < assets.length; vars.i++) { 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;
vars.totalPremiums[vars.i] = amounts[vars.i].percentMul(vars.flashloanPremiumTotal);
totalPremiums[vars.i] = amounts[vars.i].percentMul(vars.flashloanPremiumTotal); IAToken(vars.aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]);
vars.currentPremiumToProtocol = amounts[vars.i].percentMul(vars.flashloanPremiumToProtocol);
vars.currentPremiumToLP = totalPremiums[vars.i].sub(vars.currentPremiumToProtocol);
IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]);
} }
require( require(
vars.receiver.executeOperation(assets, amounts, totalPremiums, msg.sender, params), vars.receiver.executeOperation(assets, amounts, vars.totalPremiums, msg.sender, params),
Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN
); );
for (vars.i = 0; vars.i < assets.length; vars.i++) { for (vars.i = 0; vars.i < assets.length; vars.i++) {
vars.currentAsset = assets[vars.i]; vars.currentAsset = assets[vars.i];
vars.currentAmount = amounts[vars.i]; vars.currentAmount = amounts[vars.i];
vars.currentATokenAddress = aTokenAddresses[vars.i]; vars.currentATokenAddress = vars.aTokenAddresses[vars.i];
vars.currentAmountPlusPremium = vars.currentAmount.add(totalPremiums[vars.i]); vars.currentAmountPlusPremium = vars.currentAmount.add(vars.totalPremiums[vars.i]);
vars.currentPremiumToProtocol = amounts[vars.i].percentMul(vars.flashloanPremiumToProtocol);
vars.currentPremiumToLP = vars.totalPremiums[vars.i].sub(vars.currentPremiumToProtocol);
if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) { if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) {
_reserves[vars.currentAsset].updateState(); DataTypes.ReserveData storage reserve = _reserves[vars.currentAsset];
_reserves[vars.currentAsset].cumulateToLiquidityIndex( DataTypes.ReserveCache memory reserveCache = reserve.cache();
reserve.updateState(reserveCache);
reserve.cumulateToLiquidityIndex(
IERC20(vars.currentATokenAddress).totalSupply(), IERC20(vars.currentATokenAddress).totalSupply(),
vars.currentPremiumToLP vars.currentPremiumToLP
); );
_reserves[vars.currentAsset].accruedToTreasury = _reserves[vars.currentAsset] reserve.accruedToTreasury = reserve.accruedToTreasury.add(
.accruedToTreasury vars.currentPremiumToProtocol.rayDiv(reserve.liquidityIndex)
.add(vars.currentPremiumToProtocol.rayDiv(_reserves[vars.currentAsset].liquidityIndex)); );
_reserves[vars.currentAsset].updateInterestRates( reserve.updateInterestRates(
reserveCache,
vars.currentAsset, vars.currentAsset,
vars.currentATokenAddress,
vars.currentAmountPlusPremium, vars.currentAmountPlusPremium,
0 0
); );
@ -532,7 +542,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
onBehalfOf, onBehalfOf,
vars.currentAmount, vars.currentAmount,
modes[vars.i], modes[vars.i],
vars.currentATokenAddress,
referralCode, referralCode,
false false
) )
@ -543,7 +552,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
msg.sender, msg.sender,
vars.currentAsset, vars.currentAsset,
vars.currentAmount, vars.currentAmount,
totalPremiums[vars.i], vars.totalPremiums[vars.i],
referralCode referralCode
); );
} }
@ -620,7 +629,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
ltv, ltv,
currentLiquidationThreshold, currentLiquidationThreshold,
healthFactor healthFactor
) = GenericLogic.calculateUserAccountData( ) = GenericLogic.getUserAccountData(
user, user,
_reserves, _reserves,
_usersConfig[user], _usersConfig[user],
@ -937,7 +946,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
address onBehalfOf; address onBehalfOf;
uint256 amount; uint256 amount;
uint256 interestRateMode; uint256 interestRateMode;
address aTokenAddress;
uint16 referralCode; uint16 referralCode;
bool releaseUnderlying; bool releaseUnderlying;
} }
@ -945,50 +953,45 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
function _executeBorrow(ExecuteBorrowParams memory vars) internal { function _executeBorrow(ExecuteBorrowParams memory vars) internal {
DataTypes.ReserveData storage reserve = _reserves[vars.asset]; DataTypes.ReserveData storage reserve = _reserves[vars.asset];
DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
address oracle = _addressesProvider.getPriceOracle(); reserve.updateState(reserveCache);
uint256 amountInETH =
IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div(
10**reserve.configuration.getDecimals()
);
ValidationLogic.validateBorrow( ValidationLogic.validateBorrow(
reserveCache,
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();
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(reserveCache.stableDebtTokenAddress).mint(
vars.user, vars.user,
vars.onBehalfOf, vars.onBehalfOf,
vars.amount, vars.amount,
currentStableRate currentStableRate
); );
reserveCache.refreshDebt(vars.amount, 0, 0, 0);
} else { } else {
isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress).mint( isFirstBorrowing = IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint(
vars.user, vars.user,
vars.onBehalfOf, vars.onBehalfOf,
vars.amount, vars.amount,
reserve.variableBorrowIndex reserveCache.nextVariableBorrowIndex
); );
reserveCache.refreshDebt(0, 0, vars.amount, 0);
} }
if (isFirstBorrowing) { if (isFirstBorrowing) {
@ -996,14 +999,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
} }
reserve.updateInterestRates( reserve.updateInterestRates(
reserveCache,
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(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
} }
emit Borrow( emit Borrow(
@ -1026,17 +1029,18 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
uint16 referralCode uint16 referralCode
) internal { ) internal {
DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveData storage reserve = _reserves[asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
ValidationLogic.validateDeposit(reserve, amount); reserve.updateState(reserveCache);
address aToken = reserve.aTokenAddress; ValidationLogic.validateDeposit(reserveCache, amount);
reserve.updateState(); reserve.updateInterestRates(reserveCache, asset, amount, 0);
reserve.updateInterestRates(asset, aToken, amount, 0);
IERC20(asset).safeTransferFrom(msg.sender, aToken, amount); IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, amount);
bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex); bool isFirstDeposit =
IAToken(reserveCache.aTokenAddress).mint(onBehalfOf, amount, reserveCache.nextLiquidityIndex);
if (isFirstDeposit) { if (isFirstDeposit) {
_usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true);
@ -1053,14 +1057,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
) internal returns (uint256) { ) internal returns (uint256) {
DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveData storage reserve = _reserves[asset];
DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
address aToken = reserve.aTokenAddress; reserve.updateState(reserveCache);
reserve.updateState(); uint256 userBalance =
IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul(
uint256 liquidityIndex = reserve.liquidityIndex; reserveCache.nextLiquidityIndex
);
uint256 userBalance = IAToken(aToken).scaledBalanceOf(msg.sender).rayMul(liquidityIndex);
uint256 amountToWithdraw = amount; uint256 amountToWithdraw = amount;
@ -1068,11 +1072,16 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
amountToWithdraw = userBalance; amountToWithdraw = userBalance;
} }
ValidationLogic.validateWithdraw(reserve, amountToWithdraw, userBalance); ValidationLogic.validateWithdraw(reserveCache, amountToWithdraw, userBalance);
reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw); reserve.updateInterestRates(reserveCache, asset, 0, amountToWithdraw);
IAToken(aToken).burn(msg.sender, to, amountToWithdraw, liquidityIndex); IAToken(reserveCache.aTokenAddress).burn(
msg.sender,
to,
amountToWithdraw,
reserveCache.nextLiquidityIndex
);
if (userConfig.isUsingAsCollateral(reserve.id)) { if (userConfig.isUsingAsCollateral(reserve.id)) {
if (userConfig.isBorrowingAny()) { if (userConfig.isBorrowingAny()) {
@ -1104,13 +1113,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
address onBehalfOf address onBehalfOf
) internal returns (uint256) { ) internal returns (uint256) {
DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveData storage reserve = _reserves[asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve); (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve);
DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
ValidationLogic.validateRepay( ValidationLogic.validateRepay(
reserve, reserveCache,
amount, amount,
interestRateMode, interestRateMode,
onBehalfOf, onBehalfOf,
@ -1125,28 +1135,29 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
paybackAmount = amount; paybackAmount = amount;
} }
reserve.updateState(); reserve.updateState(reserveCache);
if (interestRateMode == DataTypes.InterestRateMode.STABLE) { if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount);
reserveCache.refreshDebt(0, paybackAmount, 0, 0);
} else { } else {
IVariableDebtToken(reserve.variableDebtTokenAddress).burn( IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn(
onBehalfOf, onBehalfOf,
paybackAmount, paybackAmount,
reserve.variableBorrowIndex reserveCache.nextVariableBorrowIndex
); );
reserveCache.refreshDebt(0, 0, 0, paybackAmount);
} }
address aToken = reserve.aTokenAddress; reserve.updateInterestRates(reserveCache, 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, reserveCache.aTokenAddress, paybackAmount);
IAToken(aToken).handleRepayment(msg.sender, paybackAmount); IAToken(reserveCache.aTokenAddress).handleRepayment(msg.sender, paybackAmount);
emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); emit Repay(asset, onBehalfOf, msg.sender, paybackAmount);

View File

@ -1,6 +1,8 @@
// SPDX-License-Identifier: agpl-3.0 // SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12; pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import {SafeMath} from '../../dependencies/openzeppelin/contracts//SafeMath.sol'; import {SafeMath} from '../../dependencies/openzeppelin/contracts//SafeMath.sol';
import {IERC20} from '../../dependencies/openzeppelin/contracts//IERC20.sol'; import {IERC20} from '../../dependencies/openzeppelin/contracts//IERC20.sol';
import {IAToken} from '../../interfaces/IAToken.sol'; import {IAToken} from '../../interfaces/IAToken.sol';
@ -9,7 +11,7 @@ import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol';
import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol'; import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
import {ILendingPoolCollateralManager} from '../../interfaces/ILendingPoolCollateralManager.sol'; import {ILendingPoolCollateralManager} from '../../interfaces/ILendingPoolCollateralManager.sol';
import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
import {GenericLogic} from '../libraries/logic/GenericLogic.sol'; import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
import {Helpers} from '../libraries/helpers/Helpers.sol'; import {Helpers} from '../libraries/helpers/Helpers.sol';
import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol';
import {PercentageMath} from '../libraries/math/PercentageMath.sol'; import {PercentageMath} from '../libraries/math/PercentageMath.sol';
@ -35,6 +37,7 @@ contract LendingPoolCollateralManager is
using SafeMath for uint256; using SafeMath for uint256;
using WadRayMath for uint256; using WadRayMath for uint256;
using PercentageMath for uint256; using PercentageMath for uint256;
using ReserveLogic for DataTypes.ReserveCache;
uint256 internal constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 5000; uint256 internal constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 5000;
@ -52,6 +55,7 @@ contract LendingPoolCollateralManager is
uint256 healthFactor; uint256 healthFactor;
uint256 liquidatorPreviousATokenBalance; uint256 liquidatorPreviousATokenBalance;
IAToken collateralAtoken; IAToken collateralAtoken;
IPriceOracleGetter oracle;
bool isCollateralEnabled; bool isCollateralEnabled;
DataTypes.InterestRateMode borrowRateMode; DataTypes.InterestRateMode borrowRateMode;
uint256 errorCode; uint256 errorCode;
@ -87,28 +91,25 @@ contract LendingPoolCollateralManager is
) external override returns (uint256, string memory) { ) external override returns (uint256, string memory) {
DataTypes.ReserveData storage collateralReserve = _reserves[collateralAsset]; DataTypes.ReserveData storage collateralReserve = _reserves[collateralAsset];
DataTypes.ReserveData storage debtReserve = _reserves[debtAsset]; DataTypes.ReserveData storage debtReserve = _reserves[debtAsset];
DataTypes.ReserveCache memory debtReserveCache = debtReserve.cache();
DataTypes.UserConfigurationMap storage userConfig = _usersConfig[user]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[user];
LiquidationCallLocalVars memory vars; LiquidationCallLocalVars memory vars;
(, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
(vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve);
vars.oracle = IPriceOracleGetter(_addressesProvider.getPriceOracle());
(vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall(
collateralReserve,
debtReserveCache,
vars.userStableDebt.add(vars.userVariableDebt),
user, user,
_reserves, _reserves,
userConfig, userConfig,
_reservesList, _reservesList,
_reservesCount, _reservesCount,
_addressesProvider.getPriceOracle() address(vars.oracle)
);
(vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve);
(vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall(
collateralReserve,
debtReserve,
userConfig,
vars.healthFactor,
vars.userStableDebt,
vars.userVariableDebt
); );
if (Errors.CollateralManagerErrors(vars.errorCode) != Errors.CollateralManagerErrors.NO_ERROR) { if (Errors.CollateralManagerErrors(vars.errorCode) != Errors.CollateralManagerErrors.NO_ERROR) {
@ -132,11 +133,12 @@ contract LendingPoolCollateralManager is
vars.debtAmountNeeded vars.debtAmountNeeded
) = _calculateAvailableCollateralToLiquidate( ) = _calculateAvailableCollateralToLiquidate(
collateralReserve, collateralReserve,
debtReserve, debtReserveCache,
collateralAsset, collateralAsset,
debtAsset, debtAsset,
vars.actualDebtToLiquidate, vars.actualDebtToLiquidate,
vars.userCollateralBalance vars.userCollateralBalance,
vars.oracle
); );
// If debtAmountNeeded < actualDebtToLiquidate, there isn't enough // If debtAmountNeeded < actualDebtToLiquidate, there isn't enough
@ -160,35 +162,38 @@ contract LendingPoolCollateralManager is
} }
} }
debtReserve.updateState(); debtReserve.updateState(debtReserveCache);
if (vars.userVariableDebt >= vars.actualDebtToLiquidate) { if (vars.userVariableDebt >= vars.actualDebtToLiquidate) {
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn( IVariableDebtToken(debtReserveCache.variableDebtTokenAddress).burn(
user, user,
vars.actualDebtToLiquidate, vars.actualDebtToLiquidate,
debtReserve.variableBorrowIndex debtReserveCache.nextVariableBorrowIndex
); );
debtReserveCache.refreshDebt(0, 0, 0, vars.actualDebtToLiquidate);
debtReserve.updateInterestRates(debtReserveCache, debtAsset, vars.actualDebtToLiquidate, 0);
} 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(debtReserveCache.variableDebtTokenAddress).burn(
user, user,
vars.userVariableDebt, vars.userVariableDebt,
debtReserve.variableBorrowIndex debtReserveCache.nextVariableBorrowIndex
); );
} }
IStableDebtToken(debtReserve.stableDebtTokenAddress).burn( IStableDebtToken(debtReserveCache.stableDebtTokenAddress).burn(
user, user,
vars.actualDebtToLiquidate.sub(vars.userVariableDebt) vars.actualDebtToLiquidate.sub(vars.userVariableDebt)
); );
} debtReserveCache.refreshDebt(
0,
vars.actualDebtToLiquidate.sub(vars.userVariableDebt),
0,
vars.userVariableDebt
);
debtReserve.updateInterestRates( debtReserve.updateInterestRates(debtReserveCache, debtAsset, vars.actualDebtToLiquidate, 0);
debtAsset, }
debtReserve.aTokenAddress,
vars.actualDebtToLiquidate,
0
);
if (receiveAToken) { if (receiveAToken) {
vars.liquidatorPreviousATokenBalance = IERC20(vars.collateralAtoken).balanceOf(msg.sender); vars.liquidatorPreviousATokenBalance = IERC20(vars.collateralAtoken).balanceOf(msg.sender);
@ -200,10 +205,11 @@ contract LendingPoolCollateralManager is
emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender); emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender);
} }
} else { } else {
collateralReserve.updateState(); DataTypes.ReserveCache memory collateralReserveCache = collateralReserve.cache();
collateralReserve.updateState(collateralReserveCache);
collateralReserve.updateInterestRates( collateralReserve.updateInterestRates(
collateralReserveCache,
collateralAsset, collateralAsset,
address(vars.collateralAtoken),
0, 0,
vars.maxCollateralToLiquidate vars.maxCollateralToLiquidate
); );
@ -213,7 +219,7 @@ contract LendingPoolCollateralManager is
user, user,
msg.sender, msg.sender,
vars.maxCollateralToLiquidate, vars.maxCollateralToLiquidate,
collateralReserve.liquidityIndex collateralReserveCache.nextLiquidityIndex
); );
} }
@ -227,7 +233,7 @@ contract LendingPoolCollateralManager is
// Transfers the debt asset being repaid to the aToken, where the liquidity is kept // Transfers the debt asset being repaid to the aToken, where the liquidity is kept
IERC20(debtAsset).safeTransferFrom( IERC20(debtAsset).safeTransferFrom(
msg.sender, msg.sender,
debtReserve.aTokenAddress, debtReserveCache.aTokenAddress,
vars.actualDebtToLiquidate vars.actualDebtToLiquidate
); );
@ -260,7 +266,7 @@ contract LendingPoolCollateralManager is
* - This function needs to be called after all the checks to validate the liquidation have been performed, * - This function needs to be called after all the checks to validate the liquidation have been performed,
* otherwise it might fail. * otherwise it might fail.
* @param collateralReserve The data of the collateral reserve * @param collateralReserve The data of the collateral reserve
* @param debtReserve The data of the debt reserve * @param debtReserveCache The cached data of the debt reserve
* @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
* @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
* @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
@ -271,15 +277,15 @@ contract LendingPoolCollateralManager is
**/ **/
function _calculateAvailableCollateralToLiquidate( function _calculateAvailableCollateralToLiquidate(
DataTypes.ReserveData storage collateralReserve, DataTypes.ReserveData storage collateralReserve,
DataTypes.ReserveData storage debtReserve, DataTypes.ReserveCache memory debtReserveCache,
address collateralAsset, address collateralAsset,
address debtAsset, address debtAsset,
uint256 debtToCover, uint256 debtToCover,
uint256 userCollateralBalance uint256 userCollateralBalance,
IPriceOracleGetter oracle
) internal view returns (uint256, uint256) { ) internal view returns (uint256, uint256) {
uint256 collateralAmount = 0; uint256 collateralAmount = 0;
uint256 debtAmountNeeded = 0; uint256 debtAmountNeeded = 0;
IPriceOracleGetter oracle = IPriceOracleGetter(_addressesProvider.getPriceOracle());
AvailableCollateralToLiquidateLocalVars memory vars; AvailableCollateralToLiquidateLocalVars memory vars;
@ -289,7 +295,7 @@ contract LendingPoolCollateralManager is
(, , vars.liquidationBonus, vars.collateralDecimals, ) = collateralReserve (, , vars.liquidationBonus, vars.collateralDecimals, ) = collateralReserve
.configuration .configuration
.getParams(); .getParams();
vars.debtAssetDecimals = debtReserve.configuration.getDecimals(); vars.debtAssetDecimals = debtReserveCache.reserveConfiguration.getDecimalsMemory();
// This is the maximum possible amount of the selected collateral that can be liquidated, given the // This is the maximum possible amount of the selected collateral that can be liquidated, given the
// max amount of liquidatable debt // max amount of liquidatable debt

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 pure 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,19 @@ 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
pure
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 +316,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
pure
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

@ -198,4 +198,36 @@ library GenericLogic {
availableBorrowsETH = availableBorrowsETH.sub(totalDebtInETH); availableBorrowsETH = availableBorrowsETH.sub(totalDebtInETH);
return availableBorrowsETH; return availableBorrowsETH;
} }
/**
* @dev proxy call for calculateUserAccountData as external function.
* Used in LendingPool to work around contract size limit issues
* @param user The address of the user
* @param reservesData Data of all the reserves
* @param userConfig The configuration of the user
* @param reserves The list of the available reserves
* @param oracle The price oracle address
* @return The total collateral and total debt of the user in ETH, the avg ltv, liquidation threshold and the HF
**/
function getUserAccountData(
address user,
mapping(address => DataTypes.ReserveData) storage reservesData,
DataTypes.UserConfigurationMap memory userConfig,
mapping(uint256 => address) storage reserves,
uint256 reservesCount,
address oracle
)
external
view
returns (
uint256,
uint256,
uint256,
uint256,
uint256
)
{
return
calculateUserAccountData(user, reservesData, userConfig, reserves, reservesCount, oracle);
}
} }

View File

@ -107,30 +107,12 @@ 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) internal { function updateState(
uint256 scaledVariableDebt = DataTypes.ReserveData storage reserve,
IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply(); DataTypes.ReserveCache memory reserveCache
uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; ) internal {
uint256 previousLiquidityIndex = reserve.liquidityIndex; _updateIndexes(reserve, reserveCache);
uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp; _accrueToTreasury(reserve, reserveCache);
(uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) =
_updateIndexes(
reserve,
scaledVariableDebt,
previousLiquidityIndex,
previousVariableBorrowIndex,
lastUpdatedTimestamp
);
_accrueToTreasury(
reserve,
scaledVariableDebt,
previousVariableBorrowIndex,
newLiquidityIndex,
newVariableBorrowIndex,
lastUpdatedTimestamp
);
} }
/** /**
@ -179,9 +161,6 @@ library ReserveLogic {
} }
struct UpdateInterestRatesLocalVars { struct UpdateInterestRatesLocalVars {
address stableDebtTokenAddress;
uint256 availableLiquidity;
uint256 totalStableDebt;
uint256 newLiquidityRate; uint256 newLiquidityRate;
uint256 newStableRate; uint256 newStableRate;
uint256 newVariableRate; uint256 newVariableRate;
@ -197,38 +176,29 @@ library ReserveLogic {
**/ **/
function updateInterestRates( function updateInterestRates(
DataTypes.ReserveData storage reserve, DataTypes.ReserveData storage reserve,
DataTypes.ReserveCache memory reserveCache,
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; vars.totalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul(
reserveCache.nextVariableBorrowIndex
(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);
( (
vars.newLiquidityRate, vars.newLiquidityRate,
vars.newStableRate, vars.newStableRate,
vars.newVariableRate vars.newVariableRate
) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates( ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates(
reserveAddress, reserveAddress,
aTokenAddress, reserveCache.aTokenAddress,
liquidityAdded, liquidityAdded,
liquidityTaken, liquidityTaken,
vars.totalStableDebt, reserveCache.nextTotalStableDebt,
vars.totalVariableDebt, vars.totalVariableDebt,
vars.avgStableRate, reserveCache.nextAvgStableBorrowRate,
reserve.configuration.getReserveFactor() reserveCache.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);
@ -243,17 +213,15 @@ library ReserveLogic {
vars.newLiquidityRate, vars.newLiquidityRate,
vars.newStableRate, vars.newStableRate,
vars.newVariableRate, vars.newVariableRate,
reserve.liquidityIndex, reserveCache.nextLiquidityIndex,
reserve.variableBorrowIndex reserveCache.nextVariableBorrowIndex
); );
} }
struct MintToTreasuryLocalVars { struct MintToTreasuryLocalVars {
uint256 currentStableDebt; uint256 prevTotalStableDebt;
uint256 principalStableDebt; uint256 prevTotalVariableDebt;
uint256 previousStableDebt; uint256 currTotalVariableDebt;
uint256 currentVariableDebt;
uint256 previousVariableDebt;
uint256 avgStableRate; uint256 avgStableRate;
uint256 cumulatedStableInterest; uint256 cumulatedStableInterest;
uint256 totalDebtAccrued; uint256 totalDebtAccrued;
@ -266,62 +234,53 @@ 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 reserveCache 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, DataTypes.ReserveCache memory reserveCache
uint256 previousVariableBorrowIndex,
uint256 newLiquidityIndex,
uint256 newVariableBorrowIndex,
uint40 timestamp
) internal { ) internal {
MintToTreasuryLocalVars memory vars; MintToTreasuryLocalVars memory vars;
vars.reserveFactor = reserve.configuration.getReserveFactor(); vars.reserveFactor = reserveCache.reserveConfiguration.getReserveFactorMemory();
if (vars.reserveFactor == 0) { if (vars.reserveFactor == 0) {
return; return;
} }
//fetching the principal, total stable debt and the avg stable rate //calculate the total variable debt at moment of the last interaction
( vars.prevTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
vars.principalStableDebt, reserveCache.currVariableBorrowIndex
vars.currentStableDebt, );
vars.avgStableRate,
vars.stableSupplyUpdatedTimestamp
) = IStableDebtToken(reserve.stableDebtTokenAddress).getSupplyData();
//calculate the last principal variable debt //calculate the new total variable debt after accumulation of the interest on the index
vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex); vars.currTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
reserveCache.nextVariableBorrowIndex
//calculate the new total supply after accumulation of the index );
vars.currentVariableDebt = scaledVariableDebt.rayMul(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, reserveCache.currAvgStableBorrowRate,
vars.stableSupplyUpdatedTimestamp, reserveCache.stableDebtLastUpdateTimestamp,
timestamp reserveCache.reserveLastUpdateTimestamp
); );
vars.previousStableDebt = vars.principalStableDebt.rayMul(vars.cumulatedStableInterest); vars.prevTotalStableDebt = reserveCache.currPrincipalStableDebt.rayMul(
vars.cumulatedStableInterest
);
//debt accrued is the sum of the current debt minus the sum of the debt at the last update //debt accrued is the sum of the current debt minus the sum of the debt at the last update
vars.totalDebtAccrued = vars vars.totalDebtAccrued = vars
.currentVariableDebt .currTotalVariableDebt
.add(vars.currentStableDebt) .add(reserveCache.currTotalStableDebt)
.sub(vars.previousVariableDebt) .sub(vars.prevTotalVariableDebt)
.sub(vars.previousStableDebt); .sub(vars.prevTotalStableDebt);
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( reserve.accruedToTreasury = reserve.accruedToTreasury.add(
vars.amountToMint.rayDiv(newLiquidityIndex) vars.amountToMint.rayDiv(reserveCache.nextLiquidityIndex)
); );
} }
} }
@ -329,47 +288,133 @@ library ReserveLogic {
/** /**
* @dev Updates the reserve indexes and the timestamp of the update * @dev Updates the reserve indexes and the timestamp of the update
* @param reserve The reserve reserve to be updated * @param reserve The reserve reserve to be updated
* @param scaledVariableDebt The scaled variable debt * @param reserveCache The cache layer holding the cached protocol data
* @param liquidityIndex The last stored liquidity index
* @param variableBorrowIndex The last stored variable borrow index
**/ **/
function _updateIndexes( function _updateIndexes(
DataTypes.ReserveData storage reserve, DataTypes.ReserveData storage reserve,
uint256 scaledVariableDebt, DataTypes.ReserveCache memory reserveCache
uint256 liquidityIndex, ) internal {
uint256 variableBorrowIndex, reserveCache.nextLiquidityIndex = reserveCache.currLiquidityIndex;
uint40 timestamp reserveCache.nextVariableBorrowIndex = reserveCache.currVariableBorrowIndex;
) internal returns (uint256, uint256) {
uint256 currentLiquidityRate = reserve.currentLiquidityRate;
uint256 newLiquidityIndex = liquidityIndex;
uint256 newVariableBorrowIndex = variableBorrowIndex;
//only cumulating if there is any income being produced //only cumulating if there is any income being produced
if (currentLiquidityRate > 0) { if (reserveCache.currLiquidityRate > 0) {
uint256 cumulatedLiquidityInterest = uint256 cumulatedLiquidityInterest =
MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp); MathUtils.calculateLinearInterest(
newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex); reserveCache.currLiquidityRate,
require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); reserveCache.reserveLastUpdateTimestamp
);
reserve.liquidityIndex = uint128(newLiquidityIndex); reserveCache.nextLiquidityIndex = cumulatedLiquidityInterest.rayMul(
reserveCache.currLiquidityIndex
);
require(
reserveCache.nextLiquidityIndex <= type(uint128).max,
Errors.RL_LIQUIDITY_INDEX_OVERFLOW
);
reserve.liquidityIndex = uint128(reserveCache.nextLiquidityIndex);
//as the liquidity rate might come only from stable rate loans, we need to ensure //as the liquidity rate might come only from stable rate loans, we need to ensure
//that there is actual variable debt before accumulating //that there is actual variable debt before accumulating
if (scaledVariableDebt != 0) { if (reserveCache.currScaledVariableDebt != 0) {
uint256 cumulatedVariableBorrowInterest = uint256 cumulatedVariableBorrowInterest =
MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp); MathUtils.calculateCompoundedInterest(
newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex); reserveCache.currVariableBorrowRate,
reserveCache.reserveLastUpdateTimestamp
);
reserveCache.nextVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(
reserveCache.currVariableBorrowIndex
);
require( require(
newVariableBorrowIndex <= type(uint128).max, reserveCache.nextVariableBorrowIndex <= type(uint128).max,
Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW
); );
reserve.variableBorrowIndex = uint128(newVariableBorrowIndex); reserve.variableBorrowIndex = uint128(reserveCache.nextVariableBorrowIndex);
} }
} }
//solium-disable-next-line //solium-disable-next-line
reserve.lastUpdateTimestamp = uint40(block.timestamp); reserve.lastUpdateTimestamp = uint40(block.timestamp);
return (newLiquidityIndex, newVariableBorrowIndex); }
/**
* @dev Creates a cache object to avoid repeated storage reads and external contract calls when updating state and interest rates.
* @param reserve The reserve object for which the cache will be filled
* @return The cache object
*/
function cache(DataTypes.ReserveData storage reserve)
internal
view
returns (DataTypes.ReserveCache memory)
{
DataTypes.ReserveCache memory reserveCache;
reserveCache.reserveConfiguration = reserve.configuration;
reserveCache.currLiquidityIndex = reserve.liquidityIndex;
reserveCache.currVariableBorrowIndex = reserve.variableBorrowIndex;
reserveCache.currLiquidityRate = reserve.currentLiquidityRate;
reserveCache.currVariableBorrowRate = reserve.currentVariableBorrowRate;
reserveCache.aTokenAddress = reserve.aTokenAddress;
reserveCache.stableDebtTokenAddress = reserve.stableDebtTokenAddress;
reserveCache.variableDebtTokenAddress = reserve.variableDebtTokenAddress;
reserveCache.reserveLastUpdateTimestamp = reserve.lastUpdateTimestamp;
reserveCache.currScaledVariableDebt = reserveCache.nextScaledVariableDebt = IVariableDebtToken(
reserveCache
.variableDebtTokenAddress
)
.scaledTotalSupply();
(
reserveCache.currPrincipalStableDebt,
reserveCache.currTotalStableDebt,
reserveCache.currAvgStableBorrowRate,
reserveCache.stableDebtLastUpdateTimestamp
) = IStableDebtToken(reserveCache.stableDebtTokenAddress).getSupplyData();
// by default the actions are considered as not affecting the debt balances.
// if the action involves mint/burn of debt, the cache needs to be updated through refreshDebt()
reserveCache.nextTotalStableDebt = reserveCache.currTotalStableDebt;
reserveCache.nextAvgStableBorrowRate = reserveCache.currAvgStableBorrowRate;
return reserveCache;
}
/**
* @dev Updates the debt data in the cache object. MUST be invoked before updateInterestRates() when a protocol interaction
* causes minting or burning of debt.
* @param cache The cache object
* @param stableDebtMinted The stable debt minted as a consequence of the interaction
* @param stableDebtBurned The stable debt burned as a consequence of the interaction
* @param variableDebtMinted The variable debt minted as a consequence of the interaction
* @param variableDebtBurned The variable debt burned as a consequence of the interaction
*/
function refreshDebt(
DataTypes.ReserveCache memory cache,
uint256 stableDebtMinted,
uint256 stableDebtBurned,
uint256 variableDebtMinted,
uint256 variableDebtBurned
) internal view {
if (stableDebtMinted != 0 || stableDebtBurned != 0) {
if (cache.currTotalStableDebt.add(stableDebtMinted) > stableDebtBurned) {
cache.nextTotalStableDebt = cache.currTotalStableDebt.add(stableDebtMinted).sub(
stableDebtBurned
);
cache.nextAvgStableBorrowRate = IStableDebtToken(cache.stableDebtTokenAddress)
.getAverageStableRate();
} else {
cache.nextTotalStableDebt = cache.nextAvgStableBorrowRate = 0;
}
}
if (variableDebtMinted != 0 || variableDebtBurned != 0) {
uint256 scaledVariableDebtMinted = variableDebtMinted.rayDiv(cache.nextVariableBorrowIndex);
uint256 scaledVariableDebtBurned = variableDebtBurned.rayDiv(cache.nextVariableBorrowIndex);
cache.nextScaledVariableDebt = cache.currScaledVariableDebt.add(scaledVariableDebtMinted).sub(
scaledVariableDebtBurned
);
}
} }
} }

View File

@ -16,7 +16,10 @@ 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 {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.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
@ -37,14 +40,17 @@ library ValidationLogic {
/** /**
* @dev Validates a deposit action * @dev Validates a deposit action
* @param reserve The reserve object on which the user is depositing * @param reserveCache The cached data of the reserve
* @param amount The amount to be deposited * @param amount The amount to be deposited
*/ */
function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) external view { function validateDeposit(DataTypes.ReserveCache memory reserveCache, uint256 amount)
DataTypes.ReserveConfigurationMap memory reserveConfiguration = reserve.configuration; internal
(bool isActive, bool isFrozen, , , bool isPaused) = reserveConfiguration.getFlagsMemory(); view
(, , , uint256 reserveDecimals, ) = reserveConfiguration.getParamsMemory(); {
uint256 supplyCap = reserveConfiguration.getSupplyCapMemory(); (bool isActive, bool isFrozen, , , bool isPaused) =
reserveCache.reserveConfiguration.getFlagsMemory();
(, , , uint256 reserveDecimals, ) = reserveCache.reserveConfiguration.getParamsMemory();
uint256 supplyCap = reserveCache.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 +58,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(reserveCache.aTokenAddress)
.scaledTotalSupply()
.rayMul(reserveCache.nextLiquidityIndex)
.add(amount)
.div(10**reserveDecimals) <
supplyCap, supplyCap,
Errors.VL_SUPPLY_CAP_EXCEEDED Errors.VL_SUPPLY_CAP_EXCEEDED
); );
@ -60,19 +70,19 @@ library ValidationLogic {
/** /**
* @dev Validates a withdraw action * @dev Validates a withdraw action
* @param reserve The reserve object * @param reserveCache The cached data of the reserve
* @param amount The amount to be withdrawn * @param amount The amount to be withdrawn
* @param userBalance The balance of the user * @param userBalance The balance of the user
*/ */
function validateWithdraw( function validateWithdraw(
DataTypes.ReserveData storage reserve, DataTypes.ReserveCache memory reserveCache,
uint256 amount, uint256 amount,
uint256 userBalance uint256 userBalance
) external view { ) internal pure {
require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount != 0, Errors.VL_INVALID_AMOUNT);
require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); 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(isActive, Errors.VL_NO_ACTIVE_RESERVE);
require(!isPaused, Errors.VL_RESERVE_PAUSED); require(!isPaused, Errors.VL_RESERVE_PAUSED);
} }
@ -85,10 +95,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;
@ -98,11 +109,10 @@ library ValidationLogic {
/** /**
* @dev Validates a borrow action * @dev Validates a borrow action
* @param reserveCache the cached data of the reserve
* @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(
DataTypes.ReserveCache memory reserveCache,
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, ) = reserveCache.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(); ) = reserveCache.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 = reserveCache.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 = reserveCache.currScaledVariableDebt.rayMul(
10**vars.reserveDecimals reserveCache.nextVariableBorrowIndex
) < );
vars.borrowCap,
Errors.VL_BORROW_CAP_EXCEEDED vars.totalDebt = reserveCache.currTotalStableDebt.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 || reserveCache.reserveConfiguration.getLtvMemory() == 0 ||
amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress), amount > IERC20(reserveCache.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(reserveCache.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
@ -229,21 +245,22 @@ library ValidationLogic {
/** /**
* @dev Validates a repay action * @dev Validates a repay action
* @param reserve The reserve state from which the user is repaying * @param reserveCache The cached data of the reserve
* @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1) * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1)
* @param rateMode the interest rate mode of the debt being repaid
* @param onBehalfOf The address of the user msg.sender is repaying for * @param onBehalfOf The address of the user msg.sender is repaying for
* @param stableDebt The borrow balance of the user * @param stableDebt The borrow balance of the user
* @param variableDebt The borrow balance of the user * @param variableDebt The borrow balance of the user
*/ */
function validateRepay( function validateRepay(
DataTypes.ReserveData storage reserve, DataTypes.ReserveCache memory reserveCache,
uint256 amountSent, uint256 amountSent,
DataTypes.InterestRateMode rateMode, DataTypes.InterestRateMode rateMode,
address onBehalfOf, address onBehalfOf,
uint256 stableDebt, uint256 stableDebt,
uint256 variableDebt uint256 variableDebt
) external view { ) internal view {
(bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory();
require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
require(!isPaused, Errors.VL_RESERVE_PAUSED); require(!isPaused, Errors.VL_RESERVE_PAUSED);
@ -266,20 +283,22 @@ library ValidationLogic {
/** /**
* @dev Validates a swap of borrow rate mode. * @dev Validates a swap of borrow rate mode.
* @param reserve The reserve state on which the user is swapping the rate * @param reserve The reserve state on which the user is swapping the rate
* @param reserveCache The cached data of the reserve
* @param userConfig The user reserves configuration * @param userConfig The user reserves configuration
* @param stableDebt The stable debt of the user * @param stableDebt The stable debt of the user
* @param variableDebt The variable debt of the user * @param variableDebt The variable debt of the user
* @param currentRateMode The rate mode of the borrow * @param currentRateMode The rate mode of the debt being swapped
*/ */
function validateSwapRateMode( function validateSwapRateMode(
DataTypes.ReserveData storage reserve, DataTypes.ReserveData storage reserve,
DataTypes.ReserveCache memory reserveCache,
DataTypes.UserConfigurationMap storage userConfig, DataTypes.UserConfigurationMap storage userConfig,
uint256 stableDebt, uint256 stableDebt,
uint256 variableDebt, uint256 variableDebt,
DataTypes.InterestRateMode currentRateMode DataTypes.InterestRateMode currentRateMode
) external view { ) external view {
(bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) =
reserve.configuration.getFlags(); reserveCache.reserveConfiguration.getFlagsMemory();
require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
require(!isPaused, Errors.VL_RESERVE_PAUSED); require(!isPaused, Errors.VL_RESERVE_PAUSED);
@ -300,8 +319,8 @@ library ValidationLogic {
require( require(
!userConfig.isUsingAsCollateral(reserve.id) || !userConfig.isUsingAsCollateral(reserve.id) ||
reserve.configuration.getLtv() == 0 || reserveCache.reserveConfiguration.getLtvMemory() == 0 ||
stableDebt.add(variableDebt) > IERC20(reserve.aTokenAddress).balanceOf(msg.sender), stableDebt.add(variableDebt) > IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender),
Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY
); );
} else { } else {
@ -312,6 +331,7 @@ library ValidationLogic {
/** /**
* @dev Validates a stable borrow rate rebalance action * @dev Validates a stable borrow rate rebalance action
* @param reserve The reserve state on which the user is getting rebalanced * @param reserve The reserve state on which the user is getting rebalanced
* @param reserveCache The cached state of the reserve
* @param reserveAddress The address of the reserve * @param reserveAddress The address of the reserve
* @param stableDebtToken The stable debt token instance * @param stableDebtToken The stable debt token instance
* @param variableDebtToken The variable debt token instance * @param variableDebtToken The variable debt token instance
@ -319,12 +339,13 @@ library ValidationLogic {
*/ */
function validateRebalanceStableBorrowRate( function validateRebalanceStableBorrowRate(
DataTypes.ReserveData storage reserve, DataTypes.ReserveData storage reserve,
DataTypes.ReserveCache memory reserveCache,
address reserveAddress, address reserveAddress,
IERC20 stableDebtToken, IERC20 stableDebtToken,
IERC20 variableDebtToken, IERC20 variableDebtToken,
address aTokenAddress address aTokenAddress
) external view { ) external view {
(bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory();
require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
require(!isPaused, Errors.VL_RESERVE_PAUSED); require(!isPaused, Errors.VL_RESERVE_PAUSED);
@ -338,7 +359,7 @@ library ValidationLogic {
//if the liquidity rate is below REBALANCE_UP_THRESHOLD of the max variable APR at 95% usage, //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. //then we allow rebalancing of the stable rate positions.
uint256 currentLiquidityRate = reserve.currentLiquidityRate; uint256 currentLiquidityRate = reserveCache.currLiquidityRate;
uint256 maxVariableBorrowRate = uint256 maxVariableBorrowRate =
IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).getMaxVariableBorrowRate(); IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).getMaxVariableBorrowRate();
@ -352,12 +373,16 @@ library ValidationLogic {
/** /**
* @dev Validates the action of setting an asset as collateral * @dev Validates the action of setting an asset as collateral
* @param reserve The state of the reserve that the user is enabling or disabling as collateral * @param reserveCache The cached data of the reserve
*/ */
function validateSetUseReserveAsCollateral(DataTypes.ReserveData storage reserve) external view { function validateSetUseReserveAsCollateral(DataTypes.ReserveCache memory reserveCache)
uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender); external
bool isPaused = reserve.configuration.getPaused(); 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(!isPaused, Errors.VL_RESERVE_PAUSED);
require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0);
@ -372,62 +397,95 @@ library ValidationLogic {
address[] memory assets, address[] memory assets,
uint256[] memory amounts, uint256[] memory amounts,
mapping(address => DataTypes.ReserveData) storage reservesData mapping(address => DataTypes.ReserveData) storage reservesData
) external view { ) internal view {
for (uint256 i = 0; i < assets.length; i++) { for (uint256 i = 0; i < assets.length; i++) {
require(!reservesData[assets[i]].configuration.getPaused(), Errors.VL_RESERVE_PAUSED); require(!reservesData[assets[i]].configuration.getPaused(), Errors.VL_RESERVE_PAUSED);
} }
require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS); require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS);
} }
struct ValidateLiquidationCallLocalVars {
uint256 healthFactor;
bool collateralReserveActive;
bool collateralReservePaused;
bool principalReserveActive;
bool principalReservePaused;
bool isCollateralEnabled;
}
/** /**
* @dev Validates the liquidation action * @dev Validates the liquidation action
* @param collateralReserve The reserve data of the collateral * @param collateralReserve The reserve data of the collateral
* @param principalReserve The reserve data of the principal * @param principalReserveCache The cached reserve data of the principal
* @param userConfig The user configuration * @param userConfig The user configuration
* @param userHealthFactor The user's health factor * @param totalDebt Total debt balance of the user
* @param userStableDebt Total stable debt balance of the user * @param user The address of the user being liquidated
* @param userVariableDebt Total variable debt balance of the user * @param reservesData The mapping of the reserves data
* @param userConfig The user configuration mapping
* @param reserves The list of the reserves
* @param reservesCount The number of reserves in the list
* @param oracle The address of the price oracle
**/ **/
function validateLiquidationCall( function validateLiquidationCall(
DataTypes.ReserveData storage collateralReserve, DataTypes.ReserveData storage collateralReserve,
DataTypes.ReserveData storage principalReserve, DataTypes.ReserveCache memory principalReserveCache,
uint256 totalDebt,
address user,
mapping(address => DataTypes.ReserveData) storage reservesData,
DataTypes.UserConfigurationMap storage userConfig, DataTypes.UserConfigurationMap storage userConfig,
uint256 userHealthFactor, mapping(uint256 => address) storage reserves,
uint256 userStableDebt, uint256 reservesCount,
uint256 userVariableDebt address oracle
) internal view returns (uint256, string memory) { ) internal view returns (uint256, string memory) {
if ( ValidateLiquidationCallLocalVars memory vars;
!collateralReserve.configuration.getActive() || !principalReserve.configuration.getActive()
) { (vars.collateralReserveActive, , , , vars.collateralReservePaused) = collateralReserve
.configuration
.getFlagsMemory();
(vars.principalReserveActive, , , , vars.principalReservePaused) = principalReserveCache
.reserveConfiguration
.getFlagsMemory();
if (!vars.collateralReserveActive || !vars.principalReserveActive) {
return ( return (
uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE), uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE),
Errors.VL_NO_ACTIVE_RESERVE Errors.VL_NO_ACTIVE_RESERVE
); );
} }
if (collateralReserve.configuration.getPaused() || principalReserve.configuration.getPaused()) { if (vars.collateralReservePaused || vars.principalReservePaused) {
return (uint256(Errors.CollateralManagerErrors.PAUSED_RESERVE), Errors.VL_RESERVE_PAUSED); return (uint256(Errors.CollateralManagerErrors.PAUSED_RESERVE), Errors.VL_RESERVE_PAUSED);
} }
if (userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) { (, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
user,
reservesData,
userConfig,
reserves,
reservesCount,
oracle
);
if (vars.healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) {
return ( return (
uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_ABOVE_THRESHOLD), uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
Errors.LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD Errors.LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD
); );
} }
bool isCollateralEnabled = vars.isCollateralEnabled =
collateralReserve.configuration.getLiquidationThreshold() > 0 && collateralReserve.configuration.getLiquidationThreshold() > 0 &&
userConfig.isUsingAsCollateral(collateralReserve.id); userConfig.isUsingAsCollateral(collateralReserve.id);
//if collateral isn't enabled as collateral by user, it cannot be liquidated //if collateral isn't enabled as collateral by user, it cannot be liquidated
if (!isCollateralEnabled) { if (!vars.isCollateralEnabled) {
return ( return (
uint256(Errors.CollateralManagerErrors.COLLATERAL_CANNOT_BE_LIQUIDATED), uint256(Errors.CollateralManagerErrors.COLLATERAL_CANNOT_BE_LIQUIDATED),
Errors.LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED Errors.LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED
); );
} }
if (userStableDebt == 0 && userVariableDebt == 0) { if (totalDebt == 0) {
return ( return (
uint256(Errors.CollateralManagerErrors.CURRRENCY_NOT_BORROWED), uint256(Errors.CollateralManagerErrors.CURRRENCY_NOT_BORROWED),
Errors.LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER Errors.LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER
@ -474,7 +532,7 @@ library ValidationLogic {
* @dev Validates a transfer action * @dev Validates a transfer action
* @param reserve The reserve object * @param reserve The reserve object
*/ */
function validateTransfer(DataTypes.ReserveData storage reserve) external view { function validateTransfer(DataTypes.ReserveData storage reserve) internal view {
require(!reserve.configuration.getPaused(), Errors.VL_RESERVE_PAUSED); require(!reserve.configuration.getPaused(), Errors.VL_RESERVE_PAUSED);
} }

View File

@ -51,4 +51,26 @@ library DataTypes {
} }
enum InterestRateMode {NONE, STABLE, VARIABLE} enum InterestRateMode {NONE, STABLE, VARIABLE}
struct ReserveCache {
uint256 currScaledVariableDebt;
uint256 nextScaledVariableDebt;
uint256 currPrincipalStableDebt;
uint256 currAvgStableBorrowRate;
uint256 currTotalStableDebt;
uint256 nextAvgStableBorrowRate;
uint256 nextTotalStableDebt;
uint256 currLiquidityIndex;
uint256 nextLiquidityIndex;
uint256 currVariableBorrowIndex;
uint256 nextVariableBorrowIndex;
uint256 currLiquidityRate;
uint256 currVariableBorrowRate;
DataTypes.ReserveConfigurationMap reserveConfiguration;
address aTokenAddress;
address stableDebtTokenAddress;
address variableDebtTokenAddress;
uint40 reserveLastUpdateTimestamp;
uint40 stableDebtLastUpdateTimestamp;
}
} }

View File

@ -188,7 +188,8 @@ export const deployAaveLibraries = async (
return { return {
['__$de8c0cf1a7d7c36c802af9a64fb9d86036$__']: validationLogic.address, ['__$de8c0cf1a7d7c36c802af9a64fb9d86036$__']: validationLogic.address,
['__$22cd43a9dda9ce44e9b92ba393b88fb9ac$__']: reserveLogic.address, ['__$22cd43a9dda9ce44e9b92ba393b88fb9ac$__']: reserveLogic.address,
}; ["__$52a8a86ab43135662ff256bbc95497e8e3$__"]: genericLogic.address,
}
}; };
export const deployLendingPool = async (verify?: boolean) => { export const deployLendingPool = async (verify?: boolean) => {

16
package-lock.json generated
View File

@ -11142,14 +11142,6 @@
"requires": { "requires": {
"min-document": "^2.19.0", "min-document": "^2.19.0",
"process": "^0.11.10" "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": { "got": {
@ -12794,6 +12786,12 @@
"integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
"dev": true "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": { "process-nextick-args": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@ -14795,7 +14793,7 @@
} }
}, },
"ethereumjs-abi": { "ethereumjs-abi": {
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#1a27c59c15ab1e95ee8e5c4ed6ad814c49cc439e", "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#ee3994657fa7a427238e6ba92a84d0b529bbcde0",
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
"dev": true, "dev": true,
"requires": { "requires": {