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

981 lines
29 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';
/**
* @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 whenNotPaused() {
_whenNotPaused();
_;
}
modifier onlyLendingPoolConfigurator() {
_onlyLendingPoolConfigurator();
_;
}
function _whenNotPaused() internal view {
require(!_paused, Errors.LP_IS_PAUSED);
}
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 whenNotPaused {
_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 whenNotPaused returns (uint256) {
return _executeWithdraw(asset, amount, to);
}
///@inheritdoc ILendingPool
function borrow(
address asset,
uint256 amount,
uint256 interestRateMode,
uint16 referralCode,
address onBehalfOf
) external override whenNotPaused {
_executeBorrow(
ExecuteBorrowParams(
asset,
msg.sender,
onBehalfOf,
amount,
interestRateMode,
referralCode,
true
)
);
}
///@inheritdoc ILendingPool
function repay(
address asset,
uint256 amount,
uint256 rateMode,
address onBehalfOf
) external override whenNotPaused 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 whenNotPaused {
DataTypes.ReserveData storage reserve = _reserves[asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve);
DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
ValidationLogic.validateSwapRateMode(
reserve,
reserveCache,
_usersConfig[msg.sender],
stableDebt,
variableDebt,
interestRateMode
);
reserve.updateState(reserveCache);
if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(msg.sender, stableDebt);
IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint(
msg.sender,
msg.sender,
stableDebt,
reserveCache.nextVariableBorrowIndex
);
reserveCache.refreshDebt(0, stableDebt, stableDebt, 0);
} else {
IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn(
msg.sender,
variableDebt,
reserveCache.nextVariableBorrowIndex
);
IStableDebtToken(reserveCache.stableDebtTokenAddress).mint(
msg.sender,
msg.sender,
variableDebt,
reserve.currentStableBorrowRate
);
reserveCache.refreshDebt(variableDebt, 0, 0, variableDebt);
}
reserve.updateInterestRates(reserveCache, asset, 0, 0);
emit Swap(asset, msg.sender, rateMode);
}
///@inheritdoc ILendingPool
function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused {
DataTypes.ReserveData storage reserve = _reserves[asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
IERC20 stableDebtToken = IERC20(reserveCache.stableDebtTokenAddress);
IERC20 variableDebtToken = IERC20(reserveCache.variableDebtTokenAddress);
uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user);
ValidationLogic.validateRebalanceStableBorrowRate(
reserve,
reserveCache,
asset,
stableDebtToken,
variableDebtToken,
reserveCache.aTokenAddress
);
reserve.updateState(reserveCache);
IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt);
IStableDebtToken(address(stableDebtToken)).mint(
user,
user,
stableDebt,
reserve.currentStableBorrowRate
);
reserveCache.refreshDebt(stableDebt, stableDebt, 0, 0);
reserve.updateInterestRates(reserveCache, asset, 0, 0);
emit RebalanceStableBorrowRate(asset, user);
}
///@inheritdoc ILendingPool
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral)
external
override
whenNotPaused
{
DataTypes.ReserveData storage reserve = _reserves[asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
ValidationLogic.validateSetUseReserveAsCollateral(reserveCache);
_usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral);
if (useAsCollateral) {
emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
} else {
ValidationLogic.validateWithdrawCollateral(
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 whenNotPaused {
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 whenNotPaused {
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(
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 whenNotPaused {
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.validateWithdrawCollateral(
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 val) external override onlyLendingPoolConfigurator {
_paused = val;
if (_paused) {
emit Paused();
} else {
emit Unpaused();
}
}
///@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;
}
struct ExecuteBorrowParams {
address asset;
address user;
address onBehalfOf;
uint256 amount;
uint256 interestRateMode;
uint16 referralCode;
bool releaseUnderlying;
}
function _executeBorrow(ExecuteBorrowParams memory vars) internal {
DataTypes.ReserveData storage reserve = _reserves[vars.asset];
DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
reserve.updateState(reserveCache);
ValidationLogic.validateBorrow(
reserveCache,
vars.asset,
vars.onBehalfOf,
vars.amount,
vars.interestRateMode,
_maxStableRateBorrowSizePercent,
_reserves,
userConfig,
_reservesList,
_reservesCount,
_addressesProvider.getPriceOracle()
);
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 _executeDeposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) internal {
DataTypes.ReserveData storage reserve = _reserves[asset];
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) {
_usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true);
emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf);
}
emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode);
}
function _executeWithdraw(
address asset,
uint256 amount,
address to
) internal returns (uint256) {
DataTypes.ReserveData storage reserve = _reserves[asset];
DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender];
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()) {
ValidationLogic.validateWithdrawCollateral(
asset,
msg.sender,
_reserves,
userConfig,
_reservesList,
_reservesCount,
_addressesProvider.getPriceOracle()
);
}
if (amountToWithdraw == userBalance) {
userConfig.setUsingAsCollateral(reserve.id, false);
emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
}
}
emit Withdraw(asset, msg.sender, to, amountToWithdraw);
return amountToWithdraw;
}
function _executeRepay(
address asset,
uint256 amount,
uint256 rateMode,
address onBehalfOf
) internal returns (uint256) {
DataTypes.ReserveData storage reserve = _reserves[asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve);
DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
ValidationLogic.validateRepay(
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);
}
reserve.updateInterestRates(reserveCache, asset, paybackAmount, 0);
if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) {
_usersConfig[onBehalfOf].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;
}
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);
}
}