// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol'; import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol'; import {IAToken} from '../../../interfaces/IAToken.sol'; import {Helpers} from '../helpers/Helpers.sol'; import {ValidationLogic} from './ValidationLogic.sol'; import {ReserveLogic} from './ReserveLogic.sol'; import {DataTypes} from './../types/DataTypes.sol'; import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; import {UserConfiguration} from './../configuration/UserConfiguration.sol'; import {WadRayMath} from '../math/WadRayMath.sol'; /** * @title LendingPoolBaseLogic Library * @author Aave * @title Implements lendingpool basic logic to perform actions to limit the contract size. */ library LendingPoolBaseLogic { using ReserveLogic for DataTypes.ReserveCache; using ReserveLogic for DataTypes.ReserveData; using UserConfiguration for DataTypes.UserConfigurationMap; using SafeMath for uint256; using SafeERC20 for IERC20; using WadRayMath for uint256; /** * @dev Emitted on deposit() * @param reserve The address of the underlying asset of the reserve * @param user The address initiating the deposit * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens * @param amount The amount deposited * @param referral The referral code used **/ event Deposit( address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint16 indexed referral ); /** * @dev Emitted on withdraw() * @param reserve The address of the underlyng asset being withdrawn * @param user The address initiating the withdrawal, owner of aTokens * @param to Address that will receive the underlying * @param amount The amount to be withdrawn **/ event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount); /** * @dev Emitted on borrow() and flashLoan() when debt needs to be opened * @param reserve The address of the underlying asset being borrowed * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just * initiator of the transaction on flashLoan() * @param onBehalfOf The address that will be getting the debt * @param amount The amount borrowed out * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable * @param borrowRate The numeric rate at which the user has borrowed * @param referral The referral code used **/ event Borrow( address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint256 borrowRateMode, uint256 borrowRate, uint16 indexed referral ); /** * @dev Emitted on repay() * @param reserve The address of the underlying asset of the reserve * @param user The beneficiary of the repayment, getting his debt reduced * @param repayer The address of the user initiating the repay(), providing the funds * @param amount The amount repaid **/ event Repay( address indexed reserve, address indexed user, address indexed repayer, uint256 amount ); /** * @dev Emitted on setUserUseReserveAsCollateral() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user enabling the usage as collateral **/ event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user); /** * @dev Emitted on setUserUseReserveAsCollateral() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user enabling the usage as collateral **/ event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); function _executeDeposit( DataTypes.ReserveData storage reserve, DataTypes.UserConfigurationMap storage userConfig, address asset, uint256 amount, address onBehalfOf, uint16 referralCode ) public { 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) { userConfig.setUsingAsCollateral(reserve.id, true); emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf); } emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode); } function _executeWithdraw( mapping(address => DataTypes.ReserveData) storage reserves, DataTypes.UserConfigurationMap storage userConfig, address asset, uint256 amount, address to, mapping(uint256 => address) storage reservesList, uint256 reservesCount, address priceOracle ) public returns (uint256) { DataTypes.ReserveData storage reserve = reserves[asset]; 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()) { _validateHFAndLtv(asset, reserves, userConfig, reservesList, reservesCount, priceOracle); } if (amountToWithdraw == userBalance) { userConfig.setUsingAsCollateral(reserve.id, false); emit ReserveUsedAsCollateralDisabled(asset, msg.sender); } } emit Withdraw(asset, msg.sender, to, amountToWithdraw); return amountToWithdraw; } function _validateHFAndLtv( address asset, mapping(address => DataTypes.ReserveData) storage reserves, DataTypes.UserConfigurationMap storage userConfig, mapping(uint256 => address) storage reservesList, uint256 reservesCount, address priceOracle ) public { ValidationLogic.validateHFAndLtv( asset, msg.sender, reserves, userConfig, reservesList, reservesCount, priceOracle ); } function _executeBorrow( mapping(address => DataTypes.ReserveData) storage reserves, DataTypes.ReserveCache memory reserveCache, DataTypes.UserConfigurationMap storage userConfig, DataTypes.ExecuteBorrowParams memory vars ) public { DataTypes.ReserveData storage reserve = reserves[vars.asset]; 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 _executeRepay( DataTypes.ReserveData storage reserve, DataTypes.UserConfigurationMap storage userConfig, address asset, uint256 amount, uint256 rateMode, address onBehalfOf, address lastBorrower, uint40 lastBorrowTimestamp ) public returns (uint256) { DataTypes.ReserveCache memory reserveCache = reserve.cache(); (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve); DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); ValidationLogic.validateRepay( lastBorrower, lastBorrowTimestamp, 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); } return _executeRepayHelper( reserve, reserveCache, userConfig, asset, onBehalfOf, paybackAmount, variableDebt, stableDebt ); } function _executeRepayHelper( DataTypes.ReserveData storage reserve, DataTypes.ReserveCache memory reserveCache, DataTypes.UserConfigurationMap storage userConfig, address asset, address onBehalfOf, uint256 paybackAmount, uint256 variableDebt, uint256 stableDebt ) public returns (uint256) { reserve.updateInterestRates(reserveCache, asset, paybackAmount, 0); if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) { userConfig.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; } }