M-1: High interest rates with penalty can make liquidations unprofitable, leading to bad debt
Description:
The liquidation discount is capped at 15% (LayerCredit.sol#L170), but there's no validation that bond parameters create liquidatable positions.
When a liquidator assumes debt during liquidation, they must either:
- Repay immediately and pay the early repayment penalty
- Hold the debt until maturity and pay accrued interest
Both options become unprofitable when remaining interest exceeds the 15% max discount:
| Interest Rate | Time Remaining | Remaining Interest | Profitable? |
|--------------|----------------|-------------------|-------------|
| 15% APY | 1 year | ~16% | no |
| 20% APY | 1 year | ~22% | no |
| 10% APY | 2 years | ~21% | no |
The whitepaper suggests setting LTVs carefully, but this doesn't solve the issue since:
- LTV controls when liquidation triggers, not the liquidator's profit margin
- Remaining interest is based on time until maturity, not position health
- The 15% discount cap is independent of LTV settings
Impact:
Medium. Bonds with penalty and high interest rates or long durations can have positions that are unprofitable to liquidate, leading to bad debt accumulation and losses for lenders.
Recommendation:
Validate at deployment that maxPossiblePenalty <= maxLiquidationDiscount. Add in deployBond():
// Calculate maximum interest over the full term duration
// multiplier is in 1e27 scale (e.g., 1.22e27 for 22% total interest)
(uint256 multiplier,) = RPow.rpow(uint256(p.interestRate) + 1e27, p.termDuration, 1e27);
// maxInterestFraction: the interest portion as a fraction in 1e27 scale
// e.g., 0.22e27 represents 22% interest
uint256 maxInterestFraction = multiplier - 1e27;
// maxPenaltyFraction: penalty as a fraction of principal in 1e27 scale
// earlyRepayPenalty is in 1e4 scale (1e4 = 100%), so divide by 1e4
uint256 maxPenaltyFraction = maxInterestFraction * p.earlyRepayPenalty / 1e4;
// Max liquidation discount is 15% = 0.15e27 in 1e27 scale
require(maxPenaltyFraction <= 0.15e27, "Penalty exceeds max liquidation discount");
Developer Response:
Fixed in commit 6257ae2 and 4a9f75a.
The penalty model was changed from a percentage of remaining interest (earlyRepayPenalty) to a duration-based model (penaltyDuration), where the penalty equals the interest over that duration. Max liquidation discount was increased to 25%, and a deployment-time check ensures the penalty cannot exceed 20% of principal resulting in a 5% buffer.