// SPDX-License-Identifier: agpl-3.0 pragma solidity ^0.6.8; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {CoreLibrary} from "./CoreLibrary.sol"; import {UserLogic} from "./UserLogic.sol"; import {IPriceOracleGetter} from "../interfaces/IPriceOracleGetter.sol"; import {UniversalERC20} from "./UniversalERC20.sol"; import {IStableDebtToken} from '../tokenization/interfaces/IStableDebtToken.sol'; import "../configuration/LendingPoolAddressesProvider.sol"; import "../interfaces/ILendingRateOracle.sol"; import "../interfaces/IReserveInterestRateStrategy.sol"; import "../tokenization/AToken.sol"; import "./WadRayMath.sol"; /** * @title ReserveLogic library * @author Aave * @notice Implements the logic to update the state of the reserves */ library ReserveLogic { using SafeMath for uint256; using WadRayMath for uint256; using CoreLibrary for CoreLibrary.ReserveData; using CoreLibrary for CoreLibrary.UserReserveData; using UniversalERC20 for IERC20; using Address for address; using UserLogic for CoreLibrary.UserReserveData; /** * @dev Emitted when the state of a reserve is updated * @dev NOTE: This event replaces the Deprecated ReserveUpdated() event, which didn't emit the average stable borrow rate * @param reserve the address of the reserve * @param liquidityRate the new liquidity rate * @param stableBorrowRate the new stable borrow rate * @param averageStableBorrowRate the new average stable borrow rate * @param variableBorrowRate the new variable borrow rate * @param liquidityIndex the new liquidity index * @param variableBorrowIndex the new variable borrow index **/ event ReserveDataUpdated( address indexed reserve, uint256 liquidityRate, uint256 stableBorrowRate, uint256 averageStableBorrowRate, uint256 variableBorrowRate, uint256 liquidityIndex, uint256 variableBorrowIndex ); /** * @dev updates the state of the core as a result of a flashloan action * @param _reserve the address of the reserve in which the flashloan is happening * @param _income the income of the protocol as a result of the action **/ function updateStateOnFlashLoan( CoreLibrary.ReserveData storage _reserve, address _reserveAddress, uint256 _availableLiquidityBefore, uint256 _income, uint256 _protocolFee ) external { //compounding the cumulated interest _reserve.updateCumulativeIndexesAndTimestamp(); uint256 totalLiquidityBefore = _availableLiquidityBefore.add(_reserve.getTotalBorrows()); //compounding the received fee into the reserve _reserve.cumulateToLiquidityIndex(totalLiquidityBefore, _income); //refresh interest rates updateInterestRates(_reserve, _reserveAddress, _income, 0); } /** * @dev updates the state of the core as a consequence of a liquidation action. * @param _collateralReserve the collateral reserve that is being liquidated * @param _collateralToLiquidate the amount of collateral being liquidated * @param _liquidatorReceivesAToken true if the liquidator will receive aTokens, false otherwise **/ function updateStateOnLiquidationAsCollateral( CoreLibrary.ReserveData storage _collateralReserve, address _collateralReserveAddress, uint256 _collateralToLiquidate, bool _liquidatorReceivesAToken ) external { _collateralReserve.updateCumulativeIndexesAndTimestamp(); if (!_liquidatorReceivesAToken) { updateInterestRates( _collateralReserve, _collateralReserveAddress, 0, _collateralToLiquidate ); } } /** * @dev gets the total liquidity in the reserve. The total liquidity is the balance of the core contract + total borrows * @param _reserve the reserve address * @return the total liquidity **/ function getTotalLiquidity(CoreLibrary.ReserveData storage _reserve, address _reserveAddress) public view returns (uint256) { return IERC20(_reserveAddress).universalBalanceOf(address(this)).add( _reserve.getTotalBorrows() ); } /** * @dev Updates the reserve current stable borrow rate Rf, the current variable borrow rate Rv and the current liquidity rate Rl. * Also updates the lastUpdateTimestamp value. Please refer to the whitepaper for further information. * @param _reserve the address of the reserve to be updated * @param _liquidityAdded the amount of liquidity added to the protocol (deposit or repay) in the previous action * @param _liquidityTaken the amount of liquidity taken from the protocol (redeem or borrow) **/ function updateInterestRates( CoreLibrary.ReserveData storage _reserve, address _reserveAddress, uint256 _liquidityAdded, uint256 _liquidityTaken ) internal { uint256 currentAvgStableRate = IStableDebtToken(_reserve.stableDebtTokenAddress).getAverageStableRate(); uint256 balance = IERC20(_reserveAddress).universalBalanceOf(address(this)); //if the reserve is ETH, the msg.value has already been cumulated to the balance of the reserve if(IERC20(_reserveAddress).isETH()){ balance = balance.sub(msg.value); } (uint256 newLiquidityRate, uint256 newStableRate, uint256 newVariableRate) = IReserveInterestRateStrategy( _reserve .interestRateStrategyAddress ) .calculateInterestRates( _reserveAddress, balance.add(_liquidityAdded).sub(_liquidityTaken), IERC20(_reserve.stableDebtTokenAddress).totalSupply(), IERC20(_reserve.variableDebtTokenAddress).totalSupply(), currentAvgStableRate ); _reserve.currentLiquidityRate = newLiquidityRate; _reserve.currentStableBorrowRate = newStableRate; _reserve.currentVariableBorrowRate = newVariableRate; emit ReserveDataUpdated( _reserveAddress, newLiquidityRate, newStableRate, currentAvgStableRate, newVariableRate, _reserve.lastLiquidityCumulativeIndex, _reserve.lastVariableBorrowCumulativeIndex ); } /** * @dev gets the reserve current variable borrow rate. Is the base variable borrow rate if the reserve is empty * @param _reserve the reserve address * @return the reserve current variable borrow rate **/ function getReserveCurrentVariableBorrowRate(CoreLibrary.ReserveData storage _reserve) external view returns (uint256) { if (_reserve.currentVariableBorrowRate == 0) { return IReserveInterestRateStrategy(_reserve.interestRateStrategyAddress) .getBaseVariableBorrowRate(); } return _reserve.currentVariableBorrowRate; } /** * @dev gets the reserve current stable borrow rate. Is the market rate if the reserve is empty * @param _reserve the reserve address * @return the reserve current stable borrow rate **/ function getReserveCurrentStableBorrowRate( CoreLibrary.ReserveData storage _reserve, uint256 _baseRate ) public view returns (uint256) { return _reserve.currentStableBorrowRate == 0 ? _baseRate : _reserve.currentStableBorrowRate; } /** * @dev returns the utilization rate U of a specific reserve * @param _reserve the reserve for which the information is needed * @return the utilization rate in ray **/ function getUtilizationRate(CoreLibrary.ReserveData storage _reserve, address _reserveAddress) public view returns (uint256) { uint256 totalBorrows = _reserve.getTotalBorrows(); if (totalBorrows == 0) { return 0; } uint256 availableLiquidity = IERC20(_reserveAddress).universalBalanceOf(address(this)); return totalBorrows.rayDiv(availableLiquidity.add(totalBorrows)); } }