HIGH severity: Impossible to liquidate accounts with multiple active markets as `LiquidationBranch::liquidateAccounts` reverts due to corruption of ordering in `TradingAccount::activeMarketsIds`. **Description:** `LiquidationBranch::liquidateAccounts` iterates through the active markets of the account being liquidated, assuming that the ordering of these active markets will remain constant:

However this is not true as `activeMarketIds` is an `EnumerableSet` which explicitly provides [no...

Vulnerable code:
// load open markets for account being liquidated
ctx.amountOfOpenPositions = tradingAccount.activeMarketsIds.length();

// iterate through open markets
for (uint256 j = 0; j < ctx.amountOfOpenPositions; j++) {
    // load current active market id into working data
    // @audit assumes constant ordering of active markets
    ctx.marketId = tradingAccount.activeMarketsIds.at(j).toUint128();

    PerpMarket.Data storage perpMarket = PerpMarket.load(ctx.marketId);
    Position.Data storage position = Position.load(ctx.tradingAccountId, ctx.marketId);

    ctx.oldPositionSizeX18 = sd59x18(position.size);
    ctx.liquidationSizeX18 = unary(ctx.oldPositionSizeX18);

    ctx.markPriceX18 = perpMarket.getMarkPrice(ctx.liquidationSizeX18, perpMarket.getIndexPrice());

    ctx.fundingRateX18 = perpMarket.getCurrentFundingRate();
    ctx.fundingFeePerUnitX18 = perpMarket.getNextFundingFeePerUnit(ctx.fundingRateX18, ctx.markPriceX18);

    perpMarket.updateFunding(ctx.fundingRateX18, ctx.fundingFeePerUnitX18);
    position.clear();

    // @audit this calls `EnumerableSet::remove` which changes the order of `activeMarketIds`
    tradingAccount.updateActiveMarkets(ctx.marketId, ctx.oldPositionSizeX18, SD_ZERO);

function openManualPosition(
    uint128 marketId,
    bytes32 streamId,
    uint256 mockUsdPrice,
    uint128 tradingAccountId,
    int128 sizeDelta
) internal {
    perpsEngine.createMarketOrder(
        OrderBranch.CreateMarketOrderParams({
            tradingAccountId: tradingAccountId,
            marketId: marketId,
            sizeDelta: sizeDelta
        })
    );

    bytes memory mockSignedReport = getMockedSignedReport(streamId, mockUsdPrice);

    changePrank({ msgSender: marketOrderKeepers[marketId] });

    // fill first order and open position
    perpsEngine.fillMarketOrder(tradingAccountId, marketId, mockSignedReport);

    changePrank({ msgSender: users.naruto });
}

function test_ImpossibleToLiquidateAccountWithMultipleMarkets() external {
    // give naruto some tokens
    uint256 USER_STARTING_BALANCE = 100_000e18;
    int128  USER_POS_SIZE_DELTA   = 10e18;
    deal({ token: address(usdToken), to: users.naruto, give: USER_STARTING_BALANCE });

    // naruto creates a trading account and deposits their tokens as collateral
    changePrank({ msgSender: users.naruto });
    uint128 tradingAccountId = createAccountAndDeposit(USER_STARTING_BALANCE, address(usdToken));

    // naruto opens first position in BTC market
    openManualPosition(BTC_USD_MARKET_ID, BTC_USD_STREAM_ID, MOCK_BTC_USD_PRICE, tradingAccountId, USER_POS_SIZE_DELTA);

    // naruto opens second position in ETH market
    openManualPosition(ETH_USD_MARKET_ID, ETH_USD_STREAM_ID, MOCK_ETH_USD_PRICE, tradingAccountId, USER_POS_SIZE_DELTA);

    // make BTC position liquidatable
    updateMockPriceFeed(BTC_USD_MARKET_ID, MOCK_BTC_USD_PRICE/2);

    // make ETH position liquidatable
    updateMockPriceFeed(ETH_USD_MARKET_ID, MOCK_ETH_USD_PRICE/2);

    // verify naruto can now be liquidated
    uint128[] memory liquidatableAccountsIds = perpsEngine.checkLiquidatableAccounts(0, 1);
    assertEq(1, liquidatableAccountsIds.length);
    assertEq(tradingAccountId, liquidatableAccountsIds[0]);

    // attempt to liquidate naruto
    changePrank({ msgSender: liquidationKeeper });

    // this reverts with "panic: array out-of-bounds access"
    // due to the order of `activeMarketIds` being corrupted by
    // the removal of the first active market then when attempting
    // to remove the second active market it triggers this error
    perpsEngine.liquidateAccounts(liquidatableAccountsIds, users.settlementFeeRecipient);

    // comment out the ETH position above it no longer reverts since
    // then user would only have 1 active market
    //
    // comment out the following line from `LiquidationBranch::liquidateAccounts`
    // and it also won't revert since the active market removal won't happen:
    //
    // tradingAccount.updateActiveMarkets(ctx.marketId, ctx.oldPositionSizeX18, SD_ZERO);
}