import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import {
    artifacts as exchangeArtifacts,
    constants as exchangeConstants,
    ExchangeCancelEventArgs,
    ExchangeCancelUpToEventArgs,
    ExchangeContract,
    exchangeDataEncoder,
    ExchangeFillEventArgs,
    ExchangeFunctionName,
} from '@0x/contracts-exchange';
import {
    chaiSetup,
    constants,
    expectTransactionFailedAsync,
    getLatestBlockTimestampAsync,
    OrderFactory,
    provider,
    TransactionFactory,
    txDefaults,
    web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
import { RevertReason } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';

import { ApprovalFactory, artifacts, CoordinatorContract } from '../src';

chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
// tslint:disable:no-unnecessary-type-assertion
describe('Coordinator tests', () => {
    let chainId: number;
    let makerAddress: string;
    let owner: string;
    let takerAddress: string;
    let feeRecipientAddress: string;

    let erc20Proxy: ERC20ProxyContract;
    let erc20TokenA: DummyERC20TokenContract;
    let erc20TokenB: DummyERC20TokenContract;
    let makerFeeToken: DummyERC20TokenContract;
    let coordinatorContract: CoordinatorContract;
    let exchange: ExchangeContract;

    let erc20Wrapper: ERC20Wrapper;
    let orderFactory: OrderFactory;
    let takerTransactionFactory: TransactionFactory;
    let makerTransactionFactory: TransactionFactory;
    let approvalFactory: ApprovalFactory;

    before(async () => {
        await blockchainLifecycle.startAsync();
    });
    after(async () => {
        await blockchainLifecycle.revertAsync();
    });
    before(async () => {
        chainId = await providerUtils.getChainIdAsync(provider);
        const accounts = await web3Wrapper.getAvailableAddressesAsync();
        const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts.slice(0, 4));

        erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
        erc20Proxy = await erc20Wrapper.deployProxyAsync();
        const numDummyErc20ToDeploy = 3;
        [erc20TokenA, erc20TokenB, makerFeeToken] = await erc20Wrapper.deployDummyTokensAsync(
            numDummyErc20ToDeploy,
            constants.DUMMY_TOKEN_DECIMALS,
        );
        await erc20Wrapper.setBalancesAndAllowancesAsync();

        exchange = await ExchangeContract.deployFrom0xArtifactAsync(
            exchangeArtifacts.Exchange,
            provider,
            txDefaults,
            new BigNumber(chainId),
        );

        await web3Wrapper.awaitTransactionSuccessAsync(
            await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }),
            constants.AWAIT_TRANSACTION_MINED_MS,
        );

        await web3Wrapper.awaitTransactionSuccessAsync(
            await exchange.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }),
            constants.AWAIT_TRANSACTION_MINED_MS,
        );

        coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
            artifacts.Coordinator,
            provider,
            txDefaults,
            artifacts,
            exchange.address,
            new BigNumber(chainId),
        );

        // Configure order defaults
        const defaultOrderParams = {
            ...constants.STATIC_ORDER_PARAMS,
            senderAddress: coordinatorContract.address,
            makerAddress,
            feeRecipientAddress,
            makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
            takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
            makerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address),
            takerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address),
            exchangeAddress: exchange.address,
            chainId,
        };
        const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
        const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
        const feeRecipientPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(feeRecipientAddress)];
        orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
        makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address, chainId);
        takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address, chainId);
        approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address);
    });
    beforeEach(async () => {
        await blockchainLifecycle.startAsync();
    });
    afterEach(async () => {
        await blockchainLifecycle.revertAsync();
    });

    describe('single order fills', () => {
        for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
            it(`${fnName} should fill the order with a signed approval`, async () => {
                const orders = [await orderFactory.newSignedOrderAsync()];
                const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
                const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
                const currentTimestamp = await getLatestBlockTimestampAsync();
                const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
                const approval = approvalFactory.newSignedApproval(
                    transaction,
                    takerAddress,
                    approvalExpirationTimeSeconds,
                );
                const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
                    await coordinatorContract.executeTransaction.sendTransactionAsync(
                        transaction,
                        takerAddress,
                        transaction.signature,
                        [approvalExpirationTimeSeconds],
                        [approval.signature],
                        { from: takerAddress },
                    ),
                    constants.AWAIT_TRANSACTION_MINED_MS,
                );
                const fillLogs = transactionReceipt.logs.filter(
                    log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
                );
                expect(fillLogs.length).to.eq(1);
                const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
                expect(fillLogArgs.makerAddress).to.eq(makerAddress);
                expect(fillLogArgs.takerAddress).to.eq(takerAddress);
                expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
                expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
                expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
                expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
                expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
                expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
                expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
                expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
                expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
            });
            it(`${fnName} should fill the order if called by approver`, async () => {
                const orders = [await orderFactory.newSignedOrderAsync()];
                const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
                const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
                const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
                    await coordinatorContract.executeTransaction.sendTransactionAsync(
                        transaction,
                        feeRecipientAddress,
                        transaction.signature,
                        [],
                        [],
                        { from: feeRecipientAddress },
                    ),
                    constants.AWAIT_TRANSACTION_MINED_MS,
                );
                const fillLogs = transactionReceipt.logs.filter(
                    log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
                );
                expect(fillLogs.length).to.eq(1);
                const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
                expect(fillLogArgs.makerAddress).to.eq(makerAddress);
                expect(fillLogArgs.takerAddress).to.eq(takerAddress);
                expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
                expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
                expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
                expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
                expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
                expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
                expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
                expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
                expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
            });
            it(`${fnName} should revert with no approval signature`, async () => {
                const orders = [await orderFactory.newSignedOrderAsync()];
                const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
                const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
                await expectTransactionFailedAsync(
                    coordinatorContract.executeTransaction.sendTransactionAsync(
                        transaction,
                        takerAddress,
                        transaction.signature,
                        [],
                        [],
                        {
                            from: takerAddress,
                            gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
                        },
                    ),
                    RevertReason.InvalidApprovalSignature,
                );
            });
            it(`${fnName} should revert with an invalid approval signature`, async () => {
                const orders = [await orderFactory.newSignedOrderAsync()];
                const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
                const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
                const currentTimestamp = await getLatestBlockTimestampAsync();
                const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
                const approval = approvalFactory.newSignedApproval(
                    transaction,
                    takerAddress,
                    approvalExpirationTimeSeconds,
                );
                const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
                await expectTransactionFailedAsync(
                    coordinatorContract.executeTransaction.sendTransactionAsync(
                        transaction,
                        takerAddress,
                        transaction.signature,
                        [approvalExpirationTimeSeconds],
                        [signature],
                        { from: takerAddress },
                    ),
                    RevertReason.InvalidApprovalSignature,
                );
            });
            it(`${fnName} should revert with an expired approval`, async () => {
                const orders = [await orderFactory.newSignedOrderAsync()];
                const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
                const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
                const currentTimestamp = await getLatestBlockTimestampAsync();
                const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
                const approval = approvalFactory.newSignedApproval(
                    transaction,
                    takerAddress,
                    approvalExpirationTimeSeconds,
                );
                await expectTransactionFailedAsync(
                    coordinatorContract.executeTransaction.sendTransactionAsync(
                        transaction,
                        takerAddress,
                        transaction.signature,
                        [approvalExpirationTimeSeconds],
                        [approval.signature],
                        { from: takerAddress },
                    ),
                    RevertReason.ApprovalExpired,
                );
            });
            it(`${fnName} should revert if not called by tx signer or approver`, async () => {
                const orders = [await orderFactory.newSignedOrderAsync()];
                const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
                const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
                const currentTimestamp = await getLatestBlockTimestampAsync();
                const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
                const approval = approvalFactory.newSignedApproval(
                    transaction,
                    takerAddress,
                    approvalExpirationTimeSeconds,
                );
                await expectTransactionFailedAsync(
                    coordinatorContract.executeTransaction.sendTransactionAsync(
                        transaction,
                        takerAddress,
                        transaction.signature,
                        [approvalExpirationTimeSeconds],
                        [approval.signature],
                        { from: owner },
                    ),
                    RevertReason.InvalidOrigin,
                );
            });
        }
    });
    describe('batch order fills', () => {
        for (const fnName of [...exchangeConstants.MARKET_FILL_FN_NAMES, ...exchangeConstants.BATCH_FILL_FN_NAMES]) {
            it(`${fnName} should fill the orders with a signed approval`, async () => {
                const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
                const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
                const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
                const currentTimestamp = await getLatestBlockTimestampAsync();
                const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
                const approval = approvalFactory.newSignedApproval(
                    transaction,
                    takerAddress,
                    approvalExpirationTimeSeconds,
                );
                const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
                    await coordinatorContract.executeTransaction.sendTransactionAsync(
                        transaction,
                        takerAddress,
                        transaction.signature,
                        [approvalExpirationTimeSeconds],
                        [approval.signature],
                        { from: takerAddress, gas: constants.MAX_EXECUTE_TRANSACTION_GAS },
                    ),
                    constants.AWAIT_TRANSACTION_MINED_MS,
                );
                const fillLogs = transactionReceipt.logs.filter(
                    log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
                );
                expect(fillLogs.length).to.eq(orders.length);
                orders.forEach((order, index) => {
                    const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
                    expect(fillLogArgs.makerAddress).to.eq(makerAddress);
                    expect(fillLogArgs.takerAddress).to.eq(takerAddress);
                    expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
                    expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
                    expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
                    expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
                    expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
                    expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
                    expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
                    expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
                    expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
                });
            });
            it(`${fnName} should fill the orders if called by approver`, async () => {
                const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
                const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
                const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
                const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
                    await coordinatorContract.executeTransaction.sendTransactionAsync(
                        transaction,
                        feeRecipientAddress,
                        transaction.signature,
                        [],
                        [],
                        { from: feeRecipientAddress, gas: constants.MAX_EXECUTE_TRANSACTION_GAS },
                    ),
                    constants.AWAIT_TRANSACTION_MINED_MS,
                );
                const fillLogs = transactionReceipt.logs.filter(
                    log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
                );
                expect(fillLogs.length).to.eq(orders.length);
                orders.forEach((order, index) => {
                    const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
                    expect(fillLogArgs.makerAddress).to.eq(makerAddress);
                    expect(fillLogArgs.takerAddress).to.eq(takerAddress);
                    expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
                    expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
                    expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
                    expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
                    expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
                    expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
                    expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
                    expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
                    expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
                });
            });
            it(`${fnName} should revert with an invalid approval signature`, async () => {
                const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
                const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
                const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
                const currentTimestamp = await getLatestBlockTimestampAsync();
                const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
                const approval = approvalFactory.newSignedApproval(
                    transaction,
                    takerAddress,
                    approvalExpirationTimeSeconds,
                );
                const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
                await expectTransactionFailedAsync(
                    coordinatorContract.executeTransaction.sendTransactionAsync(
                        transaction,
                        takerAddress,
                        transaction.signature,
                        [approvalExpirationTimeSeconds],
                        [signature],
                        { from: takerAddress },
                    ),
                    RevertReason.InvalidApprovalSignature,
                );
            });
            it(`${fnName} should revert with an expired approval`, async () => {
                const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
                const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
                const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
                const currentTimestamp = await getLatestBlockTimestampAsync();
                const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
                const approval = approvalFactory.newSignedApproval(
                    transaction,
                    takerAddress,
                    approvalExpirationTimeSeconds,
                );
                await expectTransactionFailedAsync(
                    coordinatorContract.executeTransaction.sendTransactionAsync(
                        transaction,
                        takerAddress,
                        transaction.signature,
                        [approvalExpirationTimeSeconds],
                        [approval.signature],
                        { from: takerAddress },
                    ),
                    RevertReason.ApprovalExpired,
                );
            });
            it(`${fnName} should revert if not called by tx signer or approver`, async () => {
                const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
                const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
                const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
                const currentTimestamp = await getLatestBlockTimestampAsync();
                const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
                const approval = approvalFactory.newSignedApproval(
                    transaction,
                    takerAddress,
                    approvalExpirationTimeSeconds,
                );
                await expectTransactionFailedAsync(
                    coordinatorContract.executeTransaction.sendTransactionAsync(
                        transaction,
                        takerAddress,
                        transaction.signature,
                        [approvalExpirationTimeSeconds],
                        [approval.signature],
                        { from: owner },
                    ),
                    RevertReason.InvalidOrigin,
                );
            });
        }
    });
    describe('cancels', () => {
        it('cancelOrder call should be successful without an approval', async () => {
            const orders = [await orderFactory.newSignedOrderAsync()];
            const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders);
            const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
            const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
                await coordinatorContract.executeTransaction.sendTransactionAsync(
                    transaction,
                    makerAddress,
                    transaction.signature,
                    [],
                    [],
                    {
                        from: makerAddress,
                    },
                ),
            );
            const cancelLogs = transactionReceipt.logs.filter(
                log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
            );
            expect(cancelLogs.length).to.eq(1);
            const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
            expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
            expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
            expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
            expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
            expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
            expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
        });
        it('batchCancelOrders call should be successful without an approval', async () => {
            const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
            const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchCancelOrders, orders);
            const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
            const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
                await coordinatorContract.executeTransaction.sendTransactionAsync(
                    transaction,
                    makerAddress,
                    transaction.signature,
                    [],
                    [],
                    {
                        from: makerAddress,
                    },
                ),
            );
            const cancelLogs = transactionReceipt.logs.filter(
                log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
            );
            expect(cancelLogs.length).to.eq(orders.length);
            orders.forEach((order, index) => {
                const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
                expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
                expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
                expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
                expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
                expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
                expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
            });
        });
        it('cancelOrdersUpTo call should be successful without an approval', async () => {
            const targetEpoch = constants.ZERO_AMOUNT;
            const data = exchange.cancelOrdersUpTo.getABIEncodedTransactionData(targetEpoch);
            const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
            const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
                await coordinatorContract.executeTransaction.sendTransactionAsync(
                    transaction,
                    makerAddress,
                    transaction.signature,
                    [],
                    [],
                    {
                        from: makerAddress,
                    },
                ),
            );
            const cancelLogs = transactionReceipt.logs.filter(
                log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo',
            );
            expect(cancelLogs.length).to.eq(1);
            const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args;
            expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
            expect(cancelLogArgs.orderSenderAddress).to.eq(coordinatorContract.address);
            expect(cancelLogArgs.orderEpoch).to.bignumber.eq(targetEpoch.plus(1));
        });
    });
});
// tslint:disable:max-file-line-count
