Malicious PartyB can block unfavorable close position requests causing a loss of profits for PartyB
Malicious PartyB can block close position requests that are unfavorable toward them by intentionally choose not to fulfill the close request and continuously prolonging the force close position cooldown period, causing a loss of profits for PartyA.\\nIf PartyA invokes the `requestToClosePosition` function for an open quote, the quote's status will transition from `QuoteStatus.OPEN` to `QuoteStatus.CLOSE_PENDING`. In case PartyB fails to fulfill the close request (fillCloseRequest) during the cooldown period (maLayout.forceCloseCooldown), PartyA has the option to forcibly close the quote by utilizing the `forceClosePosition` function.\\n```\\nFile: PartyAFacetImpl.sol\\n    function forceClosePosition(uint256 quoteId, PairUpnlAndPriceSig memory upnlSig) internal {\\n        AccountStorage.Layout storage accountLayout = AccountStorage.layout();\\n        MAStorage.Layout storage maLayout = MAStorage.layout();\\n        Quote storage quote = QuoteStorage.layout().quotes[quoteId];\\n\\n        uint256 filledAmount = quote.quantityToClose;\\n        require(quote.quoteStatus == QuoteStatus.CLOSE_PENDING, "PartyAFacet: Invalid state");\\n        require(\\n            block.timestamp > quote.modifyTimestamp + maLayout.forceCloseCooldown,\\n            "PartyAFacet: Cooldown not reached"\\n        );\\n..SNIP..\\n```\\n\\nNevertheless, malicious PartyB can intentionally choose not to fulfill the close request and can continuously prolong the `quote.modifyTimestamp`, thereby preventing PartyA from ever being able to activate the `forceClosePosition` function.\\nMalicious PartyB could extend the `quote.modifyTimestamp` via the following steps:\\nLine 282 of the `fillCloseRequest` show that it is possible to partially fill a close request. As such, calls the `fillCloseRequest` function with the minimum possible `filledAmount` for the purpose of triggering the `LibQuote.closeQuote` function at Line 292.\\n```\\nFile: PartyBFacetImpl.sol\\n    function fillCloseRequest(\\n        uint256 quoteId,\\n        uint256 filledAmount,\\n        uint256 closedPrice,\\n        PairUpnlAndPriceSig memory upnlSig\\n    ) internal {\\n..SNIP..\\n        if (quote.orderType == OrderType.LIMIT) {\\n            require(quote.quantityToClose >= filledAmount, "PartyBFacet: Invalid filledAmount");\\n        } else {\\n            require(quote.quantityToClose == filledAmount, "PartyBFacet: Invalid filledAmount");\\n        }\\n..SNIP..\\n        LibQuote.closeQuote(quote, filledAmount, closedPrice);\\n    }\\n```\\n\\nOnce the `LibQuote.closeQuote` function is triggered, Line 153 will update the `quote.modifyTimestamp` to the current timestamp, which effectively extends the cooldown period that PartyA has to wait before allowing to forcefully close the position.\\n```\\nFile: LibQuote.sol\\n    function closeQuote(Quote storage quote, uint256 filledAmount, uint256 closedPrice) internal {\\n        QuoteStorage.Layout storage quoteLayout = QuoteStorage.layout();\\n        AccountStorage.Layout storage accountLayout = AccountStorage.layout();\\n\\n        quote.modifyTimestamp = block.timestamp;\\n..SNIP..\\n```\\n
The `quote.modifyTimestamp` is updated to the current timestamp in many functions, including the `closeQuote` function, as shown in the above example. A quick search within the codebase shows that there are around 17 functions that update the `quote.modifyTimestamp` to the current timestamp when triggered. Each of these functions serves as a potential attack vector for malicious PartyB to extend the `quote.modifyTimestamp` and deny users from forcefully closing their positions\\nIt is recommended not to use the `quote.modifyTimestamp` for the purpose of determining if the force close position cooldown has reached, as this variable has been used in many other places. Instead, consider creating a new variable, such as `quote.requestClosePositionTimestamp` solely for the purpose of computing the force cancel quote cooldown.\\nThe following fixes will prevent malicious PartyB from extending the cooldown period since the `quote.requestClosePositionTimestamp` variable is only used solely for the purpose of determining if the force close position cooldown has reached.\\n```\\nfunction requestToClosePosition(\\n    uint256 quoteId,\\n    uint256 closePrice,\\n    uint256 quantityToClose,\\n    OrderType orderType,\\n    uint256 deadline,\\n    SingleUpnlAndPriceSig memory upnlSig\\n) internal {\\n..SNIP..\\n    accountLayout.partyANonces[quote.partyA] // Add the line below\\n= 1;\\n    quote.modifyTimestamp = block.timestamp;\\n// Add the line below\\n quote.requestCancelQuoteTimestamp = block.timestamp;\\n```\\n\\n```\\nfunction forceClosePosition(uint256 quoteId, PairUpnlAndPriceSig memory upnlSig) internal {\\n    AccountStorage.Layout storage accountLayout = AccountStorage.layout();\\n    MAStorage.Layout storage maLayout = MAStorage.layout();\\n    Quote storage quote = QuoteStorage.layout().quotes[quoteId];\\n\\n    uint256 filledAmount = quote.quantityToClose;\\n    require(quote.quoteStatus == QuoteStatus.CLOSE_PENDING, "PartyAFacet: Invalid state");\\n    require(\\n// Remove the line below\\n       block.timestamp > quote.modifyTimestamp // Add the line below\\n maLayout.forceCloseCooldown,\\n// Add the line below\\n       block.timestamp > quote.requestCancelQuoteTimestamp // Add the line below\\n maLayout.forceCloseCooldown,\\n        "PartyAFacet: Cooldown not reached"\\n    );\\n```\\n\\nIn addition, review the `forceClosePosition` function and applied the same fix to it since it is vulnerable to the same issue, but with a different impact.
PartyB has the ability to deny users from forcefully closing their positions by exploiting the issue. Malicious PartyB could abuse this by blocking PartyA from closing their positions against them when the price is unfavorable toward them. For instance, when PartyA is winning the game and decided to close some of its positions against PartyB, PartyB could block the close position request to deny PartyA of their profits and prevent themselves from losing the game.
```\\nFile: PartyAFacetImpl.sol\\n    function forceClosePosition(uint256 quoteId, PairUpnlAndPriceSig memory upnlSig) internal {\\n        AccountStorage.Layout storage accountLayout = AccountStorage.layout();\\n        MAStorage.Layout storage maLayout = MAStorage.layout();\\n        Quote storage quote = QuoteStorage.layout().quotes[quoteId];\\n\\n        uint256 filledAmount = quote.quantityToClose;\\n        require(quote.quoteStatus == QuoteStatus.CLOSE_PENDING, "PartyAFacet: Invalid state");\\n        require(\\n            block.timestamp > quote.modifyTimestamp + maLayout.forceCloseCooldown,\\n            "PartyAFacet: Cooldown not reached"\\n        );\\n..SNIP..\\n```\\n