From c06f9bfef91222501a80ea343f3a3b2d3e714bb5 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Mon, 4 Sep 2023 12:14:40 +0400 Subject: [PATCH] test for token mismatch --- .gas-snapshot | 32 +++--- src/concrete/OrderBook.sol | 18 ++-- src/interface/unstable/IOrderBookV3.sol | 98 +------------------ test/concrete/OrderBook.takeOrder.noop.t.sol | 5 + .../OrderBook.takeOrder.tokenMismatch.t.sol | 86 ++++++++++++++++ 5 files changed, 120 insertions(+), 119 deletions(-) create mode 100644 test/concrete/OrderBook.takeOrder.tokenMismatch.t.sol diff --git a/.gas-snapshot b/.gas-snapshot index 2a7a1e9f3..2ff7ba557 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -3,8 +3,8 @@ GenericPoolOrderBookV3FlashBorrowerTest:testTakeOrdersSender((address,bool,(addr LibOrderTest:testHashEqual((address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[])) (runs: 5096, μ: 194389, ~: 191140) LibOrderTest:testHashNotEqual((address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[]),(address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[])) (runs: 5096, μ: 298734, ~: 298554) OrderBookAddOrderMockTest:testAddOrderSameAccountWithDifferentConfig(address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address,address) (runs: 5096, μ: 2771808, ~: 2761475) -OrderBookAddOrderMockTest:testAddOrderTwoAccountsWithDifferentConfig(address,address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address,address) (runs: 5096, μ: 2626737, ~: 2600883) -OrderBookAddOrderMockTest:testAddOrderTwoAccountsWithSameConfig(address,address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 2492972, ~: 2492266) +OrderBookAddOrderMockTest:testAddOrderTwoAccountsWithDifferentConfig(address,address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address,address) (runs: 5096, μ: 2626417, ~: 2600441) +OrderBookAddOrderMockTest:testAddOrderTwoAccountsWithSameConfig(address,address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 2493275, ~: 2492266) OrderBookAddOrderMockTest:testAddOrderWithCalculationsInputsAndOutputsSucceeds(address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 1295793, ~: 1281958) OrderBookAddOrderMockTest:testAddOrderWithNonEmptyMetaEmitsMetaV1(address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 1303904, ~: 1290881) OrderBookAddOrderMockTest:testAddOrderWithNonEmptyMetaReverts(address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 703690, ~: 697922) @@ -18,30 +18,32 @@ OrderBookAddOrderTest:testAddOrderRealThreeStackCalculate(address,((address,uint OrderBookAddOrderTest:testAddOrderRealTwoStackCalculateReverts(address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes)) (runs: 5096, μ: 715326, ~: 711971) OrderBookAddOrderTest:testAddOrderRealZeroStackCalculateReverts(address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes)) (runs: 5096, μ: 181302, ~: 180079) OrderBookDepositTest:testDepositEvent(address,uint256,uint256) (runs: 5096, μ: 38710, ~: 38710) -OrderBookDepositTest:testDepositFail(address,uint256,uint256) (runs: 5096, μ: 8937393460516740787, ~: 8937393460516740786) +OrderBookDepositTest:testDepositFail(address,uint256,uint256) (runs: 5096, μ: 8937393460516740786, ~: 8937393460516740786) OrderBookDepositTest:testDepositGas00() (gas: 8176) OrderBookDepositTest:testDepositGas01() (gas: 34620) -OrderBookDepositTest:testDepositMany((address,address,uint256,uint248)[]) (runs: 5096, μ: 5162681, ~: 4894479) +OrderBookDepositTest:testDepositMany((address,address,uint256,uint248)[]) (runs: 5096, μ: 5184765, ~: 4940766) OrderBookDepositTest:testDepositOverflow(address,uint256,uint256,uint256) (runs: 5096, μ: 46645, ~: 46645) -OrderBookDepositTest:testDepositReentrancy(address,uint256,uint256,address,uint256,uint256) (runs: 5096, μ: 495210, ~: 496632) +OrderBookDepositTest:testDepositReentrancy(address,uint256,uint256,address,uint256,uint256) (runs: 5096, μ: 495218, ~: 496632) OrderBookDepositTest:testDepositSimple(address,uint256,uint256) (runs: 5096, μ: 37840, ~: 37840) OrderBookDepositTest:testDepositZero(address,uint256) (runs: 5096, μ: 12639, ~: 12639) OrderBookDepositTest:testVaultBalanceNoDeposits(address,uint256) (runs: 5096, μ: 8263, ~: 8263) -OrderBookDepositTest:testVaultBalanceReentrant(address,uint256,uint256,address,address,uint256) (runs: 5096, μ: 494148, ~: 495964) +OrderBookDepositTest:testVaultBalanceReentrant(address,uint256,uint256,address,address,uint256) (runs: 5096, μ: 494050, ~: 495964) OrderBookRemoveOrderMockTest:testRemoveOrderAddRemoveMulti(address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 7256622, ~: 7139425) OrderBookRemoveOrderMockTest:testRemoveOrderDifferent(address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 5070365, ~: 5046372) -OrderBookRemoveOrderMockTest:testRemoveOrderDifferentOwners(address,address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 4854788, ~: 4841901) +OrderBookRemoveOrderMockTest:testRemoveOrderDifferentOwners(address,address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 4854223, ~: 4841901) OrderBookRemoveOrderMockTest:testRemoveOrderDifferentOwnersDifferent(address,address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 10466985, ~: 10453491) OrderBookRemoveOrderMockTest:testRemoveOrderDoesNotExist(address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 386811, ~: 382063) -OrderBookRemoveOrderMockTest:testRemoveOrderOnlyOwner(address,address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 2603276, ~: 2601080) -OrderBookTakeOrderNoopTest:testTakeOrderNoopNonLiveOrderOne((address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[]),uint256,uint256,(address,uint256[],bytes)) (runs: 5096, μ: 427777, ~: 424024) -OrderBookTakeOrderNoopTest:testTakeOrderNoopNonLiveOrderTwo((address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[]),(address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[]),uint256,uint256,uint256,uint256,(address,uint256[],bytes),(address,uint256[],bytes)) (runs: 5096, μ: 841710, ~: 837539) +OrderBookRemoveOrderMockTest:testRemoveOrderOnlyOwner(address,address,((address,uint8,uint256)[],(address,uint8,uint256)[],(address,bytes,uint256[]),bytes),address) (runs: 5096, μ: 2603102, ~: 2601080) +OrderBookTakeOrderNoopTest:testTakeOrderNoopNonLiveOrderOne((address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[]),uint256,uint256,(address,uint256[],bytes)) (runs: 5096, μ: 428003, ~: 424249) +OrderBookTakeOrderNoopTest:testTakeOrderNoopNonLiveOrderTwo((address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[]),(address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[]),uint256,uint256,uint256,uint256,(address,uint256[],bytes),(address,uint256[],bytes)) (runs: 5096, μ: 842490, ~: 838321) OrderBookTakeOrderNoopTest:testTakeOrderNoopZeroOrders() (gas: 12403) -OrderBookTakeOrderTest:testTakeOrderPrecisionKnownBad01() (gas: 2576588) +OrderBookTakeOrderTest:testTakeOrderPrecisionKnownBad01() (gas: 2576621) +OrderBookTakeOrderTokenMismatchTest:testTokenMismatchInputs((address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[]),uint256,uint256,(address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[]),uint256,uint256) (runs: 5096, μ: 613519, ~: 607165) +OrderBookTakeOrderTokenMismatchTest:testTokenMismatchOutputs((address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[]),uint256,uint256,(address,bool,(address,address,address),(address,uint8,uint256)[],(address,uint8,uint256)[]),uint256,uint256) (runs: 5096, μ: 613634, ~: 607304) OrderBookWithdrawTest:testWithdrawEmptyVault(address,address,uint256,uint256) (runs: 5096, μ: 15251, ~: 15251) -OrderBookWithdrawTest:testWithdrawFailure(address,uint256,uint256,uint256) (runs: 5096, μ: 8937393460516719305, ~: 8937393460516700418) -OrderBookWithdrawTest:testWithdrawFullVault(address,uint256,uint256,uint256) (runs: 5096, μ: 41257, ~: 41254) -OrderBookWithdrawTest:testWithdrawMany((bool,address,address,uint256,uint248)[]) (runs: 5096, μ: 1447310, ~: 1371034) +OrderBookWithdrawTest:testWithdrawFailure(address,uint256,uint256,uint256) (runs: 5096, μ: 8937393460516719678, ~: 8937393460516738938) +OrderBookWithdrawTest:testWithdrawFullVault(address,uint256,uint256,uint256) (runs: 5096, μ: 41256, ~: 41254) +OrderBookWithdrawTest:testWithdrawMany((bool,address,address,uint256,uint248)[]) (runs: 5096, μ: 1443910, ~: 1381982) OrderBookWithdrawTest:testWithdrawPartialVault(address,uint256,uint256,uint256) (runs: 5096, μ: 51929, ~: 51929) -OrderBookWithdrawTest:testWithdrawReentrant(address,uint256,uint256,address,address,uint256) (runs: 5096, μ: 506196, ~: 507997) +OrderBookWithdrawTest:testWithdrawReentrant(address,uint256,uint256,address,address,uint256) (runs: 5096, μ: 506294, ~: 507997) OrderBookWithdrawTest:testWithdrawZero(address,address,uint256) (runs: 5096, μ: 12809, ~: 12809) \ No newline at end of file diff --git a/src/concrete/OrderBook.sol b/src/concrete/OrderBook.sol index 8aee79b44..6c3838a10 100644 --- a/src/concrete/OrderBook.sol +++ b/src/concrete/OrderBook.sol @@ -353,19 +353,19 @@ contract OrderBook is IOrderBookV3, ReentrancyGuard, Multicall, OrderBookV3Flash while (i < config.orders.length && remainingTakerInput > 0) { takeOrderConfig = config.orders[i]; order = takeOrderConfig.order; + // Every order needs the same input token. + if (order.validInputs[takeOrderConfig.inputIOIndex].token != expectedOrderInputToken) { + revert TokenMismatch(order.validInputs[takeOrderConfig.inputIOIndex].token, expectedOrderInputToken); + } + // Every order needs the same output token. + if (order.validOutputs[takeOrderConfig.outputIOIndex].token != expectedOrderOutputToken) { + revert TokenMismatch(order.validOutputs[takeOrderConfig.outputIOIndex].token, expectedOrderOutputToken); + } + bytes32 orderHash = order.hash(); if (sOrders[orderHash] == ORDER_DEAD) { emit OrderNotFound(msg.sender, order.owner, orderHash); } else { - if (order.validInputs[takeOrderConfig.inputIOIndex].token != expectedOrderInputToken) { - revert TokenMismatch(order.validInputs[takeOrderConfig.inputIOIndex].token, expectedOrderInputToken); - } - if (order.validOutputs[takeOrderConfig.outputIOIndex].token != expectedOrderOutputToken) { - revert TokenMismatch( - order.validOutputs[takeOrderConfig.outputIOIndex].token, expectedOrderOutputToken - ); - } - OrderIOCalculation memory orderIOCalculation = calculateOrderIO( order, takeOrderConfig.inputIOIndex, diff --git a/src/interface/unstable/IOrderBookV3.sol b/src/interface/unstable/IOrderBookV3.sol index a5794046c..538b05d94 100644 --- a/src/interface/unstable/IOrderBookV3.sol +++ b/src/interface/unstable/IOrderBookV3.sol @@ -5,29 +5,12 @@ import "../ierc3156/IERC3156FlashLender.sol"; import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; import "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; +/// Import unmodified structures from older versions of `IOrderBook`. +import {IO, Order, TakeOrderConfig, ClearConfig, ClearStateChange} from "../IOrderBookV2.sol"; + /// Thrown when take orders is called with no orders. error NoOrders(); -/// Configuration for a single input or output on an `Order`. -/// @param token The token to either send from the owner as an output or receive -/// from the counterparty to the owner as an input. The tokens are not moved -/// during an order, only internal vault balances are updated, until a separate -/// withdraw step. -/// @param decimals The decimals to use for internal scaling calculations for -/// `token`. This is provided directly in IO to save gas on external lookups and -/// to respect the ERC20 spec that mandates NOT assuming or using the `decimals` -/// method for onchain calculations. Ostensibly the decimals exists so that all -/// calculate order entrypoints can treat amounts and ratios as 18 decimal fixed -/// point values. Order max amounts MUST be rounded down and IO ratios rounded up -/// to compensate for any loss of precision during decimal rescaling. -/// @param vaultId The vault ID that tokens will move into if this is an input -/// or move out from if this is an output. -struct IO { - address token; - uint8 decimals; - uint256 vaultId; -} - /// Config the order owner may provide to define their order. The `msg.sender` /// that adds an order cannot modify the owner nor bypass the integrity check of /// the expression deployer that they specify. However they MAY specify a @@ -47,27 +30,6 @@ struct OrderConfigV2 { bytes meta; } -/// Defines a fully deployed order ready to evaluate by Orderbook. -/// @param owner The owner of the order is the `msg.sender` that added the order. -/// @param handleIO true if there is a "handle IO" entrypoint to run. If false -/// the order book MAY skip calling the interpreter to save gas. -/// @param evaluable Standard `Evaluable` with entrypoints for both -/// "calculate order" and "handle IO". The latter MAY be empty bytes, in which -/// case it will be skipped at runtime to save gas. -/// @param validInputs A list of input tokens that are economically equivalent -/// for the purpose of processing this order. Inputs are relative to the order -/// so these tokens will be sent to the owners vault. -/// @param validOutputs A list of output tokens that are economically equivalent -/// for the purpose of processing this order. Outputs are relative to the order -/// so these tokens will be sent from the owners vault. -struct Order { - address owner; - bool handleIO; - Evaluable evaluable; - IO[] validInputs; - IO[] validOutputs; -} - /// Config for a list of orders to take sequentially as part of a `takeOrders` /// call. /// @param minimumInput Minimum input from the perspective of the order taker. @@ -91,60 +53,6 @@ struct TakeOrdersConfigV2 { bytes data; } -/// Config for an individual take order from the overall list of orders in a -/// call to `takeOrders`. -/// @param order The order being taken this iteration. -/// @param inputIOIndex The index of the input token in `order` to match with the -/// take order output. -/// @param outputIOIndex The index of the output token in `order` to match with -/// the take order input. -/// @param signedContext Optional additional signed context relevant to the -/// taken order. -struct TakeOrderConfig { - Order order; - uint256 inputIOIndex; - uint256 outputIOIndex; - SignedContextV1[] signedContext; -} - -/// Additional config to a `clear` that allows two orders to be fully matched to -/// a specific token moment. Also defines the bounty for the clearer. -/// @param aliceInputIOIndex The index of the input token in order A. -/// @param aliceOutputIOIndex The index of the output token in order A. -/// @param bobInputIOIndex The index of the input token in order B. -/// @param bobOutputIOIndex The index of the output token in order B. -/// @param aliceBountyVaultId The vault ID that the bounty from order A should -/// move to for the clearer. -/// @param bobBountyVaultId The vault ID that the bounty from order B should move -/// to for the clearer. -struct ClearConfig { - uint256 aliceInputIOIndex; - uint256 aliceOutputIOIndex; - uint256 bobInputIOIndex; - uint256 bobOutputIOIndex; - uint256 aliceBountyVaultId; - uint256 bobBountyVaultId; -} - -/// Summary of the vault state changes due to clearing an order. NOT the state -/// changes sent to the interpreter store, these are the LOCAL CHANGES in vault -/// balances. Note that the difference in inputs/outputs overall between the -/// counterparties is the bounty paid to the entity that cleared the order. -/// @param aliceOutput Amount of counterparty A's output token that moved out of -/// their vault. -/// @param bobOutput Amount of counterparty B's output token that moved out of -/// their vault. -/// @param aliceInput Amount of counterparty A's input token that moved into -/// their vault. -/// @param bobInput Amount of counterparty B's input token that moved into their -/// vault. -struct ClearStateChange { - uint256 aliceOutput; - uint256 bobOutput; - uint256 aliceInput; - uint256 bobInput; -} - /// @title IOrderBookV3 /// @notice An orderbook that deploys _strategies_ represented as interpreter /// expressions rather than individual orders. The order book contract itself diff --git a/test/concrete/OrderBook.takeOrder.noop.t.sol b/test/concrete/OrderBook.takeOrder.noop.t.sol index 97b61acf4..2dd10bffe 100644 --- a/test/concrete/OrderBook.takeOrder.noop.t.sol +++ b/test/concrete/OrderBook.takeOrder.noop.t.sol @@ -75,6 +75,11 @@ contract OrderBookTakeOrderNoopTest is OrderBookExternalRealTest { vm.assume(order2.validOutputs.length > 0); outputIOIndex2 = bound(outputIOIndex2, 0, order2.validOutputs.length - 1); + // The inputs and outputs need to match or we will trigger the token + // mismatch error. + order1.validInputs[inputIOIndex1].token = order2.validInputs[inputIOIndex2].token; + order1.validOutputs[outputIOIndex1].token = order2.validOutputs[outputIOIndex2].token; + TakeOrdersConfigV2 memory config; { TakeOrderConfig[] memory orders; diff --git a/test/concrete/OrderBook.takeOrder.tokenMismatch.t.sol b/test/concrete/OrderBook.takeOrder.tokenMismatch.t.sol new file mode 100644 index 000000000..14245c293 --- /dev/null +++ b/test/concrete/OrderBook.takeOrder.tokenMismatch.t.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.19; + +import "test/util/abstract/OrderBookExternalRealTest.sol"; +import {TokenMismatch} from "src/concrete/OrderBook.sol"; + +/// @title OrderBookTakeOrderTokenMismatchTest +/// @notice A test harness for testing the OrderBook takeOrder function. +/// Focuses on the token mismatch case. +contract OrderBookTakeOrderTokenMismatchTest is OrderBookExternalRealTest { + /// It is only possible to get a token mismatch when there are at least two + /// orders. This is because `takeOrders` is interactive so we assume that + /// the caller's desired input and output tokens match the first order they + /// pass in. + /// Test a mismatch in the input tokens. + function testTokenMismatchInputs( + Order memory a, + uint256 aInputIOIndex, + uint256 aOutputIOIndex, + Order memory b, + uint256 bInputIOIndex, + uint256 bOutputIOIndex + ) external { + vm.assume(a.validInputs.length > 0); + aInputIOIndex = bound(aInputIOIndex, 0, a.validInputs.length - 1); + vm.assume(b.validInputs.length > 0); + bInputIOIndex = bound(bInputIOIndex, 0, b.validInputs.length - 1); + vm.assume(a.validOutputs.length > 0); + aOutputIOIndex = bound(aOutputIOIndex, 0, a.validOutputs.length - 1); + vm.assume(b.validOutputs.length > 0); + bOutputIOIndex = bound(bOutputIOIndex, 0, b.validOutputs.length - 1); + + // Mismatch on inputs across orders taken. + vm.assume(a.validInputs[aInputIOIndex].token != b.validInputs[bInputIOIndex].token); + // Line up outputs so we don't trigger that code path. + b.validOutputs[bOutputIOIndex].token = a.validOutputs[aOutputIOIndex].token; + + TakeOrderConfig[] memory orders = new TakeOrderConfig[](2); + orders[0] = TakeOrderConfig(a, aInputIOIndex, aOutputIOIndex, new SignedContextV1[](0)); + orders[1] = TakeOrderConfig(b, bInputIOIndex, bOutputIOIndex, new SignedContextV1[](0)); + TakeOrdersConfigV2 memory config = TakeOrdersConfigV2(0, type(uint256).max, type(uint256).max, orders, ""); + vm.expectRevert( + abi.encodeWithSelector( + TokenMismatch.selector, b.validInputs[bInputIOIndex].token, a.validInputs[aInputIOIndex].token + ) + ); + (uint256 totalTakerInput, uint256 totalTakerOutput) = iOrderbook.takeOrders(config); + (totalTakerInput, totalTakerOutput); + } + + /// Test a mismatch in the output tokens. + function testTokenMismatchOutputs( + Order memory a, + uint256 aInputIOIndex, + uint256 aOutputIOIndex, + Order memory b, + uint256 bInputIOIndex, + uint256 bOutputIOIndex + ) external { + vm.assume(a.validInputs.length > 0); + aInputIOIndex = bound(aInputIOIndex, 0, a.validInputs.length - 1); + vm.assume(b.validInputs.length > 0); + bInputIOIndex = bound(bInputIOIndex, 0, b.validInputs.length - 1); + vm.assume(a.validOutputs.length > 0); + aOutputIOIndex = bound(aOutputIOIndex, 0, a.validOutputs.length - 1); + vm.assume(b.validOutputs.length > 0); + bOutputIOIndex = bound(bOutputIOIndex, 0, b.validOutputs.length - 1); + + // Mismatch on outputs across orders taken. + vm.assume(a.validOutputs[aOutputIOIndex].token != b.validOutputs[bOutputIOIndex].token); + // Line up inputs so we don't trigger that code path. + b.validInputs[bInputIOIndex].token = a.validInputs[aInputIOIndex].token; + + TakeOrderConfig[] memory orders = new TakeOrderConfig[](2); + orders[0] = TakeOrderConfig(a, aInputIOIndex, aOutputIOIndex, new SignedContextV1[](0)); + orders[1] = TakeOrderConfig(b, bInputIOIndex, bOutputIOIndex, new SignedContextV1[](0)); + TakeOrdersConfigV2 memory config = TakeOrdersConfigV2(0, type(uint256).max, type(uint256).max, orders, ""); + vm.expectRevert( + abi.encodeWithSelector( + TokenMismatch.selector, b.validOutputs[bOutputIOIndex].token, a.validOutputs[aOutputIOIndex].token + ) + ); + (uint256 totalTakerInput, uint256 totalTakerOutput) = iOrderbook.takeOrders(config); + (totalTakerInput, totalTakerOutput); + } +}