# Loss for protocol by incorrectly assuming the position has been fully closed

Backstop liquidation writes off full margin assuming position closed, but IOC order may only partially fill, leaving residual position and protocol loss.

- Payout: Contest reward
- Severity: medium
- Program: GTE
- Credited: dhank
- Disclosed: 2026-02-05
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2025-08-gte-perps-and-launchpad#m-15
- Library page: https://bugbountylibrary.com/r/c4%3A2025-08-gte-perps-and-launchpad%3AM-15
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium technique:source-review vuln:accounting-error vuln:business-logic vuln:liquidation

**Target:** perps LiquidatorPanel backstopLiquidate
**Root cause:** backstopLiquidate treats cache.positions.length == 1 as a full close and deletes margin, but the IOC fill order only requests position.amount and may partially fill, leaving residual position.
**Steps:**
1. Liquidator calls `backstopLiquidate()` on an underwater position.
2. Contract places an IOC order with amount = position.amount against the thin backstop orderbook.
3. Order partially fills; IOC does not revert on incomplete fill.
4. Code checks `cache.margin < 0 && cache.positions.length == 1` and writes off `cache.margin` as bad debt.
5. Residual position remains open while margin was deleted, causing protocol loss.
**How found:** Code review of the full-close condition in backstopLiquidate, noticing IOC semantics don't guarantee full fill of position.amount.
**Exploitation:** Trigger backstop liquidation on an underwater position in a thin backstop orderbook so the IOC order partially fills; protocol writes off margin while user retains residual position, shifting loss to protocol.
**Insight:** When a liquidation path treats 'position exists' as 'position fully closed', verify the order type guarantees full fill (FOK) or check remaining amount == 0 before writing off margin.
