# Liquidation cap omits debt components

Does the repay cap sum every debt component and match the amount actually forwarded downstream?

_Area: DeFi oracles, liquidations and flash loans. Seen in 3 disclosed reports; latest 2026-08-20. Updated 2026-11-01._

## How it works
A liquidation is only safe if the amount of debt repaid is bounded by the debt the protocol actually recognizes and by the collateral available to seize. This pattern breaks when the cap is computed from a *subset* of the debt, or when the capped value is computed but not the one used. The assumption that breaks: "the cap I computed is the cap that gets enforced." Either the aggregation helper silently excludes some debt (other chains, other markets, in-flight positions), or the capped number is discarded and the raw input is forwarded. The result is a cap that is too small (blocking valid liquidations) or too large (letting liquidators over-repay and over-seize).

## Where it shows up
- A helper named `borrowWithInterest`/`getDebt` that sums only *origin-chain* borrows, used to size a cap for *destination-chain* positions [E2](https://bugbountylibrary.com/r/sherlock%3A2025-05-lend-audit-contest%3AH-8.md).
- A cap function that loops `id <= currentId` while the canonical market check loops `id < currentId` [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-9.md).
- A function that computes `actual_debt_to_cover` (capped) but passes the original `debt_to_cover` to the router [E3](https://bugbountylibrary.com/r/c4%3A2026-04-k2%3AM-15.md).
- A liquidation path that repays requested shares without checking they are covered by available collateral plus fee [E4](https://bugbountylibrary.com/r/c4%3A2022-08-frax%3AM-05.md).
- Per-call validation in a router that never sees the cumulative total across calls in one transaction [E3](https://bugbountylibrary.com/r/c4%3A2026-04-k2%3AM-15.md).

## How to review
Find the canonical invariant (the market's own closable/close-factor check) and compare it line by line with every reimplementation in peripheral contracts, vaults, and routers. The property that must hold: **the amount of debt actually repaid equals the capped amount, and that cap sums every debt component the position owes, bounded by seizable collateral.** Concretely: (1) trace the capped variable to the call site that moves funds — if the raw input is forwarded, it's broken [E3](https://bugbountylibrary.com/r/c4%3A2026-04-k2%3AM-15.md); (2) confirm the aggregation helper includes inbound cross-chain borrows, not just origin-chain ones [E2](https://bugbountylibrary.com/r/sherlock%3A2025-05-lend-audit-contest%3AH-8.md); (3) match loop bounds and ordering to the canonical check [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-9.md); (4) confirm repaid shares ≤ collateral available plus fee [E4](https://bugbountylibrary.com/r/c4%3A2022-08-frax%3AM-05.md).

## How finders spotted it
- Compared the market's closable loop (`id < currentId`) against the peripheral loops (`id <= currentId`) and reasoned through the edge case where the current pending position changes within the active oracle version [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-9.md).
- Read `getMaxLiquidationRepayAmount` and `borrowWithInterest`, saw the latter only aggregates origin-chain borrows, so the cap is zero for destination-chain positions [E2](https://bugbountylibrary.com/r/sherlock%3A2025-05-lend-audit-contest%3AH-8.md).
- Noticed the capped `actual_debt_to_cover` is computed and recorded but the original `debt_to_cover` is what reaches the router, and the router validates each call in isolation [E3](https://bugbountylibrary.com/r/c4%3A2026-04-k2%3AM-15.md).
- Read `liquidateClean`, saw no check that repaid shares are covered by available collateral, then wrote an e2e test dropping the price to confirm overpayment [E4](https://bugbountylibrary.com/r/c4%3A2022-08-frax%3AM-05.md).

## Variants seen
- Cap too small: cross-chain borrows omitted, blocking valid liquidations [E2](https://bugbountylibrary.com/r/sherlock%3A2025-05-lend-audit-contest%3AH-8.md).
- Cap computed but discarded: uncapped input forwarded to router [E3](https://bugbountylibrary.com/r/c4%3A2026-04-k2%3AM-15.md).
- Cap too large: repaid shares not bounded by collateral [E4](https://bugbountylibrary.com/r/c4%3A2022-08-frax%3AM-05.md).
- Loop-bound mismatch: pending current position included in cap but not in canonical check [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-9.md).

## Impact and severity
E2 blocks all cross-chain liquidations, leaving bad debt unaddressed [E2](https://bugbountylibrary.com/r/sherlock%3A2025-05-lend-audit-contest%3AH-8.md). E3 lets liquidators exceed the close-factor cap and seize extra collateral bonus, amplified by splitting calls within one atomic transaction [E3](https://bugbountylibrary.com/r/c4%3A2026-04-k2%3AM-15.md). E4 causes liquidators to overpay assets for less collateral, worsened by MEV/validator ordering inserting repayment ahead of a well-calculated liquidation [E4](https://bugbountylibrary.com/r/c4%3A2022-08-frax%3AM-05.md). E1 makes some liquidations revert [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-9.md). Severity rises with how far the cap diverges from the true debt and whether the excess is exploitable atomically.

## Evidence
- E1: https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-9.md
- E2: https://bugbountylibrary.com/r/sherlock%3A2025-05-lend-audit-contest%3AH-8.md
- E3: https://bugbountylibrary.com/r/c4%3A2026-04-k2%3AM-15.md
- E4: https://bugbountylibrary.com/r/c4%3A2022-08-frax%3AM-05.md
