Vulnerability: Queued transfers can become stuck on the source chain if Transceiver instructions are encoded in the incorrect order
Severity: high

Description:
In the case of multiple Transceivers, the current logic expects that a sender encodes Transceiver instructions in order of increasing Transceiver registration index, as validated in `TransceiverStructs::parseTransceiverInstructions`. Under normal circumstances, this logic works as expected, and the transaction fails when the user packs transceiver instructions in the incorrect order.\\n```\\n/* snip */\\nfor (uint256 i = 0; i < instructionsLength; i++) {\\n    TransceiverInstruction memory instruction;\\n    (instruction, offset) = parseTransceiverInstructionUnchecked(encoded, offset);\\n\\n    uint8 instructionIndex = instruction.index;\\n\\n    // The instructions passed in have to be strictly increasing in terms of transceiver index\\n    if (i != 0 && instructionIndex <= lastIndex) {\\n        revert UnorderedInstructions();\\n    }\\n    lastIndex = instructionIndex;\\n\\n    instructions[instructionIndex] = instruction;\\n}\\n/* snip */\\n```\\n\\nHowever, this requirement on the order of Transceiver indices is not checked when transfers are initially queued for delayed execution. As a result, a transaction where this is the case will fail when the user calls `NttManager::completeOutboundQueuedTransfer` to execute a queued transfer.

