// 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'; /** * @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 whenNotPaused() { _whenNotPaused(); _; } modifier onlyLendingPoolConfigurator() { _onlyLendingPoolConfigurator(); _; } function _whenNotPaused() internal view { require(!_paused, Errors.LP_IS_PAUSED); } 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 whenNotPaused { _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 whenNotPaused returns (uint256) { return _executeWithdraw(asset, amount, to); } ///@inheritdoc ILendingPool function borrow( address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf ) external override whenNotPaused { _executeBorrow( ExecuteBorrowParams( asset, msg.sender, onBehalfOf, amount, interestRateMode, referralCode, true ) ); } ///@inheritdoc ILendingPool function repay( address asset, uint256 amount, uint256 rateMode, address onBehalfOf ) external override whenNotPaused 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 whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve); DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); ValidationLogic.validateSwapRateMode( reserve, reserveCache, _usersConfig[msg.sender], stableDebt, variableDebt, interestRateMode ); reserve.updateState(reserveCache); if (interestRateMode == DataTypes.InterestRateMode.STABLE) { IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(msg.sender, stableDebt); IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( msg.sender, msg.sender, stableDebt, reserveCache.nextVariableBorrowIndex ); reserveCache.refreshDebt(0, stableDebt, stableDebt, 0); } else { IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( msg.sender, variableDebt, reserveCache.nextVariableBorrowIndex ); IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( msg.sender, msg.sender, variableDebt, reserve.currentStableBorrowRate ); reserveCache.refreshDebt(variableDebt, 0, 0, variableDebt); } reserve.updateInterestRates(reserveCache, asset, 0, 0); emit Swap(asset, msg.sender, rateMode); } ///@inheritdoc ILendingPool function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); IERC20 stableDebtToken = IERC20(reserveCache.stableDebtTokenAddress); IERC20 variableDebtToken = IERC20(reserveCache.variableDebtTokenAddress); uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user); ValidationLogic.validateRebalanceStableBorrowRate( reserve, reserveCache, asset, stableDebtToken, variableDebtToken, reserveCache.aTokenAddress ); reserve.updateState(reserveCache); IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt); IStableDebtToken(address(stableDebtToken)).mint( user, user, stableDebt, reserve.currentStableBorrowRate ); reserveCache.refreshDebt(stableDebt, stableDebt, 0, 0); reserve.updateInterestRates(reserveCache, asset, 0, 0); emit RebalanceStableBorrowRate(asset, user); } ///@inheritdoc ILendingPool function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); ValidationLogic.validateSetUseReserveAsCollateral(reserveCache); _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); if (useAsCollateral) { emit ReserveUsedAsCollateralEnabled(asset, msg.sender); } else { ValidationLogic.validateWithdrawCollateral( 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 whenNotPaused { 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 whenNotPaused { 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( 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 whenNotPaused { 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.validateWithdrawCollateral( 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 val) external override onlyLendingPoolConfigurator { _paused = val; if (_paused) { emit Paused(); } else { emit Unpaused(); } } ///@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; } struct ExecuteBorrowParams { address asset; address user; address onBehalfOf; uint256 amount; uint256 interestRateMode; uint16 referralCode; bool releaseUnderlying; } function _executeBorrow(ExecuteBorrowParams memory vars) internal { DataTypes.ReserveData storage reserve = _reserves[vars.asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); reserve.updateState(reserveCache); ValidationLogic.validateBorrow( reserveCache, vars.asset, vars.onBehalfOf, vars.amount, vars.interestRateMode, _maxStableRateBorrowSizePercent, _reserves, userConfig, _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); uint256 currentStableRate = 0; bool isFirstBorrowing = false; if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) { currentStableRate = reserve.currentStableBorrowRate; isFirstBorrowing = IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( vars.user, vars.onBehalfOf, vars.amount, currentStableRate ); reserveCache.refreshDebt(vars.amount, 0, 0, 0); } else { isFirstBorrowing = IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( vars.user, vars.onBehalfOf, vars.amount, reserveCache.nextVariableBorrowIndex ); reserveCache.refreshDebt(0, 0, vars.amount, 0); } if (isFirstBorrowing) { userConfig.setBorrowing(reserve.id, true); } reserve.updateInterestRates( reserveCache, vars.asset, 0, vars.releaseUnderlying ? vars.amount : 0 ); if (vars.releaseUnderlying) { IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); } emit Borrow( vars.asset, vars.user, vars.onBehalfOf, vars.amount, vars.interestRateMode, DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE ? currentStableRate : reserve.currentVariableBorrowRate, vars.referralCode ); } function _executeDeposit( address asset, uint256 amount, address onBehalfOf, uint16 referralCode ) internal { DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); reserve.updateState(reserveCache); ValidationLogic.validateDeposit(reserveCache, amount); reserve.updateInterestRates(reserveCache, asset, amount, 0); IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, amount); bool isFirstDeposit = IAToken(reserveCache.aTokenAddress).mint(onBehalfOf, amount, reserveCache.nextLiquidityIndex); if (isFirstDeposit) { _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf); } emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode); } function _executeWithdraw( address asset, uint256 amount, address to ) internal returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); reserve.updateState(reserveCache); uint256 userBalance = IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul( reserveCache.nextLiquidityIndex ); uint256 amountToWithdraw = amount; if (amount == type(uint256).max) { amountToWithdraw = userBalance; } ValidationLogic.validateWithdraw(reserveCache, amountToWithdraw, userBalance); reserve.updateInterestRates(reserveCache, asset, 0, amountToWithdraw); IAToken(reserveCache.aTokenAddress).burn( msg.sender, to, amountToWithdraw, reserveCache.nextLiquidityIndex ); if (userConfig.isUsingAsCollateral(reserve.id)) { if (userConfig.isBorrowingAny()) { ValidationLogic.validateWithdrawCollateral( asset, msg.sender, _reserves, userConfig, _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); } if (amountToWithdraw == userBalance) { userConfig.setUsingAsCollateral(reserve.id, false); emit ReserveUsedAsCollateralDisabled(asset, msg.sender); } } emit Withdraw(asset, msg.sender, to, amountToWithdraw); return amountToWithdraw; } function _executeRepay( address asset, uint256 amount, uint256 rateMode, address onBehalfOf ) internal returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve); DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); ValidationLogic.validateRepay( reserveCache, amount, interestRateMode, onBehalfOf, stableDebt, variableDebt ); uint256 paybackAmount = interestRateMode == DataTypes.InterestRateMode.STABLE ? stableDebt : variableDebt; if (amount < paybackAmount) { paybackAmount = amount; } reserve.updateState(reserveCache); if (interestRateMode == DataTypes.InterestRateMode.STABLE) { IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); reserveCache.refreshDebt(0, paybackAmount, 0, 0); } else { IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( onBehalfOf, paybackAmount, reserveCache.nextVariableBorrowIndex ); reserveCache.refreshDebt(0, 0, 0, paybackAmount); } reserve.updateInterestRates(reserveCache, asset, paybackAmount, 0); if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) { _usersConfig[onBehalfOf].setBorrowing(reserve.id, false); } IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, paybackAmount); IAToken(reserveCache.aTokenAddress).handleRepayment(msg.sender, paybackAmount); emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); return paybackAmount; } 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); } }