Liquidation of full debt fails at sufficiently low collateral price
Summary
When liquidating an undercollateralized borrower, the protocol will calculate the amount of IBTokens to seize for the liquidator based on the price of the borrowed asset. If this price has dipped significantly since the loan was taken out, the calculated amount may be greater than the borrower's IBToken balance, which will cause the liquidation to revert.
Vulnerability Detail
The function IronBank._getLiquidationSeizeAmount() calculates the amount of IBToken that the liquidator will seize from the borrower using this formula:
The amount of tokens to seize will increase as collateralMarketPrice decreases, with all else held constant. For sufficiently low values of collateralMarketPrice, the amount to seize could be greater than the borrower's total balance.
If ibTokenAmount is greater than the the borrower's collateral balance, the require() on line 873 will fail, reverting the liquidation.
The view function IronBank.calculateLiquidationOpportunity() also uses _getLiquidationSeizeAmount(), so under these circumstances, it will return a seizable amount greater than the borrower's balance.
Impact
Severely undercollateralized borrowers cannot be liquidated for their total debt in a single call to liquidate(). Partial liquidations for small enough amounts may still be possible. Additionally, IronBank.calculateLiquidationOpportunity() will return an incorrect value under these circumstances.
Code Snippet
A Foundry test based on test/TestLiquidate.sol which demonstrates this issue is provided below:
dy
medium
Liquidation of full debt fails at sufficiently low collateral price
Summary
When liquidating an undercollateralized borrower, the protocol will calculate the amount of IBTokens to seize for the liquidator based on the price of the borrowed asset. If this price has dipped significantly since the loan was taken out, the calculated amount may be greater than the borrower's IBToken balance, which will cause the liquidation to revert.
Vulnerability Detail
The function
IronBank._getLiquidationSeizeAmount()
calculates the amount of IBToken that the liquidator will seize from the borrower using this formula:The amount of tokens to seize will increase as
collateralMarketPrice
decreases, with all else held constant. For sufficiently low values ofcollateralMarketPrice
, the amount to seize could be greater than the borrower's total balance.In
IronBank.liquidate()
, this value is passed directly toIronBank._transferIBToken()
.If
ibTokenAmount
is greater than the the borrower's collateral balance, therequire()
on line 873 will fail, reverting the liquidation.The view function
IronBank.calculateLiquidationOpportunity()
also uses_getLiquidationSeizeAmount()
, so under these circumstances, it will return a seizable amount greater than the borrower's balance.Impact
Severely undercollateralized borrowers cannot be liquidated for their total debt in a single call to
liquidate()
. Partial liquidations for small enough amounts may still be possible. Additionally,IronBank.calculateLiquidationOpportunity()
will return an incorrect value under these circumstances.Code Snippet
A Foundry test based on
test/TestLiquidate.sol
which demonstrates this issue is provided below:To run the POC, add the function to
TestLiquidate
and run this forge command:The test should fail with the message "transfer amount exceeds balance".
Tool used
Forge Fuzz Testing
Recommendation
A check could be added to
_getLiquidationSeizeAmount()
that returns the borrower's balance if it is lower than the seizable amount calculated.