# It is possible to open and liquidate your own position in 1 transaction to overcome efficiency and liquidity removal limits at almost no cost

Perennial's margin check used maintained amount for settled positions, letting a user reduce position, commit a favorable oracle price, and self-liquidate in one tx.

- Payout: Contest reward
- Severity: medium
- Program: Perennial
- Credited: panprog
- Disclosed: 2023-10-26
- Write-up quality: 8/10
- Original: https://github.com/sherlock-audit/2023-10-perennial-judging/issues/23
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-2
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:incomplete-fix vuln:business-logic vuln:liquidation vuln:oracle-manipulation

**Target:** Perennial V2 Market contract margin/liquidation logic
**Root cause:** Market._invariant checks margined amount only for the current pending position, while maxPendingMagnitude (including settled position) is checked against the lower maintained amount, allowing withdrawal to the liquidation edge.
**Steps:**
1. Record Pyth oracle prices with signatures until finding one higher/lower than latest version price by any amount.
2. Reduce position by (margin/maintenance) and withdraw max collateral so margined(current) == maintained(settled), sitting at liquidation edge.
3. Commit the recorded non-requested oracle version with the favorable price.
4. Call liquidate on own position; liquidation fee returns to self, costing only keeper/open-close fees.
**How found:** Reviewed the prior 2.0 fix (issue 104) and noticed the fix only applied margined check to current position while settled position used maintained amount; reasoned that reducing position makes margined(current) equal maintained(settled), enabling withdrawal to liquidation edge.
**Exploitation:** Attacker pre-records a Pyth price signature favorable to their position direction, then in one transaction reduces position and withdraws collateral to the liquidation boundary, commits that oracle version, and self-liquidates. Liquidation fee goes to the attacker, so cost is only keeper and position fees.
**Escalation:** Can be chained with other issues (e.g. set closable=0 and liquidate while increasing to max position size 2^62-1), or used to open large maker then liquidate to create long/short-maker mismatch and socialize losses, or to drive taker interest and profit from a small remaining maker position.
**Insight:** When a fix splits a check into two thresholds (margined vs maintained), verify every code path that can change position or collateral uses the stricter threshold; a partial fix that only guards the pending position leaves the settled position exploitable via position reduction.
