# Accounting error in PartyB's pending locked balance led to loss of funds

Partial fill in Symmetrical's openPosition subtracts only filled locked value from PartyB's pending locked balance, permanently inflating it and locking PartyB's funds.

- Payout: Contest reward
- Severity: high
- Program: Symmetrical
- Credited: Ch_301
- Disclosed: 2023-06-05
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2023-06-symmetrical-judging/issues/226
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-06-symmetrical%3AH-6
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** PartyBFacetImpl.openPosition (symmio-core PartyB facet)
**Root cause:** openPosition removes the current quote from PartyB's pendingQuotes but only subtracts filledLockedValues from partyBPendingLockedBalances, while the new partially-filled quote is never added to PartyB's pending list; the unfilled locked value stays locked forever.
**Steps:**
1. PartyA and PartyB each have pendingLockedBalance = 100 USD for quote_current (lockedValue_total = 100).
2. PartyB calls `openPosition(quoteId, filledAmount, openedPrice, upnlSig)` with a partial fill (e.g. 30% of quantity).
3. Line 156 `LibQuote.removeFromPendingQuotes(quote)` removes quote_current from both pendingQuotes_a and pendingQuotes_b.
4. A new quote_new is created for the unfilled amount and pushed only to `quoteLayout.quoteIdsOf[quote.partyA]` (line 225); PartyB's pending list stays empty.
5. Line 238 subtracts filledLockedValues (30) from pendingLockedBalances[partyA]; line 239 subtracts filledLockedValues (30) from partyBPendingLockedBalances[partyB][partyA].
6. Result: pendingLockedBalance_b = 70 USD with pendingQuotes_b empty; repeat partial fills to inflate further.
**How found:** Code-reading of the partial-fill branch in openPosition: noticed quote_current is removed from PartyB's pending list but quote_new is only re-added to PartyA's list, then traced the locked-balance subtraction to confirm only filledLockedValues is removed instead of the full lockedValue_total.
**Exploitation:** Any PartyB that partially fills quotes accumulates phantom pending locked balance; partyBAvailableForQuote returns a shrinking available balance until all allocated balance is consumed, blocking new positions and deallocate/withdraw, effectively trapping PartyB funds.
**Insight:** When a partial fill splits a quote into a new quote, check every per-party accounting structure (pending lists, locked balances) for asymmetric handling: the new quote is re-registered for one party but not the other, so the subtraction must match the removed quote's full locked value, not the filled fraction.
