Position value can fall below the minimum acceptable quote value
PartyB can fill a LIMIT order position till the point where the value is below the minimum acceptable quote value (minAcceptableQuoteValue). As a result, it breaks the invariant that the value of position must be above the minimum acceptable quote value, leading to various issues and potentially losses for the users.\\n```\\nFile: LibQuote.sol\\n    function closeQuote(Quote storage quote, uint256 filledAmount, uint256 closedPrice) internal {\\n..SNIP..\\n        if (quote.closedAmount == quote.quantity) {\\n            quote.quoteStatus = QuoteStatus.CLOSED;\\n            quote.requestedClosePrice = 0;\\n            removeFromOpenPositions(quote.id);\\n            quoteLayout.partyAPositionsCount[quote.partyA] -= 1;\\n            quoteLayout.partyBPositionsCount[quote.partyB][quote.partyA] -= 1;\\n        } else if (\\n            quote.quoteStatus == QuoteStatus.CANCEL_CLOSE_PENDING || quote.quantityToClose == 0\\n        ) {\\n            quote.quoteStatus = QuoteStatus.OPENED;\\n            quote.requestedClosePrice = 0;\\n            quote.quantityToClose = 0; // for CANCEL_CLOSE_PENDING status\\n        } else {\\n            require(\\n                quote.lockedValues.total() >=\\n                    SymbolStorage.layout().symbols[quote.symbolId].minAcceptableQuoteValue,\\n                "LibQuote: Remaining quote value is low"\\n            );\\n        }\\n    }\\n```\\n\\nIf the user has already sent the close request, but partyB has not filled it yet, the user can request to cancel it by calling the `CancelCloseRequest` function. This will cause the quote's status to change to `QuoteStatus.CANCEL_CLOSE_PENDING`.\\nPartyB can either accept the cancel request or fill the close request ignoring the user's request. If PartyB decided to go ahead to fill the close request partially, the second branch of the if-else statement at Line 196 will be executed. However, the issue is that within this branch, PartyB is not subjected to the `minAcceptableQuoteValue` validation check. Thus, it is possible for PartyB to fill a LIMIT order position till the point where the value is below the minimum acceptable quote value (minAcceptableQuoteValue).
If the user sends a close request and PartyB decides to go ahead to fill the close request partially, consider checking if the remaining value of the position is above the minimum acceptable quote value (minAcceptableQuoteValue) after PartyB has filled the position.\\n```\\nfunction closeQuote(Quote storage quote, uint256 filledAmount, uint256 closedPrice) internal {\\n ..SNIP..\\n    if (quote.closedAmount == quote.quantity) {\\n        quote.quoteStatus = QuoteStatus.CLOSED;\\n        quote.requestedClosePrice = 0;\\n        removeFromOpenPositions(quote.id);\\n        quoteLayout.partyAPositionsCount[quote.partyA] -= 1;\\n        quoteLayout.partyBPositionsCount[quote.partyB][quote.partyA] -= 1;\\n    } else if (\\n        quote.quoteStatus == QuoteStatus.CANCEL_CLOSE_PENDING || quote.quantityToClose == 0\\n    ) {\\n        quote.quoteStatus = QuoteStatus.OPENED;\\n        quote.requestedClosePrice = 0;\\n        quote.quantityToClose = 0; // for CANCEL_CLOSE_PENDING status\\n// Add the line below\\n        \\n// Add the line below\\n        require(\\n// Add the line below\\n            quote.lockedValues.total() >=\\n// Add the line below\\n                SymbolStorage.layout().symbols[quote.symbolId].minAcceptableQuoteValue,\\n// Add the line below\\n            "LibQuote: Remaining quote value is low"\\n// Add the line below\\n        );\\n    } else {\\n        require(\\n            quote.lockedValues.total() >=\\n                SymbolStorage.layout().symbols[quote.symbolId].minAcceptableQuoteValue,\\n            "LibQuote: Remaining quote value is low"\\n        );\\n    }\\n}\\n```\\n
In the codebase, the `minAcceptableQuoteValue` is currently set to 5 USD. There are many reasons for having a minimum quote value in the first place. For instance, if the value of a position is too low, it will be uneconomical for the liquidator to liquidate the position because the liquidation fee would be too small or insufficient to cover the cost of liquidation. Note that the liquidation fee is computed as a percentage of the position value.\\nThis has a negative impact on the overall efficiency of the liquidation mechanism within the protocol, which could delay or stop the liquidation of accounts or positions, exposing users to greater market risks, including the risk of incurring larger losses or having to exit at an unfavorable price.
```\\nFile: LibQuote.sol\\n    function closeQuote(Quote storage quote, uint256 filledAmount, uint256 closedPrice) internal {\\n..SNIP..\\n        if (quote.closedAmount == quote.quantity) {\\n            quote.quoteStatus = QuoteStatus.CLOSED;\\n            quote.requestedClosePrice = 0;\\n            removeFromOpenPositions(quote.id);\\n            quoteLayout.partyAPositionsCount[quote.partyA] -= 1;\\n            quoteLayout.partyBPositionsCount[quote.partyB][quote.partyA] -= 1;\\n        } else if (\\n            quote.quoteStatus == QuoteStatus.CANCEL_CLOSE_PENDING || quote.quantityToClose == 0\\n        ) {\\n            quote.quoteStatus = QuoteStatus.OPENED;\\n            quote.requestedClosePrice = 0;\\n            quote.quantityToClose = 0; // for CANCEL_CLOSE_PENDING status\\n        } else {\\n            require(\\n                quote.lockedValues.total() >=\\n                    SymbolStorage.layout().symbols[quote.symbolId].minAcceptableQuoteValue,\\n                "LibQuote: Remaining quote value is low"\\n            );\\n        }\\n    }\\n```\\n