Accounting error in PartyB's pending locked balance led to loss of funds
Accounting error in the PartyB's pending locked balance during the partial filling of a position could lead to a loss of assets for PartyB.\\n```\\nFile: PartyBFacetImpl.sol\\n    function openPosition(\\n        uint256 quoteId,\\n        uint256 filledAmount,\\n        uint256 openedPrice,\\n        PairUpnlAndPriceSig memory upnlSig\\n    ) internal returns (uint256 currentId) {\\n..SNIP..\\n\\n        LibQuote.removeFromPendingQuotes(quote);\\n\\n..SNIP..\\n            quoteLayout.quoteIdsOf[quote.partyA].push(currentId);\\n..SNIP..\\n            } else {\\n                accountLayout.pendingLockedBalances[quote.partyA].sub(filledLockedValues);\\n                accountLayout.partyBPendingLockedBalances[quote.partyB][quote.partyA].sub(\\n                    filledLockedValues\\n                );\\n            }\\n```\\n\\nParameter Description\\n$quote_{current}$ Current quote (Quote ID = 1)\\n$quote_{new}$ Newly created quote (Quote ID = 2) due to partially filling\\n$lockedValue_{total}$ 100 USD. The locked values of $quote_{current}$\\n$lockedValue_{filled}$ 30 USD. $lockedValue_{filled} = lockedValue_{total}\\times\\frac{filledAmount}{quote.quantity}$\\n$lockedValue_{unfilled}$ 70 USD. $lockedValue_{unfilled} = lockedValue_{total}-lockedValue_{filled}$\\n$pendingLockedBalance_{a}$ 100 USD. PartyA's pending locked balance\\n$pendingLockedBalance_{b}$ 100 USD. PartyB's pending locked balance\\n$pendingQuotes_a$ PartyA's pending quotes. $pendingQuotes_a = [quote_{current}]$\\n$pendingQuotes_b$ PartyB's pending quotes. $pendingQuotes_b = [quote_{current}]$\\nAssume the following states before the execution of the `openPosition` function:\\n$pendingQuotes_a = [quote_{current}]$\\n$pendingQuotes_b = [quote_{current}]$\\n$pendingLockedBalance_{a} = 100\\ USD$\\n$pendingLockedBalance_{b} = 100\\ USD$\\nWhen the `openPosition` function is executed, $quote_{current}$ will be removed from $pendingQuotes_a$ and $pendingQuotes_b$ in Line 156.\\nIf the position is partially filled, $quote_{current}$ will be filled, and $quote_{new}$ will be created with the unfilled amount ($lockedValue_{unfilled}$). The $quote_{new}$ is automatically added to PartyA's pending quote list in Line 225.\\nThe states at this point are as follows:\\n$pendingQuotes_a = [quote_{new}]$\\n$pendingQuotes_b = []$\\n$pendingLockedBalance_{a} = 100\\ USD$\\n$pendingLockedBalance_{b} = 100\\ USD$\\nLine 238 removes the balance already filled ($lockedValue_{filled}$) from $pendingLockedBalance_{a}$ . The unfilled balance ($lockedValue_{unfilled}$) does not need to be removed from $pendingLockedBalance_{a}$ because it is now the balance of $quote_{new}$ that belong to PartyA. The value in $pendingLockedBalance_a$ is correct.\\nThe states at this point are as follows:\\n$pendingQuotes_a = [quote_{new}]$\\n$pendingQuotes_b = []$\\n$pendingLockedBalance_{a} = 70\\ USD$\\n$pendingLockedBalance_{b} = 100\\ USD$\\nIn Line 239, the code removes the balance already filled ($lockedValue_{filled}$) from $pendingLockedBalance_{b}$\\nThe end state is as follows:\\n$pendingQuotes_a = [quote_{new}]$\\n$pendingQuotes_b = []$\\n$pendingLockedBalance_{a} = 70\\ USD$\\n$pendingLockedBalance_{b} = 70\\ USD$\\nAs shown above, the value of $pendingLockedBalance_{b}$ is incorrect. Even though PartyB has no pending quote, 70 USD is still locked in the pending balance.\\nThere are three (3) important points to note:\\n$quote_{current}$ has already been removed from $pendingQuotes_b$ in Line 156\\n$quote_{new}$ is not automatically added to $pendingQuotes_b$. When $quote_{new}$ is created, it is not automatically locked to PartyB.\\n$pendingQuotes_b$ is empty\\nAs such, $lockedValue_{total}$ should be removed from the $pendingLockedBalance_{b}$ instead of only $lockedvalue_{filled}$.
Update the affected function to remove $lockedValue_{total}$ from the $pendingLockedBalance_{b}$ instead of only $lockedvalue_{filled}$.\\n```\\naccountLayout.pendingLockedBalances[quote.partyA].sub(filledLockedValues);\\naccountLayout.partyBPendingLockedBalances[quote.partyB][quote.partyA].sub(\\n// Remove the line below\\n    filledLockedValues\\n// Add the line below\\n    quote.lockedValues\\n);\\n```\\n
Every time PartyB partially fill a position, their $pendingLockedBalance_b$ will silently increase and become inflated. The pending locked balance plays a key role in the protocol's accounting system. Thus, an error in the accounting breaks many of the computations and invariants of the protocol.\\nFor instance, it is used to compute the available balance of an account in `partyBAvailableForQuote` function. Assuming that the allocated balance remains the same. If the pending locked balance increases silently due to the bug, the available balance returned from the `partyBAvailableForQuote` function will decrease. Eventually, it will "consume" all the allocated balance, and there will be no available funds left for PartyB to open new positions or to deallocate+withdraw funds. Thus, leading to lost of assets for PartyB.
```\\nFile: PartyBFacetImpl.sol\\n    function openPosition(\\n        uint256 quoteId,\\n        uint256 filledAmount,\\n        uint256 openedPrice,\\n        PairUpnlAndPriceSig memory upnlSig\\n    ) internal returns (uint256 currentId) {\\n..SNIP..\\n\\n        LibQuote.removeFromPendingQuotes(quote);\\n\\n..SNIP..\\n            quoteLayout.quoteIdsOf[quote.partyA].push(currentId);\\n..SNIP..\\n            } else {\\n                accountLayout.pendingLockedBalances[quote.partyA].sub(filledLockedValues);\\n                accountLayout.partyBPendingLockedBalances[quote.partyB][quote.partyA].sub(\\n                    filledLockedValues\\n                );\\n            }\\n```\\n