# `liquidatePartyA` requires signature which doesn't have nonce, making possible unfair liquidation and loss of funds for all parties

Liquidation signature lacks party nonces, so a stale signed upnl can liquidate a party that already closed positions, wiping funds from partyA, partyB and liquidator.

- Payout: Contest reward
- Severity: high
- Program: Symmetrical
- Credited: panprog
- Disclosed: 2023-08-28
- Write-up quality: 8/10
- Original: https://github.com/sherlock-audit/2023-08-symmetrical-judging/issues/5
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-08-symmetrical%3AH-1
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:solidity technique:race-window technique:source-review vuln:business-logic vuln:oracle-manipulation vuln:signature-replay

**Target:** Symmio-core liquidation facet (liquidatePartyA / LibMuon signature verification)
**Root cause:** The Muon-provided liquidationSig commits only to upnl/prices/timestamp, not to partyA/partyB nonces, so it stays valid after either party changes state (e.g. fills a close request).
**Steps:**
1. Open a position: partyA allocated=109, upnl=-99, cva+lf=10 (liquidatable threshold near).
2. partyA calls `requestToClosePosition(1, quantityToClose=50, closePrice=9)` to reduce exposure.
3. Liquidator obtains `getDummyLiquidationSig("0x10", -100, [1], [9], -100)` with timestamp+5.
4. partyB calls `fillCloseRequest(1, filledAmount=50, closedPrice=9)` first; partyA now allocated=59, upnl=-50, not liquidatable.
5. Liquidator submits `liquidatePartyA(partyA, sig)`, `setSymbolsPrice(partyA, sig)`, `liquidatePositionsPartyA(partyA,[1])` using stale upnl=-100.
6. Contract computes available=59-5-100=-46, OVERDUE deficit=41, partyB gets 27 instead of 50, partyA allocated set to 0, liquidator fee 0.
**How found:** Code review of LibMuon.sol signature struct: noticed liquidationSig omits partyA/partyB nonces, then reasoned that any state change between signing and execution makes the signed upnl stale, and built a Foundry/TS PoC racing fillCloseRequest against liquidation.
**Exploitation:** A liquidator (or colluding partyB) holds a signed liquidation payload and races it against a pending close/fill; because the signature has no nonce, it executes against post-close state, forcing an OVERDUE liquidation that zeroes partyA and underpays partyB while paying the liquidator nothing.
**Insight:** When an off-chain oracle signs state used for liquidation, check whether the signed struct binds to per-party nonces/state versions; any signed value that can go stale lets an attacker replay it after the victim improves their position, converting a solvent account into an OVERDUE liquidation.
