532 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			532 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 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
 |