Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor liquidation on deprecated market #1305

Merged
merged 8 commits into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions contracts/aave-v2/ExitPositionsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils {
uint256 collateralTokenUnit; // The collateral token unit considering its decimals.
uint256 borrowedReserveDecimals; // The number of decimals of the borrowed asset in the reserve.
uint256 borrowedTokenUnit; // The unit of borrowed token considering its decimals.
uint256 closeFactor; // The close factor used during the liquidation.
bool liquidationAllowed; // Whether the liquidation is allowed or not.
}

// Struct to avoid stack too deep.
Expand Down Expand Up @@ -221,23 +223,22 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils {
_updateIndexes(_poolTokenBorrowed);
_updateIndexes(_poolTokenCollateral);

if (!borrowedMarket.isDeprecated && !_liquidationAllowed(_borrower))
revert UnauthorisedLiquidate();

address tokenBorrowedAddress = market[_poolTokenBorrowed].underlyingToken;
LiquidateVars memory vars;
(vars.closeFactor, vars.liquidationAllowed) = _liquidationAllowed(
_borrower,
borrowedMarket.isDeprecated
);
if (!vars.liquidationAllowed) revert UnauthorisedLiquidate();
MerlinEgalite marked this conversation as resolved.
Show resolved Hide resolved

uint256 amountToLiquidate = Math.min(
_amount,
_getUserBorrowBalanceInOf(_poolTokenBorrowed, _borrower).percentMul(
DEFAULT_LIQUIDATION_CLOSE_FACTOR
) // Max liquidatable debt.
_getUserBorrowBalanceInOf(_poolTokenBorrowed, _borrower).percentMul(vars.closeFactor) // Max liquidatable debt.
);

MathisGD marked this conversation as resolved.
Show resolved Hide resolved
address tokenCollateralAddress = market[_poolTokenCollateral].underlyingToken;

IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle());

