It is a known issue that if the system is insolvent, loans can be broken. However, even if the system is solvent, positions can be illiquidatable under certain conditions as described below.
The liquidate
function requires the health factor of a position to improve, which is enforced with the following lines:
The objective is to show that a solvent position can be created which always violates this rule, and is thus illiquidatable.
Lets assume the total collateral in USD amounts is , and the debt is . Health factor is defined as , due to the 200% collateralization.
After a liquidation event of an amount say delta, the debt decreases to: , and the collateral decreases to:
The factor 1.1 is the liquidation incentive, and the liquidator gets to take out 1.1 times the amount of debt they pay off, both denominated in usd.
New health factor is thus . We want to show that this is always less than the old health factor, which is , for any choice of liquidation amount delta.
It can be trivially shown that for any , liquidation of any amount will always result in a lower health factor, and is left as an exercise for the reader.
Solvency is defined as , since this means the system has enough collateral to cover the debt. Thus if the position is , it is solvent as well as illiquidatable, which is a high severity isssue.
This can be demonstrated with a simple POC:
Assume collateral = 1000 USD. Debt = 950 USD. Thus the system is solvent, but the position is supposed to be liquidatable. But this is not the case, since liquidating by any amount will always reduce its health factor. If the entire debt is paid off, the health factor should hit 0. But the issue with this is that the system tries to pay the liquidator 1.1*950USD=1045USD, which is more than the collateral available, and thus leads to a revert due to underflow. This functionality can be seen in the github link above.
The following foundry test creates a position as described, as is shown to be illiquidatable. This is a fuzz test showing that any value passed is unable to liquidate. The minimum bound is set to 1 ether since for very low values passed, the rounding error lets the transaction go through, but it is impractical to expect liquidations of hundreds of wei.
This position can be created during flash crashes, where the token can devalue large amounts in short periods of time, and liquidations cannot go through due to network congestion. Lending protocols make the fundamental assumption that any position which is solvent should be profitably liquidatable when health factor is below 1. This contract breaks this assumption as is thus a high severity issue.
Positions can be illiquidatable while being solvent.
Foundry
When calculating liquidation incentives, cap it to the amount of collateral available. This will prevent the liquidator from being paid more than the collateral available, and thus prevent the liquidation from reverting, allowing complete liquidations at less than 10% liquidation incentives.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.