Impact:
The sender's funds are transferred to the NTT Manager when messages are queued. However, this queued message can never be executed if the Transceiver indices are incorrectly ordered and, as a result, the user funds remain stuck in the NTT Manager.\\nProof of Concept: Run the following test:\\n```\\ncontract TestWrongTransceiverOrder is Test, INttManagerEvents, IRateLimiterEvents {\\n    NttManager nttManagerChain1;\\n    NttManager nttManagerChain2;\\n\\n    using TrimmedAmountLib for uint256;\\n    using TrimmedAmountLib for TrimmedAmount;\\n\\n    uint16 constant chainId1 = 7;\\n    uint16 constant chainId2 = 100;\\n    uint8 constant FAST_CONSISTENCY_LEVEL = 200;\\n    uint256 constant GAS_LIMIT = 500000;\\n\\n    uint16 constant SENDING_CHAIN_ID = 1;\\n    uint256 constant DEVNET_GUARDIAN_PK =\\n        0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0;\\n    WormholeSimulator guardian;\\n    uint256 initialBlockTimestamp;\\n\\n    WormholeTransceiver wormholeTransceiverChain1;\\n    WormholeTransceiver wormholeTransceiver2Chain1;\\n\\n    WormholeTransceiver wormholeTransceiverChain2;\\n    address userA = address(0x123);\\n    address userB = address(0x456);\\n    address userC = address(0x789);\\n    address userD = address(0xABC);\\n\\n    address relayer = address(0x28D8F1Be96f97C1387e94A53e00eCcFb4E75175a);\\n    IWormhole wormhole = IWormhole(0x706abc4E45D419950511e474C7B9Ed348A4a716c);\\n\\n    function setUp() public {\\n        string memory url = "https://goerli.blockpi.network/v1/rpc/public";\\n        vm.createSelectFork(url);\\n        initialBlockTimestamp = vm.getBlockTimestamp();\\n\\n        guardian = new WormholeSimulator(address(wormhole), DEVNET_GUARDIAN_PK);\\n\\n        vm.chainId(chainId1);\\n        DummyToken t1 = new DummyToken();\\n        NttManager implementation =\\n            new MockNttManagerContract(address(t1), INttManager.Mode.LOCKING, chainId1, 1 days);\\n\\n        nttManagerChain1 =\\n            MockNttManagerContract(address(new ERC1967Proxy(address(implementation), "")));\\n        nttManagerChain1.initialize();\\n\\n        WormholeTransceiver wormholeTransceiverChain1Implementation = new MockWormholeTransceiverContract(\\n            address(nttManagerChain1),\\n            address(wormhole),\\n            address(relayer),\\n            address(0x0),\\n            FAST_CONSISTENCY_LEVEL,\\n            GAS_LIMIT\\n        );\\n        wormholeTransceiverChain1 = MockWormholeTransceiverContract(\\n            address(new ERC1967Proxy(address(wormholeTransceiverChain1Implementation), ""))\\n        );\\n\\n        WormholeTransceiver wormholeTransceiverChain1Implementation2 = new MockWormholeTransceiverContract(\\n            address(nttManagerChain1),\\n            address(wormhole),\\n            address(relayer),\\n            address(0x0),\\n            FAST_CONSISTENCY_LEVEL,\\n            GAS_LIMIT\\n        );\\n        wormholeTransceiver2Chain1 = MockWormholeTransceiverContract(\\n            address(new ERC1967Proxy(address(wormholeTransceiverChain1Implementation2), ""))\\n        );\\n\\n\\n        // Actually initialize properly now\\n        wormholeTransceiverChain1.initialize();\\n        wormholeTransceiver2Chain1.initialize();\\n\\n\\n        nttManagerChain1.setTransceiver(address(wormholeTransceiverChain1));\\n        nttManagerChain1.setTransceiver(address(wormholeTransceiver2Chain1));\\n        nttManagerChain1.setOutboundLimit(type(uint64).max);\\n        nttManagerChain1.setInboundLimit(type(uint64).max, chainId2);\\n\\n        // Chain 2 setup\\n        vm.chainId(chainId2);\\n        DummyToken t2 = new DummyTokenMintAndBurn();\\n        NttManager implementationChain2 =\\n            new MockNttManagerContract(address(t2), INttManager.Mode.BURNING, chainId2, 1 days);\\n\\n        nttManagerChain2 =\\n            MockNttManagerContract(address(new ERC1967Proxy(address(implementationChain2), "")));\\n        nttManagerChain2.initialize();\\n\\n        WormholeTransceiver wormholeTransceiverChain2Implementation = new MockWormholeTransceiverContract(\\n            address(nttManagerChain2),\\n            address(wormhole),\\n            address(relayer),\\n            address(0x0),\\n            FAST_CONSISTENCY_LEVEL,\\n            GAS_LIMIT\\n        );\\n\\n        wormholeTransceiverChain2 = MockWormholeTransceiverContract(\\n            address(new ERC1967Proxy(address(wormholeTransceiverChain2Implementation), ""))\\n        );\\n        wormholeTransceiverChain2.initialize();\\n\\n        nttManagerChain2.setTransceiver(address(wormholeTransceiverChain2));\\n        nttManagerChain2.setOutboundLimit(type(uint64).max);\\n        nttManagerChain2.setInboundLimit(type(uint64).max, chainId1);\\n\\n        // Register peer contracts for the nttManager and transceiver. Transceivers and nttManager each have the concept of peers here.\\n        nttManagerChain1.setPeer(chainId2, bytes32(uint256(uint160(address(nttManagerChain2)))), 9);\\n        nttManagerChain2.setPeer(chainId1, bytes32(uint256(uint160(address(nttManagerChain1)))), 7);\\n\\n        // Set peers for the transceivers\\n        wormholeTransceiverChain1.setWormholePeer(\\n            chainId2, bytes32(uint256(uint160(address(wormholeTransceiverChain2))))\\n        );\\n\\n       wormholeTransceiver2Chain1.setWormholePeer(\\n            chainId2, bytes32(uint256(uint160(address(wormholeTransceiverChain2))))\\n        );\\n\\n        wormholeTransceiverChain2.setWormholePeer(\\n            chainId1, bytes32(uint256(uint160(address(wormholeTransceiverChain1))))\\n        );\\n\\n        require(nttManagerChain1.getThreshold() != 0, "Threshold is zero with active transceivers");\\n\\n        // Actually set it\\n        nttManagerChain1.setThreshold(2);\\n        nttManagerChain2.setThreshold(1);\\n    }\\n\\n    function testWrongTransceiverOrder() external {\\n        vm.chainId(chainId1);\\n\\n        // Setting up the transfer\\n        DummyToken token1 = DummyToken(nttManagerChain1.token());\\n        uint8 decimals = token1.decimals();\\n\\n        token1.mintDummy(address(userA), 5 * 10 ** decimals);\\n        uint256 outboundLimit = 4 * 10 ** decimals;\\n        nttManagerChain1.setOutboundLimit(outboundLimit);\\n\\n        vm.startPrank(userA);\\n\\n        uint256 transferAmount = 5 * 10 ** decimals;\\n        token1.approve(address(nttManagerChain1), transferAmount);\\n\\n        // transfer with shouldQueue == true\\n        uint64 qSeq = nttManagerChain1.transfer(\\n            transferAmount, chainId2, toWormholeFormat(userB), true, encodeTransceiverInstructionsJumbled(true)\\n        );\\n\\n        assertEq(qSeq, 0);\\n        IRateLimiter.OutboundQueuedTransfer memory qt = nttManagerChain1.getOutboundQueuedTransfer(0);\\n        assertEq(qt.amount.getAmount(), transferAmount.trim(decimals, decimals).getAmount());\\n        assertEq(qt.recipientChain, chainId2);\\n        assertEq(qt.recipient, toWormholeFormat(userB));\\n        assertEq(qt.txTimestamp, initialBlockTimestamp);\\n\\n        // assert that the contract also locked funds from the user\\n        assertEq(token1.balanceOf(address(userA)), 0);\\n        assertEq(token1.balanceOf(address(nttManagerChain1)), transferAmount);\\n\\n         // elapse rate limit duration - 1\\n        uint256 durationElapsedTime = initialBlockTimestamp + nttManagerChain1.rateLimitDuration();\\n\\n        vm.warp(durationElapsedTime);\\n\\n        vm.expectRevert(0x71f23ef2); //UnorderedInstructions() selector\\n        nttManagerChain1.completeOutboundQueuedTransfer(0);\\n    }\\n\\n    // Encode an instruction for each of the relayers\\n    function encodeTransceiverInstructionsJumbled(bool relayer_off) public view returns (bytes memory) {\\n        WormholeTransceiver.WormholeTransceiverInstruction memory instruction =\\n            IWormholeTransceiver.WormholeTransceiverInstruction(relayer_off);\\n\\n        bytes memory encodedInstructionWormhole =\\n            wormholeTransceiverChain1.encodeWormholeTransceiverInstruction(instruction);\\n\\n        TransceiverStructs.TransceiverInstruction memory TransceiverInstruction1 =\\n        TransceiverStructs.TransceiverInstruction({index: 0, payload: encodedInstructionWormhole});\\n        TransceiverStructs.TransceiverInstruction memory TransceiverInstruction2 =\\n        TransceiverStructs.TransceiverInstruction({index: 1, payload: encodedInstructionWormhole});\\n\\n        TransceiverStructs.TransceiverInstruction[] memory TransceiverInstructions =\\n            new TransceiverStructs.TransceiverInstruction[](2);\\n\\n        TransceiverInstructions[0] = TransceiverInstruction2;\\n        TransceiverInstructions[1] = TransceiverInstruction1;\\n\\n        return TransceiverStructs.encodeTransceiverInstructions(TransceiverInstructions);\\n    }\\n}\\n```\\n

Recommended mitigation:
When the transfer amount exceeds the current outbound capacity, verify the Transceiver instructions are ordered correctly before adding a message to the list of queued transfers.