# Liquidation PnL netting errors

Per-position caps, selective settlement, or shared pools that skip net signed PnL before solvency tests

_Area: DeFi accounting and math bugs. Seen in 3 disclosed reports; latest 2023-08-28. Updated 2026-11-02._

## How it works
Liquidation and solvency math must net a party's *total signed* profit-and-loss (realized plus unrealized) before applying any cap or solvency test. The assumption that breaks is that positions can be settled one at a time, or that a cap applied per position equals a cap applied to the net. When a loss is capped at the current balance and a profit is credited in full, or when only losing positions are settled while winners stay counted, the books no longer match reality: one side's balance is inflated and bad debt is pushed onto the counterparty or the protocol [E1](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AH-2.md)[E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-symmetrical%3AH-2.md). A related break is handling only the loss direction of a price delta while ignoring the profit direction [E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AM-7.md).

## Where it shows up
- Two-phase liquidation (transfer funds, then settle positions) where the counterparty can still deallocate, close or act between phases [E1](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AH-2.md).
- Liquidation functions taking an arbitrary array of quotes/positions, so a caller can settle a subset [E1](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AH-2.md).
- `min(loss, currentBalance)` or similar caps applied inside a per-position loop [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-symmetrical%3AH-2.md).
- Solvency checks comparing close price to oracle/uPnL price that branch on loss but not on profit [E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AM-7.md).

## How to review
The invariant: for any account, the value used for caps and solvency equals the sum of signed PnL across *all* open positions, computed once, and no state transition may leave a position both credited and unsettled. Check that (a) caps are applied to the net, not per position, so ordering cannot change the result [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-symmetrical%3AH-2.md); (b) no party can act while its counterparty is mid-liquidation [E1](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AH-2.md); (c) liquidation cannot be restricted to a chosen subset [E1](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AH-2.md); (d) price-delta handling is symmetric for profit and loss [E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AM-7.md).

## How finders spotted it
- Read the two-step partyB liquidation flow and asked what partyA can still do between the steps; noticed the arbitrary quote array and no lock on partyA actions [E1](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AH-2.md).
- Read `amountToDeduct` as `min(loss, balance)` per position and noticed order-dependence when total uPnL is small but individual positions are large [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-symmetrical%3AH-2.md).
- Compared the loss branch and profit branch of the closePrice-vs-oracle delta in `LibSolvency` and found the profit branch missing [E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AM-7.md).

## Variants seen
- Selective liquidation leaving winners counted in counterparty uPnL [E1](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AH-2.md).
- Per-position loss cap making outcome order-dependent [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-symmetrical%3AH-2.md).
- One-sided solvency delta blocking legitimate closes (adjacent: asymmetry, not netting) [E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AM-7.md).

## Impact and severity
High: inflated balances let one party withdraw funds backed by unsettled positions and dump bad debt on the counterparty or protocol [E1](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AH-2.md)[E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-symmetrical%3AH-2.md); repeated cycles drain reserves, causing a bank run where last users cannot withdraw [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-symmetrical%3AH-2.md). Impact rose when chained with a separate missing-nonce weakness (stale uPnL replay) and off-chain counting of liquidated quotes, reaching theft of protocol funds [E1](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AH-2.md). The one-sided delta wrongly reverted closes that would leave users solvent [E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AM-7.md).

## Evidence
- E1: https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AH-2.md
- E2: https://bugbountylibrary.com/r/sherlock%3A2023-08-symmetrical%3AH-2.md
- E3: https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AM-7.md
