# Unhealthy troves with LTV > 90% cannot always be absorbed as intended

Purger checks threshold instead of LTV against 90% absorption threshold, so unhealthy troves with LTV>90% and threshold<90% can't be absorbed, temporarily blocking liquidation.

- Payout: Contest reward
- Severity: medium
- Program: Opus
- Credited: 0xTheC0der
- Disclosed: 2024-03-08
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2024-01-opus-findings/issues/11
- Library page: https://bugbountylibrary.com/r/c4%3A2024-01-opus%3AM-09
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:business-logic vuln:liquidation

**Target:** Cairo purger contract get_absorption_penalty_internal
**Root cause:** get_absorption_penalty_internal compares `threshold > ABSORPTION_THRESHOLD` instead of `ltv > ABSORPTION_THRESHOLD`, so troves with LTV above 90% but threshold below 90% fall through to the max-penalty equality check and return None.
**Steps:**
1. Set threshold=88%, ltv=91%, ltv_after_compensation=93%.
2. Call get_absorption_penalty_internal: `ltv <= threshold` is false, continue.
3. max_possible_penalty = min((1-0.93)/0.93, MAX_PENALTY) = 7.53%.
4. `threshold > 90%` is false (88%), so skip the early-return branch.
5. penalty = min(MIN_PENALTY + ltv/threshold - 1, max) = 6.41%.
6. `penalty == max_possible_penalty` is false, returns Option::None — trove not absorbable despite ltv>90%.
**How found:** Compared protocol docs (absorption allowed when LTV>90%) against code; noticed the if-condition used `threshold` rather than `ltv`, then built a numeric example and Desmos graph showing an absorption gap for thresholds >83%.
**Insight:** When a contract gates a branch on a ratio, verify which variable (threshold vs current LTV) the spec actually means; mismatched variable selection creates silent dead zones that only appear for specific parameter ranges — plot the inequality to find them.
