# User would liquidate his account to sidestep `takerInvariant` modifier

A maker can self-liquidate to force-close a maker position that the takerInvariant modifier would otherwise block, spiking funding fees to maxRate and penalizing takers.

- Payout: Contest reward
- Severity: medium
- Program: Perennial
- Credited: Emmanuel
- Disclosed: 2023-05-05
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-05-perennial-judging/issues/77
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-10
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:chained technique:source-review vuln:business-logic vuln:liquidation

**Target:** Perennial Product contract (closeMakeFor / takerInvariant, Collateral.closeAll)
**Root cause:** The takerInvariant modifier only guards closeMakeFor; liquidation via Collateral.closeAll force-closes all maker/taker positions without enforcing that global maker OI stays >= taker OI.
**Steps:**
1. Open a large maker position at max leverage (e.g. 3000 ETH at 15x) so global maker OI dominates.
2. Wait until taker OI reaches high utilization (~80%), making funding rate high.
3. Withdraw collateral down to just below maintenance margin (e.g. 262.5k USDC vs 263.25k maintenance at ETH=1755).
4. Price ticks up slightly so account becomes liquidatable.
5. Call liquidation on own account: `closeAll(account)` closes the 3000 ETH maker position and pays liquidator the 20% liquidation fee.
6. Withdraw remaining collateral; global maker OI now far below taker OI, funding rate pinned at maxRate (120%).
7. Optionally reopen a smaller maker position to collect the inflated funding fees from takers.
**How found:** Code reading of closeMakeFor's takerInvariant modifier versus Collateral.closeAll, noticing liquidation force-closes positions without the same invariant check, then modeling market parameters to show funding rate impact.
**Exploitation:** Attacker supplies collateral, opens max-leverage maker position, withdraws collateral to the liquidation threshold, triggers a small adverse price move, self-liquidates to force-close the maker position (paying only the liquidation fee), then reopens a smaller maker position to harvest maxRate funding fees paid by unaware takers.
**Escalation:** Reopening a smaller maker position keeps maker OI below taker OI so the attacker becomes the beneficiary of the maxRate funding fees.
**Insight:** When an invariant is enforced in one exit path (closeMakeFor), check every other path that mutates the same state (liquidation, force-close, admin close) for the missing check; liquidation is a common bypass door for position-close guards.
