* generate wrappers in @0x/contract-wrappers and delete abi-gen-wrappers * trim exports from contract-wrappers * separate contract-wrappers tests to get rid of dependency cycle * remove dummy token contracts * temporarily skip coordinator test until we can upgrade coordinator server
		
			
				
	
	
		
			635 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			635 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import { ContractAddresses, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
 | 
						|
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
 | 
						|
import { ExchangeContract } from '@0x/contracts-exchange';
 | 
						|
import { blockchainTests, constants, expect, OrderFactory } from '@0x/contracts-test-utils';
 | 
						|
import { defaultOrmConfig, getAppAsync } from '@0x/coordinator-server';
 | 
						|
import { devConstants, tokenUtils } from '@0x/dev-utils';
 | 
						|
import { runMigrationsOnceAsync } from '@0x/migrations';
 | 
						|
import { SignedOrder } from '@0x/types';
 | 
						|
import { BigNumber, fetchAsync, logUtils } from '@0x/utils';
 | 
						|
import * as nock from 'nock';
 | 
						|
 | 
						|
import { CoordinatorClient, CoordinatorRegistryContract, CoordinatorServerErrorMsg } from '@0x/contracts-coordinator';
 | 
						|
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
 | 
						|
 | 
						|
const coordinatorPort = '3000';
 | 
						|
const anotherCoordinatorPort = '4000';
 | 
						|
const coordinatorEndpoint = 'http://localhost:';
 | 
						|
 | 
						|
const DEFAULT_PROTOCOL_FEE_MULTIPLIER = new BigNumber(150000);
 | 
						|
 | 
						|
// tslint:disable:custom-no-magic-numbers
 | 
						|
blockchainTests.skip('Coordinator Client', env => {
 | 
						|
    const takerTokenFillAmount = new BigNumber(0);
 | 
						|
    const chainId = 1337;
 | 
						|
    const assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
 | 
						|
 | 
						|
    let contractAddresses: ContractAddresses;
 | 
						|
    let coordinatorRegistry: CoordinatorRegistryContract;
 | 
						|
    let coordinatorClient: CoordinatorClient;
 | 
						|
    let orderFactory: OrderFactory;
 | 
						|
    let userAddresses: string[];
 | 
						|
    let makerAddress: string;
 | 
						|
    let takerAddress: string;
 | 
						|
    let feeRecipientAddressOne: string;
 | 
						|
    let feeRecipientAddressTwo: string;
 | 
						|
    let feeRecipientAddressThree: string;
 | 
						|
    let feeRecipientAddressFour: string;
 | 
						|
    let makerAssetData: string;
 | 
						|
    let takerAssetData: string;
 | 
						|
    let feeAssetData: string;
 | 
						|
 | 
						|
    let makerToken: DummyERC20TokenContract;
 | 
						|
    let takerToken: DummyERC20TokenContract;
 | 
						|
    let txHash: string;
 | 
						|
    let signedOrder: SignedOrder;
 | 
						|
    let anotherSignedOrder: SignedOrder;
 | 
						|
    let signedOrderWithDifferentFeeRecipient: SignedOrder;
 | 
						|
    let signedOrderWithDifferentCoordinatorOperator: SignedOrder;
 | 
						|
 | 
						|
    // for testing server error responses
 | 
						|
    let serverValidationError: any;
 | 
						|
 | 
						|
    before(async () => {
 | 
						|
        contractAddresses = await runMigrationsOnceAsync(env.provider, {
 | 
						|
            gas: devConstants.GAS_LIMIT,
 | 
						|
            from: devConstants.TESTRPC_FIRST_ADDRESS,
 | 
						|
        });
 | 
						|
 | 
						|
        coordinatorClient = new CoordinatorClient(
 | 
						|
            contractAddresses.coordinator,
 | 
						|
            env.provider,
 | 
						|
            chainId,
 | 
						|
            {}, // txDefaults
 | 
						|
            contractAddresses.exchange,
 | 
						|
            contractAddresses.coordinatorRegistry,
 | 
						|
        );
 | 
						|
        coordinatorRegistry = new CoordinatorRegistryContract(contractAddresses.coordinatorRegistry, env.provider);
 | 
						|
        userAddresses = await env.web3Wrapper.getAvailableAddressesAsync();
 | 
						|
        [
 | 
						|
            ,
 | 
						|
            makerAddress,
 | 
						|
            takerAddress,
 | 
						|
            feeRecipientAddressOne,
 | 
						|
            feeRecipientAddressTwo,
 | 
						|
            feeRecipientAddressThree,
 | 
						|
            feeRecipientAddressFour,
 | 
						|
        ] = userAddresses.slice(0, 7);
 | 
						|
 | 
						|
        // declare encoded asset data
 | 
						|
        const [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
 | 
						|
        const feeTokenAddress = contractAddresses.zrxToken;
 | 
						|
        [makerAssetData, takerAssetData, feeAssetData] = [
 | 
						|
            assetDataEncoder.ERC20Token(makerTokenAddress).getABIEncodedTransactionData(),
 | 
						|
            assetDataEncoder.ERC20Token(takerTokenAddress).getABIEncodedTransactionData(),
 | 
						|
            assetDataEncoder.ERC20Token(feeTokenAddress).getABIEncodedTransactionData(),
 | 
						|
        ];
 | 
						|
 | 
						|
        // set initial balances
 | 
						|
        makerToken = new DummyERC20TokenContract(makerTokenAddress, env.provider);
 | 
						|
        takerToken = new DummyERC20TokenContract(takerTokenAddress, env.provider);
 | 
						|
 | 
						|
        // Configure order defaults
 | 
						|
        const defaultOrderParams = {
 | 
						|
            ...constants.STATIC_ORDER_PARAMS,
 | 
						|
            makerAddress,
 | 
						|
            feeRecipientAddress: feeRecipientAddressOne,
 | 
						|
            makerAssetData,
 | 
						|
            takerAssetData,
 | 
						|
            makerFeeAssetData: feeAssetData,
 | 
						|
            takerFeeAssetData: feeAssetData,
 | 
						|
            senderAddress: contractAddresses.coordinator,
 | 
						|
            exchangeAddress: contractAddresses.exchange,
 | 
						|
            chainId,
 | 
						|
        };
 | 
						|
        const privateKey = constants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
 | 
						|
        orderFactory = new OrderFactory(privateKey, defaultOrderParams);
 | 
						|
 | 
						|
        // configure mock coordinator servers
 | 
						|
        const coordinatorServerConfigs = {
 | 
						|
            HTTP_PORT: 3000, // Only used in default instantiation in 0x-coordinator-server/server.js; not used here
 | 
						|
            CHAIN_ID_TO_SETTINGS: {
 | 
						|
                // TODO: change to CHAIN_ID_TO_SETTINGS when @0x/coordinator-server is ready
 | 
						|
                [chainId]: {
 | 
						|
                    FEE_RECIPIENTS: [feeRecipientAddressOne, feeRecipientAddressTwo, feeRecipientAddressThree].map(
 | 
						|
                        address => {
 | 
						|
                            return {
 | 
						|
                                ADDRESS: address,
 | 
						|
                                PRIVATE_KEY: constants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(address)].toString(
 | 
						|
                                    'hex',
 | 
						|
                                ),
 | 
						|
                            };
 | 
						|
                        },
 | 
						|
                    ),
 | 
						|
                    // Ethereum RPC url, only used in the default instantiation in 0x-coordinator-server/server.js
 | 
						|
                    // Not used here when instantiating with the imported app
 | 
						|
                    RPC_URL: 'http://ignore',
 | 
						|
                },
 | 
						|
            },
 | 
						|
            NETWORK_ID_TO_CONTRACT_ADDRESSES: {
 | 
						|
                // TODO: change to CHAIN_ID_TO_CONTRACT_ADDRESSES when @0x/coordinator-server is ready
 | 
						|
                [chainId]: getContractAddressesForChainOrThrow(chainId),
 | 
						|
            },
 | 
						|
            // Optional selective delay on fill requests
 | 
						|
            SELECTIVE_DELAY_MS: 0,
 | 
						|
            EXPIRATION_DURATION_SECONDS: 60, // 1 minute
 | 
						|
        };
 | 
						|
 | 
						|
        // start coordinator servers
 | 
						|
        const serverScenarios: Array<[string, string]> = [
 | 
						|
            ['coord_server_1', coordinatorPort],
 | 
						|
            ['coord_server_2', anotherCoordinatorPort],
 | 
						|
        ];
 | 
						|
        await Promise.all(
 | 
						|
            serverScenarios.map(async ([name, port]) => {
 | 
						|
                const app = await getAppAsync(
 | 
						|
                    {
 | 
						|
                        [chainId]: env.provider,
 | 
						|
                    },
 | 
						|
                    coordinatorServerConfigs,
 | 
						|
                    {
 | 
						|
                        name,
 | 
						|
                        type: 'sqlite',
 | 
						|
                        database: ':memory:',
 | 
						|
                        entities: defaultOrmConfig.entities,
 | 
						|
                        cli: defaultOrmConfig.cli,
 | 
						|
                        logging: defaultOrmConfig.logging,
 | 
						|
                        synchronize: defaultOrmConfig.synchronize,
 | 
						|
                    },
 | 
						|
                );
 | 
						|
                app.listen(port, () => {
 | 
						|
                    logUtils.log(`Coordinator SERVER API (HTTP) listening on port ${port}!`);
 | 
						|
                });
 | 
						|
                return app;
 | 
						|
            }),
 | 
						|
        );
 | 
						|
 | 
						|
        // register coordinator servers
 | 
						|
        [
 | 
						|
            [feeRecipientAddressOne, coordinatorPort],
 | 
						|
            [feeRecipientAddressTwo, coordinatorPort],
 | 
						|
            [feeRecipientAddressThree, anotherCoordinatorPort],
 | 
						|
        ].forEach(async ([address, port]) => {
 | 
						|
            await coordinatorRegistry
 | 
						|
                .setCoordinatorEndpoint(`${coordinatorEndpoint}${port}`)
 | 
						|
                .awaitTransactionSuccessAsync(
 | 
						|
                    { from: address },
 | 
						|
                    { pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS },
 | 
						|
                );
 | 
						|
        });
 | 
						|
    });
 | 
						|
    beforeEach(async () => {
 | 
						|
        signedOrder = await orderFactory.newSignedOrderAsync();
 | 
						|
        anotherSignedOrder = await orderFactory.newSignedOrderAsync();
 | 
						|
        signedOrderWithDifferentFeeRecipient = await orderFactory.newSignedOrderAsync({
 | 
						|
            feeRecipientAddress: feeRecipientAddressTwo,
 | 
						|
        });
 | 
						|
        signedOrderWithDifferentCoordinatorOperator = await orderFactory.newSignedOrderAsync({
 | 
						|
            feeRecipientAddress: feeRecipientAddressThree,
 | 
						|
        });
 | 
						|
        makerToken.setBalance(makerAddress, constants.INITIAL_ERC20_BALANCE);
 | 
						|
        takerToken.setBalance(takerAddress, constants.INITIAL_ERC20_BALANCE);
 | 
						|
    });
 | 
						|
    describe('test setup', () => {
 | 
						|
        it('should have coordinator registry which returns an endpoint', async () => {
 | 
						|
            const setCoordinatorEndpoint = await coordinatorRegistry
 | 
						|
                .getCoordinatorEndpoint(feeRecipientAddressOne)
 | 
						|
                .callAsync();
 | 
						|
            const anotherSetCoordinatorEndpoint = await coordinatorRegistry
 | 
						|
                .getCoordinatorEndpoint(feeRecipientAddressThree)
 | 
						|
                .callAsync();
 | 
						|
            expect(setCoordinatorEndpoint).to.equal(`${coordinatorEndpoint}${coordinatorPort}`);
 | 
						|
            expect(anotherSetCoordinatorEndpoint).to.equal(`${coordinatorEndpoint}${anotherCoordinatorPort}`);
 | 
						|
        });
 | 
						|
        it('should have coordinator server endpoints which respond to pings', async () => {
 | 
						|
            let result = await fetchAsync(`${coordinatorEndpoint}${coordinatorPort}/v2/ping`);
 | 
						|
            expect(result.status).to.equal(200);
 | 
						|
            expect(await result.text()).to.equal('pong');
 | 
						|
 | 
						|
            result = await fetchAsync(`${coordinatorEndpoint}${anotherCoordinatorPort}/v2/ping`);
 | 
						|
            expect(result.status).to.equal(200);
 | 
						|
            expect(await result.text()).to.equal('pong');
 | 
						|
        });
 | 
						|
    });
 | 
						|
    // fill handling is the same for all fill methods so we can test them all through the fillOrder and batchFillOrders interfaces
 | 
						|
    describe('#fillOrderAsync', () => {
 | 
						|
        it('should fill a valid order', async () => {
 | 
						|
            txHash = await coordinatorClient.fillOrderAsync(
 | 
						|
                signedOrder,
 | 
						|
                takerTokenFillAmount,
 | 
						|
                signedOrder.signature,
 | 
						|
                { from: takerAddress },
 | 
						|
                { shouldValidate: true },
 | 
						|
            );
 | 
						|
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
 | 
						|
        });
 | 
						|
    });
 | 
						|
    describe('#batchFillOrdersAsync', () => {
 | 
						|
        it('should fill a batch of valid orders', async () => {
 | 
						|
            const signedOrders = [signedOrder, anotherSignedOrder];
 | 
						|
            const takerAssetFillAmounts = Array(2).fill(takerTokenFillAmount);
 | 
						|
            txHash = await coordinatorClient.batchFillOrdersAsync(
 | 
						|
                signedOrders,
 | 
						|
                takerAssetFillAmounts,
 | 
						|
                signedOrders.map(o => o.signature),
 | 
						|
                { from: takerAddress, value: DEFAULT_PROTOCOL_FEE_MULTIPLIER.times(signedOrders.length) },
 | 
						|
            );
 | 
						|
 | 
						|
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
 | 
						|
        });
 | 
						|
        it('should fill a batch of orders with different feeRecipientAddresses with the same coordinator server', async () => {
 | 
						|
            const signedOrders = [signedOrder, anotherSignedOrder, signedOrderWithDifferentFeeRecipient];
 | 
						|
            const takerAssetFillAmounts = Array(3).fill(takerTokenFillAmount);
 | 
						|
            txHash = await coordinatorClient.batchFillOrdersAsync(
 | 
						|
                signedOrders,
 | 
						|
                takerAssetFillAmounts,
 | 
						|
                signedOrders.map(o => o.signature),
 | 
						|
                { from: takerAddress, value: DEFAULT_PROTOCOL_FEE_MULTIPLIER.times(signedOrders.length) },
 | 
						|
            );
 | 
						|
 | 
						|
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
 | 
						|
        });
 | 
						|
        // coordinator-server, as currently implemented, shares a singleton database connection across
 | 
						|
        // all instantiations. Making the request to two different mocked server endpoints still hits the
 | 
						|
        // same database and fails because of the uniqueness constraint on transactions in the database.
 | 
						|
        it.skip('should fill a batch of orders with different feeRecipientAddresses with different coordinator servers', async () => {
 | 
						|
            const signedOrders = [
 | 
						|
                signedOrder,
 | 
						|
                anotherSignedOrder,
 | 
						|
                signedOrderWithDifferentFeeRecipient,
 | 
						|
                signedOrderWithDifferentCoordinatorOperator,
 | 
						|
            ];
 | 
						|
            const takerAssetFillAmounts = Array(4).fill(takerTokenFillAmount);
 | 
						|
            txHash = await coordinatorClient.batchFillOrdersAsync(
 | 
						|
                signedOrders,
 | 
						|
                takerAssetFillAmounts,
 | 
						|
                signedOrders.map(o => o.signature),
 | 
						|
                { from: takerAddress },
 | 
						|
            );
 | 
						|
 | 
						|
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should fill a batch of mixed coordinator and non-coordinator orders', async () => {
 | 
						|
            const nonCoordinatorOrder = await orderFactory.newSignedOrderAsync({
 | 
						|
                senderAddress: constants.NULL_ADDRESS,
 | 
						|
            });
 | 
						|
            const signedOrders = [signedOrder, nonCoordinatorOrder];
 | 
						|
            const takerAssetFillAmounts = Array(2).fill(takerTokenFillAmount);
 | 
						|
            txHash = await coordinatorClient.batchFillOrdersAsync(
 | 
						|
                signedOrders,
 | 
						|
                takerAssetFillAmounts,
 | 
						|
                signedOrders.map(o => o.signature),
 | 
						|
                { from: takerAddress, value: DEFAULT_PROTOCOL_FEE_MULTIPLIER.multipliedBy(signedOrders.length) },
 | 
						|
            );
 | 
						|
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
 | 
						|
        });
 | 
						|
    });
 | 
						|
    describe('#softCancelAsync, #batchSoftCancelAsync', () => {
 | 
						|
        it('should soft cancel a valid order', async () => {
 | 
						|
            const response = await coordinatorClient.softCancelAsync(signedOrder);
 | 
						|
            expect(response.outstandingFillSignatures).to.have.lengthOf(0);
 | 
						|
            expect(response.cancellationSignatures).to.have.lengthOf(1);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should soft cancel a batch of valid orders', async () => {
 | 
						|
            const orders = [signedOrder, anotherSignedOrder];
 | 
						|
            const response = await coordinatorClient.batchSoftCancelAsync(orders);
 | 
						|
            expect(response).to.have.lengthOf(1);
 | 
						|
            expect(response[0].outstandingFillSignatures).to.have.lengthOf(0);
 | 
						|
            expect(response[0].cancellationSignatures).to.have.lengthOf(1);
 | 
						|
        });
 | 
						|
        it('should soft cancel a batch of orders with different feeRecipientAddresses', async () => {
 | 
						|
            const orders = [signedOrder, anotherSignedOrder, signedOrderWithDifferentFeeRecipient];
 | 
						|
            const response = await coordinatorClient.batchSoftCancelAsync(orders);
 | 
						|
            expect(response).to.have.lengthOf(1);
 | 
						|
            expect(response[0].outstandingFillSignatures).to.have.lengthOf(0);
 | 
						|
            expect(response[0].cancellationSignatures).to.have.lengthOf(2);
 | 
						|
        });
 | 
						|
        it('should soft cancel a batch of orders with different coordinatorOperator and concatenate responses', async () => {
 | 
						|
            const orders = [
 | 
						|
                signedOrder,
 | 
						|
                anotherSignedOrder,
 | 
						|
                signedOrderWithDifferentFeeRecipient,
 | 
						|
                signedOrderWithDifferentCoordinatorOperator,
 | 
						|
            ];
 | 
						|
            const response = await coordinatorClient.batchSoftCancelAsync(orders);
 | 
						|
            expect(response).to.have.lengthOf(2);
 | 
						|
            expect(response[0].outstandingFillSignatures).to.have.lengthOf(0);
 | 
						|
            expect(response[0].cancellationSignatures).to.have.lengthOf(3);
 | 
						|
            expect(response[1].cancellationSignatures).to.have.lengthOf(3); // both coordinator servers support the same feeRecipients
 | 
						|
        });
 | 
						|
    });
 | 
						|
    describe('#hardCancelOrderAsync, #batchHardCancelOrdersAsync, #cancelOrdersUpToAsync', () => {
 | 
						|
        it('should hard cancel a valid order', async () => {
 | 
						|
            txHash = await coordinatorClient.hardCancelOrderAsync(signedOrder, { from: makerAddress });
 | 
						|
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should hard cancel a batch of valid orders', async () => {
 | 
						|
            const orders = [signedOrder, anotherSignedOrder];
 | 
						|
            txHash = await coordinatorClient.batchHardCancelOrdersAsync(orders, { from: makerAddress });
 | 
						|
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
 | 
						|
        });
 | 
						|
        it('should hard cancel orders up to target order epoch', async () => {
 | 
						|
            const targetOrderEpoch = new BigNumber(42);
 | 
						|
            txHash = await coordinatorClient.hardCancelOrdersUpToAsync(targetOrderEpoch, { from: makerAddress });
 | 
						|
 | 
						|
            await env.web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
 | 
						|
 | 
						|
            const exchange = new ExchangeContract(contractAddresses.exchange, env.provider);
 | 
						|
            const orderEpoch = await exchange.orderEpoch(makerAddress, contractAddresses.coordinator).callAsync();
 | 
						|
            expect(orderEpoch).to.be.bignumber.equal(targetOrderEpoch.plus(1));
 | 
						|
        });
 | 
						|
    });
 | 
						|
    describe('coordinator edge cases', () => {
 | 
						|
        let badOrder: SignedOrder;
 | 
						|
        beforeEach('setup order with non-registered feeRecipient', async () => {
 | 
						|
            badOrder = await orderFactory.newSignedOrderAsync({
 | 
						|
                feeRecipientAddress: feeRecipientAddressFour,
 | 
						|
            });
 | 
						|
        });
 | 
						|
        it('should throw error when feeRecipientAddress is not in registry', async () => {
 | 
						|
            await expect(
 | 
						|
                coordinatorClient.fillOrderAsync(badOrder, takerTokenFillAmount, badOrder.signature, {
 | 
						|
                    from: takerAddress,
 | 
						|
                }),
 | 
						|
            ).to.eventually.be.rejected();
 | 
						|
        });
 | 
						|
        it('should throw informative error when coordinator endpoint does not work', async () => {
 | 
						|
            await env.web3Wrapper.awaitTransactionSuccessAsync(
 | 
						|
                await coordinatorRegistry.setCoordinatorEndpoint('http://badUri.com').sendTransactionAsync({
 | 
						|
                    from: feeRecipientAddressFour,
 | 
						|
                }),
 | 
						|
                constants.AWAIT_TRANSACTION_MINED_MS,
 | 
						|
            );
 | 
						|
            await expect(
 | 
						|
                coordinatorClient.fillOrderAsync(badOrder, takerTokenFillAmount, badOrder.signature, {
 | 
						|
                    from: takerAddress,
 | 
						|
                }),
 | 
						|
            ).to.eventually.be.rejectedWith(CoordinatorServerErrorMsg.FillFailed);
 | 
						|
        });
 | 
						|
    });
 | 
						|
    describe('coordinator server errors', () => {
 | 
						|
        serverValidationError = {
 | 
						|
            code: 1004,
 | 
						|
            reason: '',
 | 
						|
            validationErrors: [
 | 
						|
                {
 | 
						|
                    field: 'signedTransaction',
 | 
						|
                    code: 1004,
 | 
						|
                    reason:
 | 
						|
                        'A transaction can only be approved once. To request approval to perform the same actions, generate and sign an identical transaction with a different salt value.',
 | 
						|
                },
 | 
						|
            ],
 | 
						|
        };
 | 
						|
        beforeEach(async () => {
 | 
						|
            nock(`${coordinatorEndpoint}${coordinatorPort}`)
 | 
						|
                .post('/v2/request_transaction', () => true)
 | 
						|
                .query({
 | 
						|
                    chainId: 1337,
 | 
						|
                })
 | 
						|
                .reply(400, serverValidationError);
 | 
						|
        });
 | 
						|
        afterEach(async () => {
 | 
						|
            nock.cleanAll();
 | 
						|
        });
 | 
						|
        it('should throw error when softCancel fails', async () => {
 | 
						|
            await coordinatorClient
 | 
						|
                .softCancelAsync(signedOrder)
 | 
						|
                .then(res => {
 | 
						|
                    expect(res).to.be.undefined();
 | 
						|
                })
 | 
						|
                .catch(err => {
 | 
						|
                    expect(err.message).equal(CoordinatorServerErrorMsg.CancellationFailed);
 | 
						|
                    expect(err.approvedOrders).to.be.empty('array');
 | 
						|
                    expect(err.cancellations).to.be.empty('array');
 | 
						|
 | 
						|
                    const errorBody = err.errors[0];
 | 
						|
                    expect(errorBody.isError).to.be.true();
 | 
						|
                    expect(errorBody.status).to.equal(400);
 | 
						|
                    expect(errorBody.error).to.deep.equal(serverValidationError);
 | 
						|
                    expect(errorBody.orders).to.deep.equal([signedOrder]);
 | 
						|
                    expect(errorBody.coordinatorOperator).to.equal(`${coordinatorEndpoint}${coordinatorPort}`);
 | 
						|
                });
 | 
						|
        });
 | 
						|
 | 
						|
        it('should throw error when batch soft cancel totally fails with single coordinator operator', async () => {
 | 
						|
            const orders = [signedOrder, signedOrderWithDifferentFeeRecipient];
 | 
						|
            await coordinatorClient
 | 
						|
                .batchSoftCancelAsync(orders)
 | 
						|
                .then(res => {
 | 
						|
                    expect(res).to.be.undefined();
 | 
						|
                })
 | 
						|
                .catch(err => {
 | 
						|
                    expect(err.message).equal(CoordinatorServerErrorMsg.CancellationFailed);
 | 
						|
                    expect(err.approvedOrders).to.be.empty('array');
 | 
						|
                    expect(err.cancellations).to.be.empty('array');
 | 
						|
 | 
						|
                    const errorBody = err.errors[0];
 | 
						|
                    expect(errorBody.isError).to.be.true();
 | 
						|
                    expect(errorBody.status).to.equal(400);
 | 
						|
                    expect(errorBody.error).to.deep.equal(serverValidationError);
 | 
						|
                    expect(errorBody.orders).to.deep.equal(orders);
 | 
						|
                    expect(errorBody.coordinatorOperator).to.equal(`${coordinatorEndpoint}${coordinatorPort}`);
 | 
						|
                });
 | 
						|
        });
 | 
						|
        it('should throw consolidated error when batch soft cancel partially fails with different coordinator operators', async () => {
 | 
						|
            const serverCancellationSuccess = {
 | 
						|
                outstandingFillSignatures: [
 | 
						|
                    {
 | 
						|
                        orderHash: '0xd1dc61f3e7e5f41d72beae7863487beea108971de678ca00d903756f842ef3ce',
 | 
						|
                        approvalSignatures: [
 | 
						|
                            '0x1c7383ca8ebd6de8b5b20b1c2d49bea166df7dfe4af1932c9c52ec07334e859cf2176901da35f4480ceb3ab63d8d0339d851c31929c40d88752689b9a8a535671303',
 | 
						|
                        ],
 | 
						|
                        expirationTimeSeconds: 1552390380,
 | 
						|
                        takerAssetFillAmount: 100000000000000000000,
 | 
						|
                    },
 | 
						|
                ],
 | 
						|
                cancellationSignatures: [
 | 
						|
                    '0x2ea3117a8ebd6de8b5b20b1c2d49bea166df7dfe4af1932c9c52ec07334e859cf2176901da35f4480ceb3ab63d8d0339d851c31929c40d88752689b9a855b5a7b401',
 | 
						|
                ],
 | 
						|
            };
 | 
						|
            nock(`${coordinatorEndpoint}${anotherCoordinatorPort}`)
 | 
						|
                .post('/v2/request_transaction', () => true)
 | 
						|
                .query({
 | 
						|
                    chainId: 1337,
 | 
						|
                })
 | 
						|
                .reply(200, serverCancellationSuccess);
 | 
						|
 | 
						|
            const signedOrders = [signedOrder, signedOrderWithDifferentCoordinatorOperator];
 | 
						|
            await coordinatorClient
 | 
						|
                .batchSoftCancelAsync(signedOrders)
 | 
						|
                .then(res => {
 | 
						|
                    expect(res).to.be.undefined();
 | 
						|
                })
 | 
						|
                .catch(err => {
 | 
						|
                    expect(err.message).equal(CoordinatorServerErrorMsg.CancellationFailed);
 | 
						|
                    expect(err.approvedOrders).to.be.empty('array');
 | 
						|
                    expect(err.cancellations).to.deep.equal([serverCancellationSuccess]);
 | 
						|
 | 
						|
                    const errorBody = err.errors[0];
 | 
						|
                    expect(errorBody.isError).to.be.true();
 | 
						|
                    expect(errorBody.status).to.equal(400);
 | 
						|
                    expect(errorBody.error).to.deep.equal(serverValidationError);
 | 
						|
                    expect(errorBody.orders).to.deep.equal([signedOrder]);
 | 
						|
                    expect(errorBody.coordinatorOperator).to.equal(`${coordinatorEndpoint}${coordinatorPort}`);
 | 
						|
                });
 | 
						|
        });
 | 
						|
        it('should throw consolidated error when batch soft cancel totally fails with different coordinator operators', async () => {
 | 
						|
            nock(`${coordinatorEndpoint}${anotherCoordinatorPort}`)
 | 
						|
                .post('/v2/request_transaction', () => true)
 | 
						|
                .query({
 | 
						|
                    chainId: 1337,
 | 
						|
                })
 | 
						|
                .reply(400, serverValidationError);
 | 
						|
 | 
						|
            const signedOrders = [signedOrder, signedOrderWithDifferentCoordinatorOperator];
 | 
						|
            await coordinatorClient
 | 
						|
                .batchSoftCancelAsync(signedOrders)
 | 
						|
                .then(res => {
 | 
						|
                    expect(res).to.be.undefined();
 | 
						|
                })
 | 
						|
                .catch(err => {
 | 
						|
                    expect(err.message).equal(CoordinatorServerErrorMsg.CancellationFailed);
 | 
						|
                    expect(err.approvedOrders).to.be.empty('array');
 | 
						|
                    expect(err.cancellations).to.be.empty('array');
 | 
						|
 | 
						|
                    const errorBody = err.errors[0];
 | 
						|
                    expect(errorBody.isError).to.be.true();
 | 
						|
                    expect(errorBody.status).to.equal(400);
 | 
						|
                    expect(errorBody.error).to.deep.equal(serverValidationError);
 | 
						|
                    expect(errorBody.orders).to.deep.equal([signedOrder]);
 | 
						|
                    expect(errorBody.coordinatorOperator).to.equal(`${coordinatorEndpoint}${coordinatorPort}`);
 | 
						|
 | 
						|
                    const anotherErrorBody = err.errors[1];
 | 
						|
                    expect(anotherErrorBody.isError).to.be.true();
 | 
						|
                    expect(anotherErrorBody.status).to.equal(400);
 | 
						|
                    expect(anotherErrorBody.error).to.deep.equal(serverValidationError);
 | 
						|
                    expect(anotherErrorBody.orders).to.deep.equal([signedOrderWithDifferentCoordinatorOperator]);
 | 
						|
                    expect(anotherErrorBody.coordinatorOperator).to.equal(
 | 
						|
                        `${coordinatorEndpoint}${anotherCoordinatorPort}`,
 | 
						|
                    );
 | 
						|
                });
 | 
						|
        });
 | 
						|
        it('should throw error when a fill fails', async () => {
 | 
						|
            await coordinatorClient
 | 
						|
                .fillOrderAsync(signedOrder, takerTokenFillAmount, signedOrder.signature, { from: takerAddress })
 | 
						|
                .then(res => {
 | 
						|
                    expect(res).to.be.undefined();
 | 
						|
                })
 | 
						|
                .catch(err => {
 | 
						|
                    expect(err.message).equal(CoordinatorServerErrorMsg.FillFailed);
 | 
						|
                    expect(err.approvedOrders).to.be.empty('array');
 | 
						|
                    expect(err.cancellations).to.be.empty('array');
 | 
						|
 | 
						|
                    const errorBody = err.errors[0];
 | 
						|
                    expect(errorBody.isError).to.be.true();
 | 
						|
                    expect(errorBody.status).to.equal(400);
 | 
						|
                    expect(errorBody.error).to.deep.equal(serverValidationError);
 | 
						|
                    expect(errorBody.orders).to.deep.equal([signedOrder]);
 | 
						|
                    expect(errorBody.coordinatorOperator).to.equal(`${coordinatorEndpoint}${coordinatorPort}`);
 | 
						|
                });
 | 
						|
        });
 | 
						|
        it('should throw error when batch fill fails with single coordinator operator', async () => {
 | 
						|
            const signedOrders = [signedOrder, signedOrderWithDifferentFeeRecipient];
 | 
						|
            const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount, takerTokenFillAmount];
 | 
						|
            await coordinatorClient
 | 
						|
                .batchFillOrdersAsync(signedOrders, takerAssetFillAmounts, signedOrders.map(o => o.signature), {
 | 
						|
                    from: takerAddress,
 | 
						|
                })
 | 
						|
                .then(res => {
 | 
						|
                    expect(res).to.be.undefined();
 | 
						|
                })
 | 
						|
                .catch(err => {
 | 
						|
                    expect(err.message).equal(CoordinatorServerErrorMsg.FillFailed);
 | 
						|
                    expect(err.approvedOrders).to.be.empty('array');
 | 
						|
                    expect(err.cancellations).to.be.empty('array');
 | 
						|
 | 
						|
                    const errorBody = err.errors[0];
 | 
						|
                    expect(errorBody.isError).to.be.true();
 | 
						|
                    expect(errorBody.status).to.equal(400);
 | 
						|
                    expect(errorBody.error).to.deep.equal(serverValidationError);
 | 
						|
                    expect(errorBody.orders).to.deep.equal(signedOrders);
 | 
						|
                    expect(errorBody.coordinatorOperator).to.equal(`${coordinatorEndpoint}${coordinatorPort}`);
 | 
						|
                });
 | 
						|
        });
 | 
						|
        it('should throw consolidated error when batch fill partially fails with different coordinator operators', async () => {
 | 
						|
            const serverApprovalSuccess = {
 | 
						|
                signatures: [
 | 
						|
                    '0x1cc07d7ae39679690a91418d46491520f058e4fb14debdf2e98f2376b3970de8512ace44af0be6d1c65617f7aae8c2364ff63f241515ee1559c3eeecb0f671d9e903',
 | 
						|
                ],
 | 
						|
                expirationTimeSeconds: 1552390014,
 | 
						|
            };
 | 
						|
            nock(`${coordinatorEndpoint}${anotherCoordinatorPort}`)
 | 
						|
                .post('/v2/request_transaction', () => true)
 | 
						|
                .query({
 | 
						|
                    chainId: 1337,
 | 
						|
                })
 | 
						|
                .reply(200, serverApprovalSuccess);
 | 
						|
 | 
						|
            const signedOrders = [signedOrder, signedOrderWithDifferentCoordinatorOperator];
 | 
						|
            const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount, takerTokenFillAmount];
 | 
						|
            await coordinatorClient
 | 
						|
                .batchFillOrdersAsync(signedOrders, takerAssetFillAmounts, signedOrders.map(o => o.signature), {
 | 
						|
                    from: takerAddress,
 | 
						|
                })
 | 
						|
                .then(res => {
 | 
						|
                    expect(res).to.be.undefined();
 | 
						|
                })
 | 
						|
                .catch(err => {
 | 
						|
                    expect(err.message).equal(CoordinatorServerErrorMsg.FillFailed);
 | 
						|
                    expect(err.approvedOrders).to.deep.equal([signedOrderWithDifferentCoordinatorOperator]);
 | 
						|
                    expect(err.cancellations).to.be.empty('array');
 | 
						|
 | 
						|
                    const errorBody = err.errors[0];
 | 
						|
                    expect(errorBody.isError).to.be.true();
 | 
						|
                    expect(errorBody.status).to.equal(400);
 | 
						|
                    expect(errorBody.error).to.deep.equal(serverValidationError);
 | 
						|
                    expect(errorBody.orders).to.deep.equal([signedOrder]);
 | 
						|
                    expect(errorBody.coordinatorOperator).to.equal(`${coordinatorEndpoint}${coordinatorPort}`);
 | 
						|
                });
 | 
						|
        });
 | 
						|
        it('should throw consolidated error when batch fill totally fails with different coordinator operators', async () => {
 | 
						|
            nock(`${coordinatorEndpoint}${anotherCoordinatorPort}`)
 | 
						|
                .post('/v2/request_transaction', () => true)
 | 
						|
                .query({
 | 
						|
                    chainId: 1337,
 | 
						|
                })
 | 
						|
                .reply(400, serverValidationError);
 | 
						|
 | 
						|
            const signedOrders = [signedOrder, signedOrderWithDifferentCoordinatorOperator];
 | 
						|
            const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount, takerTokenFillAmount];
 | 
						|
            await coordinatorClient
 | 
						|
                .batchFillOrdersAsync(signedOrders, takerAssetFillAmounts, signedOrders.map(o => o.signature), {
 | 
						|
                    from: takerAddress,
 | 
						|
                })
 | 
						|
                .then(res => {
 | 
						|
                    expect(res).to.be.undefined();
 | 
						|
                })
 | 
						|
                .catch(err => {
 | 
						|
                    expect(err.message).equal(CoordinatorServerErrorMsg.FillFailed);
 | 
						|
                    expect(err.approvedOrders).to.be.empty('array');
 | 
						|
                    expect(err.cancellations).to.be.empty('array');
 | 
						|
 | 
						|
                    const errorBody = err.errors[0];
 | 
						|
                    expect(errorBody.isError).to.be.true();
 | 
						|
                    expect(errorBody.status).to.equal(400);
 | 
						|
                    expect(errorBody.error).to.deep.equal(serverValidationError);
 | 
						|
                    expect(errorBody.orders).to.deep.equal([signedOrder]);
 | 
						|
                    expect(errorBody.coordinatorOperator).to.equal(`${coordinatorEndpoint}${coordinatorPort}`);
 | 
						|
 | 
						|
                    const anotherErrorBody = err.errors[1];
 | 
						|
                    expect(anotherErrorBody.isError).to.be.true();
 | 
						|
                    expect(anotherErrorBody.status).to.equal(400);
 | 
						|
                    expect(anotherErrorBody.error).to.deep.equal(serverValidationError);
 | 
						|
                    expect(anotherErrorBody.orders).to.deep.equal([signedOrderWithDifferentCoordinatorOperator]);
 | 
						|
                    expect(anotherErrorBody.coordinatorOperator).to.equal(
 | 
						|
                        `${coordinatorEndpoint}${anotherCoordinatorPort}`,
 | 
						|
                    );
 | 
						|
                });
 | 
						|
        });
 | 
						|
    });
 | 
						|
});
 | 
						|
// tslint:disable:max-file-line-count
 |