mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
- Implemented repayWithCollateral() on LendingPoolLiquidationManager.
This commit is contained in:
parent
e4485f12fe
commit
2cbb1f5714
|
@ -231,6 +231,27 @@ interface ILendingPool {
|
||||||
bool receiveAToken
|
bool receiveAToken
|
||||||
) external;
|
) external;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev allows smartcontracts to access the liquidity of the pool within one transaction,
|
* @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
|
* as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
|
||||||
|
|
|
@ -455,6 +455,49 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable, ILendingPool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 nonReentrant {
|
||||||
|
address liquidationManager = _addressesProvider.getLendingPoolLiquidationManager();
|
||||||
|
|
||||||
|
//solium-disable-next-line
|
||||||
|
(bool success, bytes memory result) = liquidationManager.delegatecall(
|
||||||
|
abi.encodeWithSignature(
|
||||||
|
'repayWithCollateral(address,address,address,uint256,address,bytes)',
|
||||||
|
collateral,
|
||||||
|
principal,
|
||||||
|
user,
|
||||||
|
principalAmount,
|
||||||
|
receiver,
|
||||||
|
params
|
||||||
|
)
|
||||||
|
);
|
||||||
|
require(success, 'FAILED_REPAY_WITH_COLLATERAL');
|
||||||
|
|
||||||
|
(uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
|
||||||
|
|
||||||
|
if (returnCode != 0) {
|
||||||
|
revert(string(abi.encodePacked(returnMessage)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev allows smartcontracts to access the liquidity of the pool within one transaction,
|
* @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
|
* as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
|
||||||
|
@ -519,115 +562,6 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable, ILendingPool {
|
||||||
emit FlashLoan(receiverAddress, asset, amount, amountFee);
|
emit FlashLoan(receiverAddress, asset, amount, amountFee);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev flashes collateral, by both a flash liquidator or the user owning it.
|
|
||||||
* @param collateralAsset The address of the collateral asset.
|
|
||||||
* @param debtAsset The address of the debt asset.
|
|
||||||
* @param collateralAmount Collateral amount to flash.
|
|
||||||
* @param user Address of the user owning the collateral.
|
|
||||||
* @param receiverAddress Address of the contract receiving the collateral.
|
|
||||||
* @param debtMode Numeric variable, managing how to operate with the debt side.
|
|
||||||
* 1 -> With final repayment, to do it on the stable debt.
|
|
||||||
* 2 -> With final repayment, to do it on the variable debt.
|
|
||||||
* 3 -> On movement of the debt to the liquidator, to move the stable debt
|
|
||||||
* 4 -> On movement of the debt to the liquidator, to move the variable debt
|
|
||||||
* @param receiveAToken "true" to send aToken to the receiver contract, "false" to send underlying tokens.
|
|
||||||
* @param referralCode Integrators are assigned a referral code and can potentially receive rewards.
|
|
||||||
**/
|
|
||||||
function flashCollateral(
|
|
||||||
address collateralAsset,
|
|
||||||
address debtAsset,
|
|
||||||
uint256 collateralAmount,
|
|
||||||
address user,
|
|
||||||
address receiverAddress,
|
|
||||||
uint256 debtMode,
|
|
||||||
bool receiveAToken,
|
|
||||||
uint16 referralCode
|
|
||||||
) external override {
|
|
||||||
require(debtMode > 0, 'INVALID_DEBT_FLAG');
|
|
||||||
|
|
||||||
ReserveLogic.ReserveData storage collateralReserve = _reserves[collateralAsset];
|
|
||||||
ReserveLogic.ReserveData storage debtReserve = _reserves[debtAsset];
|
|
||||||
|
|
||||||
address collateralAToken = collateralReserve.aTokenAddress;
|
|
||||||
uint256 availableCollateral = IERC20(collateralAToken).balanceOf(user);
|
|
||||||
|
|
||||||
require(collateralAmount <= availableCollateral, 'NOT_ENOUGH_BALANCE');
|
|
||||||
|
|
||||||
address oracle = addressesProvider.getPriceOracle();
|
|
||||||
(, , , , healthFactor) = GenericLogic.calculateUserAccountData(
|
|
||||||
user,
|
|
||||||
_reserves,
|
|
||||||
_usersConfig[user],
|
|
||||||
_reservesList,
|
|
||||||
oracle
|
|
||||||
);
|
|
||||||
|
|
||||||
if (healthFactor > GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD && msg.sender != user) {
|
|
||||||
revert('INVALID_FLASH_COLLATERAL_BY_NON_OWNER');
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 amountToFlash = (msg.sender == user || healthFactor < 0.98 ether) // TODO: better constant
|
|
||||||
? collateralAmount
|
|
||||||
: collateralAmount.div(2); // TODO: better constant
|
|
||||||
|
|
||||||
// If liquidator reclaims the aToken, he receives the equivalent atoken amount,
|
|
||||||
// otherwise receives the underlying asset
|
|
||||||
if (receiveAToken) {
|
|
||||||
IAToken(collateralAToken).transferOnLiquidation(user, receiverAddress, amountToFlash);
|
|
||||||
} else {
|
|
||||||
collateralReserve.updateCumulativeIndexesAndTimestamp();
|
|
||||||
collateralReserve.updateInterestRates(collateral, aTokenAddress, 0, collateralAmount);
|
|
||||||
|
|
||||||
// Burn of aToken and send the underlying to the receiver
|
|
||||||
IAToken(aTokenAddress).burn(user, receiver, collateralAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notifies the receiver to proceed, sending the underlying or the aToken amount already transferred
|
|
||||||
IFlashLoanReceiver(receiverAddress).executeOperation(
|
|
||||||
collateralAsset,
|
|
||||||
aTokenAddress,
|
|
||||||
(!receiveAToken) ? collateralAmount : 0,
|
|
||||||
receiverAToken ? aTokenAmount : 0,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
|
|
||||||
// Calculation of the minimum amount of the debt asset to be received
|
|
||||||
uint256 debtAmountNeeded = oracle
|
|
||||||
.getAssetPrice(collateralAsset)
|
|
||||||
.mul(collateralAmount)
|
|
||||||
.mul(10**debtReserve.configuration.getDecimals())
|
|
||||||
.div(oracle.getAssetPrice(debtAsset).mul(10**collateralReserve.configuration.getDecimals()))
|
|
||||||
.percentDiv(collateralReserve.configuration.getLiquidationBonus());
|
|
||||||
|
|
||||||
// Or the debt is transferred to the msg.sender, or funds are transferred from the receiver to repay the debt
|
|
||||||
if (debtMode > 2) {
|
|
||||||
(uint256 userStableDebt, uint256 userVariableDebt) = Helpers.getUserCurrentDebt(
|
|
||||||
user,
|
|
||||||
debtReserve
|
|
||||||
);
|
|
||||||
|
|
||||||
uint256 debtToTransfer;
|
|
||||||
if (debtMode.div(3) == 1) {
|
|
||||||
// stable
|
|
||||||
debtToTransfer = (userStableDebt > debtAmountNeeded) ? debtAmountNeeded : userStableDebt;
|
|
||||||
IStableDebtToken(debtReserve.stableDebtTokenAddress).burn(user, debtToTransfer);
|
|
||||||
} else if (debtMode.div(3) == 2) {
|
|
||||||
// variable
|
|
||||||
debtToTransfer = (userVariableDebt > debtAmountNeeded)
|
|
||||||
? debtAmountNeeded
|
|
||||||
: userVariableDebt;
|
|
||||||
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(user, debtToTransfer);
|
|
||||||
}
|
|
||||||
_executeBorrow(
|
|
||||||
BorrowLocalVars(debtAsset, msg.sender, debtToTransfer, debt, false, referralCode)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
IERC20(debtAsset).transferFrom(receiverAddress, address(this), debtAmountNeeded);
|
|
||||||
_executeRepay(asset, msg.sender, amount, debtMode, user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev accessory functions to fetch data from the core contract
|
* @dev accessory functions to fetch data from the core contract
|
||||||
**/
|
**/
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {Helpers} from '../libraries/helpers/Helpers.sol';
|
||||||
import {WadRayMath} from '../libraries/math/WadRayMath.sol';
|
import {WadRayMath} from '../libraries/math/WadRayMath.sol';
|
||||||
import {PercentageMath} from '../libraries/math/PercentageMath.sol';
|
import {PercentageMath} from '../libraries/math/PercentageMath.sol';
|
||||||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
||||||
|
import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title LendingPoolLiquidationManager contract
|
* @title LendingPoolLiquidationManager contract
|
||||||
|
@ -65,6 +66,24 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
|
||||||
bool receiveAToken
|
bool receiveAToken
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
@dev emitted when a borrower/liquidator repays with the borrower's collateral
|
||||||
|
@param collateral the address of the collateral being swapped to repay
|
||||||
|
@param principal the address of the reserve of the debt
|
||||||
|
@param user the borrower's address
|
||||||
|
@param liquidator the address of the liquidator, same as the one of the borrower on self-repayment
|
||||||
|
@param principalAmount the amount of the debt finally covered
|
||||||
|
@param swappedCollateralAmount the amount of collateral finally swapped
|
||||||
|
*/
|
||||||
|
event RepaidWithCollateral(
|
||||||
|
address indexed collateral,
|
||||||
|
address indexed principal,
|
||||||
|
address indexed user,
|
||||||
|
address liquidator,
|
||||||
|
uint256 principalAmount,
|
||||||
|
uint256 swappedCollateralAmount
|
||||||
|
);
|
||||||
|
|
||||||
enum LiquidationErrors {
|
enum LiquidationErrors {
|
||||||
NO_ERROR,
|
NO_ERROR,
|
||||||
NO_COLLATERAL_AVAILABLE,
|
NO_COLLATERAL_AVAILABLE,
|
||||||
|
@ -271,6 +290,166 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
|
||||||
return (uint256(LiquidationErrors.NO_ERROR), 'No errors');
|
return (uint256(LiquidationErrors.NO_ERROR), 'No errors');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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.
|
||||||
|
* @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 returns (uint256, string memory) {
|
||||||
|
ReserveLogic.ReserveData storage debtReserve = reserves[principal];
|
||||||
|
ReserveLogic.ReserveData storage collateralReserve = reserves[collateral];
|
||||||
|
|
||||||
|
UserConfiguration.Map storage userConfig = usersConfig[user];
|
||||||
|
|
||||||
|
LiquidationCallLocalVars memory vars;
|
||||||
|
|
||||||
|
(, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
|
||||||
|
user,
|
||||||
|
reserves,
|
||||||
|
usersConfig[user],
|
||||||
|
reservesList,
|
||||||
|
addressesProvider.getPriceOracle()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (msg.sender != user && vars.healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) {
|
||||||
|
return (
|
||||||
|
uint256(LiquidationErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
|
||||||
|
'HEALTH_FACTOR_ABOVE_THRESHOLD'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.sender != user) {
|
||||||
|
vars.isCollateralEnabled = collateralReserve.configuration.getLiquidationThreshold() > 0 &&
|
||||||
|
userConfig.isUsingAsCollateral(collateralReserve.index);
|
||||||
|
|
||||||
|
//if collateral isn't enabled as collateral by user, it cannot be liquidated
|
||||||
|
if (!vars.isCollateralEnabled) {
|
||||||
|
return (
|
||||||
|
uint256(LiquidationErrors.COLLATERAL_CANNOT_BE_LIQUIDATED),
|
||||||
|
'COLLATERAL_CANNOT_BE_LIQUIDATED'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(
|
||||||
|
user,
|
||||||
|
debtReserve
|
||||||
|
);
|
||||||
|
|
||||||
|
if (vars.userStableDebt == 0 && vars.userVariableDebt == 0) {
|
||||||
|
return (
|
||||||
|
uint256(LiquidationErrors.CURRRENCY_NOT_BORROWED),
|
||||||
|
'CURRRENCY_NOT_BORROWED'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.sender == user || vars.healthFactor < GenericLogic.HEALTH_FACTOR_CRITICAL_THRESHOLD) {
|
||||||
|
vars.maxPrincipalAmountToLiquidate = vars.userStableDebt.add(vars.userVariableDebt);
|
||||||
|
} else {
|
||||||
|
vars.maxPrincipalAmountToLiquidate = vars.userStableDebt.add(vars.userVariableDebt).percentMul(
|
||||||
|
LIQUIDATION_CLOSE_FACTOR_PERCENT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
vars.actualAmountToLiquidate = principalAmount > vars.maxPrincipalAmountToLiquidate
|
||||||
|
? vars.maxPrincipalAmountToLiquidate
|
||||||
|
: principalAmount;
|
||||||
|
|
||||||
|
vars.collateralAtoken = IAToken(collateralReserve.aTokenAddress);
|
||||||
|
vars.userCollateralBalance = vars.collateralAtoken.balanceOf(user);
|
||||||
|
|
||||||
|
(
|
||||||
|
vars.maxCollateralToLiquidate,
|
||||||
|
vars.principalAmountNeeded
|
||||||
|
) = calculateAvailableCollateralToLiquidate(
|
||||||
|
collateralReserve,
|
||||||
|
debtReserve,
|
||||||
|
collateral,
|
||||||
|
principal,
|
||||||
|
vars.actualAmountToLiquidate,
|
||||||
|
vars.userCollateralBalance
|
||||||
|
);
|
||||||
|
|
||||||
|
//if principalAmountNeeded < vars.ActualAmountToLiquidate, there isn't enough
|
||||||
|
//of collateral to cover the actual amount that is being liquidated, hence we liquidate
|
||||||
|
//a smaller amount
|
||||||
|
if (vars.principalAmountNeeded < vars.actualAmountToLiquidate) {
|
||||||
|
vars.actualAmountToLiquidate = vars.principalAmountNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
vars.collateralAtoken.burn(user, receiver, vars.maxCollateralToLiquidate);
|
||||||
|
|
||||||
|
// Notifies the receiver to proceed, sending as param the underlying already transferred
|
||||||
|
IFlashLoanReceiver(receiver).executeOperation(
|
||||||
|
collateral,
|
||||||
|
address(vars.collateralAtoken),
|
||||||
|
vars.maxCollateralToLiquidate,
|
||||||
|
0,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
//updating debt reserve
|
||||||
|
debtReserve.updateCumulativeIndexesAndTimestamp();
|
||||||
|
debtReserve.updateInterestRates(
|
||||||
|
principal,
|
||||||
|
debtReserve.aTokenAddress,
|
||||||
|
vars.actualAmountToLiquidate,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
IERC20(principal).transferFrom(receiver, debtReserve.aTokenAddress, vars.actualAmountToLiquidate);
|
||||||
|
|
||||||
|
if (vars.userVariableDebt >= vars.actualAmountToLiquidate) {
|
||||||
|
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
|
||||||
|
user,
|
||||||
|
vars.actualAmountToLiquidate
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
|
||||||
|
user,
|
||||||
|
vars.userVariableDebt
|
||||||
|
);
|
||||||
|
IStableDebtToken(debtReserve.stableDebtTokenAddress).burn(
|
||||||
|
user,
|
||||||
|
vars.actualAmountToLiquidate.sub(vars.userVariableDebt)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//updating collateral reserve
|
||||||
|
collateralReserve.updateCumulativeIndexesAndTimestamp();
|
||||||
|
collateralReserve.updateInterestRates(
|
||||||
|
collateral,
|
||||||
|
address(vars.collateralAtoken),
|
||||||
|
0,
|
||||||
|
vars.maxCollateralToLiquidate
|
||||||
|
);
|
||||||
|
|
||||||
|
emit RepaidWithCollateral(
|
||||||
|
collateral,
|
||||||
|
principal,
|
||||||
|
user,
|
||||||
|
msg.sender,
|
||||||
|
vars.actualAmountToLiquidate,
|
||||||
|
vars.maxCollateralToLiquidate
|
||||||
|
);
|
||||||
|
|
||||||
|
return (uint256(LiquidationErrors.NO_ERROR), 'SUCCESS');
|
||||||
|
}
|
||||||
|
|
||||||
struct AvailableCollateralToLiquidateLocalVars {
|
struct AvailableCollateralToLiquidateLocalVars {
|
||||||
uint256 userCompoundedBorrowBalance;
|
uint256 userCompoundedBorrowBalance;
|
||||||
uint256 liquidationBonus;
|
uint256 liquidationBonus;
|
||||||
|
|
|
@ -24,7 +24,8 @@ library GenericLogic {
|
||||||
using ReserveConfiguration for ReserveConfiguration.Map;
|
using ReserveConfiguration for ReserveConfiguration.Map;
|
||||||
using UserConfiguration for UserConfiguration.Map;
|
using UserConfiguration for UserConfiguration.Map;
|
||||||
|
|
||||||
uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1e18;
|
uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether;
|
||||||
|
uint256 public constant HEALTH_FACTOR_CRITICAL_THRESHOLD = 0.98 ether;
|
||||||
|
|
||||||
struct balanceDecreaseAllowedLocalVars {
|
struct balanceDecreaseAllowedLocalVars {
|
||||||
uint256 decimals;
|
uint256 decimals;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user