# Exact-collateral liquidation skips deficit accounting

Exact-collateral liquidation in K2 router skips the collateral_cap_triggered branch, leaving untracked bad debt while borrower reclaims all collateral.

- Payout: Contest reward
- Severity: high
- Program: K2
- Credited: v12
- Disclosed: 2026-08-20
- Write-up quality: 7/10
- Original: https://code4rena.com/reports/2026-04-k2#h-16
- Library page: https://bugbountylibrary.com/r/c4%3A2026-04-k2%3AH-16
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:code4rena sev:high tech:rust technique:source-review vuln:accounting-error vuln:business-logic vuln:liquidation

**Target:** Kinetic Router liquidation calculation and router contracts
**Root cause:** Bad-debt cleanup is gated on collateral_cap_triggered, which is set only when computed seizure is strictly greater than user collateral balance; the equality case (seize == balance && remaining_debt > 0) is unhandled.
**Steps:**
1. Identify an underwater borrower position with collateral balance C and debt D.
2. Choose debt_to_cover such that calculate_liquidation_amounts_with_reserves returns collateral_amount_to_seize exactly equal to C.
3. Call liquidation (or flash-liquidation in router.rs) with that debt_to_cover.
4. Observe the `>` comparison fails to set collateral_cap_triggered, so leftover debt is not burned into reserve_deficit and MIN_LEFTOVER_BASE guard is skipped.
5. Borrower ends with zero collateral and positive untracked debt.
**How found:** Code review of the strict `>` comparison against the helper's discrete seizure amount in both liquidation callers, noticing the equality case falls outside the collateral_cap_triggered branch.
**Exploitation:** Borrower self-liquidates or uses a cooperating liquidator, selecting debt_to_cover to make seizure exactly equal full collateral, reclaiming all collateral while leaving unrecoverable debt untracked in the reserve.
**Insight:** When a helper returns a discrete amount compared against a balance to trigger a cap/cleanup branch, audit the equality boundary: `==` often falls into the wrong branch and skips bad-debt or cap accounting.
