sherlock-audit / 2024-08-sentiment-v2-judging

0 stars 0 forks source link

AlexCzm - Missing 'minDebt' check from liquidation can lead to bad debt accumulation #198

Open sherlock-admin4 opened 3 weeks ago

sherlock-admin4 commented 3 weeks ago

AlexCzm

Medium

Missing 'minDebt' check from liquidation can lead to bad debt accumulation

Summary

The missing minDebt check from PositionManager.liquidate can leave positions with a small amount of debt that is unappealing to further liquidations and can lead to accumulation of bad debt.

Root Cause

Protocol implements a borrowAssets < minDebt check in Pool.borrow (link) and Pool.repay (link2), but this check is missing from liquidations.

Internal pre-conditions

  1. An unhealthy position must exist.

External pre-conditions

none

Attack Path

  1. An liquidator/attacker observes an unhealthy positions and calls PositionManager.liquidate and repays just enough debt such that after liquidations 0 < the new position's debt < minDebt.
  2. After 1st liquidation position became sound with debt < assets deposited.
  3. After some time, due to market conditions, same position became unhealthy again. But due to gas prices and small position the liquidators are disincentivized to liquidate it.
  4. Due to further asset's prices decrease, position accumulate bad debt and lenders must take a loss. Since the protocol can be deployed to Ethereum L1 small

Impact

Protocol can have many positions with debt < minDebt. Over time, since there will be no incentive for liquidators to liquidate small underwater positions given the gas cost, protocol accumulates bad debt at the detrimental of lenders.

PoC

No response

Mitigation

Ensure that liquidators liquidate entire position's debt or, that the remaining debt after liquidation is bigger than minDebt.

samuraii77 commented 3 days ago

This issue is the complete opposite of #194. Thus, one of the issues should be invalidated as they contradict each other.

neko-nyaa commented 3 days ago

Escalate based on the above comment

sherlock-admin3 commented 3 days ago

Escalate based on the above comment

You've created a valid escalation!

To remove the escalation from consideration: Delete your comment.

You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final.

iamnmt commented 3 days ago

Incorrect example.

  1. An liquidator/attacker observes an unhealthy positions and calls PositionManager.liquidate and repays just enough debt such that after liquidations 0 < the new position's debt < minDebt.

Due to this check

https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/0b472f4bffdb2c7432a5d21f1636139cc01561a5/protocol-v2/src/Pool.sol#L511

    function repay(uint256 poolId, address position, uint256 amt) external returns (uint256 remainingShares) {
---
        // revert if repaid amt is too small
        if (borrowShares == 0) revert Pool_ZeroSharesRepay(poolId, amt);

        // check that final debt amount is greater than min debt
        remainingShares = borrowSharesOf[poolId][position] - borrowShares;
        if (remainingShares > 0) {
            uint256 newBorrowAssets = _convertToAssets(
                remainingShares, pool.totalBorrowAssets - amt, pool.totalBorrowShares - borrowShares, Math.Rounding.Down
            );
@>            if (_getValueOf(pool.asset, newBorrowAssets) < minDebt) {
@>                revert Pool_DebtTooLow(poolId, pool.asset, newBorrowAssets);
@>            }
        }

It is impossible to repay in the the described way.