// SPDX-License-Identifier: agpl-3.0 pragma solidity ^0.6.8; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol"; import "../libraries/CoreLibrary.sol"; import "../configuration/LendingPoolAddressesProvider.sol"; import "../interfaces/ILendingRateOracle.sol"; import "../interfaces/IReserveInterestRateStrategy.sol"; import "../libraries/WadRayMath.sol"; import "../tokenization/AToken.sol"; import "../libraries/EthAddressLib.sol"; /** * @title LendingPoolCore contract * @author Aave * @notice Holds the state of the lending pool and all the funds deposited * @dev NOTE: The core does not enforce security checks on the update of the state * (eg, updateStateOnBorrow() does not enforce that borrowed is enabled on the reserve). * The check that an action can be performed is a duty of the overlying LendingPool contract. **/ contract LendingPoolCore is VersionedInitializable { using SafeMath for uint256; using WadRayMath for uint256; using CoreLibrary for CoreLibrary.ReserveData; using CoreLibrary for CoreLibrary.UserReserveData; using SafeERC20 for IERC20; using Address for address payable; /** * @dev DEPRECATED: This event was used in previous LendingPoolCore implementations, and it has been replaced by ReserveDataUpdated() * @param reserve the address of the reserve * @param liquidityRate the new liquidity rate * @param stableBorrowRate the new stable borrow rate * @param variableBorrowRate the new variable borrow rate * @param liquidityIndex the new liquidity index * @param variableBorrowIndex the new variable borrow index **/ event ReserveUpdated( address indexed reserve, uint256 liquidityRate, uint256 stableBorrowRate, uint256 variableBorrowRate, uint256 liquidityIndex, uint256 variableBorrowIndex ); /** * @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 ); address public lendingPoolAddress; LendingPoolAddressesProvider public addressesProvider; /** * @dev only lending pools can use functions affected by this modifier **/ modifier onlyLendingPool { require(lendingPoolAddress == msg.sender, "The caller must be a lending pool contract"); _; } /** * @dev only lending pools configurator can use functions affected by this modifier **/ modifier onlyLendingPoolConfigurator { require( addressesProvider.getLendingPoolConfigurator() == msg.sender, "The caller must be a lending pool configurator contract" ); _; } mapping(address => CoreLibrary.ReserveData) internal reserves; mapping(address => mapping(address => CoreLibrary.UserReserveData)) internal usersReserveData; address[] public reservesList; uint256 private constant CORE_REVISION = 0x7; /** * @dev returns the revision number of the contract **/ function getRevision() internal virtual override pure returns (uint256) { return CORE_REVISION; } /** * @dev initializes the Core contract, invoked upon registration on the AddressesProvider * @param _addressesProvider the addressesProvider contract **/ function initialize(LendingPoolAddressesProvider _addressesProvider) public virtual initializer { addressesProvider = _addressesProvider; refreshConfigInternal(); } /** * @dev updates the state of the core as a result of a deposit action * @param _reserve the address of the reserve in which the deposit is happening * @param _user the address of the the user depositing * @param _amount the amount being deposited * @param _isFirstDeposit true if the user is depositing for the first time **/ function updateStateOnDeposit( address _reserve, address _user, uint256 _amount, bool _isFirstDeposit ) external onlyLendingPool { reserves[_reserve].updateCumulativeIndexes(); updateReserveInterestRatesAndTimestampInternal(_reserve, _amount, 0); if (_isFirstDeposit) { //if this is the first deposit of the user, we configure the deposit as enabled to be used as collateral setUserUseReserveAsCollateral(_reserve, _user, true); } } /** * @dev updates the state of the core as a result of a redeem action * @param _reserve the address of the reserve in which the redeem is happening * @param _user the address of the the user redeeming * @param _amountRedeemed the amount being redeemed * @param _userRedeemedEverything true if the user is redeeming everything **/ function updateStateOnRedeem( address _reserve, address _user, uint256 _amountRedeemed, bool _userRedeemedEverything ) external onlyLendingPool { //compound liquidity and variable borrow interests reserves[_reserve].updateCumulativeIndexes(); updateReserveInterestRatesAndTimestampInternal(_reserve, 0, _amountRedeemed); //if user redeemed everything the useReserveAsCollateral flag is reset if (_userRedeemedEverything) { setUserUseReserveAsCollateral(_reserve, _user, false); } } /** * @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( address _reserve, uint256 _availableLiquidityBefore, uint256 _income, uint256 _protocolFee ) external onlyLendingPool { transferFlashLoanProtocolFeeInternal(_reserve, _protocolFee); //compounding the cumulated interest reserves[_reserve].updateCumulativeIndexes(); uint256 totalLiquidityBefore = _availableLiquidityBefore.add( getReserveTotalBorrows(_reserve) ); //compounding the received fee into the reserve reserves[_reserve].cumulateToLiquidityIndex(totalLiquidityBefore, _income); //refresh interest rates updateReserveInterestRatesAndTimestampInternal(_reserve, _income, 0); } /** * @dev updates the state of the core as a consequence of a borrow action. * @param _reserve the address of the reserve on which the user is borrowing * @param _user the address of the borrower * @param _amountBorrowed the new amount borrowed * @param _borrowFee the fee on the amount borrowed * @param _rateMode the borrow rate mode (stable, variable) * @return the new borrow rate for the user **/ function updateStateOnBorrow( address _reserve, address _user, uint256 _amountBorrowed, uint256 _borrowFee, CoreLibrary.InterestRateMode _rateMode ) external onlyLendingPool returns (uint256, uint256) { // getting the previous borrow data of the user (uint256 principalBorrowBalance, , uint256 balanceIncrease) = getUserBorrowBalances( _reserve, _user ); updateReserveStateOnBorrowInternal( _reserve, _user, principalBorrowBalance, balanceIncrease, _amountBorrowed, _rateMode ); updateUserStateOnBorrowInternal( _reserve, _user, _amountBorrowed, balanceIncrease, _borrowFee, _rateMode ); updateReserveInterestRatesAndTimestampInternal(_reserve, 0, _amountBorrowed); return (getUserCurrentBorrowRate(_reserve, _user), balanceIncrease); } /** * @dev updates the state of the core as a consequence of a repay action. * @param _reserve the address of the reserve on which the user is repaying * @param _user the address of the borrower * @param _paybackAmountMinusFees the amount being paid back minus fees * @param _originationFeeRepaid the fee on the amount that is being repaid * @param _balanceIncrease the accrued interest on the borrowed amount * @param _repaidWholeLoan true if the user is repaying the whole loan **/ function updateStateOnRepay( address _reserve, address _user, uint256 _paybackAmountMinusFees, uint256 _originationFeeRepaid, uint256 _balanceIncrease, bool _repaidWholeLoan ) external onlyLendingPool { updateReserveStateOnRepayInternal( _reserve, _user, _paybackAmountMinusFees, _balanceIncrease ); updateUserStateOnRepayInternal( _reserve, _user, _paybackAmountMinusFees, _originationFeeRepaid, _balanceIncrease, _repaidWholeLoan ); updateReserveInterestRatesAndTimestampInternal(_reserve, _paybackAmountMinusFees, 0); } /** * @dev updates the state of the core as a consequence of a swap rate action. * @param _reserve the address of the reserve on which the user is repaying * @param _user the address of the borrower * @param _principalBorrowBalance the amount borrowed by the user * @param _compoundedBorrowBalance the amount borrowed plus accrued interest * @param _balanceIncrease the accrued interest on the borrowed amount * @param _currentRateMode the current interest rate mode for the user **/ function updateStateOnSwapRate( address _reserve, address _user, uint256 _principalBorrowBalance, uint256 _compoundedBorrowBalance, uint256 _balanceIncrease, CoreLibrary.InterestRateMode _currentRateMode ) external onlyLendingPool returns (CoreLibrary.InterestRateMode, uint256) { updateReserveStateOnSwapRateInternal( _reserve, _user, _principalBorrowBalance, _compoundedBorrowBalance, _currentRateMode ); CoreLibrary.InterestRateMode newRateMode = updateUserStateOnSwapRateInternal( _reserve, _user, _balanceIncrease, _currentRateMode ); updateReserveInterestRatesAndTimestampInternal(_reserve, 0, 0); return (newRateMode, getUserCurrentBorrowRate(_reserve, _user)); } /** * @dev updates the state of the core as a consequence of a liquidation action. * @param _principalReserve the address of the principal reserve that is being repaid * @param _collateralReserve the address of the collateral reserve that is being liquidated * @param _user the address of the borrower * @param _amountToLiquidate the amount being repaid by the liquidator * @param _collateralToLiquidate the amount of collateral being liquidated * @param _feeLiquidated the amount of origination fee being liquidated * @param _liquidatedCollateralForFee the amount of collateral equivalent to the origination fee + bonus * @param _balanceIncrease the accrued interest on the borrowed amount * @param _liquidatorReceivesAToken true if the liquidator will receive aTokens, false otherwise **/ function updateStateOnLiquidation( address _principalReserve, address _collateralReserve, address _user, uint256 _amountToLiquidate, uint256 _collateralToLiquidate, uint256 _feeLiquidated, uint256 _liquidatedCollateralForFee, uint256 _balanceIncrease, bool _liquidatorReceivesAToken ) external onlyLendingPool { updatePrincipalReserveStateOnLiquidationInternal( _principalReserve, _user, _amountToLiquidate, _balanceIncrease ); updateCollateralReserveStateOnLiquidationInternal( _collateralReserve ); updateUserStateOnLiquidationInternal( _principalReserve, _user, _amountToLiquidate, _feeLiquidated, _balanceIncrease ); updateReserveInterestRatesAndTimestampInternal(_principalReserve, _amountToLiquidate, 0); if (!_liquidatorReceivesAToken) { updateReserveInterestRatesAndTimestampInternal( _collateralReserve, 0, _collateralToLiquidate.add(_liquidatedCollateralForFee) ); } } /** * @dev updates the state of the core as a consequence of a stable rate rebalance * @param _reserve the address of the principal reserve where the user borrowed * @param _user the address of the borrower * @param _balanceIncrease the accrued interest on the borrowed amount * @return the new stable rate for the user **/ function updateStateOnRebalance(address _reserve, address _user, uint256 _balanceIncrease) external onlyLendingPool returns (uint256) { updateReserveStateOnRebalanceInternal(_reserve, _user, _balanceIncrease); //update user data and rebalance the rate updateUserStateOnRebalanceInternal(_reserve, _user, _balanceIncrease); updateReserveInterestRatesAndTimestampInternal(_reserve, 0, 0); return usersReserveData[_user][_reserve].stableBorrowRate; } /** * @dev enables or disables a reserve as collateral * @param _reserve the address of the principal reserve where the user deposited * @param _user the address of the depositor * @param _useAsCollateral true if the depositor wants to use the reserve as collateral **/ function setUserUseReserveAsCollateral(address _reserve, address _user, bool _useAsCollateral) public onlyLendingPool { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; user.useAsCollateral = _useAsCollateral; } /** * @notice ETH/token transfer functions **/ /** * @dev fallback function enforces that the caller is a contract, to support flashloan transfers **/ receive() external payable { //only contracts can send ETH to the core require(msg.sender.isContract(), "Only contracts can send ether to the Lending pool core"); } /** * @dev transfers to the user a specific amount from the reserve. * @param _reserve the address of the reserve where the transfer is happening * @param _user the address of the user receiving the transfer * @param _amount the amount being transferred **/ function transferToUser(address _reserve, address payable _user, uint256 _amount) external onlyLendingPool { if (_reserve != EthAddressLib.ethAddress()) { IERC20(_reserve).safeTransfer(_user, _amount); } else { //solium-disable-next-line (bool result, ) = _user.call{value: _amount, gas: 50000}(""); require(result, "Transfer of ETH failed"); } } /** * @dev transfers the protocol fees to the fees collection address * @param _token the address of the token being transferred * @param _user the address of the user from where the transfer is happening * @param _amount the amount being transferred * @param _destination the fee receiver address **/ function transferToFeeCollectionAddress( address _token, address _user, uint256 _amount, address _destination ) external payable onlyLendingPool { address payable feeAddress = address(uint160(_destination)); //cast the address to payable if (_token != EthAddressLib.ethAddress()) { require( msg.value == 0, "User is sending ETH along with the ERC20 transfer. Check the value attribute of the transaction" ); IERC20(_token).safeTransferFrom(_user, feeAddress, _amount); } else { require(msg.value >= _amount, "The amount and the value sent to deposit do not match"); //solium-disable-next-line (bool result, ) = feeAddress.call{ value: _amount, gas: 50000}(""); require(result, "Transfer of ETH failed"); } } /** * @dev transfers the fees to the fees collection address in the case of liquidation * @param _token the address of the token being transferred * @param _amount the amount being transferred * @param _destination the fee receiver address **/ function liquidateFee( address _token, uint256 _amount, address _destination ) external payable onlyLendingPool { address payable feeAddress = address(uint160(_destination)); //cast the address to payable require( msg.value == 0, "Fee liquidation does not require any transfer of value" ); if (_token != EthAddressLib.ethAddress()) { IERC20(_token).safeTransfer(feeAddress, _amount); } else { //solium-disable-next-line (bool result, ) = feeAddress.call{ value: _amount, gas: 50000}(""); require(result, "Transfer of ETH failed"); } } /** * @dev transfers an amount from a user to the destination reserve * @param _reserve the address of the reserve where the amount is being transferred * @param _user the address of the user from where the transfer is happening * @param _amount the amount being transferred **/ function transferToReserve(address _reserve, address payable _user, uint256 _amount) external payable onlyLendingPool { if (_reserve != EthAddressLib.ethAddress()) { require(msg.value == 0, "User is sending ETH along with the ERC20 transfer."); IERC20(_reserve).safeTransferFrom(_user, address(this), _amount); } else { require(msg.value >= _amount, "The amount and the value sent to deposit do not match"); if (msg.value > _amount) { //send back excess ETH uint256 excessAmount = msg.value.sub(_amount); //solium-disable-next-line (bool result, ) = _user.call{ value: _amount, gas: 50000}(""); require(result, "Transfer of ETH failed"); } } } /** * @notice data access functions **/ /** * @dev returns the basic data (balances, fee accrued, reserve enabled/disabled as collateral) * needed to calculate the global account data in the LendingPoolDataProvider * @param _reserve the address of the reserve * @param _user the address of the user * @return the user deposited balance, the principal borrow balance, the fee, and if the reserve is enabled as collateral or not **/ function getUserBasicReserveData(address _reserve, address _user) external view returns (uint256, uint256, uint256, bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; uint256 underlyingBalance = getUserUnderlyingAssetBalance(_reserve, _user); if (user.principalBorrowBalance == 0) { return (underlyingBalance, 0, 0, user.useAsCollateral); } return ( underlyingBalance, user.getCompoundedBorrowBalance(reserve), user.originationFee, user.useAsCollateral ); } /** * @dev checks if a user is allowed to borrow at a stable rate * @param _reserve the reserve address * @param _user the user * @param _amount the amount the the user wants to borrow * @return true if the user is allowed to borrow at a stable rate, false otherwise **/ function isUserAllowedToBorrowAtStable(address _reserve, address _user, uint256 _amount) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; if (!reserve.isStableBorrowRateEnabled) return false; return !user.useAsCollateral || !reserve.usageAsCollateralEnabled || _amount > getUserUnderlyingAssetBalance(_reserve, _user); } /** * @dev gets the underlying asset balance of a user based on the corresponding aToken balance. * @param _reserve the reserve address * @param _user the user address * @return the underlying deposit balance of the user **/ function getUserUnderlyingAssetBalance(address _reserve, address _user) public view returns (uint256) { AToken aToken = AToken(reserves[_reserve].aTokenAddress); return aToken.balanceOf(_user); } /** * @dev gets the interest rate strategy contract address for the reserve * @param _reserve the reserve address * @return the address of the interest rate strategy contract **/ function getReserveInterestRateStrategyAddress(address _reserve) public view returns (address) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.interestRateStrategyAddress; } /** * @dev gets the aToken contract address for the reserve * @param _reserve the reserve address * @return the address of the aToken contract **/ function getReserveATokenAddress(address _reserve) public view returns (address) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.aTokenAddress; } /** * @dev gets the available liquidity in the reserve. The available liquidity is the balance of the core contract * @param _reserve the reserve address * @return the available liquidity **/ function getReserveAvailableLiquidity(address _reserve) public view returns (uint256) { uint256 balance = 0; if (_reserve == EthAddressLib.ethAddress()) { balance = address(this).balance; } else { balance = IERC20(_reserve).balanceOf(address(this)); } return balance; } /** * @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 getReserveTotalLiquidity(address _reserve) public view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return getReserveAvailableLiquidity(_reserve).add(reserve.getTotalBorrows()); } /** * @dev gets the normalized income of the reserve. a value of 1e27 means there is no income. A value of 2e27 means there * there has been 100% income. * @param _reserve the reserve address * @return the reserve normalized income **/ function getReserveNormalizedIncome(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.getNormalizedIncome(); } /** * @dev gets the reserve total borrows * @param _reserve the reserve address * @return the total borrows (stable + variable) **/ function getReserveTotalBorrows(address _reserve) public view returns (uint256) { return reserves[_reserve].getTotalBorrows(); } /** * @dev gets the reserve total borrows stable * @param _reserve the reserve address * @return the total borrows stable **/ function getReserveTotalBorrowsStable(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.totalBorrowsStable; } /** * @dev gets the reserve total borrows variable * @param _reserve the reserve address * @return the total borrows variable **/ function getReserveTotalBorrowsVariable(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.totalBorrowsVariable; } /** * @dev gets the reserve liquidation threshold * @param _reserve the reserve address * @return the reserve liquidation threshold **/ function getReserveLiquidationThreshold(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.liquidationThreshold; } /** * @dev gets the reserve liquidation bonus * @param _reserve the reserve address * @return the reserve liquidation bonus **/ function getReserveLiquidationBonus(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.liquidationBonus; } /** * @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(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; 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(address _reserve) public view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; ILendingRateOracle oracle = ILendingRateOracle(addressesProvider.getLendingRateOracle()); if (reserve.currentStableBorrowRate == 0) { //no stable rate borrows yet return oracle.getMarketBorrowRate(_reserve); } return reserve.currentStableBorrowRate; } /** * @dev gets the reserve average stable borrow rate. The average stable rate is the weighted average * of all the loans taken at stable rate. * @param _reserve the reserve address * @return the reserve current average borrow rate **/ function getReserveCurrentAverageStableBorrowRate(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.currentAverageStableBorrowRate; } /** * @dev gets the reserve liquidity rate * @param _reserve the reserve address * @return the reserve liquidity rate **/ function getReserveCurrentLiquidityRate(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.currentLiquidityRate; } /** * @dev gets the reserve liquidity cumulative index * @param _reserve the reserve address * @return the reserve liquidity cumulative index **/ function getReserveLiquidityCumulativeIndex(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.lastLiquidityCumulativeIndex; } /** * @dev gets the reserve variable borrow index * @param _reserve the reserve address * @return the reserve variable borrow index **/ function getReserveVariableBorrowsCumulativeIndex(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.lastVariableBorrowCumulativeIndex; } /** * @dev this function aggregates the configuration parameters of the reserve. * It's used in the LendingPoolDataProvider specifically to save gas, and avoid * multiple external contract calls to fetch the same data. * @param _reserve the reserve address * @return the reserve decimals * @return the base ltv as collateral * @return the liquidation threshold * @return if the reserve is used as collateral or not **/ function getReserveConfiguration(address _reserve) external view returns (uint256, uint256, uint256, bool) { uint256 decimals; uint256 baseLTVasCollateral; uint256 liquidationThreshold; bool usageAsCollateralEnabled; CoreLibrary.ReserveData storage reserve = reserves[_reserve]; decimals = reserve.decimals; baseLTVasCollateral = reserve.baseLTVasCollateral; liquidationThreshold = reserve.liquidationThreshold; usageAsCollateralEnabled = reserve.usageAsCollateralEnabled; return (decimals, baseLTVasCollateral, liquidationThreshold, usageAsCollateralEnabled); } /** * @dev returns the decimals of the reserve * @param _reserve the reserve address * @return the reserve decimals **/ function getReserveDecimals(address _reserve) external view returns (uint256) { return reserves[_reserve].decimals; } /** * @dev returns true if the reserve is enabled for borrowing * @param _reserve the reserve address * @return true if the reserve is enabled for borrowing, false otherwise **/ function isReserveBorrowingEnabled(address _reserve) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.borrowingEnabled; } /** * @dev returns true if the reserve is enabled as collateral * @param _reserve the reserve address * @return true if the reserve is enabled as collateral, false otherwise **/ function isReserveUsageAsCollateralEnabled(address _reserve) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.usageAsCollateralEnabled; } /** * @dev returns true if the stable rate is enabled on reserve * @param _reserve the reserve address * @return true if the stable rate is enabled on reserve, false otherwise **/ function getReserveIsStableBorrowRateEnabled(address _reserve) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.isStableBorrowRateEnabled; } /** * @dev returns true if the reserve is active * @param _reserve the reserve address * @return true if the reserve is active, false otherwise **/ function getReserveIsActive(address _reserve) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.isActive; } /** * @notice returns if a reserve is freezed * @param _reserve the reserve for which the information is needed * @return true if the reserve is freezed, false otherwise **/ function getReserveIsFreezed(address _reserve) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.isFreezed; } /** * @notice returns the timestamp of the last action on the reserve * @param _reserve the reserve for which the information is needed * @return timestamp the last updated timestamp of the reserve **/ function getReserveLastUpdate(address _reserve) external view returns (uint40 timestamp) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; timestamp = reserve.lastUpdateTimestamp; } /** * @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 getReserveUtilizationRate(address _reserve) public view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; uint256 totalBorrows = reserve.getTotalBorrows(); if (totalBorrows == 0) { return 0; } uint256 availableLiquidity = getReserveAvailableLiquidity(_reserve); return totalBorrows.rayDiv(availableLiquidity.add(totalBorrows)); } /** * @return the array of reserves configured on the core **/ function getReserves() external view returns (address[] memory) { return reservesList; } /** * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return true if the user has chosen to use the reserve as collateral, false otherwise **/ function isUserUseReserveAsCollateralEnabled(address _reserve, address _user) external view returns (bool) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; return user.useAsCollateral; } /** * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return the origination fee for the user **/ function getUserOriginationFee(address _reserve, address _user) external view returns (uint256) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; return user.originationFee; } /** * @dev users with no loans in progress have NONE as borrow rate mode * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return the borrow rate mode for the user, **/ function getUserCurrentBorrowRateMode(address _reserve, address _user) public view returns (CoreLibrary.InterestRateMode) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; if (user.principalBorrowBalance == 0) { return CoreLibrary.InterestRateMode.NONE; } return user.stableBorrowRate > 0 ? CoreLibrary.InterestRateMode.STABLE : CoreLibrary.InterestRateMode.VARIABLE; } /** * @dev gets the current borrow rate of the user * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return the borrow rate for the user, **/ function getUserCurrentBorrowRate(address _reserve, address _user) internal view returns (uint256) { CoreLibrary.InterestRateMode rateMode = getUserCurrentBorrowRateMode(_reserve, _user); if (rateMode == CoreLibrary.InterestRateMode.NONE) { return 0; } return rateMode == CoreLibrary.InterestRateMode.STABLE ? usersReserveData[_user][_reserve].stableBorrowRate : reserves[_reserve].currentVariableBorrowRate; } /** * @dev the stable rate returned is 0 if the user is borrowing at variable or not borrowing at all * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return the user stable rate **/ function getUserCurrentStableBorrowRate(address _reserve, address _user) external view returns (uint256) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; return user.stableBorrowRate; } /** * @dev calculates and returns the borrow balances of the user * @param _reserve the address of the reserve * @param _user the address of the user * @return the principal borrow balance, the compounded balance and the balance increase since the last borrow/repay/swap/rebalance **/ function getUserBorrowBalances(address _reserve, address _user) public view returns (uint256, uint256, uint256) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; if (user.principalBorrowBalance == 0) { return (0, 0, 0); } uint256 principal = user.principalBorrowBalance; uint256 compoundedBalance = CoreLibrary.getCompoundedBorrowBalance( user, reserves[_reserve] ); return (principal, compoundedBalance, compoundedBalance.sub(principal)); } /** * @dev the variable borrow index of the user is 0 if the user is not borrowing or borrowing at stable * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return the variable borrow index for the user **/ function getUserVariableBorrowCumulativeIndex(address _reserve, address _user) external view returns (uint256) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; return user.lastVariableBorrowCumulativeIndex; } /** * @dev the variable borrow index of the user is 0 if the user is not borrowing or borrowing at stable * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return timestamp the variable borrow index for the user **/ function getUserLastUpdate(address _reserve, address _user) external view returns (uint256 timestamp) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; timestamp = user.lastUpdateTimestamp; } /** * @dev updates the lending pool core configuration **/ function refreshConfiguration() external onlyLendingPoolConfigurator { refreshConfigInternal(); } /** * @dev initializes a reserve * @param _reserve the address of the reserve * @param _aTokenAddress the address of the overlying aToken contract * @param _decimals the decimals of the reserve currency * @param _interestRateStrategyAddress the address of the interest rate strategy contract **/ function initReserve( address _reserve, address _aTokenAddress, uint256 _decimals, address _interestRateStrategyAddress ) external onlyLendingPoolConfigurator { reserves[_reserve].init(_aTokenAddress, _decimals, _interestRateStrategyAddress); addReserveToListInternal(_reserve); } /** * @dev removes the last added reserve in the reservesList array * @param _reserveToRemove the address of the reserve **/ function removeLastAddedReserve(address _reserveToRemove) external onlyLendingPoolConfigurator { address lastReserve = reservesList[reservesList.length-1]; require(lastReserve == _reserveToRemove, "Reserve being removed is different than the reserve requested"); //as we can't check if totalLiquidity is 0 (since the reserve added might not be an ERC20) we at least check that there is nothing borrowed require(getReserveTotalBorrows(lastReserve) == 0, "Cannot remove a reserve with liquidity deposited"); reserves[lastReserve].isActive = false; reserves[lastReserve].aTokenAddress = address(0); reserves[lastReserve].decimals = 0; reserves[lastReserve].lastLiquidityCumulativeIndex = 0; reserves[lastReserve].lastVariableBorrowCumulativeIndex = 0; reserves[lastReserve].borrowingEnabled = false; reserves[lastReserve].usageAsCollateralEnabled = false; reserves[lastReserve].baseLTVasCollateral = 0; reserves[lastReserve].liquidationThreshold = 0; reserves[lastReserve].liquidationBonus = 0; reserves[lastReserve].interestRateStrategyAddress = address(0); reservesList.pop(); } /** * @dev updates the address of the interest rate strategy contract * @param _reserve the address of the reserve * @param _rateStrategyAddress the address of the interest rate strategy contract **/ function setReserveInterestRateStrategyAddress(address _reserve, address _rateStrategyAddress) external onlyLendingPoolConfigurator { reserves[_reserve].interestRateStrategyAddress = _rateStrategyAddress; } /** * @dev enables borrowing on a reserve. Also sets the stable rate borrowing * @param _reserve the address of the reserve * @param _stableBorrowRateEnabled true if the stable rate needs to be enabled, false otherwise **/ function enableBorrowingOnReserve(address _reserve, bool _stableBorrowRateEnabled) external onlyLendingPoolConfigurator { reserves[_reserve].enableBorrowing(_stableBorrowRateEnabled); } /** * @dev disables borrowing on a reserve * @param _reserve the address of the reserve **/ function disableBorrowingOnReserve(address _reserve) external onlyLendingPoolConfigurator { reserves[_reserve].disableBorrowing(); } /** * @dev enables a reserve to be used as collateral * @param _reserve the address of the reserve **/ function enableReserveAsCollateral( address _reserve, uint256 _baseLTVasCollateral, uint256 _liquidationThreshold, uint256 _liquidationBonus ) external onlyLendingPoolConfigurator { reserves[_reserve].enableAsCollateral( _baseLTVasCollateral, _liquidationThreshold, _liquidationBonus ); } /** * @dev disables a reserve to be used as collateral * @param _reserve the address of the reserve **/ function disableReserveAsCollateral(address _reserve) external onlyLendingPoolConfigurator { reserves[_reserve].disableAsCollateral(); } /** * @dev enable the stable borrow rate mode on a reserve * @param _reserve the address of the reserve **/ function enableReserveStableBorrowRate(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.isStableBorrowRateEnabled = true; } /** * @dev disable the stable borrow rate mode on a reserve * @param _reserve the address of the reserve **/ function disableReserveStableBorrowRate(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.isStableBorrowRateEnabled = false; } /** * @dev activates a reserve * @param _reserve the address of the reserve **/ function activateReserve(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; require( reserve.lastLiquidityCumulativeIndex > 0 && reserve.lastVariableBorrowCumulativeIndex > 0, "Reserve has not been initialized yet" ); reserve.isActive = true; } /** * @dev deactivates a reserve * @param _reserve the address of the reserve **/ function deactivateReserve(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.isActive = false; } /** * @notice allows the configurator to freeze the reserve. * A freezed reserve does not allow any action apart from repay, redeem, liquidationCall, rebalance. * @param _reserve the address of the reserve **/ function freezeReserve(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.isFreezed = true; } /** * @notice allows the configurator to unfreeze the reserve. A unfreezed reserve allows any action to be executed. * @param _reserve the address of the reserve **/ function unfreezeReserve(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.isFreezed = false; } /** * @notice allows the configurator to update the loan to value of a reserve * @param _reserve the address of the reserve * @param _ltv the new loan to value **/ function setReserveBaseLTVasCollateral(address _reserve, uint256 _ltv) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.baseLTVasCollateral = _ltv; } /** * @notice allows the configurator to update the liquidation threshold of a reserve * @param _reserve the address of the reserve * @param _threshold the new liquidation threshold **/ function setReserveLiquidationThreshold(address _reserve, uint256 _threshold) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.liquidationThreshold = _threshold; } /** * @notice allows the configurator to update the liquidation bonus of a reserve * @param _reserve the address of the reserve * @param _bonus the new liquidation bonus **/ function setReserveLiquidationBonus(address _reserve, uint256 _bonus) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.liquidationBonus = _bonus; } /** * @notice allows the configurator to update the reserve decimals * @param _reserve the address of the reserve * @param _decimals the decimals of the reserve **/ function setReserveDecimals(address _reserve, uint256 _decimals) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.decimals = _decimals; } /** * @notice internal functions **/ /** * @dev updates the state of a reserve as a consequence of a borrow action. * @param _reserve the address of the reserve on which the user is borrowing * @param _user the address of the borrower * @param _principalBorrowBalance the previous borrow balance of the borrower before the action * @param _balanceIncrease the accrued interest of the user on the previous borrowed amount * @param _amountBorrowed the new amount borrowed * @param _rateMode the borrow rate mode (stable, variable) **/ function updateReserveStateOnBorrowInternal( address _reserve, address _user, uint256 _principalBorrowBalance, uint256 _balanceIncrease, uint256 _amountBorrowed, CoreLibrary.InterestRateMode _rateMode ) internal { reserves[_reserve].updateCumulativeIndexes(); //increasing reserve total borrows to account for the new borrow balance of the user //NOTE: Depending on the previous borrow mode, the borrows might need to be switched from variable to stable or vice versa updateReserveTotalBorrowsByRateModeInternal( _reserve, _user, _principalBorrowBalance, _balanceIncrease, _amountBorrowed, _rateMode ); } /** * @dev updates the state of a user as a consequence of a borrow action. * @param _reserve the address of the reserve on which the user is borrowing * @param _user the address of the borrower * @param _amountBorrowed the amount borrowed * @param _balanceIncrease the accrued interest of the user on the previous borrowed amount * @param _rateMode the borrow rate mode (stable, variable) **/ function updateUserStateOnBorrowInternal( address _reserve, address _user, uint256 _amountBorrowed, uint256 _balanceIncrease, uint256 _fee, CoreLibrary.InterestRateMode _rateMode ) internal { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; if (_rateMode == CoreLibrary.InterestRateMode.STABLE) { //stable //reset the user variable index, and update the stable rate user.stableBorrowRate = reserve.currentStableBorrowRate; user.lastVariableBorrowCumulativeIndex = 0; } else if (_rateMode == CoreLibrary.InterestRateMode.VARIABLE) { //variable //reset the user stable rate, and store the new borrow index user.stableBorrowRate = 0; user.lastVariableBorrowCumulativeIndex = reserve.lastVariableBorrowCumulativeIndex; } else { revert("Invalid borrow rate mode"); } //increase the principal borrows and the origination fee user.principalBorrowBalance = user.principalBorrowBalance.add(_amountBorrowed).add( _balanceIncrease ); user.originationFee = user.originationFee.add(_fee); //solium-disable-next-line user.lastUpdateTimestamp = uint40(block.timestamp); } /** * @dev updates the state of the reserve as a consequence of a repay action. * @param _reserve the address of the reserve on which the user is repaying * @param _user the address of the borrower * @param _paybackAmountMinusFees the amount being paid back minus fees * @param _balanceIncrease the accrued interest on the borrowed amount **/ function updateReserveStateOnRepayInternal( address _reserve, address _user, uint256 _paybackAmountMinusFees, uint256 _balanceIncrease ) internal { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; CoreLibrary.InterestRateMode borrowRateMode = getUserCurrentBorrowRateMode(_reserve, _user); //update the indexes reserves[_reserve].updateCumulativeIndexes(); //compound the cumulated interest to the borrow balance and then subtracting the payback amount if (borrowRateMode == CoreLibrary.InterestRateMode.STABLE) { reserve.increaseTotalBorrowsStableAndUpdateAverageRate( _balanceIncrease, user.stableBorrowRate ); reserve.decreaseTotalBorrowsStableAndUpdateAverageRate( _paybackAmountMinusFees, user.stableBorrowRate ); } else { reserve.increaseTotalBorrowsVariable(_balanceIncrease); reserve.decreaseTotalBorrowsVariable(_paybackAmountMinusFees); } } /** * @dev updates the state of the user as a consequence of a repay action. * @param _reserve the address of the reserve on which the user is repaying * @param _user the address of the borrower * @param _paybackAmountMinusFees the amount being paid back minus fees * @param _originationFeeRepaid the fee on the amount that is being repaid * @param _balanceIncrease the accrued interest on the borrowed amount * @param _repaidWholeLoan true if the user is repaying the whole loan **/ function updateUserStateOnRepayInternal( address _reserve, address _user, uint256 _paybackAmountMinusFees, uint256 _originationFeeRepaid, uint256 _balanceIncrease, bool _repaidWholeLoan ) internal { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; //update the user principal borrow balance, adding the cumulated interest and then subtracting the payback amount user.principalBorrowBalance = user.principalBorrowBalance.add(_balanceIncrease).sub( _paybackAmountMinusFees ); user.lastVariableBorrowCumulativeIndex = reserve.lastVariableBorrowCumulativeIndex; //if the balance decrease is equal to the previous principal (user is repaying the whole loan) //and the rate mode is stable, we reset the interest rate mode of the user if (_repaidWholeLoan) { user.stableBorrowRate = 0; user.lastVariableBorrowCumulativeIndex = 0; } user.originationFee = user.originationFee.sub(_originationFeeRepaid); //solium-disable-next-line user.lastUpdateTimestamp = uint40(block.timestamp); } /** * @dev updates the state of the user as a consequence of a swap rate action. * @param _reserve the address of the reserve on which the user is performing the rate swap * @param _user the address of the borrower * @param _principalBorrowBalance the the principal amount borrowed by the user * @param _compoundedBorrowBalance the principal amount plus the accrued interest * @param _currentRateMode the rate mode at which the user borrowed **/ function updateReserveStateOnSwapRateInternal( address _reserve, address _user, uint256 _principalBorrowBalance, uint256 _compoundedBorrowBalance, CoreLibrary.InterestRateMode _currentRateMode ) internal { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; //compounding reserve indexes reserve.updateCumulativeIndexes(); if (_currentRateMode == CoreLibrary.InterestRateMode.STABLE) { uint256 userCurrentStableRate = user.stableBorrowRate; //swap to variable reserve.decreaseTotalBorrowsStableAndUpdateAverageRate( _principalBorrowBalance, userCurrentStableRate ); //decreasing stable from old principal balance reserve.increaseTotalBorrowsVariable(_compoundedBorrowBalance); //increase variable borrows } else if (_currentRateMode == CoreLibrary.InterestRateMode.VARIABLE) { //swap to stable uint256 currentStableRate = reserve.currentStableBorrowRate; reserve.decreaseTotalBorrowsVariable(_principalBorrowBalance); reserve.increaseTotalBorrowsStableAndUpdateAverageRate( _compoundedBorrowBalance, currentStableRate ); } else { revert("Invalid rate mode received"); } } /** * @dev updates the state of the user as a consequence of a swap rate action. * @param _reserve the address of the reserve on which the user is performing the swap * @param _user the address of the borrower * @param _balanceIncrease the accrued interest on the borrowed amount * @param _currentRateMode the current rate mode of the user **/ function updateUserStateOnSwapRateInternal( address _reserve, address _user, uint256 _balanceIncrease, CoreLibrary.InterestRateMode _currentRateMode ) internal returns (CoreLibrary.InterestRateMode) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.InterestRateMode newMode = CoreLibrary.InterestRateMode.NONE; if (_currentRateMode == CoreLibrary.InterestRateMode.VARIABLE) { //switch to stable newMode = CoreLibrary.InterestRateMode.STABLE; user.stableBorrowRate = reserve.currentStableBorrowRate; user.lastVariableBorrowCumulativeIndex = 0; } else if (_currentRateMode == CoreLibrary.InterestRateMode.STABLE) { newMode = CoreLibrary.InterestRateMode.VARIABLE; user.stableBorrowRate = 0; user.lastVariableBorrowCumulativeIndex = reserve.lastVariableBorrowCumulativeIndex; } else { revert("Invalid interest rate mode received"); } //compounding cumulated interest user.principalBorrowBalance = user.principalBorrowBalance.add(_balanceIncrease); //solium-disable-next-line user.lastUpdateTimestamp = uint40(block.timestamp); return newMode; } /** * @dev updates the state of the principal reserve as a consequence of a liquidation action. * @param _principalReserve the address of the principal reserve that is being repaid * @param _user the address of the borrower * @param _amountToLiquidate the amount being repaid by the liquidator * @param _balanceIncrease the accrued interest on the borrowed amount **/ function updatePrincipalReserveStateOnLiquidationInternal( address _principalReserve, address _user, uint256 _amountToLiquidate, uint256 _balanceIncrease ) internal { CoreLibrary.ReserveData storage reserve = reserves[_principalReserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_principalReserve]; //update principal reserve data reserve.updateCumulativeIndexes(); CoreLibrary.InterestRateMode borrowRateMode = getUserCurrentBorrowRateMode( _principalReserve, _user ); if (borrowRateMode == CoreLibrary.InterestRateMode.STABLE) { //increase the total borrows by the compounded interest reserve.increaseTotalBorrowsStableAndUpdateAverageRate( _balanceIncrease, user.stableBorrowRate ); //decrease by the actual amount to liquidate reserve.decreaseTotalBorrowsStableAndUpdateAverageRate( _amountToLiquidate, user.stableBorrowRate ); } else { //increase the total borrows by the compounded interest reserve.increaseTotalBorrowsVariable(_balanceIncrease); //decrease by the actual amount to liquidate reserve.decreaseTotalBorrowsVariable(_amountToLiquidate); } } /** * @dev updates the state of the collateral reserve as a consequence of a liquidation action. * @param _collateralReserve the address of the collateral reserve that is being liquidated **/ function updateCollateralReserveStateOnLiquidationInternal( address _collateralReserve ) internal { //update collateral reserve reserves[_collateralReserve].updateCumulativeIndexes(); } /** * @dev updates the state of the user being liquidated as a consequence of a liquidation action. * @param _reserve the address of the principal reserve that is being repaid * @param _user the address of the borrower * @param _amountToLiquidate the amount being repaid by the liquidator * @param _feeLiquidated the amount of origination fee being liquidated * @param _balanceIncrease the accrued interest on the borrowed amount **/ function updateUserStateOnLiquidationInternal( address _reserve, address _user, uint256 _amountToLiquidate, uint256 _feeLiquidated, uint256 _balanceIncrease ) internal { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; CoreLibrary.ReserveData storage reserve = reserves[_reserve]; //first increase by the compounded interest, then decrease by the liquidated amount user.principalBorrowBalance = user.principalBorrowBalance.add(_balanceIncrease).sub( _amountToLiquidate ); if ( getUserCurrentBorrowRateMode(_reserve, _user) == CoreLibrary.InterestRateMode.VARIABLE ) { user.lastVariableBorrowCumulativeIndex = reserve.lastVariableBorrowCumulativeIndex; } if(_feeLiquidated > 0){ user.originationFee = user.originationFee.sub(_feeLiquidated); } //solium-disable-next-line user.lastUpdateTimestamp = uint40(block.timestamp); } /** * @dev updates the state of the reserve as a consequence of a stable rate rebalance * @param _reserve the address of the principal reserve where the user borrowed * @param _user the address of the borrower * @param _balanceIncrease the accrued interest on the borrowed amount **/ function updateReserveStateOnRebalanceInternal( address _reserve, address _user, uint256 _balanceIncrease ) internal { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; reserve.updateCumulativeIndexes(); reserve.increaseTotalBorrowsStableAndUpdateAverageRate( _balanceIncrease, user.stableBorrowRate ); } /** * @dev updates the state of the user as a consequence of a stable rate rebalance * @param _reserve the address of the principal reserve where the user borrowed * @param _user the address of the borrower * @param _balanceIncrease the accrued interest on the borrowed amount **/ function updateUserStateOnRebalanceInternal( address _reserve, address _user, uint256 _balanceIncrease ) internal { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; CoreLibrary.ReserveData storage reserve = reserves[_reserve]; user.principalBorrowBalance = user.principalBorrowBalance.add(_balanceIncrease); user.stableBorrowRate = reserve.currentStableBorrowRate; //solium-disable-next-line user.lastUpdateTimestamp = uint40(block.timestamp); } /** * @dev updates the state of the user as a consequence of a stable rate rebalance * @param _reserve the address of the principal reserve where the user borrowed * @param _user the address of the borrower * @param _balanceIncrease the accrued interest on the borrowed amount * @param _amountBorrowed the accrued interest on the borrowed amount **/ function updateReserveTotalBorrowsByRateModeInternal( address _reserve, address _user, uint256 _principalBalance, uint256 _balanceIncrease, uint256 _amountBorrowed, CoreLibrary.InterestRateMode _newBorrowRateMode ) internal { CoreLibrary.InterestRateMode previousRateMode = getUserCurrentBorrowRateMode( _reserve, _user ); CoreLibrary.ReserveData storage reserve = reserves[_reserve]; if (previousRateMode == CoreLibrary.InterestRateMode.STABLE) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; reserve.decreaseTotalBorrowsStableAndUpdateAverageRate( _principalBalance, user.stableBorrowRate ); } else if (previousRateMode == CoreLibrary.InterestRateMode.VARIABLE) { reserve.decreaseTotalBorrowsVariable(_principalBalance); } uint256 newPrincipalAmount = _principalBalance.add(_balanceIncrease).add(_amountBorrowed); if (_newBorrowRateMode == CoreLibrary.InterestRateMode.STABLE) { reserve.increaseTotalBorrowsStableAndUpdateAverageRate( newPrincipalAmount, reserve.currentStableBorrowRate ); } else if (_newBorrowRateMode == CoreLibrary.InterestRateMode.VARIABLE) { reserve.increaseTotalBorrowsVariable(newPrincipalAmount); } else { revert("Invalid new borrow rate mode"); } } /** * @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 updateReserveInterestRatesAndTimestampInternal( address _reserve, uint256 _liquidityAdded, uint256 _liquidityTaken ) internal virtual { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; uint256 currentAvgStableRate = reserve.currentAverageStableBorrowRate; (uint256 newLiquidityRate, uint256 newStableRate, uint256 newVariableRate) = IReserveInterestRateStrategy( reserve .interestRateStrategyAddress ) .calculateInterestRates( _reserve, getReserveAvailableLiquidity(_reserve).add(_liquidityAdded).sub(_liquidityTaken), reserve.totalBorrowsStable, reserve.totalBorrowsVariable, currentAvgStableRate ); reserve.currentLiquidityRate = newLiquidityRate; reserve.currentStableBorrowRate = newStableRate; reserve.currentVariableBorrowRate = newVariableRate; //solium-disable-next-line reserve.lastUpdateTimestamp = uint40(block.timestamp); emit ReserveDataUpdated( _reserve, newLiquidityRate, newStableRate, currentAvgStableRate, newVariableRate, reserve.lastLiquidityCumulativeIndex, reserve.lastVariableBorrowCumulativeIndex ); } /** * @dev transfers to the protocol fees of a flashloan to the fees collection address * @param _token the address of the token being transferred * @param _amount the amount being transferred **/ function transferFlashLoanProtocolFeeInternal(address _token, uint256 _amount) internal { address payable receiver = payable(addressesProvider.getTokenDistributor()); if (_token != EthAddressLib.ethAddress()) { IERC20(_token).safeTransfer(receiver, _amount); } else { //solium-disable-next-line (bool result, ) = receiver.call{ value: _amount }(""); require(result, "Transfer to token distributor failed"); } } /** * @dev updates the internal configuration of the core **/ function refreshConfigInternal() internal { lendingPoolAddress = addressesProvider.getLendingPool(); } /** * @dev adds a reserve to the array of the reserves address **/ function addReserveToListInternal(address _reserve) internal { bool reserveAlreadyAdded = false; for (uint256 i = 0; i < reservesList.length; i++) if (reservesList[i] == _reserve) { reserveAlreadyAdded = true; } if (!reserveAlreadyAdded) reservesList.push(_reserve); } }