1084 lines
55 KiB
TypeScript

import { encodeERC20AssetData } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import { ExchangeRevertErrors } from '@0x/contracts-exchange';
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
import { blockchainTests, constants, expect, orderHashUtils, toBaseUnitAmount } from '@0x/contracts-test-utils';
import { OrderStatus } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Actor } from '../framework/actors/base';
import { Maker } from '../framework/actors/maker';
import { actorAddressesByName } from '../framework/actors/utils';
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
import { TokenIds } from '../framework/balances/types';
import { DeploymentManager } from '../framework/deployment_manager';
import { MatchOrderTester, TestMatchOrdersArgs, testMatchOrdersAsync } from './match_order_tester';
const { isRoundingErrorCeil, isRoundingErrorFloor } = LibReferenceFunctions;
blockchainTests.resets('matchOrders integration tests', env => {
// The fee recipient addresses.
let feeRecipientLeft: Actor;
let feeRecipientRight: Actor;
// The address that should be responsible for matching orders.
let matcher: Actor;
// Market makers who have opposite maker and taker assets.
let makerLeft: Maker;
let makerRight: Maker;
// The addresses of important assets for testing.
let makerAssetLeft: DummyERC20TokenContract;
let makerAssetRight: DummyERC20TokenContract;
let feeAsset: DummyERC20TokenContract;
let makerAssetDataLeft: string;
let makerAssetDataRight: string;
let feeAssetData: string;
let deployment: DeploymentManager;
let matchOrderTester: MatchOrderTester;
let leftId: BigNumber;
let rightId: BigNumber;
let defaultMatchOrdersArgs: TestMatchOrdersArgs;
before(async () => {
deployment = await DeploymentManager.deployAsync(env, {
numErc20TokensToDeploy: 3,
numErc721TokensToDeploy: 1,
numErc1155TokensToDeploy: 1,
});
makerAssetLeft = deployment.tokens.erc20[0];
makerAssetRight = deployment.tokens.erc20[1];
feeAsset = deployment.tokens.erc20[2];
// Create the fee recipient actors.
feeRecipientLeft = new Actor({
name: 'left fee recipient',
deployment,
});
feeRecipientRight = new Actor({
name: 'right fee recipient',
deployment,
});
// Encode the asset data.
makerAssetDataLeft = encodeERC20AssetData(makerAssetLeft.address);
makerAssetDataRight = encodeERC20AssetData(makerAssetRight.address);
feeAssetData = encodeERC20AssetData(feeAsset.address);
// Create two market makers with compatible orders for matching.
makerLeft = new Maker({
name: 'left maker',
deployment,
orderConfig: {
makerAssetData: makerAssetDataLeft,
takerAssetData: makerAssetDataRight,
makerFeeAssetData: feeAssetData,
takerFeeAssetData: feeAssetData,
feeRecipientAddress: feeRecipientLeft.address,
},
});
makerRight = new Maker({
name: 'right maker',
deployment,
orderConfig: {
makerAssetData: makerAssetDataRight,
takerAssetData: makerAssetDataLeft,
makerFeeAssetData: feeAssetData,
takerFeeAssetData: feeAssetData,
feeRecipientAddress: feeRecipientRight.address,
},
});
// Create a matcher.
matcher = new Actor({
name: 'matcher',
deployment,
});
// Configure the appropriate actors with initial balances.
await Promise.all([
...deployment.tokens.erc20.map(async token => makerLeft.configureERC20TokenAsync(token)),
...deployment.tokens.erc20.map(async token => makerRight.configureERC20TokenAsync(token)),
makerLeft.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address),
makerRight.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address),
matcher.configureERC20TokenAsync(feeAsset),
matcher.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address),
feeRecipientLeft.configureERC20TokenAsync(feeAsset),
feeRecipientLeft.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address),
feeRecipientRight.configureERC20TokenAsync(feeAsset),
feeRecipientRight.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address),
]);
leftId = await makerLeft.configureERC1155TokenAsync(deployment.tokens.erc1155[0]);
[rightId] = await makerRight.configureERC721TokenAsync(deployment.tokens.erc721[0]);
const tokenIds: TokenIds = { erc721: {}, erc1155: {} };
tokenIds.erc1155[deployment.tokens.erc1155[0].address] = { fungible: [leftId], nonFungible: [] };
tokenIds.erc721[deployment.tokens.erc721[0].address] = [rightId];
const blockchainBalanceStore = new BlockchainBalanceStore(
{
...actorAddressesByName([feeRecipientLeft, feeRecipientRight, makerLeft, makerRight, matcher]),
stakingProxy: deployment.staking.stakingProxy.address,
},
{
erc20: {
makerTokenLeft: deployment.tokens.erc20[0],
makerTokenRight: deployment.tokens.erc20[1],
feeToken: deployment.tokens.erc20[2],
weth: deployment.tokens.weth,
},
erc721: {
nft: deployment.tokens.erc721[0],
},
erc1155: {
fungible: deployment.tokens.erc1155[0],
},
},
tokenIds,
);
matchOrderTester = new MatchOrderTester(deployment, blockchainBalanceStore);
defaultMatchOrdersArgs = {
env,
matchOrderTester,
makerLeft,
makerRight,
leftOrder: constants.STATIC_ORDER_PARAMS,
rightOrder: constants.STATIC_ORDER_PARAMS,
matcherAddress: matcher.address,
expectedTransferAmounts: {},
withMaximalFill: false,
};
});
after(async () => {
Actor.reset();
});
describe('matchOrders', () => {
// TODO: Should be refactored to use `testMatchOrdersAsync` or moved to unit tests
it('should transfer correct amounts when right order is fully filled and values pass isRoundingErrorFloor but fail isRoundingErrorCeil', async () => {
// Create orders to match
const signedOrderLeft = await makerLeft.signOrderAsync({
makerAssetAmount: new BigNumber(17),
takerAssetAmount: new BigNumber(98),
});
const signedOrderRight = await makerRight.signOrderAsync({
makerAssetAmount: new BigNumber(75),
takerAssetAmount: new BigNumber(13),
});
// Assert is rounding error ceil & not rounding error floor
// These assertions are taken from MixinMatchOrders::calculateMatchedFillResults
// The rounding error is derived by computing how much the left maker will sell.
const numerator = signedOrderLeft.makerAssetAmount;
const denominator = signedOrderLeft.takerAssetAmount;
const target = signedOrderRight.makerAssetAmount;
const _isRoundingErrorCeil = isRoundingErrorCeil(numerator, denominator, target);
expect(_isRoundingErrorCeil).to.be.true();
const _isRoundingErrorFloor = isRoundingErrorFloor(numerator, denominator, target);
expect(_isRoundingErrorFloor).to.be.false();
// Match signedOrderLeft with signedOrderRight
// Note that the left maker received a slightly better sell price.
// This is intentional; see note in MixinMatchOrders.calculateMatchedFillResults.
// Because the left maker received a slightly more favorable sell price, the fee
// paid by the left taker is slightly higher than that paid by the left maker.
// Fees can be thought of as a tax paid by the seller, derived from the sale price.
const expectedTransferAmounts = {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(13),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount('76.4705882352941176', 16), // 76.47%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(75),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('76.5306122448979591', 16), // 76.53%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
};
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
{
leftOrder: signedOrderLeft,
rightOrder: signedOrderRight,
},
expectedTransferAmounts,
matcher.address,
DeploymentManager.protocolFee.times(2),
false,
);
});
// TODO: Should be refactored to use `testMatchOrdersAsync` or moved to unit tests
it('should transfer correct amounts when left order is fully filled and values pass isRoundingErrorCeil but fail isRoundingErrorFloor', async () => {
// Create orders to match
const signedOrderLeft = await makerLeft.signOrderAsync({
makerAssetAmount: new BigNumber(15),
takerAssetAmount: new BigNumber(90),
});
const signedOrderRight = await makerRight.signOrderAsync({
makerAssetAmount: new BigNumber(97),
takerAssetAmount: new BigNumber(14),
});
// Assert is rounding error floor & not rounding error ceil
// These assertions are taken from MixinMatchOrders::calculateMatchedFillResults
// The rounding error is derived computating how much the right maker will buy.
const numerator = signedOrderRight.takerAssetAmount;
const denominator = signedOrderRight.makerAssetAmount;
const target = signedOrderLeft.takerAssetAmount;
const _isRoundingErrorFloor = isRoundingErrorFloor(numerator, denominator, target);
expect(_isRoundingErrorFloor).to.be.true();
const _isRoundingErrorCeil = isRoundingErrorCeil(numerator, denominator, target);
expect(_isRoundingErrorCeil).to.be.false();
// Match signedOrderLeft isRoundingErrorFloor right maker received a slightly better purchase price.
// This is intentional; see note in MixinMatchOrders.calculateMatchedFillResults.
// Because the right maker received a slightly more favorable buy price, the fee
// paid by the right taker is slightly higher than that paid by the right maker.
// Fees can be thought of as a tax paid by the seller, derived from the sale price.
const expectedTransferAmounts = {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(15),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(13),
rightMakerAssetSoldByRightMakerAmount: new BigNumber(90),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount('92.7835051546391752', 16), // 92.78%
// Taker
leftMakerAssetReceivedByTakerAmount: new BigNumber(2),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('92.8571428571428571', 16), // 92.85%
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
};
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
{
leftOrder: signedOrderLeft,
rightOrder: signedOrderRight,
},
expectedTransferAmounts,
matcher.address,
DeploymentManager.protocolFee.times(2),
false,
);
});
it('should give right maker a better buy price when rounding', async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: new BigNumber(16),
takerAssetAmount: new BigNumber(22),
},
rightOrder: {
makerAssetAmount: new BigNumber(83),
takerAssetAmount: new BigNumber(49),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(16),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(22),
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(13),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount('26.5060240963855421', 16), // 26.506%
// Taker
leftMakerAssetReceivedByTakerAmount: new BigNumber(3),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('26.5306122448979591', 16), // 26.531%
},
});
});
it('should give left maker a better sell price when rounding', async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: new BigNumber(12),
takerAssetAmount: new BigNumber(97),
},
rightOrder: {
makerAssetAmount: new BigNumber(89),
takerAssetAmount: new BigNumber(1),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(11),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount('91.6666666666666666', 16), // 91.6%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(89),
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: new BigNumber(10),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('91.7525773195876288', 16), // 91.75%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
});
});
it('should give right maker and right taker a favorable fee price when rounding', async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: new BigNumber(16),
takerAssetAmount: new BigNumber(22),
},
rightOrder: {
makerAssetAmount: new BigNumber(83),
takerAssetAmount: new BigNumber(49),
makerFee: new BigNumber(10000),
takerFee: new BigNumber(10000),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(16),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(22),
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(13),
rightMakerFeeAssetPaidByRightMakerAmount: new BigNumber(2650), // 2650.6 rounded down to 2650
// Taker
leftMakerAssetReceivedByTakerAmount: new BigNumber(3),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: new BigNumber(2653), // 2653.1 rounded down to 2653
},
});
});
it('should give left maker and left taker a favorable fee price when rounding', async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: new BigNumber(12),
takerAssetAmount: new BigNumber(97),
makerFee: new BigNumber(10000),
takerFee: new BigNumber(10000),
},
rightOrder: {
makerAssetAmount: new BigNumber(89),
takerAssetAmount: new BigNumber(1),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(11),
leftMakerFeeAssetPaidByLeftMakerAmount: new BigNumber(9166), // 9166.6 rounded down to 9166
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(89),
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: new BigNumber(10),
leftTakerFeeAssetPaidByTakerAmount: new BigNumber(9175), // 9175.2 rounded down to 9175
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
});
});
it(`should transfer correct amounts when right order fill amount deviates
from amount derived by \`Exchange.fillOrder\``, async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: new BigNumber(1000),
takerAssetAmount: new BigNumber(1005),
},
rightOrder: {
makerAssetAmount: new BigNumber(2126),
takerAssetAmount: new BigNumber(1063),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(1000),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
// Notes:
// i.
// The left order is fully filled by the right order, so the right maker must sell 1005 units of their asset to the left maker.
// By selling 1005 units, the right maker should theoretically receive 502.5 units of the left maker's asset.
// Since the transfer amount must be an integer, this value must be rounded down to 502 or up to 503.
// ii.
// If the right order were filled via `Exchange.fillOrder` the respective fill amounts would be [1004, 502] or [1006, 503].
// It follows that we cannot trigger a sale of 1005 units of the right maker's asset through `Exchange.fillOrder`.
// iii.
// For an optimal match, the algorithm must choose either [1005, 502] or [1005, 503] as fill amounts for the right order.
// The algorithm favors the right maker when the exchange rate must be rounded, so the final fill for the right order is [1005, 503].
// iv.
// The right maker fee differs from the right taker fee because their exchange rate differs.
// The right maker always receives the better exchange and fee price.
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1005),
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(503),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount('47.2718720602069614', 16), // 47.27%
// Taker
leftMakerAssetReceivedByTakerAmount: new BigNumber(497),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('47.3189087488240827', 16), // 47.31%
},
});
});
it('should transfer the correct amounts when orders completely fill each other', async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
});
});
it(`should transfer the correct amounts when orders completely fill each
other and taker doesn't take a profit`, async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(5, 18),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
});
});
it(`should transfer the correct amounts when left order is completely filled
and right order is partially filled`, async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(20, 18),
takerAssetAmount: toBaseUnitAmount(4, 18),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(50, 16), // 50%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(50, 16), // 50%
},
});
});
it(`should transfer the correct amounts when right order is completely filled
and left order is partially filled`, async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(50, 18),
takerAssetAmount: toBaseUnitAmount(100, 18),
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(10, 16), // 10%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(10, 16), // 10%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
});
});
it('should transfer the correct amounts when consecutive calls are used to completely fill the left order', async () => {
// Create orders to match
const signedOrderLeft = await makerLeft.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(50, 18),
takerAssetAmount: toBaseUnitAmount(100, 18),
});
const signedOrderRight = await makerRight.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
});
// Match orders
const expectedTransferAmounts = {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(10, 16), // 10%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(10, 16), // 10%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
};
// prettier-ignore
const matchResults = await matchOrderTester.matchOrdersAndAssertEffectsAsync(
{
leftOrder: signedOrderLeft,
rightOrder: signedOrderRight,
},
expectedTransferAmounts,
matcher.address,
DeploymentManager.protocolFee.times(2),
false,
);
// Construct second right order
// Note: This order needs makerAssetAmount=90/takerAssetAmount=[anything <= 45] to fully fill the right order.
// However, we use 100/50 to ensure a partial fill as we want to go down the "left fill"
// branch in the contract twice for this test.
const signedOrderRight2 = await makerRight.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(100, 18),
takerAssetAmount: toBaseUnitAmount(50, 18),
});
// Match signedOrderLeft with signedOrderRight2
const expectedTransferAmounts2 = {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(45, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(90, 16), // 90% (10% paid earlier)
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(90, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(90, 16), // 90%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(90, 16), // 90% (10% paid earlier)
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(90, 16), // 90%
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
};
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
{
leftOrder: signedOrderLeft,
rightOrder: signedOrderRight2,
leftOrderTakerAssetFilledAmount: matchResults.orders.leftOrderTakerAssetFilledAmount,
},
expectedTransferAmounts2,
matcher.address,
DeploymentManager.protocolFee.times(2),
false,
);
});
it('should transfer the correct amounts when consecutive calls are used to completely fill the right order', async () => {
// Create orders to match
const signedOrderLeft = await makerLeft.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
});
const signedOrderRight = await makerRight.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(50, 18),
takerAssetAmount: toBaseUnitAmount(100, 18),
});
// Match orders
const expectedTransferAmounts = {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(10, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(2, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(4, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(4, 16), // 4%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(6, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(4, 16), // 4%
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
};
const matchResults = await matchOrderTester.matchOrdersAndAssertEffectsAsync(
{
leftOrder: signedOrderLeft,
rightOrder: signedOrderRight,
},
expectedTransferAmounts,
matcher.address,
DeploymentManager.protocolFee.times(2),
false,
);
// Create second left order
// Note: This order needs makerAssetAmount=96/takerAssetAmount=48 to fully fill the right order.
// However, we use 100/50 to ensure a partial fill as we want to go down the "right fill"
// branch in the contract twice for this test.
const signedOrderLeft2 = await makerLeft.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(100, 18),
takerAssetAmount: toBaseUnitAmount(50, 18),
});
// Match signedOrderLeft2 with signedOrderRight
const expectedTransferAmounts2 = {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(96, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(96, 16), // 96%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(48, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(96, 16), // 96%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(96, 16), // 96%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(96, 16), // 96%
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
};
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
{
leftOrder: signedOrderLeft2,
rightOrder: signedOrderRight,
rightOrderTakerAssetFilledAmount: matchResults.orders.rightOrderTakerAssetFilledAmount,
},
expectedTransferAmounts2,
matcher.address,
DeploymentManager.protocolFee.times(2),
false,
);
});
it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
feeRecipientAddress: feeRecipientLeft.address,
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
feeRecipientAddress: feeRecipientLeft.address,
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
});
});
it('should transfer the correct amounts if taker == leftMaker', async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
matcherAddress: makerLeft.address,
});
});
it('should transfer the correct amounts if taker == rightMaker', async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
matcherAddress: makerRight.address,
});
});
it('should transfer the correct amounts if taker == leftFeeRecipient', async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
matcherAddress: feeRecipientLeft.address,
});
});
it('should transfer the correct amounts if taker == rightFeeRecipient', async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
matcherAddress: feeRecipientRight.address,
});
});
it(`should transfer the correct amounts if leftMaker == leftFeeRecipient
&& rightMaker == rightFeeRecipient`, async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
feeRecipientAddress: makerLeft.address,
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
feeRecipientAddress: makerRight.address,
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
});
});
it(`should transfer the correct amounts if leftMaker == leftFeeRecipient
&& leftMakerFeeAsset == leftTakerAsset`, async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
makerFeeAssetData: makerAssetDataRight,
feeRecipientAddress: makerLeft.address,
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
});
});
it(`should transfer the correct amounts if rightMaker == rightFeeRecipient
&& rightMakerFeeAsset == rightTakerAsset`, async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
makerFeeAssetData: makerAssetDataLeft,
feeRecipientAddress: makerRight.address,
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
});
});
it(`should transfer the correct amounts if rightMaker == rightFeeRecipient && rightTakerAsset == rightMakerFeeAsset
&& leftMaker == leftFeeRecipient && leftTakerAsset == leftMakerFeeAsset`, async () => {
await testMatchOrdersAsync({
...defaultMatchOrdersArgs,
leftOrder: {
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
makerFeeAssetData: makerAssetDataRight,
feeRecipientAddress: makerLeft.address,
},
rightOrder: {
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
makerFeeAssetData: makerAssetDataRight,
feeRecipientAddress: makerRight.address,
},
expectedTransferAmounts: {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
});
});
it('should revert if left order is not fillable', async () => {
// Create orders to match
const signedOrderLeft = await makerLeft.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
});
const signedOrderRight = await makerRight.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
});
const orderHashHexLeft = orderHashUtils.getOrderHashHex(signedOrderLeft);
// Cancel left order
await makerLeft.cancelOrderAsync(signedOrderLeft);
// Match orders
const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHashHexLeft, OrderStatus.Cancelled);
const tx = deployment.exchange
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
.awaitTransactionSuccessAsync({ from: matcher.address });
return expect(tx).to.revertWith(expectedError);
});
it('should revert if right order is not fillable', async () => {
// Create orders to match
const signedOrderLeft = await makerLeft.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
});
const signedOrderRight = await makerRight.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
});
const orderHashHexRight = orderHashUtils.getOrderHashHex(signedOrderRight);
// Cancel right order
await makerRight.cancelOrderAsync(signedOrderRight);
// Match orders
const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHashHexRight, OrderStatus.Cancelled);
const tx = deployment.exchange
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
.awaitTransactionSuccessAsync({ from: matcher.address });
return expect(tx).to.revertWith(expectedError);
});
it('should revert if there is not a positive spread', async () => {
// Create orders to match
const signedOrderLeft = await makerLeft.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(100, 18),
});
const signedOrderRight = await makerRight.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(1, 18),
takerAssetAmount: toBaseUnitAmount(200, 18),
});
const orderHashHexLeft = orderHashUtils.getOrderHashHex(signedOrderLeft);
const orderHashHexRight = orderHashUtils.getOrderHashHex(signedOrderRight);
// Match orders
const expectedError = new ExchangeRevertErrors.NegativeSpreadError(orderHashHexLeft, orderHashHexRight);
const tx = deployment.exchange
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
.awaitTransactionSuccessAsync({ from: matcher.address });
return expect(tx).to.revertWith(expectedError);
});
it('should revert if the left maker asset is not equal to the right taker asset ', async () => {
// Create orders to match
const signedOrderLeft = await makerLeft.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
});
const signedOrderRight = await makerRight.signOrderAsync({
takerAssetData: encodeERC20AssetData(makerAssetRight.address),
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
});
// We are assuming assetData fields of the right order are the
// reverse of the left order, rather than checking equality. This
// saves a bunch of gas, but as a result if the assetData fields are
// off then the failure ends up happening at signature validation
const reconstructedOrderRight = {
...signedOrderRight,
takerAssetData: signedOrderLeft.makerAssetData,
};
const orderHashHex = orderHashUtils.getOrderHashHex(reconstructedOrderRight);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.BadOrderSignature,
orderHashHex,
signedOrderRight.makerAddress,
signedOrderRight.signature,
);
// Match orders
const tx = deployment.exchange
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
.awaitTransactionSuccessAsync({ from: matcher.address });
return expect(tx).to.revertWith(expectedError);
});
it('should revert if the right maker asset is not equal to the left taker asset', async () => {
// Create orders to match
const signedOrderLeft = await makerLeft.signOrderAsync({
takerAssetData: encodeERC20AssetData(makerAssetLeft.address),
makerAssetAmount: toBaseUnitAmount(5, 18),
takerAssetAmount: toBaseUnitAmount(10, 18),
});
const signedOrderRight = await makerRight.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(10, 18),
takerAssetAmount: toBaseUnitAmount(2, 18),
});
const reconstructedOrderRight = {
...signedOrderRight,
makerAssetData: signedOrderLeft.takerAssetData,
};
const orderHashHex = orderHashUtils.getOrderHashHex(reconstructedOrderRight);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.BadOrderSignature,
orderHashHex,
signedOrderRight.makerAddress,
signedOrderRight.signature,
);
// Match orders
const tx = deployment.exchange
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
.awaitTransactionSuccessAsync({ from: matcher.address });
return expect(tx).to.revertWith(expectedError);
});
});
});
// tslint:disable-line:max-file-line-count