aave-protocol-v2/contracts/lendingpool/LendingPool.sol
2020-06-21 01:40:03 +02:00

1176 lines
40 KiB
Solidity

// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
import "../configuration/LendingPoolAddressesProvider.sol";
import "../tokenization/AToken.sol";
import "../libraries/WadRayMath.sol";
import "../libraries/CoreLibrary.sol";
import "../libraries/ReserveLogic.sol";
import "../libraries/UserLogic.sol";
import "../libraries/GenericLogic.sol";
import "../libraries/ValidationLogic.sol";
import "../libraries/UniversalERC20.sol";
import "../interfaces/IFeeProvider.sol";
import "../flashloan/interfaces/IFlashLoanReceiver.sol";
import "./LendingPoolLiquidationManager.sol";
import "../interfaces/IPriceOracleGetter.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 ReentrancyGuard, VersionedInitializable {
using SafeMath for uint256;
using WadRayMath for uint256;
using Address for address payable;
using ReserveLogic for CoreLibrary.ReserveData;
using UserLogic for CoreLibrary.UserReserveData;
using CoreLibrary for CoreLibrary.ReserveData;
//main configuration parameters
uint256 private constant REBALANCE_DOWN_RATE_DELTA = (1e27) / 5;
uint256 private constant MAX_STABLE_RATE_BORROW_SIZE_PERCENT = 25;
uint256 private constant FLASHLOAN_FEE_TOTAL = 9;
uint256 private constant FLASHLOAN_FEE_PROTOCOL = 3000;
LendingPoolAddressesProvider public addressesProvider;
IFeeProvider feeProvider;
using UniversalERC20 for IERC20;
mapping(address => CoreLibrary.ReserveData) internal reserves;
mapping(address => mapping(address => CoreLibrary.UserReserveData)) internal usersReserveData;
address[] public reservesList;
/**
* @dev emitted on deposit
* @param _reserve the address of the reserve
* @param _user the address of the user
* @param _amount the amount to be deposited
* @param _referral the referral number of the action
* @param _timestamp the timestamp of the action
**/
event Deposit(
address indexed _reserve,
address indexed _user,
uint256 _amount,
uint16 indexed _referral,
uint256 _timestamp
);
/**
* @dev emitted during a redeem action.
* @param _reserve the address of the reserve
* @param _user the address of the user
* @param _amount the amount to be deposited
* @param _timestamp the timestamp of the action
**/
event RedeemUnderlying(
address indexed _reserve,
address indexed _user,
uint256 _amount,
uint256 _timestamp
);
/**
* @dev emitted on borrow
* @param _reserve the address of the reserve
* @param _user the address of the user
* @param _amount the amount to be deposited
* @param _borrowRateMode the rate mode, can be either 1-stable or 2-variable
* @param _borrowRate the rate at which the user has borrowed
* @param _originationFee the origination fee to be paid by the user
* @param _borrowBalanceIncrease the balance increase since the last borrow, 0 if it's the first time borrowing
* @param _referral the referral number of the action
* @param _timestamp the timestamp of the action
**/
event Borrow(
address indexed _reserve,
address indexed _user,
uint256 _amount,
uint256 _borrowRateMode,
uint256 _borrowRate,
uint256 _originationFee,
uint256 _borrowBalanceIncrease,
uint16 indexed _referral,
uint256 _timestamp
);
/**
* @dev emitted on repay
* @param _reserve the address of the reserve
* @param _user the address of the user for which the repay has been executed
* @param _repayer the address of the user that has performed the repay action
* @param _amountMinusFees the amount repaid minus fees
* @param _fees the fees repaid
* @param _borrowBalanceIncrease the balance increase since the last action
* @param _timestamp the timestamp of the action
**/
event Repay(
address indexed _reserve,
address indexed _user,
address indexed _repayer,
uint256 _amountMinusFees,
uint256 _fees,
uint256 _borrowBalanceIncrease,
uint256 _timestamp
);
/**
* @dev emitted when a user performs a rate swap
* @param _reserve the address of the reserve
* @param _user the address of the user executing the swap
* @param _newRateMode the new interest rate mode
* @param _newRate the new borrow rate
* @param _borrowBalanceIncrease the balance increase since the last action
* @param _timestamp the timestamp of the action
**/
event Swap(
address indexed _reserve,
address indexed _user,
uint256 _newRateMode,
uint256 _newRate,
uint256 _borrowBalanceIncrease,
uint256 _timestamp
);
/**
* @dev emitted when a user enables a reserve as collateral
* @param _reserve the address of the reserve
* @param _user the address of the user
**/
event ReserveUsedAsCollateralEnabled(address indexed _reserve, address indexed _user);
/**
* @dev emitted when a user disables a reserve as collateral
* @param _reserve the address of the reserve
* @param _user the address of the user
**/
event ReserveUsedAsCollateralDisabled(address indexed _reserve, address indexed _user);
/**
* @dev emitted when the stable rate of a user gets rebalanced
* @param _reserve the address of the reserve
* @param _user the address of the user for which the rebalance has been executed
* @param _newStableRate the new stable borrow rate after the rebalance
* @param _borrowBalanceIncrease the balance increase since the last action
* @param _timestamp the timestamp of the action
**/
event RebalanceStableBorrowRate(
address indexed _reserve,
address indexed _user,
uint256 _newStableRate,
uint256 _borrowBalanceIncrease,
uint256 _timestamp
);
/**
* @dev emitted when a flashloan is executed
* @param _target the address of the flashLoanReceiver
* @param _reserve the address of the reserve
* @param _amount the amount requested
* @param _totalFee the total fee on the amount
* @param _protocolFee the part of the fee for the protocol
* @param _timestamp the timestamp of the action
**/
event FlashLoan(
address indexed _target,
address indexed _reserve,
uint256 _amount,
uint256 _totalFee,
uint256 _protocolFee,
uint256 _timestamp
);
/**
* @dev these events are not emitted directly by the LendingPool
* but they are declared here as the LendingPoolLiquidationManager
* is executed using a delegateCall().
* This allows to have the events in the generated ABI for LendingPool.
**/
/**
* @dev emitted when a borrow fee is liquidated
* @param _collateral the address of the collateral being liquidated
* @param _reserve the address of the reserve
* @param _user the address of the user being liquidated
* @param _feeLiquidated the total fee liquidated
* @param _liquidatedCollateralForFee the amount of collateral received by the protocol in exchange for the fee
* @param _timestamp the timestamp of the action
**/
event OriginationFeeLiquidated(
address indexed _collateral,
address indexed _reserve,
address indexed _user,
uint256 _feeLiquidated,
uint256 _liquidatedCollateralForFee,
uint256 _timestamp
);
/**
* @dev emitted when a borrower is liquidated
* @param _collateral the address of the collateral being liquidated
* @param _reserve the address of the reserve
* @param _user the address of the user being liquidated
* @param _purchaseAmount the total amount liquidated
* @param _liquidatedCollateralAmount the amount of collateral being liquidated
* @param _accruedBorrowInterest the amount of interest accrued by the borrower since the last action
* @param _liquidator the address of the liquidator
* @param _receiveAToken true if the liquidator wants to receive aTokens, false otherwise
* @param _timestamp the timestamp of the action
**/
event LiquidationCall(
address indexed _collateral,
address indexed _reserve,
address indexed _user,
uint256 _purchaseAmount,
uint256 _liquidatedCollateralAmount,
uint256 _accruedBorrowInterest,
address _liquidator,
bool _receiveAToken,
uint256 _timestamp
);
/**
* @dev only lending pools configurator can use functions affected by this modifier
**/
modifier onlyLendingPoolConfigurator {
require(addressesProvider.getLendingPoolConfigurator() == msg.sender, "30");
_;
}
uint256 public constant UINT_MAX_VALUE = uint256(-1);
uint256 public constant LENDINGPOOL_REVISION = 0x2;
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 _addressesProvider the address of the LendingPoolAddressesProvider registry
**/
function initialize(LendingPoolAddressesProvider _addressesProvider) public initializer {
addressesProvider = _addressesProvider;
feeProvider = IFeeProvider(addressesProvider.getFeeProvider());
}
/**
* @dev deposits The underlying asset into the reserve. A corresponding amount of the overlying asset (aTokens)
* is minted.
* @param _reserve 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 _reserve, uint256 _amount, uint16 _referralCode)
external
payable
nonReentrant
{
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
CoreLibrary.UserReserveData storage user = usersReserveData[msg.sender][_reserve];
ValidationLogic.validateDeposit(reserve, _amount);
AToken aToken = AToken(reserve.aTokenAddress);
bool isFirstDeposit = aToken.balanceOf(msg.sender) == 0;
reserve.updateCumulativeIndexes();
reserve.updateInterestRatesAndTimestamp(_reserve, _amount, 0);
if (isFirstDeposit) {
user.useAsCollateral = true;
}
//minting AToken to user 1:1 with the specific exchange rate
aToken.mintOnDeposit(msg.sender, _amount);
//transfer to the core contract
IERC20(_reserve).universalTransferFromSenderToThis(_amount, true);
//solium-disable-next-line
emit Deposit(_reserve, msg.sender, _amount, _referralCode, block.timestamp);
}
/**
* @dev Redeems the underlying amount of assets requested by _user.
* This function is executed by the overlying aToken contract in response to a redeem action.
* @param _reserve the address of the reserve
* @param _user the address of the user performing the action
* @param _amount the underlying amount to be redeemed
**/
function redeemUnderlying(
address _reserve,
address payable _user,
uint256 _amount,
uint256 _aTokenBalanceAfterRedeem
) external nonReentrant {
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve];
ValidationLogic.validateRedeem(reserve, _reserve, _amount);
if (_aTokenBalanceAfterRedeem == 0) {
user.useAsCollateral = false;
}
reserve.updateCumulativeIndexes();
reserve.updateInterestRatesAndTimestamp(_reserve, 0, _amount);
IERC20(_reserve).universalTransfer(_user, _amount);
//solium-disable-next-line
emit RedeemUnderlying(_reserve, _user, _amount, block.timestamp);
}
/**
* @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
* already deposited enough collateral.
* @param _reserve 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)
**/
function borrow(
address _reserve,
uint256 _amount,
uint256 _interestRateMode,
uint16 _referralCode
) external nonReentrant {
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
CoreLibrary.UserReserveData storage user = usersReserveData[msg.sender][_reserve];
//calculating fees
uint256 borrowFee = feeProvider.calculateLoanOriginationFee(msg.sender, _amount);
uint256 amountInETH = IPriceOracleGetter(addressesProvider.getPriceOracle())
.getAssetPrice(_reserve)
.mul(_amount.add(borrowFee))
.div(10 ** reserve.decimals); //price is in ether
ValidationLogic.validateBorrow(
reserve,
user,
_reserve,
_amount,
amountInETH,
_interestRateMode,
borrowFee,
MAX_STABLE_RATE_BORROW_SIZE_PERCENT,
reserves,
usersReserveData,
reservesList,
addressesProvider.getPriceOracle()
);
(uint256 principalBorrowBalance, , uint256 borrowBalanceIncrease) = user.getBorrowBalances(
reserve
);
//all conditions passed - borrow is accepted
reserve.updateStateOnBorrow(
user,
principalBorrowBalance,
borrowBalanceIncrease,
_amount,
CoreLibrary.InterestRateMode(_interestRateMode)
);
user.updateStateOnBorrow(
reserve,
_amount,
borrowBalanceIncrease,
borrowFee,
CoreLibrary.InterestRateMode(_interestRateMode)
);
reserve.updateInterestRatesAndTimestamp(_reserve, 0, _amount);
//if we reached this point, we can transfer
IERC20(_reserve).universalTransfer(msg.sender, _amount);
emit Borrow(
_reserve,
msg.sender,
_amount,
_interestRateMode,
CoreLibrary.InterestRateMode(_interestRateMode) == CoreLibrary.InterestRateMode.STABLE
? user.stableBorrowRate
: reserve.currentVariableBorrowRate,
borrowFee,
borrowBalanceIncrease,
_referralCode,
//solium-disable-next-line
block.timestamp
);
}
/**
* @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 _reserve 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.
**/
struct RepayLocalVars {
uint256 principalBorrowBalance;
uint256 compoundedBorrowBalance;
uint256 borrowBalanceIncrease;
uint256 paybackAmount;
uint256 paybackAmountMinusFees;
uint256 currentStableRate;
uint256 originationFee;
}
function repay(address _reserve, uint256 _amount, address payable _onBehalfOf)
external
payable
nonReentrant
{
RepayLocalVars memory vars;
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
CoreLibrary.UserReserveData storage user = usersReserveData[_onBehalfOf][_reserve];
(
vars.principalBorrowBalance,
vars.compoundedBorrowBalance,
vars.borrowBalanceIncrease
) = user.getBorrowBalances(reserve);
vars.originationFee = user.originationFee;
//default to max amount
vars.paybackAmount = vars.compoundedBorrowBalance.add(vars.originationFee);
if (_amount != UINT_MAX_VALUE && _amount < vars.paybackAmount) {
vars.paybackAmount = _amount;
}
ValidationLogic.validateRepay(
reserve,
_reserve,
_amount,
_onBehalfOf,
vars.compoundedBorrowBalance,
vars.paybackAmount,
msg.value
);
//if the amount is smaller than the origination fee, just transfer the amount to the fee destination address
if (vars.paybackAmount <= vars.originationFee) {
reserve.updateStateOnRepay(user, _reserve, 0, vars.borrowBalanceIncrease);
user.updateStateOnRepay(
reserve,
0,
vars.paybackAmount,
vars.borrowBalanceIncrease,
false
);
reserve.updateInterestRatesAndTimestamp(_reserve, 0, 0);
IERC20(_reserve).universalTransferFrom(
msg.sender,
addressesProvider.getTokenDistributor(),
vars.paybackAmount,
true
);
emit Repay(
_reserve,
_onBehalfOf,
msg.sender,
0,
vars.paybackAmount,
vars.borrowBalanceIncrease,
//solium-disable-next-line
block.timestamp
);
return;
}
vars.paybackAmountMinusFees = vars.paybackAmount.sub(vars.originationFee);
reserve.updateStateOnRepay(
user,
_reserve,
vars.paybackAmountMinusFees,
vars.borrowBalanceIncrease
);
user.updateStateOnRepay(
reserve,
vars.paybackAmountMinusFees,
vars.originationFee,
vars.borrowBalanceIncrease,
vars.compoundedBorrowBalance == vars.paybackAmountMinusFees
);
reserve.updateInterestRatesAndTimestamp(_reserve, vars.paybackAmountMinusFees, 0);
//if the user didn't repay the origination fee, transfer the fee to the fee collection address
if (vars.originationFee > 0) {
IERC20(_reserve).universalTransferFrom(
msg.sender,
addressesProvider.getTokenDistributor(),
vars.originationFee,
false
);
}
IERC20(_reserve).universalTransferFromSenderToThis(vars.paybackAmountMinusFees, false);
if (IERC20(_reserve).isETH()) {
//send excess ETH back to the caller if needed
uint256 exceedAmount = msg.value.sub(vars.originationFee).sub(
vars.paybackAmountMinusFees
);
if (exceedAmount > 0) {
IERC20(_reserve).universalTransfer(msg.sender, exceedAmount);
}
}
emit Repay(
_reserve,
_onBehalfOf,
msg.sender,
vars.paybackAmountMinusFees,
vars.originationFee,
vars.borrowBalanceIncrease,
//solium-disable-next-line
block.timestamp
);
}
/**
* @dev borrowers can user this function to swap between stable and variable borrow rate modes.
* @param _reserve the address of the reserve on which the user borrowed
**/
function swapBorrowRateMode(address _reserve) external nonReentrant {
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
CoreLibrary.UserReserveData storage user = usersReserveData[msg.sender][_reserve];
(uint256 principalBorrowBalance, uint256 compoundedBorrowBalance, uint256 borrowBalanceIncrease) = user
.getBorrowBalances(reserve);
CoreLibrary.InterestRateMode currentRateMode = user.getCurrentBorrowRateMode();
ValidationLogic.validateSwapRateMode(
reserve,
user,
compoundedBorrowBalance,
currentRateMode
);
reserve.updateStateOnSwapRate(
user,
_reserve,
principalBorrowBalance,
compoundedBorrowBalance,
currentRateMode
);
user.updateStateOnSwapRate(reserve, borrowBalanceIncrease, currentRateMode);
reserve.updateInterestRatesAndTimestamp(_reserve, 0, 0);
CoreLibrary.InterestRateMode newRateMode = user.getCurrentBorrowRateMode();
emit Swap(
_reserve,
msg.sender,
uint256(newRateMode),
newRateMode == CoreLibrary.InterestRateMode.STABLE
? user.stableBorrowRate
: reserve.currentVariableBorrowRate,
borrowBalanceIncrease,
//solium-disable-next-line
block.timestamp
);
}
/**
* @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 though.
* @param _reserve the address of the reserve
* @param _user the address of the user to be rebalanced
**/
function rebalanceStableBorrowRate(address _reserve, address _user) external nonReentrant {
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve];
(, uint256 compoundedBalance, uint256 borrowBalanceIncrease) = user.getBorrowBalances(
reserve
);
//step 1: user must be borrowing on _reserve at a stable rate
require(compoundedBalance > 0, "User does not have any borrow for this reserve");
require(
user.getCurrentBorrowRateMode() == CoreLibrary.InterestRateMode.STABLE,
"The user borrow is variable and cannot be rebalanced"
);
uint256 rebalanceDownRateThreshold = reserve.currentStableBorrowRate.rayMul(
WadRayMath.ray().add(REBALANCE_DOWN_RATE_DELTA)
);
//step 2: we have two possible situations to rebalance:
//1. user stable borrow rate is below the current liquidity rate. The loan needs to be rebalanced,
//as this situation can be abused (user putting back the borrowed liquidity in the same reserve to earn on it)
//2. user stable rate is above the market avg borrow rate of a certain delta, and utilization rate is low.
//In this case, the user is paying an interest that is too high, and needs to be rescaled down.
if (
user.stableBorrowRate < reserve.currentLiquidityRate ||
user.stableBorrowRate > rebalanceDownRateThreshold
) {
uint256 newStableRate = reserve.updateStateOnRebalance(
user,
_reserve,
borrowBalanceIncrease
);
//update the user state
user.principalBorrowBalance = user.principalBorrowBalance.add(borrowBalanceIncrease);
user.stableBorrowRate = reserve.currentStableBorrowRate;
//solium-disable-next-line
user.lastUpdateTimestamp = uint40(block.timestamp);
reserve.updateInterestRatesAndTimestamp(_reserve, 0, 0);
emit RebalanceStableBorrowRate(
_reserve,
_user,
newStableRate,
borrowBalanceIncrease,
//solium-disable-next-line
block.timestamp
);
return;
}
revert("Interest rate rebalance conditions were not met");
}
/**
* @dev allows depositors to enable or disable a specific deposit as collateral.
* @param _reserve the address of the reserve
* @param _useAsCollateral true if the user wants to user the deposit as collateral, false otherwise.
**/
function setUserUseReserveAsCollateral(address _reserve, bool _useAsCollateral)
external
nonReentrant
{
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
CoreLibrary.UserReserveData storage user = usersReserveData[msg.sender][_reserve];
ValidationLogic.validateSetUseReserveAsCollateral(
reserve,
_reserve,
reserves,
usersReserveData,
reservesList,
addressesProvider.getPriceOracle()
);
user.useAsCollateral = _useAsCollateral;
if (_useAsCollateral) {
emit ReserveUsedAsCollateralEnabled(_reserve, msg.sender);
} else {
emit ReserveUsedAsCollateralDisabled(_reserve, msg.sender);
}
}
/**
* @dev users can invoke this function to liquidate an undercollateralized position.
* @param _reserve the address of the collateral to liquidated
* @param _reserve 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 _reserve,
address _user,
uint256 _purchaseAmount,
bool _receiveAToken
) external payable nonReentrant {
address liquidationManager = addressesProvider.getLendingPoolLiquidationManager();
//solium-disable-next-line
(bool success, bytes memory result) = liquidationManager.delegatecall(
abi.encodeWithSignature(
"liquidationCall(address,address,address,uint256,bool)",
_collateral,
_reserve,
_user,
_purchaseAmount,
_receiveAToken
)
);
require(success, "24");
(uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
if (returnCode != 0) {
//error found
revert(string(abi.encodePacked(returnMessage)));
}
}
struct FlashLoanLocalVars {
uint256 availableLiquidityBefore;
uint256 totalFeeBips;
uint256 protocolFeeBips;
uint256 amountFee;
uint256 protocolFee;
}
/**
* @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 _receiver The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
* @param _reserve the address of the principal reserve
* @param _amount the amount requested for this flashloan
**/
function flashLoan(address _receiver, address _reserve, uint256 _amount, bytes memory _params)
public
nonReentrant
{
FlashLoanLocalVars memory vars;
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
//check that the reserve has enough available liquidity
vars.availableLiquidityBefore = IERC20(_reserve).universalBalanceOf(address(this));
//calculate amount fee
vars.amountFee = _amount.mul(FLASHLOAN_FEE_TOTAL).div(10000);
//protocol fee is the part of the amountFee reserved for the protocol - the rest goes to depositors
vars.protocolFee = vars.amountFee.mul(FLASHLOAN_FEE_PROTOCOL).div(10000);
require(vars.availableLiquidityBefore >= _amount, "26");
require(vars.amountFee > 0 && vars.protocolFee > 0, "27");
//get the FlashLoanReceiver instance
IFlashLoanReceiver receiver = IFlashLoanReceiver(_receiver);
address payable userPayable = address(uint160(_receiver));
//transfer funds to the receiver
IERC20(_reserve).universalTransfer(userPayable, _amount);
//execute action of the receiver
receiver.executeOperation(_reserve, _amount, vars.amountFee, _params);
//check that the actual balance of the core contract includes the returned amount
uint256 availableLiquidityAfter = IERC20(_reserve).universalBalanceOf(address(this));
require(availableLiquidityAfter == vars.availableLiquidityBefore.add(vars.amountFee), "28");
reserve.updateStateOnFlashLoan(
_reserve,
vars.availableLiquidityBefore,
vars.amountFee.sub(vars.protocolFee),
vars.protocolFee
);
IERC20(_reserve).universalTransfer(
addressesProvider.getTokenDistributor(),
vars.protocolFee
);
//solium-disable-next-line
emit FlashLoan(
_receiver,
_reserve,
_amount,
vars.amountFee,
vars.protocolFee,
block.timestamp
);
}
/**
* @dev accessory functions to fetch data from the core contract
**/
function getReserveConfigurationData(address _reserve)
external
view
returns (
uint256 ltv,
uint256 liquidationThreshold,
uint256 liquidationBonus,
address interestRateStrategyAddress,
address aTokenAddress,
bool usageAsCollateralEnabled,
bool borrowingEnabled,
bool stableBorrowRateEnabled,
bool isActive
)
{
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
return (
reserve.baseLTVasCollateral,
reserve.liquidationThreshold,
reserve.liquidationBonus,
reserve.interestRateStrategyAddress,
reserve.aTokenAddress,
reserve.usageAsCollateralEnabled,
reserve.borrowingEnabled,
reserve.isStableBorrowRateEnabled,
reserve.isActive
);
}
function getReserveData(address _reserve)
external
view
returns (
uint256 availableLiquidity,
uint256 totalBorrowsStable,
uint256 totalBorrowsVariable,
uint256 liquidityRate,
uint256 variableBorrowRate,
uint256 stableBorrowRate,
uint256 averageStableBorrowRate,
uint256 liquidityIndex,
uint256 variableBorrowIndex,
uint40 lastUpdateTimestamp
)
{
CoreLibrary.ReserveData memory reserve = reserves[_reserve];
return (
IERC20(_reserve).universalBalanceOf(address(this)),
reserve.totalBorrowsStable,
reserve.totalBorrowsVariable,
reserve.currentLiquidityRate,
reserve.currentVariableBorrowRate,
reserve.currentStableBorrowRate,
reserve.currentAverageStableBorrowRate,
reserve.lastLiquidityCumulativeIndex,
reserve.lastVariableBorrowCumulativeIndex,
reserve.lastUpdateTimestamp
);
}
function getUserAccountData(address _user)
external
view
returns (
uint256 totalCollateralETH,
uint256 totalBorrowsETH,
uint256 totalFeesETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
)
{
(
totalCollateralETH,
totalBorrowsETH,
totalFeesETH,
ltv,
currentLiquidationThreshold,
) = GenericLogic.calculateUserAccountData(
_user,
reserves,
usersReserveData,
reservesList,
addressesProvider.getPriceOracle()
);
healthFactor = 0;
availableBorrowsETH = GenericLogic.calculateAvailableBorrowsETH(
totalCollateralETH,
totalBorrowsETH,
totalFeesETH,
ltv,
address(feeProvider)
);
}
function getUserReserveData(address _reserve, address _user)
external
view
returns (
uint256 currentATokenBalance,
uint256 currentBorrowBalance,
uint256 principalBorrowBalance,
uint256 borrowRateMode,
uint256 borrowRate,
uint256 liquidityRate,
uint256 originationFee,
uint256 variableBorrowIndex,
uint256 lastUpdateTimestamp,
bool usageAsCollateralEnabled
)
{
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve];
currentATokenBalance = IERC20(reserve.aTokenAddress).balanceOf(_user);
CoreLibrary.InterestRateMode mode = user.getCurrentBorrowRateMode();
(principalBorrowBalance, currentBorrowBalance, ) = user.getBorrowBalances(reserve);
//default is 0, if mode == CoreLibrary.InterestRateMode.NONE
if (mode == CoreLibrary.InterestRateMode.STABLE) {
borrowRate = user.stableBorrowRate;
} else if (mode == CoreLibrary.InterestRateMode.VARIABLE) {
borrowRate = reserve.currentVariableBorrowRate;
}
borrowRateMode = uint256(mode);
liquidityRate = reserve.currentLiquidityRate;
originationFee = user.originationFee;
variableBorrowIndex = user.lastVariableBorrowCumulativeIndex;
lastUpdateTimestamp = user.lastUpdateTimestamp;
usageAsCollateralEnabled = user.useAsCollateral;
}
function getReserves() external view returns (address[] memory) {
return reservesList;
}
receive() external payable {
//only contracts can send ETH to the core
require(msg.sender.isContract(), "22");
}
/**
* @dev initializes a reserve
* @param _reserve the address of the reserve
* @param _aTokenAddress the address of the overlying aToken contract
* @param _decimals the decimals of the reserve currency
* @param _interestRateStrategyAddress the address of the interest rate strategy contract
**/
function initReserve(
address _reserve,
address _aTokenAddress,
uint256 _decimals,
address _interestRateStrategyAddress
) external onlyLendingPoolConfigurator {
reserves[_reserve].init(_aTokenAddress, _decimals, _interestRateStrategyAddress);
addReserveToListInternal(_reserve);
}
/**
* @dev updates the address of the interest rate strategy contract
* @param _reserve the address of the reserve
* @param _rateStrategyAddress the address of the interest rate strategy contract
**/
function setReserveInterestRateStrategyAddress(address _reserve, address _rateStrategyAddress)
external
onlyLendingPoolConfigurator
{
reserves[_reserve].interestRateStrategyAddress = _rateStrategyAddress;
}
/**
* @dev enables borrowing on a reserve. Also sets the stable rate borrowing
* @param _reserve the address of the reserve
* @param _stableBorrowRateEnabled true if the stable rate needs to be enabled, false otherwise
**/
function setReserveBorrowingEnabled(
address _reserve,
bool _borrowingEnabled,
bool _stableBorrowRateEnabled
) external onlyLendingPoolConfigurator {
if (_borrowingEnabled) {
reserves[_reserve].enableBorrowing(_stableBorrowRateEnabled);
} else {
reserves[_reserve].disableBorrowing();
}
}
/**
* @dev enables a reserve to be used as collateral
* @param _reserve the address of the reserve
**/
function enableReserveAsCollateral(
address _reserve,
uint256 _baseLTVasCollateral,
uint256 _liquidationThreshold,
uint256 _liquidationBonus
) external onlyLendingPoolConfigurator {
reserves[_reserve].enableAsCollateral(
_baseLTVasCollateral,
_liquidationThreshold,
_liquidationBonus
);
}
/**
* @dev disables a reserve to be used as collateral
* @param _reserve the address of the reserve
**/
function disableReserveAsCollateral(address _reserve) external onlyLendingPoolConfigurator {
reserves[_reserve].disableAsCollateral();
}
/**
* @dev enable the stable borrow rate mode on a reserve
* @param _reserve the address of the reserve
**/
function setReserveStableBorrowRateEnabled(address _reserve, bool _enabled)
external
onlyLendingPoolConfigurator
{
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
reserve.isStableBorrowRateEnabled = _enabled;
}
/**
* @dev activates a reserve
* @param _reserve the address of the reserve
**/
function setReserveActive(address _reserve, bool _active) external onlyLendingPoolConfigurator {
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
require(
_active &&
reserve.lastLiquidityCumulativeIndex > 0 &&
reserve.lastVariableBorrowCumulativeIndex > 0,
"29"
);
reserve.isActive = _active;
}
/**
* @notice allows the configurator to freeze the reserve.
* A freezed reserve does not allow any action apart from repay, redeem, liquidationCall, rebalance.
* @param _reserve the address of the reserve
**/
function setReserveFreeze(address _reserve, bool _isFreezed)
external
onlyLendingPoolConfigurator
{
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
reserve.isFreezed = _isFreezed;
}
/**
* @notice allows the configurator to update the loan to value of a reserve
* @param _reserve the address of the reserve
* @param _ltv the new loan to value
**/
function setReserveBaseLTVasCollateral(address _reserve, uint256 _ltv)
external
onlyLendingPoolConfigurator
{
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
reserve.baseLTVasCollateral = _ltv;
}
/**
* @notice allows the configurator to update the liquidation threshold of a reserve
* @param _reserve the address of the reserve
* @param _threshold the new liquidation threshold
**/
function setReserveLiquidationThreshold(address _reserve, uint256 _threshold)
external
onlyLendingPoolConfigurator
{
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
reserve.liquidationThreshold = _threshold;
}
/**
* @notice allows the configurator to update the liquidation bonus of a reserve
* @param _reserve the address of the reserve
* @param _bonus the new liquidation bonus
**/
function setReserveLiquidationBonus(address _reserve, uint256 _bonus)
external
onlyLendingPoolConfigurator
{
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
reserve.liquidationBonus = _bonus;
}
/**
* @notice allows the configurator to update the reserve decimals
* @param _reserve the address of the reserve
* @param _decimals the decimals of the reserve
**/
function setReserveDecimals(address _reserve, uint256 _decimals)
external
onlyLendingPoolConfigurator
{
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
reserve.decimals = _decimals;
}
/**
* @notice internal functions
**/
/**
* @dev adds a reserve to the array of the reserves address
**/
function addReserveToListInternal(address _reserve) internal {
bool reserveAlreadyAdded = false;
for (uint256 i = 0; i < reservesList.length; i++)
if (reservesList[i] == _reserve) {
reserveAlreadyAdded = true;
}
if (!reserveAlreadyAdded) reservesList.push(_reserve);
}
function getReserveNormalizedIncome(address _reserve) external view returns (uint256) {
return reserves[_reserve].getNormalizedIncome();
}
function balanceDecreaseAllowed(address _reserve, address _user, uint256 _amount)
external
view
returns (bool)
{
return
GenericLogic.balanceDecreaseAllowed(
_reserve,
_user,
_amount,
reserves,
usersReserveData,
reservesList,
addressesProvider.getPriceOracle()
);
}
}