Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
During liquidation, it first attempts to use the user’s earmark to repay as much debt as possible.
There is a problem when the earmark is close to the debt, it does not wipe out the debt completely and leave a small debt.
Now, the outsourcedFee (calculated from the remaining debt) can become extremely small.
If the underlyingConversionFactor is larger than the outsourcedFee, the normalized value in normalizeDebtTokensToUnderlying(outsourcedFee) may round down to zero (e.g., 5e11 / 1e12 = 0).
This results in the fee vault’s withdraw to revert, since it fails the _checkNonZeroAmount validation that prevents 0 withdrawals.
The root cause of the issue is that the withdraw on the fee vault reverts if the amount is 0, this is caused by liquidating a low debt, because the outsource fee which is based on a percentage of the new debt, which will be divided by the underlyingConversionFactor,
A low debt could be created when the earmark is removed during forced repayment, so if a user has a large amount of debt before, this could become dust debt. It can also happen when minting a lot of small debts
Impact Details
This issue can temporarily cause liquidations to fail when a user’s remaining debt is very small.
It can occur naturally, but a user could also intentionally create tiny debts to avoid liquidation.
For a 6 decimal vault with a underlyingConversionFactor of 1e12 the fee bonus will be normalizeDebtTokensToUnderlying(5.125e11) = 0 causing the withdraw to revert with ZeroAmount() error.
PoC:
Please run the gist with: forge test --mt testLiquidateReverts -vv