LiquidateVars memory vars;
address tokenCollateralAddress = market[_poolTokenCollateral].underlyingToken;
address tokenBorrowedAddress = market[_poolTokenBorrowed].underlyingToken;
{
ILendingPool poolMem = pool;
(, , vars.liquidationBonus, vars.collateralReserveDecimals, ) = poolMem
Expand Down Expand Up @@ -316,7 +317,6 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils {
emit P2PBorrowDeltaUpdated(_poolToken, deltasMem.p2pBorrowDelta);

ERC20 underlyingToken = ERC20(market[_poolToken].underlyingToken);

_borrowFromPool(underlyingToken, _amount);
_supplyToPool(underlyingToken, _amount);

Expand Down Expand Up @@ -691,8 +691,21 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils {

/// @dev Checks if the user is liquidatable.
/// @param _user The user to check.
/// @return Whether the user is liquidatable or not.
function _liquidationAllowed(address _user) internal returns (bool) {
return _getUserHealthFactor(_user, address(0), 0) < HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
/// @param _isDeprecated Whether the market is deprecated or not.
/// @return closeFactor The close factor to apply.
/// @return liquidationAllowed Whether the liquidation is allowed or not.
function _liquidationAllowed(address _user, bool _isDeprecated)
internal
returns (uint256 closeFactor, bool liquidationAllowed)
{
if (_isDeprecated) {
// Allow liquidation of the whole debt.
closeFactor = MAX_BASIS_POINTS;
liquidationAllowed = true;
} else {
closeFactor = DEFAULT_LIQUIDATION_CLOSE_FACTOR;
liquidationAllowed = (_getUserHealthFactor(_user, address(0), 0) <
HEALTH_FACTOR_LIQUIDATION_THRESHOLD);
}
}
}
46 changes: 27 additions & 19 deletions contracts/aave-v3/ExitPositionsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils {
uint256 borrowedTokenUnit; // The unit of borrowed token considering its decimals.
uint256 borrowedTokenPrice; // The price of the borrowed token.
uint256 amountToLiquidate; // The amount of debt token to repay.
uint256 closeFactor;
bool liquidationAllowed;
uint256 closeFactor; // The close factor used during the liquidation.
bool liquidationAllowed; // Whether the liquidation is allowed or not.
}

// Struct to avoid stack too deep.
Expand Down Expand Up @@ -229,10 +229,11 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils {
_updateIndexes(_poolTokenCollateral);

LiquidateVars memory vars;
if (!borrowedMarket.isDeprecated) {
(vars.closeFactor, vars.liquidationAllowed) = _liquidationAllowed(_borrower);
if (!vars.liquidationAllowed) revert UnauthorisedLiquidate();
} else vars.closeFactor = DEFAULT_LIQUIDATION_CLOSE_FACTOR;
(vars.closeFactor, vars.liquidationAllowed) = _liquidationAllowed(
_borrower,
borrowedMarket.isDeprecated
);
if (!vars.liquidationAllowed) revert UnauthorisedLiquidate();

vars.amountToLiquidate = Math.min(
_amount,
Expand Down Expand Up @@ -700,23 +701,30 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils {

/// @dev Checks if the user is liquidatable.
/// @param _user The user to check.
/// @param _isDeprecated Whether the market is deprecated or not.
/// @return closeFactor The close factor to apply.
/// @return liquidationAllowed Whether the liquidation is allowed or not.
function _liquidationAllowed(address _user)
function _liquidationAllowed(address _user, bool _isDeprecated)
internal
returns (uint256 closeFactor, bool liquidationAllowed)
{
uint256 healthFactor = _getUserHealthFactor(_user, address(0), 0);
address priceOracleSentinel = addressesProvider.getPriceOracleSentinel();

closeFactor = healthFactor > MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD
? DEFAULT_LIQUIDATION_CLOSE_FACTOR
: MAX_LIQUIDATION_CLOSE_FACTOR;

if (priceOracleSentinel != address(0))
liquidationAllowed = (healthFactor < MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD ||
(IPriceOracleSentinel(priceOracleSentinel).isLiquidationAllowed() &&
healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD));
else liquidationAllowed = healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
if (_isDeprecated) {
// Allow liquidation of the whole debt.
closeFactor = MAX_BASIS_POINTS;
liquidationAllowed = true;
} else {
uint256 healthFactor = _getUserHealthFactor(_user, address(0), 0);
address priceOracleSentinel = addressesProvider.getPriceOracleSentinel();

closeFactor = healthFactor > MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD
? DEFAULT_LIQUIDATION_CLOSE_FACTOR
: MAX_LIQUIDATION_CLOSE_FACTOR;

if (priceOracleSentinel != address(0))
liquidationAllowed = (healthFactor < MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD ||
(IPriceOracleSentinel(priceOracleSentinel).isLiquidationAllowed() &&
healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD));
else liquidationAllowed = healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
}
}
}
13 changes: 9 additions & 4 deletions contracts/compound/PositionsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ contract PositionsManager is IPositionsManager, MatchingEngine {
uint256 supplyBalance;
uint256 borrowedPrice;
uint256 amountToSeize;
uint256 closeFactor;
}

/// LOGIC ///
Expand Down Expand Up @@ -500,13 +501,17 @@ contract PositionsManager is IPositionsManager, MatchingEngine {
_updateP2PIndexes(_poolTokenBorrowed);
_updateP2PIndexes(_poolTokenCollateral);

if (!borrowedMarket.isDeprecated && !_isLiquidatable(_borrower, address(0), 0, 0))
revert UnauthorisedLiquidate();

LiquidateVars memory vars;
if (borrowedMarket.isDeprecated)
vars.closeFactor = WAD; // Allow liquidation of the whole debt.
else {
if (!_isLiquidatable(_borrower, address(0), 0, 0)) revert UnauthorisedLiquidate();
vars.closeFactor = comptroller.closeFactorMantissa();
}

vars.borrowBalance = _getUserBorrowBalanceInOf(_poolTokenBorrowed, _borrower);

if (_amount > vars.borrowBalance.mul(comptroller.closeFactorMantissa()))
if (_amount > vars.borrowBalance.mul(vars.closeFactor))
revert AmountAboveWhatAllowedToRepay(); // Same mechanism as Compound. Liquidator cannot repay more than part of the debt (cf close factor on Compound).

_safeRepayLogic(_poolTokenBorrowed, msg.sender, _borrower, _amount, 0);
Expand Down
2 changes: 1 addition & 1 deletion test-foundry/aave-v2/TestLiquidate.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ contract TestLiquidate is TestSetup {
(, uint256 borrowOnPoolbefore) = morpho.borrowBalanceInOf(aDai, address(borrower1));

// Liquidate
uint256 toRepay = amount / 2;
uint256 toRepay = amount; // Full liquidation.
User liquidator = borrower3;
liquidator.approve(dai, address(morpho), toRepay);
liquidator.liquidate(aDai, aUsdc, address(borrower1), toRepay);
Expand Down
2 changes: 1 addition & 1 deletion test-foundry/aave-v3/TestLiquidate.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ contract TestLiquidate is TestSetup {
(, uint256 borrowOnPoolbefore) = morpho.borrowBalanceInOf(aDai, address(borrower1));

// Liquidate
uint256 toRepay = amount / 2;
uint256 toRepay = amount; // Full liquidation.
User liquidator = borrower3;
liquidator.approve(dai, address(morpho), toRepay);
liquidator.liquidate(aDai, aUsdc, address(borrower1), toRepay);
Expand Down
2 changes: 1 addition & 1 deletion test-foundry/compound/TestLiquidate.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ contract TestLiquidate is TestSetup {
(, uint256 borrowOnPoolbefore) = morpho.borrowBalanceInOf(cDai, address(borrower1));

// Liquidate
uint256 toRepay = amount / 3;
uint256 toRepay = amount; // Full liquidation.
User liquidator = borrower3;
liquidator.approve(dai, address(morpho), toRepay);
liquidator.liquidate(cDai, cUsdc, address(borrower1), toRepay);
Expand Down