# Misplaced liquidatable check allows healthy positions to be liquidated

Jigsaw LiquidationManager checks isLiquidatable before retrieving invested collateral, so stale accounting lets healthy positions be liquidated.

- Payout: Contest reward
- Severity: high
- Program: Jigsaw
- Credited: phil
- Disclosed: 2025-08-11
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_jigsaw_june2025.pdf#3.1.6
- Library page: https://bugbountylibrary.com/r/cantina%3Ajigsaw_june2025%3A3.1.6
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:high tech:solidity technique:source-review vuln:business-logic vuln:liquidation vuln:oracle-manipulation

**Target:** LiquidationManager::liquidate() smart contract
**Root cause:** The isLiquidatable() require runs before _retrieveCollateral(), so collateral value changes accrued while invested are not reflected in the liquidation eligibility check.
**Steps:**
1. Call `LiquidationManager::liquidate()` with `_data.strategies` non-empty on a position whose invested accounting makes it appear liquidatable.
2. Observe the `require(stablesManager.isLiquidatable(...), "3073")` passes before collateral is retrieved.
3. `_retrieveCollateral()` then updates the holding's collateral, revealing the position was actually healthy.
4. Liquidation proceeds against a non-liquidatable position.
**How found:** Code review of liquidate() ordering: noticed the isLiquidatable require precedes _retrieveCollateral(), and cross-referenced the accepted-risk note that collateral changes are only tracked on retrieval.
**Exploitation:** Liquidator submits liquidation with strategy data on a position that is only liquidatable under stale invested-collateral accounting; the pre-retrieval check passes and the healthy position is liquidated, seizing collateral.
**Insight:** When a protocol accepts 'collateral changes untracked while invested' as risk, audit every read of that collateral for ordering relative to the retrieval/sync call; eligibility checks placed before sync use stale state.
