Primary short liquidation can not be completed in the last hour of the liquidation timeline
Shorts flagged for liquidation can not be liquidated in the last and final hour of the liquidation timeline, resulting in the liquidation flag being reset and requiring the short to be flagged again.\\nIf a short's collateral ratio is below the primary liquidation threshold (determined by the `LibAsset.primaryLiquidationCR` function, by default set to 400%), anyone can flag the position for liquidation by calling the `MarginCallPrimaryFacet.flagShort` function.\\nSubsequently, the short position owner has a certain amount of time, specifically, `10 hours` (configured and determined by the `LibAsset.firstLiquidationTime` function), to repay the loan and bring the collateral ratio back above the primary liquidation threshold. If the short position owner fails to do so, the short position can be liquidated by calling the `MarginCallPrimaryFacet.liquidate` function.\\nThe specific criteria for the liquidation eligibility are defined and determined in the `MarginCallPrimaryFacet._canLiquidate` function.\\ncontracts/facets/MarginCallPrimaryFacet.sol#L387\\n```\\nfunction _canLiquidate(MTypes.MarginCallPrimary memory m)\\n    private\\n    view\\n    returns (bool)\\n{\\n// rest of code      // [// rest of code]\\n    uint256 timeDiff = LibOrders.getOffsetTimeHours() - m.short.updatedAt;\\n    uint256 resetLiquidationTime = LibAsset.resetLiquidationTime(m.asset);\\n❌  if (timeDiff >= resetLiquidationTime) {\\n        return false;\\n    } else {\\n        uint256 secondLiquidationTime = LibAsset.secondLiquidationTime(m.asset);\\n        bool isBetweenFirstAndSecondLiquidationTime = timeDiff\\n            > LibAsset.firstLiquidationTime(m.asset) && timeDiff <= secondLiquidationTime\\n            && s.flagMapping[m.short.flaggerId] == msg.sender;\\n        bool isBetweenSecondAndResetLiquidationTime =\\n            timeDiff > secondLiquidationTime && timeDiff <= resetLiquidationTime;\\n        if (\\n            !(\\n                (isBetweenFirstAndSecondLiquidationTime)\\n                    || (isBetweenSecondAndResetLiquidationTime)\\n            )\\n        ) {\\n            revert Errors.MarginCallIneligibleWindow();\\n        }\\n        return true;\\n    }\\n}\\n```\\n\\nThis function checks in lines 387-389 if the elapsed time (timeDiff) since the short was updated is equal or greater than the reset liquidation time (resetLiquidationTime), which is by default set to `16 hours`. In this case, the short position has not been liquidated in time and has to be flagged again.\\nHowever, this condition conflicts with the `isBetweenSecondAndResetLiquidationTime` criteria in lines 394-395, specifically, the `timeDiff` <= `resetLiquidationTime` check. If the `timeDiff` value is equal to `resetLiquidationTime`, both conditions, in line 387 as well as the check in line 395, are `true`. Due to line 387 taking precedence, the liquidation is considered outdated and the short position has to be flagged again.\\nBased on the check in lines 67-69 of the `flagShort` function, it is evident that a short position flagged for liquidation requires re-flagging only if the `timeDiff` value is greater (>) than the reset liquidation time (resetLiquidationTime):\\ncontracts/facets/MarginCallPrimaryFacet.sol#L67-L69\\n```\\nif (timeDiff <= resetLiquidationTime) {\\n    revert Errors.MarginCallAlreadyFlagged();\\n}\\n```\\n\\nThus, the check in line 387 is incorrect, leading to prematurely resetting the short's liquidation flagging status.\\nAs the timestamps are in `hours`, and the liquidation timeline is relatively short, having an off-by-one error in the liquidation timeline can lead to a significant impact on the liquidations. Concretely, attempting to liquidate a short position in the last hour of the timeline, i.e., `timeDiff = 16`, is not possible.
Consider using `>` instead of `>=` in line 387 to prevent the liquidation timeline from overlapping with the bounds check in line 395.
```\\nfunction _canLiquidate(MTypes.MarginCallPrimary memory m)\\n    private\\n    view\\n    returns (bool)\\n{\\n// rest of code      // [// rest of code]\\n    uint256 timeDiff = LibOrders.getOffsetTimeHours() - m.short.updatedAt;\\n    uint256 resetLiquidationTime = LibAsset.resetLiquidationTime(m.asset);\\n❌  if (timeDiff >= resetLiquidationTime) {\\n        return false;\\n    } else {\\n        uint256 secondLiquidationTime = LibAsset.secondLiquidationTime(m.asset);\\n        bool isBetweenFirstAndSecondLiquidationTime = timeDiff\\n            > LibAsset.firstLiquidationTime(m.asset) && timeDiff <= secondLiquidationTime\\n            && s.flagMapping[m.short.flaggerId] == msg.sender;\\n        bool isBetweenSecondAndResetLiquidationTime =\\n            timeDiff > secondLiquidationTime && timeDiff <= resetLiquidationTime;\\n        if (\\n            !(\\n                (isBetweenFirstAndSecondLiquidationTime)\\n                    || (isBetweenSecondAndResetLiquidationTime)\\n            )\\n        ) {\\n            revert Errors.MarginCallIneligibleWindow();\\n        }\\n        return true;\\n    }\\n}\\n```\\n