aave-protocol-v2/contracts/protocol/libraries/logic/LendingPoolBaseLogic.sol

351 lines
11 KiB
Solidity

// 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;
}
}