`MultiInvoker._latest` calculates incorrect closable for the current oracle version causing some liquidations to revert
`closable` is the value calculated as the maximum possible position size that can be closed even if some pending position updates are invalidated due to invalid oracle version. There is one tricky edge case at the current oracle version which is calculated incorrectly in `MultiInvoker` (and also in Vault). This happens when pending position is updated in the current active oracle version: it is allowed to set this current position to any value conforming to `closable` of the previous pending (or latest) position. For example:\\nlatest settled position = 10\\nuser calls update(20) - pending position at t=200 is set to 20. If we calculate `closable` normally, it will be 10 (latest settled position).\\nuser calls update(0) - pending position at t=200 is set to 0. This is valid and correct. It looks as if we've reduced position by 20, bypassing the `closable` = 10 value, but in reality the only enforced `closable` is the previous one (for latest settled position in the example, so it's 10) and it's enforced as a change from previous position, not from current.\\nNow, if the step 3 happened in the next oracle version, so 3. user calls update(0) - pending position at t=300 will revert, because user can't close more than 10, and he tries to close 20.\\nSo in such tricky edge case, `MultiInvoker` (and Vault) will calculate `closable = 10` and will try to liquidate with position = 20-10 = 10 instead of 0 and will revert, because `Market._invariant` will calculate `closable = 10` (latest = 10, pending = 10, closable = latest = 10), but it must be 0 to liquidate (step 3. in the example above)\\nIn `Vault` case, this is less severe as the market will simply allow to redeem and will close smaller amount than it actually can.\\nWhen `Market` calculates `closable`, it's calculated starting from latest settled position up to (but not including) current position:\\n```\\n// load pending positions\\nfor (uint256 id = context.local.latestId + 1; id < context.local.currentId; id++)\\n    _processPendingPosition(context, _loadPendingPositionLocal(context, account, id));\\n```\\n\\nPay attention to `id < context.local.currentId` - the loop doesn't include currentId.\\nAfter the current position is updated to a new user specified value, only then the current position is processed and closable now includes new user position change from the previous position:\\n```\\nfunction _update(\\n    // rest of code\\n    // load\\n    _loadUpdateContext(context, account);\\n    // rest of code\\n    context.currentPosition.local.update(collateral);\\n    // rest of code\\n    // process current position\\n    _processPendingPosition(context, context.currentPosition.local);\\n    // rest of code\\n    // after\\n    _invariant(context, account, newOrder, collateral, protected);\\n```\\n\\nThe `MultiInvoker._latest` logic is different and simply includes calculation of `closable` for all pending positions:\\n```\\nfor (uint256 id = local.latestId + 1; id <= local.currentId; id++) {\\n\\n    // load pending position\\n    Position memory pendingPosition = market.pendingPositions(account, id);\\n    pendingPosition.adjust(latestPosition);\\n\\n    // virtual settlement\\n    if (pendingPosition.timestamp <= latestTimestamp) {\\n        if (!market.oracle().at(pendingPosition.timestamp).valid) latestPosition.invalidate(pendingPosition);\\n        latestPosition.update(pendingPosition);\\n\\n        previousMagnitude = latestPosition.magnitude();\\n        closableAmount = previousMagnitude;\\n\\n    // process pending positions\\n    } else {\\n        closableAmount = closableAmount\\n            .sub(previousMagnitude.sub(pendingPosition.magnitude().min(previousMagnitude)));\\n        previousMagnitude = latestPosition.magnitude();\\n    }\\n}\\n```\\n\\nThe same incorrect logic is in a Vault:\\n```\\n// pending positions\\nfor (uint256 id = marketContext.local.latestId + 1; id <= marketContext.local.currentId; id++)\\n    previousClosable = _loadPosition(\\n        marketContext,\\n        marketContext.currentAccountPosition = registration.market.pendingPositions(address(this), id),\\n        previousClosable\\n    );\\n```\\n
When calculating `closable` in `MultiInvoker` and `Vault`, add the following logic:\\nif timestamp of pending position at index currentId equals current oracle version, then add the difference between position size at currentId and previous position size to `closable` (both when that position increases and decreases).\\nFor example, if\\nlatest settled position = `10`\\npending position at t=200 = 20 then initialize `closable` to `10` (latest) add (pending-latest) = (20-10) to `closable` (closable = 20)
In the following edge case:\\ncurrent oracle version = oracle version of the pending position in currentId index\\nAND this (current) pending position increases compared to previous pending/settled position\\nThe following can happen:\\nliquidation via `MultiInvoker` will revert (medium impact)\\nvault's `maxRedeem` amount will be smaller than actual allowed amount, position will be reduced by a smaller amount than they actually can (low impact)
```\\n// load pending positions\\nfor (uint256 id = context.local.latestId + 1; id < context.local.currentId; id++)\\n    _processPendingPosition(context, _loadPendingPositionLocal(context, account, id));\\n```\\n