- Refactored validation logic of liquidationCall() and repayWithCollateral() to ValidationLogic.

This commit is contained in:
eboado 2020-09-14 10:52:31 +02:00
parent 0911f907a8
commit e2500d1532
4 changed files with 155 additions and 87 deletions

View File

@ -385,7 +385,6 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 purchaseAmount,
bool receiveAToken
) external override {
ValidationLogic.validateLiquidation(_reserves[collateral], _reserves[asset]);
address liquidationManager = _addressesProvider.getLendingPoolLiquidationManager();
@ -446,8 +445,6 @@ contract LendingPool is VersionedInitializable, ILendingPool {
require(!_flashLiquidationLocked, Errors.REENTRANCY_NOT_ALLOWED);
_flashLiquidationLocked = true;
ValidationLogic.validateLiquidation(_reserves[collateral], _reserves[principal]);
address liquidationManager = _addressesProvider.getLendingPoolLiquidationManager();
//solium-disable-next-line

View File

@ -21,6 +21,7 @@ import {PercentageMath} from '../libraries/math/PercentageMath.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import {ISwapAdapter} from '../interfaces/ISwapAdapter.sol';
import {Errors} from '../libraries/helpers/Errors.sol';
import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol';
/**
* @title LendingPoolLiquidationManager contract
@ -88,15 +89,6 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
uint256 swappedCollateralAmount
);
enum LiquidationErrors {
NO_ERROR,
NO_COLLATERAL_AVAILABLE,
COLLATERAL_CANNOT_BE_LIQUIDATED,
CURRRENCY_NOT_BORROWED,
HEALTH_FACTOR_ABOVE_THRESHOLD,
NOT_ENOUGH_LIQUIDITY
}
struct LiquidationCallLocalVars {
uint256 userCollateralBalance;
uint256 userStableDebt;
@ -112,6 +104,9 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
uint256 healthFactor;
IAToken collateralAtoken;
bool isCollateralEnabled;
address principalAToken;
uint256 errorCode;
string errorMsg;
}
/**
@ -138,8 +133,8 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
uint256 purchaseAmount,
bool receiveAToken
) external returns (uint256, string memory) {
ReserveLogic.ReserveData storage principalReserve = reserves[principal];
ReserveLogic.ReserveData storage collateralReserve = reserves[collateral];
ReserveLogic.ReserveData storage principalReserve = reserves[principal];
UserConfiguration.Map storage userConfig = usersConfig[user];
LiquidationCallLocalVars memory vars;
@ -152,43 +147,29 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
addressesProvider.getPriceOracle()
);
if (vars.healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) {
return (
uint256(LiquidationErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD
);
}
vars.collateralAtoken = IAToken(collateralReserve.aTokenAddress);
vars.userCollateralBalance = vars.collateralAtoken.balanceOf(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),
Errors.COLLATERAL_CANNOT_BE_LIQUIDATED
);
}
//if the user hasn't borrowed the specific currency defined by asset, it cannot be liquidated
(vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(
user,
principalReserve
);
if (vars.userStableDebt == 0 && vars.userVariableDebt == 0) {
return (
uint256(LiquidationErrors.CURRRENCY_NOT_BORROWED),
Errors.SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER
);
(vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall(
collateralReserve,
principalReserve,
userConfig,
vars.healthFactor,
vars.userStableDebt,
vars.userVariableDebt
);
if (Errors.LiquidationErrors(vars.errorCode) != Errors.LiquidationErrors.NO_ERROR) {
return (vars.errorCode, vars.errorMsg);
}
//all clear - calculate the max principal amount that can be liquidated
vars.collateralAtoken = IAToken(collateralReserve.aTokenAddress);
vars.userCollateralBalance = vars.collateralAtoken.balanceOf(user);
vars.maxPrincipalAmountToLiquidate = vars.userStableDebt.add(vars.userVariableDebt).percentMul(
LIQUIDATION_CLOSE_FACTOR_PERCENT
);
@ -224,7 +205,7 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
);
if (currentAvailableCollateral < vars.maxCollateralToLiquidate) {
return (
uint256(LiquidationErrors.NOT_ENOUGH_LIQUIDITY),
uint256(Errors.LiquidationErrors.NOT_ENOUGH_LIQUIDITY),
Errors.NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE
);
}
@ -291,7 +272,7 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
receiveAToken
);
return (uint256(LiquidationErrors.NO_ERROR), Errors.NO_ERRORS);
return (uint256(Errors.LiquidationErrors.NO_ERROR), Errors.NO_ERRORS);
}
/**
@ -314,9 +295,8 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
address receiver,
bytes calldata params
) external returns (uint256, string memory) {
ReserveLogic.ReserveData storage debtReserve = reserves[principal];
ReserveLogic.ReserveData storage collateralReserve = reserves[collateral];
ReserveLogic.ReserveData storage debtReserve = reserves[principal];
UserConfiguration.Map storage userConfig = usersConfig[user];
LiquidationCallLocalVars memory vars;
@ -329,36 +309,20 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
addressesProvider.getPriceOracle()
);
if (
msg.sender != user && vars.healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD
) {
return (
uint256(LiquidationErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
Errors.HEALTH_FACTOR_NOT_BELOW_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),
Errors.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),
Errors.SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER
);
(vars.errorCode, vars.errorMsg) = ValidationLogic.validateRepayWithCollateral(
collateralReserve,
debtReserve,
userConfig,
user,
vars.healthFactor,
vars.userStableDebt,
vars.userVariableDebt
);
if (Errors.LiquidationErrors(vars.errorCode) != Errors.LiquidationErrors.NO_ERROR) {
return (vars.errorCode, vars.errorMsg);
}
vars.maxPrincipalAmountToLiquidate = vars.userStableDebt.add(vars.userVariableDebt);
@ -397,7 +361,7 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
usersConfig[user].setUsingAsCollateral(collateralReserve.index, false);
}
address principalAToken = debtReserve.aTokenAddress;
vars.principalAToken = debtReserve.aTokenAddress;
// Notifies the receiver to proceed, sending as param the underlying already transferred
ISwapAdapter(receiver).executeOperation(
@ -410,8 +374,8 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
//updating debt reserve
debtReserve.updateCumulativeIndexesAndTimestamp();
debtReserve.updateInterestRates(principal, principalAToken, vars.actualAmountToLiquidate, 0);
IERC20(principal).transferFrom(receiver, principalAToken, vars.actualAmountToLiquidate);
debtReserve.updateInterestRates(principal, vars.principalAToken, vars.actualAmountToLiquidate, 0);
IERC20(principal).transferFrom(receiver, vars.principalAToken, vars.actualAmountToLiquidate);
if (vars.userVariableDebt >= vars.actualAmountToLiquidate) {
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
@ -443,7 +407,7 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
vars.maxCollateralToLiquidate
);
return (uint256(LiquidationErrors.NO_ERROR), Errors.NO_ERRORS);
return (uint256(Errors.LiquidationErrors.NO_ERROR), Errors.NO_ERRORS);
}
struct AvailableCollateralToLiquidateLocalVars {

View File

@ -76,4 +76,14 @@ library Errors {
string public constant MULTIPLICATION_OVERFLOW = '44';
string public constant ADDITION_OVERFLOW = '45';
string public constant DIVISION_BY_ZERO = '46';
enum LiquidationErrors {
NO_ERROR,
NO_COLLATERAL_AVAILABLE,
COLLATERAL_CANNOT_BE_LIQUIDATED,
CURRRENCY_NOT_BORROWED,
HEALTH_FACTOR_ABOVE_THRESHOLD,
NOT_ENOUGH_LIQUIDITY,
NO_ACTIVE_RESERVE
}
}

View File

@ -13,6 +13,7 @@ import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
import {UserConfiguration} from '../configuration/UserConfiguration.sol';
import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
import {Errors} from '../helpers/Errors.sol';
import {Helpers} from '../helpers/Helpers.sol';
/**
* @title ReserveLogic library
@ -331,18 +332,114 @@ library ValidationLogic {
}
/**
* @dev Validates configurations for liquidation actions, both liquidationCall() and repayWithCollateral()
* @dev Validates the liquidationCall() action
* @param collateralReserve The reserve data of the collateral
* @param principalReserve The reserve data of the principal
* @param userConfig The user configuration
* @param userHealthFactor The user's health factor
* @param userStableDebt Total stable debt balance of the user
* @param userVariableDebt Total variable debt balance of the user
**/
function validateLiquidation(
function validateLiquidationCall(
ReserveLogic.ReserveData storage collateralReserve,
ReserveLogic.ReserveData storage principalReserve
) internal view {
require(
collateralReserve.configuration.getActive() &&
principalReserve.configuration.getActive(),
Errors.NO_ACTIVE_RESERVE
);
ReserveLogic.ReserveData storage principalReserve,
UserConfiguration.Map storage userConfig,
uint256 userHealthFactor,
uint256 userStableDebt,
uint256 userVariableDebt
) internal view returns(uint256, string memory) {
if ( !collateralReserve.configuration.getActive() || !principalReserve.configuration.getActive()) {
return (
uint256(Errors.LiquidationErrors.NO_ACTIVE_RESERVE),
Errors.NO_ACTIVE_RESERVE
);
}
if (userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) {
return (
uint256(Errors.LiquidationErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD
);
}
bool isCollateralEnabled =
collateralReserve.configuration.getLiquidationThreshold() > 0 &&
userConfig.isUsingAsCollateral(collateralReserve.index);
//if collateral isn't enabled as collateral by user, it cannot be liquidated
if (!isCollateralEnabled) {
return (
uint256(Errors.LiquidationErrors.COLLATERAL_CANNOT_BE_LIQUIDATED),
Errors.COLLATERAL_CANNOT_BE_LIQUIDATED
);
}
if (userStableDebt == 0 && userVariableDebt == 0) {
return (
uint256(Errors.LiquidationErrors.CURRRENCY_NOT_BORROWED),
Errors.SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER
);
}
return (uint256(Errors.LiquidationErrors.NO_ERROR), Errors.NO_ERRORS);
}
/**
* @dev Validates the repayWithCollateral() action
* @param collateralReserve The reserve data of the collateral
* @param principalReserve The reserve data of the principal
* @param userConfig The user configuration
* @param user The address of the user
* @param userHealthFactor The user's health factor
* @param userStableDebt Total stable debt balance of the user
* @param userVariableDebt Total variable debt balance of the user
**/
function validateRepayWithCollateral(
ReserveLogic.ReserveData storage collateralReserve,
ReserveLogic.ReserveData storage principalReserve,
UserConfiguration.Map storage userConfig,
address user,
uint256 userHealthFactor,
uint256 userStableDebt,
uint256 userVariableDebt
) internal view returns(uint256, string memory) {
if ( !collateralReserve.configuration.getActive() || !principalReserve.configuration.getActive()) {
return (
uint256(Errors.LiquidationErrors.NO_ACTIVE_RESERVE),
Errors.NO_ACTIVE_RESERVE
);
}
if (
msg.sender != user && userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD
) {
return (
uint256(Errors.LiquidationErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD
);
}
if (msg.sender != user) {
bool isCollateralEnabled =
collateralReserve.configuration.getLiquidationThreshold() > 0 &&
userConfig.isUsingAsCollateral(collateralReserve.index);
//if collateral isn't enabled as collateral by user, it cannot be liquidated
if (!isCollateralEnabled) {
return (
uint256(Errors.LiquidationErrors.COLLATERAL_CANNOT_BE_LIQUIDATED),
Errors.COLLATERAL_CANNOT_BE_LIQUIDATED
);
}
}
if (userStableDebt == 0 && userVariableDebt == 0) {
return (
uint256(Errors.LiquidationErrors.CURRRENCY_NOT_BORROWED),
Errors.SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER
);
}
return (uint256(Errors.LiquidationErrors.NO_ERROR), Errors.NO_ERRORS);
}
}