From 3c8018fab9d9c8bda7f2b717b7268c69120aef3d Mon Sep 17 00:00:00 2001 From: The3D <emilio@aave.com> Date: Mon, 14 Sep 2020 10:43:30 +0200 Subject: [PATCH] Cleaned up code, converted addressesProvider to immutable in LendingPool --- contracts/lendingpool/LendingPool.sol | 247 ++++++++---------- contracts/libraries/logic/ReserveLogic.sol | 71 ++--- contracts/tokenization/VariableDebtToken.sol | 12 + .../interfaces/IVariableDebtToken.sol | 6 + 4 files changed, 163 insertions(+), 173 deletions(-) diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 88df63af..1d969b1d 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -47,7 +47,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { uint256 public constant MAX_STABLE_RATE_BORROW_SIZE_PERCENT = 25; uint256 public constant FLASHLOAN_PREMIUM_TOTAL = 9; - ILendingPoolAddressesProvider internal _addressesProvider; + ILendingPoolAddressesProvider public immutable ADDRESSES_PROVIDER; mapping(address => ReserveLogic.ReserveData) internal _reserves; mapping(address => UserConfiguration.Map) internal _usersConfig; @@ -61,7 +61,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { **/ modifier onlyLendingPoolConfigurator { require( - _addressesProvider.getLendingPoolConfigurator() == msg.sender, + ADDRESSES_PROVIDER.getLendingPoolConfigurator() == msg.sender, Errors.CALLER_NOT_LENDING_POOL_CONFIGURATOR ); _; @@ -81,7 +81,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { * @param provider the address of the LendingPoolAddressesProvider registry **/ function initialize(ILendingPoolAddressesProvider provider) public initializer { - _addressesProvider = provider; + ADDRESSES_PROVIDER = provider; } /** @@ -146,7 +146,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { _reserves, _usersConfig[msg.sender], _reservesList, - _addressesProvider.getPriceOracle() + ADDRESSES_PROVIDER.getPriceOracle() ); reserve.updateState(); @@ -203,16 +203,6 @@ contract LendingPool is VersionedInitializable, ILendingPool { uint256 rateMode, address onBehalfOf ) external override { - _executeRepay(asset, msg.sender, amount, rateMode, onBehalfOf); - } - - function _executeRepay( - address asset, - address user, - uint256 amount, - uint256 rateMode, - address onBehalfOf - ) internal { ReserveLogic.ReserveData storage reserve = _reserves[asset]; (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve); @@ -239,12 +229,6 @@ contract LendingPool is VersionedInitializable, ILendingPool { reserve.updateState(); - address debtTokenAddress = interestRateMode == ReserveLogic.InterestRateMode.STABLE - ? reserve.stableDebtTokenAddress - : reserve.variableDebtTokenAddress; - - _mintToReserveTreasury(reserve, onBehalfOf, debtTokenAddress); - //burns an equivalent amount of debt tokens if (interestRateMode == ReserveLogic.InterestRateMode.STABLE) { IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); @@ -286,12 +270,6 @@ contract LendingPool is VersionedInitializable, ILendingPool { reserve.updateState(); - address debtTokenAddress = interestRateMode == ReserveLogic.InterestRateMode.STABLE - ? reserve.stableDebtTokenAddress - : reserve.variableDebtTokenAddress; - - _mintToReserveTreasury(reserve, msg.sender, debtTokenAddress); - if (interestRateMode == ReserveLogic.InterestRateMode.STABLE) { //burn stable rate tokens, mint variable rate tokens IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt); @@ -344,12 +322,10 @@ contract LendingPool is VersionedInitializable, ILendingPool { Errors.INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET ); - //burn old debt tokens, mint new ones reserve.updateState(); - _mintToReserveTreasury(reserve, user, address(stableDebtToken)); - + //burn old debt tokens, mint new ones stableDebtToken.burn(user, stableBorrowBalance); stableDebtToken.mint(user, stableBorrowBalance, reserve.currentStableBorrowRate); @@ -374,7 +350,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { _reserves, _usersConfig[msg.sender], _reservesList, - _addressesProvider.getPriceOracle() + ADDRESSES_PROVIDER.getPriceOracle() ); _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); @@ -402,7 +378,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { uint256 purchaseAmount, bool receiveAToken ) external override { - address liquidationManager = _addressesProvider.getLendingPoolLiquidationManager(); + address liquidationManager = ADDRESSES_PROVIDER.getLendingPoolLiquidationManager(); //solium-disable-next-line (bool success, bytes memory result) = liquidationManager.delegatecall( @@ -461,7 +437,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { require(!_flashLiquidationLocked, Errors.REENTRANCY_NOT_ALLOWED); _flashLiquidationLocked = true; - address liquidationManager = _addressesProvider.getLendingPoolLiquidationManager(); + address liquidationManager = ADDRESSES_PROVIDER.getLendingPoolLiquidationManager(); //solium-disable-next-line (bool success, bytes memory result) = liquidationManager.delegatecall( @@ -664,7 +640,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { _reserves, _usersConfig[user], _reservesList, - _addressesProvider.getPriceOracle() + ADDRESSES_PROVIDER.getPriceOracle() ); availableBorrowsETH = GenericLogic.calculateAvailableBorrowsETH( @@ -767,110 +743,6 @@ contract LendingPool is VersionedInitializable, ILendingPool { return _reserves[asset].configuration; } - // internal functions - - struct ExecuteBorrowParams { - address asset; - address user; - uint256 amount; - uint256 interestRateMode; - address aTokenAddress; - uint16 referralCode; - bool releaseUnderlying; - } - - /** - * @dev Internal function to execute a borrowing action, allowing to transfer or not the underlying - * @param vars Input struct for the borrowing action, in order to avoid STD errors - **/ - function _executeBorrow(ExecuteBorrowParams memory vars) internal { - ReserveLogic.ReserveData storage reserve = _reserves[vars.asset]; - UserConfiguration.Map storage userConfig = _usersConfig[msg.sender]; - - address oracle = _addressesProvider.getPriceOracle(); - - uint256 amountInETH = IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div( - 10**reserve.configuration.getDecimals() - ); - - ValidationLogic.validateBorrow( - reserve, - vars.asset, - vars.amount, - amountInETH, - vars.interestRateMode, - MAX_STABLE_RATE_BORROW_SIZE_PERCENT, - _reserves, - userConfig, - _reservesList, - oracle - ); - - uint256 reserveId = reserve.id; - if (!userConfig.isBorrowing(reserveId)) { - userConfig.setBorrowing(reserveId, true); - } - - address debtTokenAddress = ReserveLogic.InterestRateMode(vars.interestRateMode) == - ReserveLogic.InterestRateMode.STABLE - ? reserve.stableDebtTokenAddress - : reserve.variableDebtTokenAddress; - - _mintToReserveTreasury(reserve, vars.user, debtTokenAddress); - - reserve.updateState(); - - //caching the current stable borrow rate - uint256 currentStableRate = 0; - - if ( - ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE - ) { - currentStableRate = reserve.currentStableBorrowRate; - - IStableDebtToken(debtTokenAddress).mint(vars.user, vars.amount, currentStableRate); - } else { - IVariableDebtToken(debtTokenAddress).mint(vars.user, vars.amount, reserve.variableBorrowIndex); - } - - reserve.updateInterestRates( - vars.asset, - vars.aTokenAddress, - 0, - vars.releaseUnderlying ? vars.amount : 0 - ); - - if (vars.releaseUnderlying) { - IAToken(vars.aTokenAddress).transferUnderlyingTo(msg.sender, vars.amount); - } - - emit Borrow( - vars.asset, - msg.sender, - vars.amount, - vars.interestRateMode, - ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE - ? currentStableRate - : reserve.currentVariableBorrowRate, - vars.referralCode - ); - } - - /** - * @dev adds a reserve to the array of the _reserves address - **/ - function _addReserveToList(address asset) internal { - bool reserveAlreadyAdded = false; - for (uint256 i = 0; i < _reservesList.length; i++) - if (_reservesList[i] == asset) { - reserveAlreadyAdded = true; - } - if (!reserveAlreadyAdded) { - _reserves[asset].id = uint8(_reservesList.length); - _reservesList.push(asset); - } - } - /** * @dev returns the normalized income per unit of asset * @param asset the address of the reserve @@ -914,7 +786,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { _reserves, _usersConfig[user], _reservesList, - _addressesProvider.getPriceOracle() + ADDRESSES_PROVIDER.getPriceOracle() ); } @@ -929,6 +801,103 @@ contract LendingPool is VersionedInitializable, ILendingPool { * @dev returns the addresses provider **/ function getAddressesProvider() external view returns (ILendingPoolAddressesProvider) { - return _addressesProvider; + return ADDRESSES_PROVIDER; + } + + // internal functions + + struct ExecuteBorrowParams { + address asset; + address user; + uint256 amount; + uint256 interestRateMode; + address aTokenAddress; + uint16 referralCode; + bool releaseUnderlying; + } + + /** + * @dev Internal function to execute a borrowing action, allowing to transfer or not the underlying + * @param vars Input struct for the borrowing action, in order to avoid STD errors + **/ + function _executeBorrow(ExecuteBorrowParams memory vars) internal { + ReserveLogic.ReserveData storage reserve = _reserves[vars.asset]; + UserConfiguration.Map storage userConfig = _usersConfig[msg.sender]; + + address oracle = ADDRESSES_PROVIDER.getPriceOracle(); + + uint256 amountInETH = IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div( + 10**reserve.configuration.getDecimals() + ); + + ValidationLogic.validateBorrow( + reserve, + vars.asset, + vars.amount, + amountInETH, + vars.interestRateMode, + MAX_STABLE_RATE_BORROW_SIZE_PERCENT, + _reserves, + userConfig, + _reservesList, + oracle + ); + + uint256 reserveId = reserve.id; + if (!userConfig.isBorrowing(reserveId)) { + userConfig.setBorrowing(reserveId, true); + } + + reserve.updateState(); + + //caching the current stable borrow rate + uint256 currentStableRate = 0; + + if ( + ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE + ) { + currentStableRate = reserve.currentStableBorrowRate; + + IStableDebtToken(reserve.stableDebtTokenAddress).mint(vars.user, vars.amount, currentStableRate); + } else { + IVariableDebtToken(reserve.variableDebtTokenAddress).mint(vars.user, vars.amount, reserve.variableBorrowIndex); + } + + reserve.updateInterestRates( + vars.asset, + vars.aTokenAddress, + 0, + vars.releaseUnderlying ? vars.amount : 0 + ); + + if (vars.releaseUnderlying) { + IAToken(vars.aTokenAddress).transferUnderlyingTo(msg.sender, vars.amount); + } + + emit Borrow( + vars.asset, + msg.sender, + vars.amount, + vars.interestRateMode, + ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE + ? currentStableRate + : reserve.currentVariableBorrowRate, + vars.referralCode + ); + } + + /** + * @dev adds a reserve to the array of the _reserves address + **/ + function _addReserveToList(address asset) internal { + bool reserveAlreadyAdded = false; + for (uint256 i = 0; i < _reservesList.length; i++) + if (_reservesList[i] == asset) { + reserveAlreadyAdded = true; + } + if (!reserveAlreadyAdded) { + _reserves[asset].id = uint8(_reservesList.length); + _reservesList.push(asset); + } } } diff --git a/contracts/libraries/logic/ReserveLogic.sol b/contracts/libraries/logic/ReserveLogic.sol index e2f3df70..acb1e501 100644 --- a/contracts/libraries/logic/ReserveLogic.sol +++ b/contracts/libraries/logic/ReserveLogic.sol @@ -67,7 +67,7 @@ library ReserveLogic { address aTokenAddress; address stableDebtTokenAddress; address variableDebtTokenAddress; - + //address of the interest rate strategy address interestRateStrategyAddress; //the id of the reserve. Represents the position in the list of the active reserves @@ -121,42 +121,13 @@ library ReserveLogic { } /** - * @dev Updates the liquidity cumulative index Ci and variable borrow cumulative index Bvc. Refer to the whitepaper for - * a formal specification. + * @dev Updates the state of the reserve by minting to the reserve treasury and calculate the new + * reserve indexes * @param reserve the reserve object **/ function updateState(ReserveData storage reserve) internal { - - - uint256 currentLiquidityRate = reserve.currentLiquidityRate; - - //only cumulating if there is any income being produced - if (currentLiquidityRate > 0) { - uint40 lastUpdateTimestamp = reserve.lastUpdateTimestamp; - uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest( - currentLiquidityRate, - lastUpdateTimestamp - ); - uint256 index = cumulatedLiquidityInterest.rayMul(reserve.liquidityIndex); - require(index < (1 << 128), Errors.LIQUIDITY_INDEX_OVERFLOW); - - reserve.liquidityIndex = uint128(index); - - //as the liquidity rate might come only from stable rate loans, we need to ensure - //that there is actual variable debt before accumulating - if (IERC20(reserve.variableDebtTokenAddress).totalSupply() > 0) { - uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest( - reserve.currentVariableBorrowRate, - lastUpdateTimestamp - ); - index = cumulatedVariableBorrowInterest.rayMul(reserve.variableBorrowIndex); - require(index < (1 << 128), Errors.VARIABLE_BORROW_INDEX_OVERFLOW); - reserve.variableBorrowIndex = uint128(index); - } - } - - //solium-disable-next-line - reserve.lastUpdateTimestamp = uint40(block.timestamp); + _mintToTreasury(reserve); + _updateIndexes(reserve); } /** @@ -270,4 +241,36 @@ library ReserveLogic { reserve.variableBorrowIndex ); } + + function _updateIndexes(ReserveData storage reserve) internal { + uint256 currentLiquidityRate = reserve.currentLiquidityRate; + + //only cumulating if there is any income being produced + if (currentLiquidityRate > 0) { + uint40 lastUpdateTimestamp = reserve.lastUpdateTimestamp; + uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest( + currentLiquidityRate, + lastUpdateTimestamp + ); + uint256 index = cumulatedLiquidityInterest.rayMul(reserve.liquidityIndex); + require(index < (1 << 128), Errors.LIQUIDITY_INDEX_OVERFLOW); + + reserve.liquidityIndex = uint128(index); + + //as the liquidity rate might come only from stable rate loans, we need to ensure + //that there is actual variable debt before accumulating + if (IERC20(reserve.variableDebtTokenAddress).totalSupply() > 0) { + uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest( + reserve.currentVariableBorrowRate, + lastUpdateTimestamp + ); + index = cumulatedVariableBorrowInterest.rayMul(reserve.variableBorrowIndex); + require(index < (1 << 128), Errors.VARIABLE_BORROW_INDEX_OVERFLOW); + reserve.variableBorrowIndex = uint128(index); + } + } + + //solium-disable-next-line + reserve.lastUpdateTimestamp = uint40(block.timestamp); + } } diff --git a/contracts/tokenization/VariableDebtToken.sol b/contracts/tokenization/VariableDebtToken.sol index b43cbec4..d9a423eb 100644 --- a/contracts/tokenization/VariableDebtToken.sol +++ b/contracts/tokenization/VariableDebtToken.sol @@ -82,7 +82,19 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { return super.balanceOf(user); } + /** + * @dev Returns the total supply of the variable debt token. Represents the total debt accrued by the users + * @return the total supply + **/ function totalSupply() public virtual override view returns(uint256) { return super.totalSupply().rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET)); } + + /** + * @dev Returns the scaled total supply of the variable debt token. Represents sum(borrows/index) + * @return the scaled total supply + **/ + function scaledTotalSupply() public virtual override view returns(uint256) { + return super.totalSupply(); + } } diff --git a/contracts/tokenization/interfaces/IVariableDebtToken.sol b/contracts/tokenization/interfaces/IVariableDebtToken.sol index 3d26ed6e..320d0f5a 100644 --- a/contracts/tokenization/interfaces/IVariableDebtToken.sol +++ b/contracts/tokenization/interfaces/IVariableDebtToken.sol @@ -53,5 +53,11 @@ interface IVariableDebtToken { * @param user the user **/ function scaledBalanceOf(address user) external view returns(uint256); + + /** + * @dev Returns the scaled total supply of the variable debt token. Represents sum(borrows/index) + * @return the scaled total supply + **/ + function scaledTotalSupply() external view returns(uint256); }