aave-protocol-v2/contracts/lendingpool/LendingPool.sol
2020-10-12 10:50:06 +02:00

974 lines
31 KiB
Solidity

// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
pragma experimental ABIEncoderV2;
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {
VersionedInitializable
} from '../libraries/openzeppelin-upgradeability/VersionedInitializable.sol';
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
import {IAToken} from '../tokenization/interfaces/IAToken.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 {IStableDebtToken} from '../tokenization/interfaces/IStableDebtToken.sol';
import {IVariableDebtToken} from '../tokenization/interfaces/IVariableDebtToken.sol';
import {DebtTokenBase} from '../tokenization/base/DebtTokenBase.sol';
import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol';
import {ISwapAdapter} from '../interfaces/ISwapAdapter.sol';
import {LendingPoolCollateralManager} from './LendingPoolCollateralManager.sol';
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import {ILendingPool} from '../interfaces/ILendingPool.sol';
import {LendingPoolStorage} from './LendingPoolStorage.sol';
import {IReserveInterestRateStrategy} from '../interfaces/IReserveInterestRateStrategy.sol';
/**
* @title LendingPool contract
* @notice Implements the actions of the LendingPool, and exposes accessory methods to fetch the users and reserve data
* @author Aave
**/
contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage {
using SafeMath for uint256;
using WadRayMath for uint256;
using PercentageMath for uint256;
using SafeERC20 for IERC20;
//main configuration parameters
uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 4000;
uint256 public constant REBALANCE_UP_USAGE_RATIO_THRESHOLD = 0.95 * 1e27; //usage ratio of 95%
uint256 public constant MAX_STABLE_RATE_BORROW_SIZE_PERCENT = 2500;
uint256 public constant FLASHLOAN_PREMIUM_TOTAL = 9;
uint256 public constant MAX_NUMBER_RESERVES = 128;
uint256 public constant LENDINGPOOL_REVISION = 0x2;
/**
* @dev only lending pools configurator can use functions affected by this modifier
**/
function _onlyLendingPoolConfigurator() internal view {
require(
_addressesProvider.getLendingPoolConfigurator() == msg.sender,
Errors.CALLER_NOT_LENDING_POOL_CONFIGURATOR
);
}
/**
* @dev Function to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _whenNotPaused() internal view {
require(!_paused, Errors.IS_PAUSED);
}
function getRevision() internal override pure returns (uint256) {
return LENDINGPOOL_REVISION;
}
/**
* @dev this function is invoked by the proxy contract when the LendingPool contract is added to the
* AddressesProvider.
* @param provider the address of the LendingPoolAddressesProvider registry
**/
function initialize(ILendingPoolAddressesProvider provider) public initializer {
_addressesProvider = provider;
}
/**
* @dev deposits The underlying asset into the reserve. A corresponding amount of the overlying asset (aTokens)
* is minted.
* @param asset the address of the reserve
* @param amount the amount to be deposited
* @param referralCode integrators are assigned a referral code and can potentially receive rewards.
**/
function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external override {
_whenNotPaused();
ReserveLogic.ReserveData storage reserve = _reserves[asset];
ValidationLogic.validateDeposit(reserve, amount);
address aToken = reserve.aTokenAddress;
reserve.updateState();
reserve.updateInterestRates(asset, aToken, amount, 0);
bool isFirstDeposit = IAToken(aToken).balanceOf(onBehalfOf) == 0;
if (isFirstDeposit) {
_usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true);
}
IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex);
//transfer to the aToken contract
IERC20(asset).safeTransferFrom(msg.sender, aToken, amount);
emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode);
}
/**
* @dev withdraws the _reserves of user.
* @param asset the address of the reserve
* @param amount the underlying amount to be redeemed
**/
function withdraw(address asset, uint256 amount) external override {
_whenNotPaused();
ReserveLogic.ReserveData storage reserve = _reserves[asset];
address aToken = reserve.aTokenAddress;
uint256 userBalance = IAToken(aToken).balanceOf(msg.sender);
uint256 amountToWithdraw = amount;
//if amount is equal to uint(-1), the user wants to redeem everything
if (amount == type(uint256).max) {
amountToWithdraw = userBalance;
}
ValidationLogic.validateWithdraw(
asset,
amountToWithdraw,
userBalance,
_reserves,
_usersConfig[msg.sender],
_reservesList,
_reservesCount,
_addressesProvider.getPriceOracle()
);
reserve.updateState();
reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw);
if (amountToWithdraw == userBalance) {
_usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false);
}
IAToken(aToken).burn(msg.sender, msg.sender, amountToWithdraw, reserve.liquidityIndex);
emit Withdraw(asset, msg.sender, amount);
}
/**
* @dev returns the borrow allowance of the user
* @param asset The underlying asset of the debt token
* @param fromUser The user to giving allowance
* @param toUser The user to give allowance to
* @param interestRateMode Type of debt: 1 for stable, 2 for variable
* @return the current allowance of toUser
**/
function getBorrowAllowance(
address fromUser,
address toUser,
address asset,
uint256 interestRateMode
) external override view returns (uint256) {
return
_borrowAllowance[_reserves[asset].getDebtTokenAddress(interestRateMode)][fromUser][toUser];
}
/**
* @dev Sets allowance to borrow on a certain type of debt asset for a certain user address
* @param asset The underlying asset of the debt token
* @param user The user to give allowance to
* @param interestRateMode Type of debt: 1 for stable, 2 for variable
* @param amount Allowance amount to borrow
**/
function delegateBorrowAllowance(
address asset,
address user,
uint256 interestRateMode,
uint256 amount
) external override {
_whenNotPaused();
address debtToken = _reserves[asset].getDebtTokenAddress(interestRateMode);
_borrowAllowance[debtToken][msg.sender][user] = amount;
emit BorrowAllowanceDelegated(asset, msg.sender, user, interestRateMode, amount);
}
/**
* @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
* already deposited enough collateral.
* @param asset the address of the reserve
* @param amount the amount to be borrowed
* @param interestRateMode the interest rate mode at which the user wants to borrow. Can be 0 (STABLE) or 1 (VARIABLE)
* @param referralCode a referral code for integrators
* @param onBehalfOf address of the user who will receive the debt
**/
function borrow(
address asset,
uint256 amount,
uint256 interestRateMode,
uint16 referralCode,
address onBehalfOf
) external override {
_whenNotPaused();
ReserveLogic.ReserveData storage reserve = _reserves[asset];
if (onBehalfOf != msg.sender) {
address debtToken = reserve.getDebtTokenAddress(interestRateMode);
_borrowAllowance[debtToken][onBehalfOf][msg
.sender] = _borrowAllowance[debtToken][onBehalfOf][msg.sender].sub(
amount,
Errors.BORROW_ALLOWANCE_ARE_NOT_ENOUGH
);
}
_executeBorrow(
ExecuteBorrowParams(
asset,
msg.sender,
onBehalfOf,
amount,
interestRateMode,
reserve.aTokenAddress,
referralCode,
true
)
);
}
/**
* @notice repays a borrow on the specific reserve, for the specified amount (or for the whole amount, if uint256(-1) is specified).
* @dev the target user is defined by onBehalfOf. If there is no repayment on behalf of another account,
* onBehalfOf must be equal to msg.sender.
* @param asset the address of the reserve on which the user borrowed
* @param amount the amount to repay, or uint256(-1) if the user wants to repay everything
* @param onBehalfOf the address for which msg.sender is repaying.
**/
function repay(
address asset,
uint256 amount,
uint256 rateMode,
address onBehalfOf
) external override {
_whenNotPaused();
ReserveLogic.ReserveData storage reserve = _reserves[asset];
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve);
ReserveLogic.InterestRateMode interestRateMode = ReserveLogic.InterestRateMode(rateMode);
//default to max amount
uint256 paybackAmount = interestRateMode == ReserveLogic.InterestRateMode.STABLE
? stableDebt
: variableDebt;
if (amount != type(uint256).max && amount < paybackAmount) {
paybackAmount = amount;
}
ValidationLogic.validateRepay(
reserve,
amount,
interestRateMode,
onBehalfOf,
stableDebt,
variableDebt
);
reserve.updateState();
//burns an equivalent amount of debt tokens
if (interestRateMode == ReserveLogic.InterestRateMode.STABLE) {
IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount);
} else {
IVariableDebtToken(reserve.variableDebtTokenAddress).burn(
onBehalfOf,
paybackAmount,
reserve.variableBorrowIndex
);
}
address aToken = reserve.aTokenAddress;
reserve.updateInterestRates(asset, aToken, paybackAmount, 0);
if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) {
_usersConfig[onBehalfOf].setBorrowing(reserve.id, false);
}
IERC20(asset).safeTransferFrom(msg.sender, aToken, paybackAmount);
emit Repay(asset, onBehalfOf, msg.sender, paybackAmount);
}
/**
* @dev borrowers can user this function to swap between stable and variable borrow rate modes.
* @param asset the address of the reserve on which the user borrowed
* @param rateMode the rate mode that the user wants to swap
**/
function swapBorrowRateMode(address asset, uint256 rateMode) external override {
_whenNotPaused();
ReserveLogic.ReserveData storage reserve = _reserves[asset];
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve);
ReserveLogic.InterestRateMode interestRateMode = ReserveLogic.InterestRateMode(rateMode);
ValidationLogic.validateSwapRateMode(
reserve,
_usersConfig[msg.sender],
stableDebt,
variableDebt,
interestRateMode
);
reserve.updateState();
if (interestRateMode == ReserveLogic.InterestRateMode.STABLE) {
//burn stable rate tokens, mint variable rate tokens
IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt);
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
msg.sender,
stableDebt,
reserve.variableBorrowIndex
);
} else {
//do the opposite
IVariableDebtToken(reserve.variableDebtTokenAddress).burn(
msg.sender,
variableDebt,
reserve.variableBorrowIndex
);
IStableDebtToken(reserve.stableDebtTokenAddress).mint(
msg.sender,
variableDebt,
reserve.currentStableBorrowRate
);
}
reserve.updateInterestRates(asset, reserve.aTokenAddress, 0, 0);
emit Swap(asset, msg.sender);
}
/**
* @dev rebalances the stable interest rate of a user if current liquidity rate > user stable rate.
* this is regulated by Aave to ensure that the protocol is not abused, and the user is paying a fair
* rate. Anyone can call this function.
* @param asset the address of the reserve
* @param user the address of the user to be rebalanced
**/
function rebalanceStableBorrowRate(address asset, address user) external override {
_whenNotPaused();
ReserveLogic.ReserveData storage reserve = _reserves[asset];
IERC20 stableDebtToken = IERC20(reserve.stableDebtTokenAddress);
IERC20 variableDebtToken = IERC20(reserve.variableDebtTokenAddress);
address aTokenAddress = reserve.aTokenAddress;
uint256 stableBorrowBalance = IERC20(stableDebtToken).balanceOf(user);
//if the utilization rate is below 95%, no rebalances are needed
uint256 totalBorrows = stableDebtToken
.totalSupply()
.add(variableDebtToken.totalSupply())
.wadToRay();
uint256 availableLiquidity = IERC20(asset).balanceOf(aTokenAddress).wadToRay();
uint256 usageRatio = totalBorrows == 0
? 0
: totalBorrows.rayDiv(availableLiquidity.add(totalBorrows));
//if the liquidity rate is below REBALANCE_UP_THRESHOLD of the max variable APR at 95% usage,
//then we allow rebalancing of the stable rate positions.
uint256 currentLiquidityRate = reserve.currentLiquidityRate;
uint256 maxVariableBorrowRate = IReserveInterestRateStrategy(
reserve
.interestRateStrategyAddress
)
.getMaxVariableBorrowRate();
require(
usageRatio >= REBALANCE_UP_USAGE_RATIO_THRESHOLD &&
currentLiquidityRate <=
maxVariableBorrowRate.percentMul(REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD),
Errors.INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET
);
reserve.updateState();
IStableDebtToken(address(stableDebtToken)).burn(user, stableBorrowBalance);
IStableDebtToken(address(stableDebtToken)).mint(
user,
stableBorrowBalance,
reserve.currentStableBorrowRate
);
reserve.updateInterestRates(asset, aTokenAddress, 0, 0);
emit RebalanceStableBorrowRate(asset, user);
}
/**
* @dev allows depositors to enable or disable a specific deposit as collateral.
* @param asset the address of the reserve
* @param useAsCollateral true if the user wants to user the deposit as collateral, false otherwise.
**/
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external override {
_whenNotPaused();
ReserveLogic.ReserveData storage reserve = _reserves[asset];
ValidationLogic.validateSetUseReserveAsCollateral(
reserve,
asset,
_reserves,
_usersConfig[msg.sender],
_reservesList,
_reservesCount,
_addressesProvider.getPriceOracle()
);
_usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral);
if (useAsCollateral) {
emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
} else {
emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
}
}
/**
* @dev users can invoke this function to liquidate an undercollateralized position.
* @param asset the address of the collateral to liquidated
* @param asset the address of the principal reserve
* @param user the address of the borrower
* @param purchaseAmount the amount of principal that the liquidator wants to repay
* @param receiveAToken true if the liquidators wants to receive the aTokens, false if
* he wants to receive the underlying asset directly
**/
function liquidationCall(
address collateral,
address asset,
address user,
uint256 purchaseAmount,
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)',
collateral,
asset,
user,
purchaseAmount,
receiveAToken
)
);
require(success, Errors.LIQUIDATION_CALL_FAILED);
(uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
if (returnCode != 0) {
//error found
revert(string(abi.encodePacked(returnMessage)));
}
}
/**
* @dev flashes the underlying collateral on an user to swap for the owed asset and repay
* - Both the owner of the position and other liquidators can execute it
* - The owner can repay with his collateral at any point, no matter the health factor
* - Other liquidators can only use this function below 1 HF. To liquidate 50% of the debt > HF 0.98 or the whole below
* @param collateral The address of the collateral asset
* @param principal The address of the owed asset
* @param user Address of the borrower
* @param principalAmount Amount of the debt to repay. type(uint256).max to repay the maximum possible
* @param receiver Address of the contract receiving the collateral to swap
* @param params Variadic bytes param to pass with extra information to the receiver
**/
function repayWithCollateral(
address collateral,
address principal,
address user,
uint256 principalAmount,
address receiver,
bytes calldata params
) external override {
_whenNotPaused();
require(!_flashLiquidationLocked, Errors.REENTRANCY_NOT_ALLOWED);
_flashLiquidationLocked = true;
address collateralManager = _addressesProvider.getLendingPoolCollateralManager();
//solium-disable-next-line
(bool success, bytes memory result) = collateralManager.delegatecall(
abi.encodeWithSignature(
'repayWithCollateral(address,address,address,uint256,address,bytes)',
collateral,
principal,
user,
principalAmount,
receiver,
params
)
);
require(success, Errors.FAILED_REPAY_WITH_COLLATERAL);
(uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
if (returnCode != 0) {
revert(string(abi.encodePacked(returnMessage)));
}
_flashLiquidationLocked = false;
}
struct FlashLoanLocalVars {
uint256 premium;
uint256 amountPlusPremium;
IFlashLoanReceiver receiver;
address aTokenAddress;
address oracle;
}
/**
* @dev allows smartcontracts to access the liquidity of the pool within one transaction,
* as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
* that must be kept into consideration. For further details please visit https://developers.aave.com
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
* @param asset The address of the principal reserve
* @param amount The amount requested for this flashloan
* @param mode Type of the debt to open if the flash loan is not returned. 0 -> Don't open any debt, just revert, 1 -> stable, 2 -> variable
* @param params Variadic packed params to pass to the receiver as extra information
* @param referralCode Referral code of the flash loan
**/
function flashLoan(
address receiverAddress,
address asset,
uint256 amount,
uint256 mode,
bytes calldata params,
uint16 referralCode
) external override {
_whenNotPaused();
ReserveLogic.ReserveData storage reserve = _reserves[asset];
FlashLoanLocalVars memory vars;
vars.aTokenAddress = reserve.aTokenAddress;
vars.premium = amount.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000);
ValidationLogic.validateFlashloan(mode, vars.premium);
ReserveLogic.InterestRateMode debtMode = ReserveLogic.InterestRateMode(mode);
vars.receiver = IFlashLoanReceiver(receiverAddress);
//transfer funds to the receiver
IAToken(vars.aTokenAddress).transferUnderlyingTo(receiverAddress, amount);
//execute action of the receiver
vars.receiver.executeOperation(asset, amount, vars.premium, params);
vars.amountPlusPremium = amount.add(vars.premium);
if (debtMode == ReserveLogic.InterestRateMode.NONE) {
IERC20(asset).safeTransferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusPremium);
reserve.updateState();
reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.premium);
reserve.updateInterestRates(asset, vars.aTokenAddress, vars.premium, 0);
emit FlashLoan(receiverAddress, asset, amount, vars.premium, referralCode);
} else {
//if the user didn't choose to return the funds, the system checks if there
//is enough collateral and eventually open a position
_executeBorrow(
ExecuteBorrowParams(
asset,
msg.sender,
msg.sender,
vars.amountPlusPremium,
mode,
vars.aTokenAddress,
referralCode,
false
)
);
}
}
/**
* @dev Allows an user to release one of his assets deposited in the protocol, even if it is used as collateral, to swap for another.
* - It's not possible to release one asset to swap for the same
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the ISwapAdapter interface
* @param fromAsset Asset to swap from
* @param toAsset Asset to swap to
* @param params a bytes array to be sent (if needed) to the receiver contract with extra data
**/
function swapLiquidity(
address receiverAddress,
address fromAsset,
address toAsset,
uint256 amountToSwap,
bytes calldata params
) external override {
_whenNotPaused();
address collateralManager = _addressesProvider.getLendingPoolCollateralManager();
//solium-disable-next-line
(bool success, bytes memory result) = collateralManager.delegatecall(
abi.encodeWithSignature(
'swapLiquidity(address,address,address,uint256,bytes)',
receiverAddress,
fromAsset,
toAsset,
amountToSwap,
params
)
);
require(success, Errors.FAILED_COLLATERAL_SWAP);
(uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
if (returnCode != 0) {
revert(string(abi.encodePacked(returnMessage)));
}
}
/**
* @dev returns the state and configuration of the reserve
* @param asset the address of the reserve
* @return the state of the reserve
**/
function getReserveData(address asset)
external
override
view
returns (ReserveLogic.ReserveData memory)
{
return _reserves[asset];
}
/**
* @dev returns the user account data across all the reserves
* @param user the address of the user
* @return totalCollateralETH the total collateral in ETH of the user
* @return totalDebtETH the total debt in ETH of the user
* @return availableBorrowsETH the borrowing power left of the user
* @return currentLiquidationThreshold the liquidation threshold of the user
* @return ltv the loan to value of the user
* @return healthFactor the current health factor of the user
**/
function getUserAccountData(address user)
external
override
view
returns (
uint256 totalCollateralETH,
uint256 totalDebtETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
)
{
(
totalCollateralETH,
totalDebtETH,
ltv,
currentLiquidationThreshold,
healthFactor
) = GenericLogic.calculateUserAccountData(
user,
_reserves,
_usersConfig[user],
_reservesList,
_reservesCount,
_addressesProvider.getPriceOracle()
);
availableBorrowsETH = GenericLogic.calculateAvailableBorrowsETH(
totalCollateralETH,
totalDebtETH,
ltv
);
}
/**
* @dev returns the configuration of the reserve
* @param asset the address of the reserve
* @return the configuration of the reserve
**/
function getConfiguration(address asset)
external
override
view
returns (ReserveConfiguration.Map memory)
{
return _reserves[asset].configuration;
}
/**
* @dev returns the normalized income per unit of asset
* @param asset the address of the reserve
* @return the reserve normalized income
*/
function getReserveNormalizedIncome(address asset) external override view returns (uint256) {
return _reserves[asset].getNormalizedIncome();
}
/**
* @dev returns the normalized variable debt per unit of asset
* @param asset the address of the reserve
* @return the reserve normalized debt
*/
function getReserveNormalizedVariableDebt(address asset)
external
override
view
returns (uint256)
{
return _reserves[asset].getNormalizedDebt();
}
/**
* @dev Returns if the LendingPool is paused
*/
function paused() external override view returns (bool) {
return _paused;
}
/**
* @dev returns the list of the initialized reserves
**/
function getReservesList() external override view returns (address[] memory) {
address[] memory _activeReserves = new address[](_reservesCount);
for (uint256 i = 0; i < _reservesCount; i++) {
_activeReserves[i] = _reservesList[i];
}
return _activeReserves;
}
/**
* @dev returns the addresses provider
**/
function getAddressesProvider() external view returns (ILendingPoolAddressesProvider) {
return _addressesProvider;
}
/**
* @dev validate if a balance decrease for an asset is allowed
* @param asset the address of the reserve
* @param user the user related to the balance decrease
* @param amount the amount being transferred/redeemed
* @return true if the balance decrease can be allowed, false otherwise
*/
function balanceDecreaseAllowed(
address asset,
address user,
uint256 amount
) external override view returns (bool) {
_whenNotPaused();
return
GenericLogic.balanceDecreaseAllowed(
asset,
user,
amount,
_reserves,
_usersConfig[user],
_reservesList,
_reservesCount,
_addressesProvider.getPriceOracle()
);
}
/**
* @dev avoids direct transfers of ETH
**/
receive() external payable {
revert();
}
/**
* @dev initializes a reserve
* @param asset the address of the reserve
* @param aTokenAddress the address of the overlying aToken contract
* @param interestRateStrategyAddress the address of the interest rate strategy contract
**/
function initReserve(
address asset,
address aTokenAddress,
address stableDebtAddress,
address variableDebtAddress,
address interestRateStrategyAddress
) external override {
_onlyLendingPoolConfigurator();
_reserves[asset].init(
aTokenAddress,
stableDebtAddress,
variableDebtAddress,
interestRateStrategyAddress
);
_addReserveToList(asset);
}
/**
* @dev updates the address of the interest rate strategy contract
* @param asset the address of the reserve
* @param rateStrategyAddress the address of the interest rate strategy contract
**/
function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress)
external
override
{
_onlyLendingPoolConfigurator();
_reserves[asset].interestRateStrategyAddress = rateStrategyAddress;
}
/**
* @dev sets the configuration map of the reserve
* @param asset the address of the reserve
* @param configuration the configuration map
**/
function setConfiguration(address asset, uint256 configuration) external override {
_onlyLendingPoolConfigurator();
_reserves[asset].configuration.data = configuration;
}
/**
* @dev Set the _pause state
* @param val the boolean value to set the current pause state of LendingPool
*/
function setPause(bool val) external override {
_onlyLendingPoolConfigurator();
_paused = val;
if (_paused) {
emit Paused();
} else {
emit Unpaused();
}
}
// internal functions
struct ExecuteBorrowParams {
address asset;
address user;
address onBehalfOf;
uint256 amount;
uint256 interestRateMode;
address aTokenAddress;
uint16 referralCode;
bool releaseUnderlying;
}
/**
* @dev Internal function to execute a borrowing action, allowing to transfer or not the underlying
* @param vars Input struct for the borrowing action, in order to avoid STD errors
**/
function _executeBorrow(ExecuteBorrowParams memory vars) internal {
ReserveLogic.ReserveData storage reserve = _reserves[vars.asset];
UserConfiguration.Map storage userConfig = _usersConfig[vars.onBehalfOf];
address oracle = _addressesProvider.getPriceOracle();
uint256 amountInETH = IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div(
10**reserve.configuration.getDecimals()
);
ValidationLogic.validateBorrow(
reserve,
vars.onBehalfOf,
vars.amount,
amountInETH,
vars.interestRateMode,
MAX_STABLE_RATE_BORROW_SIZE_PERCENT,
_reserves,
userConfig,
_reservesList,
_reservesCount,
oracle
);
uint256 reserveId = reserve.id;
if (!userConfig.isBorrowing(reserveId)) {
userConfig.setBorrowing(reserveId, true);
}
reserve.updateState();
//caching the current stable borrow rate
uint256 currentStableRate = 0;
if (
ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE
) {
currentStableRate = reserve.currentStableBorrowRate;
IStableDebtToken(reserve.stableDebtTokenAddress).mint(
vars.onBehalfOf,
vars.amount,
currentStableRate
);
} else {
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
vars.onBehalfOf,
vars.amount,
reserve.variableBorrowIndex
);
}
reserve.updateInterestRates(
vars.asset,
vars.aTokenAddress,
0,
vars.releaseUnderlying ? vars.amount : 0
);
if (vars.releaseUnderlying) {
IAToken(vars.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
}
emit Borrow(
vars.asset,
vars.user,
vars.onBehalfOf,
vars.amount,
vars.interestRateMode,
ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE
? currentStableRate
: reserve.currentVariableBorrowRate,
vars.referralCode
);
}
/**
* @dev adds a reserve to the array of the _reserves address
**/
function _addReserveToList(address asset) internal {
require(_reservesCount < MAX_NUMBER_RESERVES, Errors.NO_MORE_RESERVES_ALLOWED);
bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset;
if (!reserveAlreadyAdded) {
_reserves[asset].id = uint8(_reservesCount);
_reservesList[_reservesCount] = asset;
_reservesCount++;
}
}
}