Position value can fall below minimum acceptable quote value when partially closing positions requested to be closed in full
In `LibQuote.closeQuote` there is a requirement to have the remaining quote value to not be less than minAcceptableQuoteValue:\\n```\\nif (LibQuote.quoteOpenAmount(quote) != quote.quantityToClose) {\\n    require(quote.lockedValues.total() >= symbolLayout.symbols[quote.symbolId].minAcceptableQuoteValue,\\n        "LibQuote: Remaining quote value is low");\\n}\\n```\\n\\nNotice the condition when this require happens:\\n`LibQuote.quoteOpenAmount(quote)` is remaining open amount\\n`quote.quantityToClose` is requested amount to close\\nThis means that this check is ignored if partyA has requested to close amount equal to full remaining quote value, but enforced when it's not (even if closing fully). For example, a quote with opened amount = 100 is requested to be closed in full (amount = 100): this check is ignored. But PartyB can fill the request partially, for example fill 99 out of 100, and the remainder (1) is not checked to confirm to `minAcceptableQuoteValue`.\\nThe following execution paths are possible if PartyA has open position size = 100 and `minAcceptableQuoteValue` = 5:\\n`requestToClosePosition(99)` -> revert\\n`requestToClosePosition(100)` -> `fillCloseRequest(99)` -> pass (remaining quote = 1)
The condition should be to ignore the `minAcceptableQuoteValue` if request is filled in full (filledAmount == quantityToClose):\\n```\\n-       if (LibQuote.quoteOpenAmount(quote) != quote.quantityToClose) {\\n+       if (filledAmount != quote.quantityToClose) {\\n            require(quote.lockedValues.total() >= symbolLayout.symbols[quote.symbolId].minAcceptableQuoteValue,\\n                "LibQuote: Remaining quote value is low");\\n        }\\n```\\n
There can be multiple reasons why the protocol enforces `minAcceptableQuoteValue`, one of them might be the efficiency of the liquidation mechanism: when quote value is too small (and liquidation value too small too), liquidators will not have enough incentive to liquidate these positions in case they become insolvent. Both partyA and partyB might also not have enough incentive to close or respond to request to close such small positions, possibly resulting in a loss of funds and greater market risk for either user.\\nProof of Concept\\nAdd this to any test, for example to `ClosePosition.behavior.ts`.\\n```\\nit("Close position with remainder below minAcceptableQuoteValue", async function () {\\n  const context: RunContext = this.context;\\n\\n  this.user_allocated = decimal(1000);\\n  this.hedger_allocated = decimal(1000);\\n\\n  this.user = new User(this.context, this.context.signers.user);\\n  await this.user.setup();\\n  await this.user.setBalances(this.user_allocated, this.user_allocated, this.user_allocated);\\n\\n  this.hedger = new Hedger(this.context, this.context.signers.hedger);\\n  await this.hedger.setup();\\n  await this.hedger.setBalances(this.hedger_allocated, this.hedger_allocated);\\n\\n  await this.user.sendQuote(limitQuoteRequestBuilder()\\n    .quantity(decimal(100))\\n    .price(decimal(1))\\n    .cva(decimal(10)).lf(decimal(5)).mm(decimal(15))\\n    .build()\\n  );\\n  await this.hedger.lockQuote(1, 0, decimal(5, 17));\\n  await this.hedger.openPosition(1, limitOpenRequestBuilder().filledAmount(decimal(100)).openPrice(decimal(1)).price(decimal(1)).build());\\n\\n  // now try to close full position (100)\\n  await this.user.requestToClosePosition(\\n    1,\\n    limitCloseRequestBuilder().quantityToClose(decimal(100)).closePrice(decimal(1)).build(),\\n  );\\n\\n  // now partyA cancels request\\n  //await this.user.requestToCancelCloseRequest(1);\\n\\n  // partyB can fill 99\\n  await this.hedger.fillCloseRequest(\\n    1,\\n    limitFillCloseRequestBuilder()\\n      .filledAmount(decimal(99))\\n      .closedPrice(decimal(1))\\n      .build(),\\n  );\\n\\n  var q = await context.viewFacet.getQuote(1);\\n  console.log("quote quantity: " + q.quantity.div(decimal(1)) + " closed: " + q.closedAmount.div(decimal(1)));\\n\\n});\\n```\\n\\nConsole execution result:\\n```\\nquote quantity: 100 closed: 99\\n```\\n
```\\nif (LibQuote.quoteOpenAmount(quote) != quote.quantityToClose) {\\n    require(quote.lockedValues.total() >= symbolLayout.symbols[quote.symbolId].minAcceptableQuoteValue,\\n        "LibQuote: Remaining quote value is low");\\n}\\n```\\n