aave-protocol-v2/contracts/protocol/lendingpool/LendingPool.sol

763 lines
22 KiB
Solidity

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