No mechanism for debt write-off
Description
The protocol allows users to take on collateralized debt. Normally, users are required to be overcollateralized. If there is a rapid price change, it is possible for a user to have more debts than assets. When liquidating, the liquidator pays off the user's debt in return for the user's collateral assets. However, if the user runs out of collateral assets, there is no incentive to liquidate. In fact, the liquidate function cannot be used without user assets. There is no incentive for the user to repay the debt either, since the user will not receive collateral back.
Normally, this would simply be a loss absorbed by the protocol. However, there is no deduction of bad debts from the total pool assets (the debt is not forgiven).
The protocol mints cTokens, which represent shares in a pool. The worth of a cToken is based on the amount of assets the pool holds, including both assets currently held and assets lent out. Since the bad debt is not deducted from the assets in the pool, the worth of a cToken does not reflect bad debt. However, there are no assets to back this extra accounted value.
Impact
The bad debts continue to be counted as assets of the pool and continue to accrue interest, even if they may never be repaid. In this situation, cTokens are overvalued. This situation is similar to a bank run, and users redeeming their cTokens too late will not be able to redeem.
For simplicity, interest is 0% and there is no liquidation bonus or fees, and the debt-to-asset ratio can be up to 1 in the following scenario:
Price of A is 1 USD; price of B is 2 USD.
User X deposits 100 A.
User Y deposits 50 B.
User Z deposits 50 B.
User A borrows 50 B.
Price of B increases to 4 USD.
User X is now liquidatable, with 100 A in assets.
User L liquidates user X, paying 25 B and taking 100 A from the protocol.
There is now a total of 0 A and 75 B in the protocol.
The protocol considers itself to have 75 B available and 25 B lent out, and it does not revalue shares.
User Y withdraws 50 B using Y's shares, worth 50 B.
User Z is now unable to withdraw 50 B, despite having shares worth 50 B.
Note that if any other users were in the pool for B at this time, it would make sense for them to immediately withdraw after the creation of bad debt, to not end up in User Z's situation.
The lending_market
includes a rate limiter which can stop this situation, depending on the parameters set. However, it relies on a centralized entity to set the correct parameters, and also to repay the bad debt in time.
Recommendations
Add a mechanism for forgiving bad debt. Bad debt can be assumed to be never repaid, since it is not in the user's interest to do so. Forgiving bad debt is simply an accounting correction.
Remediation
This issue has been acknowledged by Suilend, and a fix was implemented in commit e16f24e4↗.