sherlock-audit / 2023-05-ironbank-judging

2 stars 2 forks source link

qbs - Lack of proper gap between maximal LTV and liquidation LTV ratios #456

Closed sherlock-admin closed 1 year ago

sherlock-admin commented 1 year ago

qbs

medium

Lack of proper gap between maximal LTV and liquidation LTV ratios

Summary

The protocol lacks a sufficient gap between the maximal Loan-to-Value (LTV) ratio and the liquidation LTV ratio. The code permits users to borrow up to the maximum LTV with only 1 wei buffer for collateral and debt price fluctuations.

Vulnerability Detail

The _isLiquidable function allows for liquidation of user positions if the debt value exceeds the collateral value, even by 1 wei

        return debtValue > liquidationCollateralValue;

There is a lack of a gap, like the one implemented in other lending protocols such as Compound, Maker, and Aave. In these protocols, users can borrow up to the maximum LTV but are only liquidated after reaching the higher liquidation LTV. For instance, in Aave, the maximum LTV for Aave Token is 66%, while the liquidation threshold is set at 73%. This difference (7%) allows for debt and collateral value fluctuations, safeguarding users against liquidations triggered by volatility.

Impact

Users are allowed to take maximal debts and be liquidated almost immediately, with any unfavourable price fluctuation. The harsh penalties imposed on users can potentially increase the high bad debt, thereby damaging the protocol.

The possibility of liquidation occurring in the same block is considered a vulnerability of high severity. However, since it is not possible in the case of Iron Bank, thanks to the presence of a 1 wei gap, I believe it can be assumed to have a medium severity.

Code Snippet

Test case:

function testLackOfLiquidationGap() public {

    //supply user1 
    vm.startPrank(user);
    uint256 market1SupplyAmount = 1000 * (10 ** underlyingDecimals1);
    market1.approve(address(ib), market1SupplyAmount);
    ib.supply(user, user, address(market1), market1SupplyAmount);
    vm.stopPrank();

    //supply user2
    vm.startPrank(user1);
    uint256 market2SupplyAmount = 1000 * (10 ** underlyingDecimals2);
    market2.approve(address(ib), market2SupplyAmount);
    ib.supply(user1, user1, address(market2), market2SupplyAmount);
    vm.stopPrank();

    //user 1 borrow
    vm.startPrank(user);
    uint256 market2BorrowAmount = 800 * (10 ** underlyingDecimals2);
    ib.borrow(user, user, address(market2), market2BorrowAmount);
    vm.stopPrank();    

    //price grow 1 wei
    setPriceForMarket(oracle, registry, admin, address(market2), address(market2), Denominations.ETH, market2Price+1);

    //user's position becomes liquidable
    assertTrue(ib.isUserLiquidatable(address(user)));
}

Tool used

Manual Review

Recommendation

Consider introducing a liquidation LTV that is bigger than the maximal borrow LTV.