// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol'; import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; import {IERC20WithPermit} from '../../interfaces/IERC20WithPermit.sol'; import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol'; import {Address} from '../../dependencies/openzeppelin/contracts/Address.sol'; import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; import {IAToken} from '../../interfaces/IAToken.sol'; import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol'; import {IFlashLoanReceiver} from '../../flashloan/interfaces/IFlashLoanReceiver.sol'; import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol'; import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol'; import {ILendingPool} from '../../interfaces/ILendingPool.sol'; import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; import {Helpers} from '../libraries/helpers/Helpers.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {PercentageMath} from '../libraries/math/PercentageMath.sol'; import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; import {GenericLogic} from '../libraries/logic/GenericLogic.sol'; import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol'; import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; import {LendingPoolStorage} from './LendingPoolStorage.sol'; import {LendingPoolBaseLogic} from '../libraries/logic/LendingPoolBaseLogic.sol'; import {LendingPoolOtherLogic} from '../libraries/logic/LendingPoolOtherLogic.sol'; /** * @title LendingPool contract * @dev Main point of interaction with an Aave protocol's market * - Users can: * # Deposit * # Withdraw * # Borrow * # Repay * # Swap their loans between variable and stable rate * # Enable/disable their deposits as collateral rebalance stable rate borrow positions * # Liquidate positions * # Execute Flash Loans * - To be covered by a proxy contract, owned by the LendingPoolAddressesProvider of the specific market * - All admin functions are callable by the LendingPoolConfigurator contract defined also in the * LendingPoolAddressesProvider * @author Aave **/ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage { using SafeMath for uint256; using WadRayMath for uint256; using PercentageMath for uint256; using SafeERC20 for IERC20; using ReserveLogic for DataTypes.ReserveCache; uint256 public constant LENDINGPOOL_REVISION = 0x2; modifier onlyLendingPoolConfigurator() { _onlyLendingPoolConfigurator(); _; } function _onlyLendingPoolConfigurator() internal view { require( _addressesProvider.getLendingPoolConfigurator() == msg.sender, Errors.LP_CALLER_NOT_LENDING_POOL_CONFIGURATOR ); } function getRevision() internal pure override returns (uint256) { return LENDINGPOOL_REVISION; } /** * @dev Function is invoked by the proxy contract when the LendingPool contract is added to the * LendingPoolAddressesProvider of the market. * - Caching the address of the LendingPoolAddressesProvider in order to reduce gas consumption * on subsequent operations * @param provider The address of the LendingPoolAddressesProvider **/ function initialize(ILendingPoolAddressesProvider provider) public initializer { _addressesProvider = provider; _maxStableRateBorrowSizePercent = 2500; _flashLoanPremiumTotal = 9; _maxNumberOfReserves = 128; _flashLoanPremiumToProtocol = 0; } ///@inheritdoc ILendingPool function deposit( address asset, uint256 amount, address onBehalfOf, uint16 referralCode ) external override { _executeDeposit(asset, amount, onBehalfOf, referralCode); } ///@inheritdoc ILendingPool function depositWithPermit( address asset, uint256 amount, address onBehalfOf, uint16 referralCode, uint256 deadline, uint8 permitV, bytes32 permitR, bytes32 permitS ) external override { IERC20WithPermit(asset).permit( msg.sender, address(this), amount, deadline, permitV, permitR, permitS ); _executeDeposit(asset, amount, onBehalfOf, referralCode); } ///@inheritdoc ILendingPool function withdraw( address asset, uint256 amount, address to ) external override returns (uint256) { return _executeWithdraw(asset, amount, to); } ///@inheritdoc ILendingPool function borrow( address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf ) external override { _executeBorrow( DataTypes.ExecuteBorrowParams( asset, msg.sender, onBehalfOf, amount, interestRateMode, referralCode, true ) ); } ///@inheritdoc ILendingPool function repay( address asset, uint256 amount, uint256 rateMode, address onBehalfOf ) external override returns (uint256) { return _executeRepay(asset, amount, rateMode, onBehalfOf); } ///@inheritdoc ILendingPool function repayWithPermit( address asset, uint256 amount, uint256 rateMode, address onBehalfOf, uint256 deadline, uint8 permitV, bytes32 permitR, bytes32 permitS ) external override returns (uint256) { IERC20WithPermit(asset).permit( msg.sender, address(this), amount, deadline, permitV, permitR, permitS ); return _executeRepay(asset, amount, rateMode, onBehalfOf); } ///@inheritdoc ILendingPool function swapBorrowRateMode(address asset, uint256 rateMode) external override { DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; LendingPoolOtherLogic.swapBorrowRateMode(reserve, userConfig, asset, rateMode); } ///@inheritdoc ILendingPool function rebalanceStableBorrowRate(address asset, address user) external override { DataTypes.ReserveData storage reserve = _reserves[asset]; LendingPoolOtherLogic.rebalanceStableBorrowRate(reserve, asset, user); } ///@inheritdoc ILendingPool function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external override { DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; LendingPoolOtherLogic.setUserUseReserveAsCollateral( _reserves, userConfig, asset, useAsCollateral, _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); /* DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); uint256 userBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender); ValidationLogic.validateSetUseReserveAsCollateral(reserveCache, userBalance); _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); if (useAsCollateral) { emit ReserveUsedAsCollateralEnabled(asset, msg.sender); } else { ValidationLogic.validateHFAndLtv( asset, msg.sender, _reserves, _usersConfig[msg.sender], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); emit ReserveUsedAsCollateralDisabled(asset, msg.sender); }*/ } ///@inheritdoc ILendingPool function liquidationCall( address collateralAsset, address debtAsset, address user, uint256 debtToCover, bool receiveAToken ) external override { address collateralManager = _addressesProvider.getLendingPoolCollateralManager(); //solium-disable-next-line (bool success, bytes memory result) = collateralManager.delegatecall( abi.encodeWithSignature( 'liquidationCall(address,address,address,uint256,bool)', collateralAsset, debtAsset, user, debtToCover, receiveAToken ) ); require(success, Errors.LP_LIQUIDATION_CALL_FAILED); (uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string)); require(returnCode == 0, string(abi.encodePacked(returnMessage))); } struct FlashLoanLocalVars { IFlashLoanReceiver receiver; address oracle; uint256 i; address currentAsset; address currentATokenAddress; uint256 currentAmount; uint256 currentPremiumToLP; uint256 currentPremiumToProtocol; uint256 currentAmountPlusPremium; address debtToken; address[] aTokenAddresses; uint256[] totalPremiums; uint256 flashloanPremiumTotal; uint256 flashloanPremiumToProtocol; } ///@inheritdoc ILendingPool function flashLoan( address receiverAddress, address[] calldata assets, uint256[] calldata amounts, uint256[] calldata modes, address onBehalfOf, bytes calldata params, uint16 referralCode ) external override { FlashLoanLocalVars memory vars; vars.aTokenAddresses = new address[](assets.length); vars.totalPremiums = new uint256[](assets.length); ValidationLogic.validateFlashloan(assets, amounts, _reserves); vars.receiver = IFlashLoanReceiver(receiverAddress); (vars.flashloanPremiumTotal, vars.flashloanPremiumToProtocol) = _authorizedFlashBorrowers[ msg.sender ] ? (0, 0) : (_flashLoanPremiumTotal, _flashLoanPremiumToProtocol); for (vars.i = 0; vars.i < assets.length; vars.i++) { vars.aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; vars.totalPremiums[vars.i] = amounts[vars.i].percentMul(vars.flashloanPremiumTotal); IAToken(vars.aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]); } require( vars.receiver.executeOperation(assets, amounts, vars.totalPremiums, msg.sender, params), Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN ); for (vars.i = 0; vars.i < assets.length; vars.i++) { vars.currentAsset = assets[vars.i]; vars.currentAmount = amounts[vars.i]; vars.currentATokenAddress = vars.aTokenAddresses[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) { DataTypes.ReserveData storage reserve = _reserves[vars.currentAsset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); reserve.updateState(reserveCache); reserve.cumulateToLiquidityIndex( IERC20(vars.currentATokenAddress).totalSupply(), vars.currentPremiumToLP ); reserve.accruedToTreasury = reserve.accruedToTreasury.add( vars.currentPremiumToProtocol.rayDiv(reserve.liquidityIndex) ); reserve.updateInterestRates( reserveCache, vars.currentAsset, vars.currentAmountPlusPremium, 0 ); IERC20(vars.currentAsset).safeTransferFrom( receiverAddress, vars.currentATokenAddress, vars.currentAmountPlusPremium ); } else { // If the user chose to not return the funds, the system checks if there is enough collateral and // eventually opens a debt position _executeBorrow( DataTypes.ExecuteBorrowParams( vars.currentAsset, msg.sender, onBehalfOf, vars.currentAmount, modes[vars.i], referralCode, false ) ); } emit FlashLoan( receiverAddress, msg.sender, vars.currentAsset, vars.currentAmount, vars.totalPremiums[vars.i], referralCode ); } } ///@inheritdoc ILendingPool function mintToTreasury(address[] calldata assets) external override { for (uint256 i = 0; i < assets.length; i++) { address assetAddress = assets[i]; DataTypes.ReserveData storage reserve = _reserves[assetAddress]; // this cover both inactive reserves and invalid reserves since the flag will be 0 for both if (!reserve.configuration.getActive()) { continue; } uint256 accruedToTreasury = reserve.accruedToTreasury; if (accruedToTreasury != 0) { uint256 normalizedIncome = reserve.getNormalizedIncome(); uint256 amountToMint = accruedToTreasury.rayMul(normalizedIncome); IAToken(reserve.aTokenAddress).mintToTreasury(amountToMint, normalizedIncome); reserve.accruedToTreasury = 0; emit MintedToTreasury(assetAddress, amountToMint); } } } ///@inheritdoc ILendingPool function getReserveData(address asset) external view override returns (DataTypes.ReserveData memory) { return _reserves[asset]; } ///@inheritdoc ILendingPool function getUserAccountData(address user) external view override returns ( uint256 totalCollateralETH, uint256 totalDebtETH, uint256 availableBorrowsETH, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor ) { ( totalCollateralETH, totalDebtETH, ltv, currentLiquidationThreshold, healthFactor, ) = GenericLogic.getUserAccountData( user, _reserves, _usersConfig[user], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); availableBorrowsETH = GenericLogic.calculateAvailableBorrowsETH( totalCollateralETH, totalDebtETH, ltv ); } ///@inheritdoc ILendingPool function getConfiguration(address asset) external view override returns (DataTypes.ReserveConfigurationMap memory) { return _reserves[asset].configuration; } ///@inheritdoc ILendingPool function getUserConfiguration(address user) external view override returns (DataTypes.UserConfigurationMap memory) { return _usersConfig[user]; } ///@inheritdoc ILendingPool function getReserveNormalizedIncome(address asset) external view virtual override returns (uint256) { return _reserves[asset].getNormalizedIncome(); } ///@inheritdoc ILendingPool function getReserveNormalizedVariableDebt(address asset) external view override returns (uint256) { return _reserves[asset].getNormalizedDebt(); } ///@inheritdoc ILendingPool function paused() external view override returns (bool) { return _paused; } ///@inheritdoc ILendingPool function getReservesList() external view override returns (address[] memory) { uint256 reserveListCount = _reservesCount; uint256 droppedReservesCount = 0; address[] memory reserves = new address[](reserveListCount); for (uint256 i = 0; i < reserveListCount; i++) { if (_reservesList[i] != address(0)) { reserves[i - droppedReservesCount] = _reservesList[i]; } else { droppedReservesCount++; } } if (droppedReservesCount == 0) return reserves; address[] memory undroppedReserves = new address[](reserveListCount - droppedReservesCount); for (uint256 i = 0; i < reserveListCount - droppedReservesCount; i++) { undroppedReserves[i] = reserves[i]; } return undroppedReserves; } ///@inheritdoc ILendingPool function getAddressesProvider() external view override returns (ILendingPoolAddressesProvider) { return _addressesProvider; } ///@inheritdoc ILendingPool function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() public view override returns (uint256) { return _maxStableRateBorrowSizePercent; } ///@inheritdoc ILendingPool function FLASHLOAN_PREMIUM_TOTAL() public view override returns (uint256) { return _flashLoanPremiumTotal; } ///@inheritdoc ILendingPool function FLASHLOAN_PREMIUM_TO_PROTOCOL() public view override returns (uint256) { return _flashLoanPremiumToProtocol; } ///@inheritdoc ILendingPool function MAX_NUMBER_RESERVES() public view override returns (uint256) { return _maxNumberOfReserves; } ///@inheritdoc ILendingPool function finalizeTransfer( address asset, address from, address to, uint256 amount, uint256 balanceFromBefore, uint256 balanceToBefore ) external override { require(msg.sender == _reserves[asset].aTokenAddress, Errors.LP_CALLER_MUST_BE_AN_ATOKEN); ValidationLogic.validateTransfer(_reserves[asset]); uint256 reserveId = _reserves[asset].id; if (from != to) { DataTypes.UserConfigurationMap storage fromConfig = _usersConfig[from]; if (fromConfig.isUsingAsCollateral(reserveId)) { if (fromConfig.isBorrowingAny()) { ValidationLogic.validateHFAndLtv( asset, from, _reserves, _usersConfig[from], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); } if (balanceFromBefore.sub(amount) == 0) { fromConfig.setUsingAsCollateral(reserveId, false); emit ReserveUsedAsCollateralDisabled(asset, from); } } if (balanceToBefore == 0 && amount != 0) { DataTypes.UserConfigurationMap storage toConfig = _usersConfig[to]; toConfig.setUsingAsCollateral(reserveId, true); emit ReserveUsedAsCollateralEnabled(asset, to); } } } ///@inheritdoc ILendingPool function initReserve( address asset, address aTokenAddress, address stableDebtAddress, address variableDebtAddress, address interestRateStrategyAddress ) external override onlyLendingPoolConfigurator { require(Address.isContract(asset), Errors.LP_NOT_CONTRACT); _reserves[asset].init( aTokenAddress, stableDebtAddress, variableDebtAddress, interestRateStrategyAddress ); _addReserveToList(asset); } ///@inheritdoc ILendingPool function dropReserve(address asset) external override onlyLendingPoolConfigurator { ValidationLogic.validateDropReserve(_reserves[asset]); _removeReserveFromList(asset); delete _reserves[asset]; } ///@inheritdoc ILendingPool function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress) external override onlyLendingPoolConfigurator { _reserves[asset].interestRateStrategyAddress = rateStrategyAddress; } ///@inheritdoc ILendingPool function setConfiguration(address asset, uint256 configuration) external override onlyLendingPoolConfigurator { _reserves[asset].configuration.data = configuration; } ///@inheritdoc ILendingPool function setPause(bool paused) external override onlyLendingPoolConfigurator { _paused = paused; } ///@inheritdoc ILendingPool function updateFlashBorrowerAuthorization(address flashBorrower, bool authorized) external override onlyLendingPoolConfigurator { _authorizedFlashBorrowers[flashBorrower] = authorized; } ///@inheritdoc ILendingPool function isFlashBorrowerAuthorized(address flashBorrower) external view override returns (bool) { return _authorizedFlashBorrowers[flashBorrower]; } ///@inheritdoc ILendingPool function updateFlashloanPremiums( uint256 flashLoanPremiumTotal, uint256 flashLoanPremiumToProtocol ) external override onlyLendingPoolConfigurator { _flashLoanPremiumTotal = flashLoanPremiumTotal; _flashLoanPremiumToProtocol = flashLoanPremiumToProtocol; } function _executeDeposit( address asset, uint256 amount, address onBehalfOf, uint16 referralCode ) internal { DataTypes.ReserveData storage reserve = _reserves[asset]; LendingPoolBaseLogic._executeDeposit( reserve, _usersConfig[onBehalfOf], asset, amount, onBehalfOf, referralCode ); } function _executeBorrow(DataTypes.ExecuteBorrowParams memory vars) internal { DataTypes.ReserveData storage reserve = _reserves[vars.asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; reserve.updateState(reserveCache); ValidationLogic.validateBorrow( reserveCache, vars.asset, vars.onBehalfOf, vars.amount, vars.interestRateMode, _maxStableRateBorrowSizePercent, _reserves, userConfig, _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); LendingPoolBaseLogic._executeBorrow(_reserves, reserveCache, userConfig, vars); _lastBorrower = vars.user; _lastBorrowTimestamp = uint40(block.timestamp); } function _executeWithdraw( address asset, uint256 amount, address to ) internal returns (uint256) { DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; return LendingPoolBaseLogic._executeWithdraw( _reserves, userConfig, asset, amount, to, _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); } function _executeRepay( address asset, uint256 amount, uint256 rateMode, address onBehalfOf ) internal returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; return LendingPoolBaseLogic._executeRepay( reserve, userConfig, asset, amount, rateMode, onBehalfOf, _lastBorrower, _lastBorrowTimestamp ); } function _addReserveToList(address asset) internal returns (uint8) { uint256 reservesCount = _reservesCount; require(reservesCount < _maxNumberOfReserves, Errors.LP_NO_MORE_RESERVES_ALLOWED); bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset; if (!reserveAlreadyAdded) { for (uint8 i = 0; i <= reservesCount; i++) { if (_reservesList[i] == address(0)) { _reserves[asset].id = i; _reservesList[i] = asset; _reservesCount = reservesCount + 1; return i; } } } } function _removeReserveFromList(address asset) internal { _reservesList[_reserves[asset].id] = address(0); } }