From 50d5b4fa378552f52c7f53588daf2a26bc777090 Mon Sep 17 00:00:00 2001 From: Xianny <8582774+xianny@users.noreply.github.com> Date: Fri, 22 Nov 2019 12:19:00 -0800 Subject: [PATCH] Refactor/3.0/coordinator client (#2348) * deduplicate migrateOnceAsync() test helper * move and rename coordinator client to @0x/contracts-coordinator --- contracts/coordinator/package.json | 6 +- contracts/coordinator/src/client/index.ts | 892 ++++++++++++++++++ .../coordinator/src/client}/utils/assert.ts | 1 - .../client}/utils/coordinator_server_types.ts | 10 +- .../src/client}/utils/decorators.ts | 0 contracts/coordinator/src/index.ts | 27 +- contracts/coordinator/src/types.ts | 7 + contracts/integrations/package.json | 4 + .../test/coordinator/client_test.ts | 634 +++++++++++++ contracts/test-utils/package.json | 1 + .../test/exchange_swap_quote_consumer_test.ts | 2 +- .../forwarder_swap_quote_consumer_test.ts | 2 +- .../test/order_prune_utils_test.ts | 2 +- .../test/swap_quote_consumer_utils_test.ts | 2 +- packages/contract-wrappers/package.json | 4 +- .../src/contract_wrappers.ts | 4 +- .../contract_wrappers_config_schema.ts | 0 .../src/coordinator_wrapper.ts | 787 --------------- packages/contract-wrappers/src/index.ts | 12 +- .../src/schemas/order_tx_opts_schema.ts | 8 - .../src/schemas/tx_opts_schema.ts | 9 - packages/contract-wrappers/src/types.ts | 36 - .../test/calldata_decoder_test.ts | 2 +- .../test/coordinator_wrapper_test.ts | 675 ------------- .../contract-wrappers/test/utils/migrate.ts | 18 - packages/migrations/src/index.ts | 1 + .../src/migrate_with_test_defaults.ts} | 7 +- .../src/utils/doc_generate_utils.ts | 2 +- packages/types/src/index.ts | 12 - yarn.lock | 62 +- 30 files changed, 1604 insertions(+), 1625 deletions(-) create mode 100644 contracts/coordinator/src/client/index.ts rename {packages/contract-wrappers/src => contracts/coordinator/src/client}/utils/assert.ts (97%) rename {packages/contract-wrappers/src => contracts/coordinator/src/client}/utils/coordinator_server_types.ts (89%) rename {packages/contract-wrappers/src => contracts/coordinator/src/client}/utils/decorators.ts (100%) create mode 100644 contracts/integrations/test/coordinator/client_test.ts rename packages/contract-wrappers/src/{schemas => }/contract_wrappers_config_schema.ts (100%) delete mode 100644 packages/contract-wrappers/src/coordinator_wrapper.ts delete mode 100644 packages/contract-wrappers/src/schemas/order_tx_opts_schema.ts delete mode 100644 packages/contract-wrappers/src/schemas/tx_opts_schema.ts delete mode 100644 packages/contract-wrappers/test/coordinator_wrapper_test.ts delete mode 100644 packages/contract-wrappers/test/utils/migrate.ts rename packages/{asset-swapper/test/utils/migrate.ts => migrations/src/migrate_with_test_defaults.ts} (66%) diff --git a/contracts/coordinator/package.json b/contracts/coordinator/package.json index 6f38aee80d..7d8342ee87 100644 --- a/contracts/coordinator/package.json +++ b/contracts/coordinator/package.json @@ -84,12 +84,16 @@ "typescript": "3.0.1" }, "dependencies": { + "@0x/assert": "^2.2.0-beta.2", "@0x/base-contract": "^5.5.0-beta.3", "@0x/contracts-utils": "^3.3.0-beta.3", "@0x/types": "^2.5.0-beta.2", "@0x/typescript-typings": "^4.4.0-beta.2", "@0x/utils": "^4.6.0-beta.2", - "ethereum-types": "^2.2.0-beta.2" + "ethereum-types": "^2.2.0-beta.2", + "@0x/contract-addresses": "^3.3.0-beta.4", + "@0x/json-schemas": "^4.1.0-beta.2", + "http-status-codes": "^1.3.2" }, "publishConfig": { "access": "public" diff --git a/contracts/coordinator/src/client/index.ts b/contracts/coordinator/src/client/index.ts new file mode 100644 index 0000000000..4638ebc8f4 --- /dev/null +++ b/contracts/coordinator/src/client/index.ts @@ -0,0 +1,892 @@ +import { SendTransactionOpts } from '@0x/base-contract'; +import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; +import { ExchangeContract } from '@0x/contracts-exchange'; +import { ExchangeFunctionName } from '@0x/contracts-test-utils'; +import { devConstants } from '@0x/dev-utils'; +import { schemas } from '@0x/json-schemas'; +import { generatePseudoRandomSalt, signatureUtils } from '@0x/order-utils'; +import { Order, SignedOrder, SignedZeroExTransaction, ZeroExTransaction } from '@0x/types'; +import { BigNumber, fetchAsync } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import { CallData, ContractAbi, SupportedProvider, TxData } from 'ethereum-types'; +import * as HttpStatus from 'http-status-codes'; +import { flatten } from 'lodash'; + +import { artifacts } from '../artifacts'; +import { CoordinatorContract, CoordinatorRegistryContract } from '../wrappers'; + +import { assert } from './utils/assert'; +import { + CoordinatorServerApprovalResponse, + CoordinatorServerCancellationResponse, + CoordinatorServerError, + CoordinatorServerErrorMsg, + CoordinatorServerResponse, +} from './utils/coordinator_server_types'; + +import { decorators } from './utils/decorators'; + +export { CoordinatorServerErrorMsg, CoordinatorServerCancellationResponse }; + +const DEFAULT_TX_DATA = { + gas: devConstants.GAS_LIMIT, + gasPrice: new BigNumber(1), + value: new BigNumber(150000), // DEFAULT_PROTOCOL_FEE_MULTIPLIER +}; + +// tx expiration time will be set to (now + default_approval - time_buffer) +const DEFAULT_APPROVAL_EXPIRATION_TIME_SECONDS = 90; +const DEFAULT_EXPIRATION_TIME_BUFFER_SECONDS = 30; + +/** + * This class includes all the functionality related to filling or cancelling orders through + * the 0x V2 Coordinator extension contract. + */ +export class CoordinatorClient { + public abi: ContractAbi = artifacts.Coordinator.compilerOutput.abi; + public chainId: number; + public address: string; + public exchangeAddress: string; + public registryAddress: string; + + private readonly _web3Wrapper: Web3Wrapper; + private readonly _contractInstance: CoordinatorContract; + private readonly _registryInstance: CoordinatorRegistryContract; + private readonly _exchangeInstance: ExchangeContract; + private readonly _feeRecipientToEndpoint: { [feeRecipient: string]: string } = {}; + private readonly _txDefaults: CallData = DEFAULT_TX_DATA; + + /** + * Validates that the 0x transaction has been approved by all of the feeRecipients that correspond to each order in the transaction's Exchange calldata. + * Throws an error if the transaction approvals are not valid. Will not detect failures that would occur when the transaction is executed on the Exchange contract. + * @param transaction 0x transaction containing salt, signerAddress, and data. + * @param txOrigin Required signer of Ethereum transaction calling this function. + * @param transactionSignature Proof that the transaction has been signed by the signer. + * @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata. + */ + @decorators.asyncZeroExErrorHandler + public async assertValidCoordinatorApprovalsOrThrowAsync( + transaction: ZeroExTransaction, + txOrigin: string, + transactionSignature: string, + approvalSignatures: string[], + ): Promise { + assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema); + assert.isETHAddressHex('txOrigin', txOrigin); + assert.isHexString('transactionSignature', transactionSignature); + for (const approvalSignature of approvalSignatures) { + assert.isHexString('approvalSignature', approvalSignature); + } + return this._contractInstance + .assertValidCoordinatorApprovals(transaction, txOrigin, transactionSignature, approvalSignatures) + .callAsync(); + } + /** + * Instantiate CoordinatorClient + * @param web3Wrapper Web3Wrapper instance to use. + * @param chainId Desired chainId. + * @param address The address of the Coordinator contract. If undefined, will + * default to the known address corresponding to the chainId. + * @param exchangeAddress The address of the Exchange contract. If undefined, will + * default to the known address corresponding to the chainId. + * @param registryAddress The address of the CoordinatorRegistry contract. If undefined, will + * default to the known address corresponding to the chainId. + */ + constructor( + address: string, + provider: SupportedProvider, + chainId: number, + txDefaults?: Partial, + exchangeAddress?: string, + registryAddress?: string, + ) { + this.chainId = chainId; + const contractAddresses = getContractAddressesForChainOrThrow(this.chainId); + this.address = address === undefined ? contractAddresses.coordinator : address; + this.exchangeAddress = exchangeAddress === undefined ? contractAddresses.coordinator : exchangeAddress; + this.registryAddress = registryAddress === undefined ? contractAddresses.coordinatorRegistry : registryAddress; + this._web3Wrapper = new Web3Wrapper(provider); + this._txDefaults = { ...txDefaults, ...DEFAULT_TX_DATA }; + this._contractInstance = new CoordinatorContract( + this.address, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + this._registryInstance = new CoordinatorRegistryContract( + this.registryAddress, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + this._exchangeInstance = new ExchangeContract( + this.exchangeAddress, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + } + + /** + * Fills a signed order with an amount denominated in baseUnits of the taker asset. Under-the-hood, this + * method uses the `feeRecipientAddress` of the order to look up the coordinator server endpoint registered in the + * coordinator registry contract. It requests an approval from that coordinator server before + * submitting the order and approval as a 0x transaction to the coordinator extension contract. The coordinator extension + * contract validates approvals and then fills the order via the Exchange contract. + * @param order An object that conforms to the Order interface. + * @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill. + * @param signature Signature corresponding to the order. + * @param txData Transaction data. The `from` field should be the user Ethereum address who would like + * to fill these orders. Must be available via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async fillOrderAsync( + order: Order, + takerAssetFillAmount: BigNumber, + signature: string, + txData: TxData, + sendTxOpts: Partial = { shouldValidate: true }, + ): Promise { + assert.doesConformToSchema('order', order, schemas.orderSchema); + assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount); + return this._executeTxThroughCoordinatorAsync( + ExchangeFunctionName.FillOrder, + txData, + sendTxOpts, + [order], + order, + takerAssetFillAmount, + signature, + ); + } + + /** + * Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled, + * the fill order is abandoned. + * @param order An object that conforms to the Order interface. + * @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill. + * @param signature Signature corresponding to the order. + * @param txData Transaction data. The `from` field should be the user Ethereum address who would like + * to fill these orders. Must be available via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async fillOrKillOrderAsync( + order: Order, + takerAssetFillAmount: BigNumber, + signature: string, + txData: TxData, + sendTxOpts: Partial = { shouldValidate: true }, + ): Promise { + assert.doesConformToSchema('order', order, schemas.orderSchema); + assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount); + return this._executeTxThroughCoordinatorAsync( + ExchangeFunctionName.FillOrKillOrder, + txData, + sendTxOpts, + [order], + order, + takerAssetFillAmount, + signature, + ); + } + + /** + * Batch version of fillOrderAsync. Executes multiple fills atomically in a single transaction. + * If any `feeRecipientAddress` in the batch is not registered to a coordinator server through the CoordinatorRegistryContract, the whole batch fails. + * @param orders An array of orders to fill. + * @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill. + * @param signatures Signatures corresponding to the orders. + * @param txData Transaction data. The `from` field should be the user Ethereum address who would like + * to fill these orders. Must be available via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async batchFillOrdersAsync( + orders: Order[], + takerAssetFillAmounts: BigNumber[], + signatures: string[], + txData: TxData, + sendTxOpts?: Partial, + ): Promise { + return this._batchFillAsync( + ExchangeFunctionName.BatchFillOrders, + orders, + takerAssetFillAmounts, + signatures, + txData, + sendTxOpts, + ); + } + /** + * No throw version of batchFillOrdersAsync + * @param orders An array of orders to fill. + * @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill. + * @param signatures Signatures corresponding to the orders. + * @param txData Transaction data. The `from` field should be the user Ethereum address who would like + * to fill these orders. Must be available via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + + public async batchFillOrdersNoThrowAsync( + orders: Order[], + takerAssetFillAmounts: BigNumber[], + signatures: string[], + txData: TxData, + sendTxOpts?: Partial, + ): Promise { + return this._batchFillAsync( + ExchangeFunctionName.BatchFillOrdersNoThrow, + orders, + takerAssetFillAmounts, + signatures, + txData, + sendTxOpts, + ); + } + /** + * Batch version of fillOrKillOrderAsync. Executes multiple fills atomically in a single transaction. + * @param orders An array of orders to fill. + * @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill. + * @param signatures Signatures corresponding to the orders. + * @param txData Transaction data. The `from` field should be the user Ethereum address who would like + * to fill these orders. Must be available via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async batchFillOrKillOrdersAsync( + orders: Order[], + takerAssetFillAmounts: BigNumber[], + signatures: string[], + txData: TxData, + sendTxOpts?: Partial, + ): Promise { + return this._batchFillAsync( + ExchangeFunctionName.BatchFillOrKillOrders, + orders, + takerAssetFillAmounts, + signatures, + txData, + sendTxOpts, + ); + } + + /** + * Executes multiple calls of fillOrder until total amount of makerAsset is bought by taker. + * If any fill reverts, the error is caught and ignored. Finally, reverts if < makerAssetFillAmount has been bought. + * NOTE: This function does not enforce that the makerAsset is the same for each order. + * @param orders Array of order specifications. + * @param makerAssetFillAmount Desired amount of makerAsset to buy. + * @param signatures Proofs that orders have been signed by makers. + * @param txData Transaction data. The `from` field should be the user Ethereum address who would like + * to fill these orders. Must be available via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async marketBuyOrdersFillOrKillAsync( + orders: Order[], + makerAssetFillAmount: BigNumber, + signatures: string[], + txData: TxData, + sendTxOpts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + return this._marketBuySellOrdersAsync( + ExchangeFunctionName.MarketBuyOrdersFillOrKill, + orders, + makerAssetFillAmount, + signatures, + txData, + sendTxOpts, + ); + } + + /** + * No throw version of marketBuyOrdersFillOrKillAsync + * @param orders An array of orders to fill. + * @param makerAssetFillAmount Maker asset fill amount. + * @param signatures Signatures corresponding to the orders. + * @param txData Transaction data. The `from` field should be the user Ethereum address who would like + * to fill these orders. Must be available via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async marketBuyOrdersNoThrowAsync( + orders: Order[], + makerAssetFillAmount: BigNumber, + signatures: string[], + txData: TxData, + sendTxOpts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + return this._marketBuySellOrdersAsync( + ExchangeFunctionName.MarketBuyOrdersNoThrow, + orders, + makerAssetFillAmount, + signatures, + txData, + sendTxOpts, + ); + } + /** + * Executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. + * If any fill reverts, the error is caught and ignored. Finally, reverts if < takerAssetFillAmount has been sold. + * NOTE: This function does not enforce that the takerAsset is the same for each order. + * @param orders Array of order specifications. + * @param takerAssetFillAmount Desired amount of takerAsset to sell. + * @param signatures Proofs that orders have been signed by makers. + * @param txData Transaction data. The `from` field should be the user Ethereum address who would like + * to fill these orders. Must be available via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async marketSellOrdersFillOrKillAsync( + orders: Order[], + takerAssetFillAmount: BigNumber, + signatures: string[], + txData: TxData, + sendTxOpts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + return this._marketBuySellOrdersAsync( + ExchangeFunctionName.MarketSellOrdersFillOrKill, + orders, + takerAssetFillAmount, + signatures, + txData, + sendTxOpts, + ); + } + + /** + * No throw version of marketSellOrdersAsync + * @param orders An array of orders to fill. + * @param takerAssetFillAmount Taker asset fill amount. + * @param signatures Signatures corresponding to the orders. + * @param txData Transaction data. The `from` field should be the user Ethereum address who would like + * to fill these orders. Must be available via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async marketSellOrdersNoThrowAsync( + orders: Order[], + takerAssetFillAmount: BigNumber, + signatures: string[], + txData: TxData, + sendTxOpts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + return this._marketBuySellOrdersAsync( + ExchangeFunctionName.MarketSellOrdersNoThrow, + orders, + takerAssetFillAmount, + signatures, + txData, + sendTxOpts, + ); + } + + /** + * Match two complementary orders that have a profitable spread. + * Each order is filled at their respective price point. However, the calculations are + * carried out as though the orders are both being filled at the right order's price point. + * The profit made by the left order goes to the taker (who matched the two orders). + * @param leftOrder First order to match. + * @param rightOrder Second order to match. + * @param leftSignature Proof that order was created by the left maker. + * @param rightSignature Proof that order was created by the right maker. + * @param txData Transaction data. The `from` field should be the user Ethereum address who would like + * to fill these orders. Must be available via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async matchOrdersAsync( + leftOrder: Order, + rightOrder: Order, + leftSignature: string, + rightSignature: string, + txData: TxData, + sendTxOpts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + assert.doesConformToSchema('leftOrder', leftOrder, schemas.orderSchema); + assert.doesConformToSchema('rightOrder', rightOrder, schemas.orderSchema); + return this._executeTxThroughCoordinatorAsync( + ExchangeFunctionName.MatchOrders, + txData, + sendTxOpts, + [leftOrder, rightOrder], + leftOrder, + rightOrder, + leftSignature, + rightSignature, + ); + } + /** + * Match complementary orders that have a profitable spread. + * Each order is maximally filled at their respective price point, and + * the matcher receives a profit denominated in either the left maker asset, + * right maker asset, or a combination of both. + * @param leftOrders Set of orders with the same maker / taker asset. + * @param rightOrders Set of orders to match against `leftOrders` + * @param leftSignatures Proof that left orders were created by the left makers. + * @param rightSignatures Proof that right orders were created by the right makers. + * @param txData Transaction data. The `from` field should be the user Ethereum address who would like + * to fill these orders. Must be available via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async matchOrdersWithMaximalFillAsync( + leftOrder: Order, + rightOrder: Order, + leftSignature: string, + rightSignature: string, + txData: TxData, + sendTxOpts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + assert.doesConformToSchema('leftOrder', leftOrder, schemas.orderSchema); + assert.doesConformToSchema('rightOrder', rightOrder, schemas.orderSchema); + return this._executeTxThroughCoordinatorAsync( + ExchangeFunctionName.MatchOrdersWithMaximalFill, + txData, + sendTxOpts, + [leftOrder, rightOrder], + leftOrder, + rightOrder, + leftSignature, + rightSignature, + ); + } + + /** + * Cancels an order on-chain by submitting an Ethereum transaction. + * @param order An object that conforms to the Order interface. The order you would like to cancel. + * @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available + * via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async hardCancelOrderAsync( + order: Order, + txData: TxData, + sendTxOpts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + assert.doesConformToSchema('order', order, schemas.orderSchema); + return this._executeTxThroughCoordinatorAsync( + ExchangeFunctionName.CancelOrder, + txData, + sendTxOpts, + [order], + order, + ); + } + + /** + * Batch version of hardCancelOrderAsync. Cancels orders on-chain by submitting an Ethereum transaction. + * Executes multiple cancels atomically in a single transaction. + * @param orders An array of orders to cancel. + * @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available + * via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async batchHardCancelOrdersAsync( + orders: Order[], + txData: TxData, + sendTxOpts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + assert.doesConformToSchema('orders', orders, schemas.ordersSchema); + return this._executeTxThroughCoordinatorAsync( + ExchangeFunctionName.BatchCancelOrders, + txData, + sendTxOpts, + orders, + orders, + ); + } + + /** + * Cancels orders on-chain by submitting an Ethereum transaction. + * Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch + * and senderAddress equal to coordinator extension contract address. + * @param targetOrderEpoch Target order epoch. + * @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available + * via the Provider supplied at instantiation. + * @param sendTxOpts Optional arguments for sending the transaction. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async hardCancelOrdersUpToAsync( + targetOrderEpoch: BigNumber, + txData: TxData, + sendTxOpts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + assert.isBigNumber('targetOrderEpoch', targetOrderEpoch); + return this._executeTxThroughCoordinatorAsync( + ExchangeFunctionName.CancelOrdersUpTo, + txData, + sendTxOpts, + [], + targetOrderEpoch, + ); + } + /** + * Soft cancel a given order. + * Soft cancels are recorded only on coordinator operator servers and do not involve an Ethereum transaction. + * See [soft cancels](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#soft-cancels). + * @param order An object that conforms to the Order or SignedOrder interface. The order you would like to cancel. + * @return CoordinatorServerCancellationResponse. See [Cancellation Response](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#response). + */ + @decorators.asyncZeroExErrorHandler + public async softCancelAsync(order: Order): Promise { + assert.doesConformToSchema('order', order, schemas.orderSchema); + assert.isETHAddressHex('feeRecipientAddress', order.feeRecipientAddress); + assert.isSenderAddressAsync('makerAddress', order.makerAddress, this._web3Wrapper); + + const data = this._exchangeInstance.cancelOrder(order).getABIEncodedTransactionData(); + const transaction = await this._generateSignedZeroExTransactionAsync(data, order.makerAddress); + const endpoint = await this._getServerEndpointOrThrowAsync(order); + + const response = await this._executeServerRequestAsync(transaction, order.makerAddress, endpoint); + if (response.isError) { + const approvedOrders = new Array(); + const cancellations = new Array(); + const errors = [ + { + ...response, + orders: [order], + }, + ]; + throw new CoordinatorServerError( + CoordinatorServerErrorMsg.CancellationFailed, + approvedOrders, + cancellations, + errors, + ); + } else { + return response.body as CoordinatorServerCancellationResponse; + } + } + /** + * Batch version of softCancelOrderAsync. Requests multiple soft cancels + * @param orders An array of orders to cancel. + * @return CoordinatorServerCancellationResponse. See [Cancellation Response](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#response). + */ + @decorators.asyncZeroExErrorHandler + public async batchSoftCancelAsync(orders: SignedOrder[]): Promise { + assert.doesConformToSchema('orders', orders, schemas.ordersSchema); + const makerAddress = getMakerAddressOrThrow(orders); + assert.isSenderAddressAsync('makerAddress', makerAddress, this._web3Wrapper); + const data = this._exchangeInstance.batchCancelOrders(orders).getABIEncodedTransactionData(); + const transaction = await this._generateSignedZeroExTransactionAsync(data, makerAddress); + + // make server requests + const errorResponses: CoordinatorServerResponse[] = []; + const successResponses: CoordinatorServerCancellationResponse[] = []; + const serverEndpointsToOrders = await this._mapServerEndpointsToOrdersAsync(orders); + for (const endpoint of Object.keys(serverEndpointsToOrders)) { + const response = await this._executeServerRequestAsync(transaction, makerAddress, endpoint); + if (response.isError) { + errorResponses.push(response); + } else { + successResponses.push(response.body as CoordinatorServerCancellationResponse); + } + } + + // if no errors + if (errorResponses.length === 0) { + return successResponses; + } else { + // lookup orders with errors + const errorsWithOrders = errorResponses.map(resp => { + const endpoint = resp.coordinatorOperator; + const _orders = serverEndpointsToOrders[endpoint]; + return { + ...resp, + orders: _orders, + }; + }); + + const approvedOrders = new Array(); + const cancellations = successResponses; + // return errors and approvals + throw new CoordinatorServerError( + CoordinatorServerErrorMsg.CancellationFailed, + approvedOrders, + cancellations, + errorsWithOrders, + ); + } + } + + /** + * Recovers the address of a signer given a hash and signature. + * @param hash Any 32 byte hash. + * @param signature Proof that the hash has been signed by signer. + * @returns Signer address. + */ + @decorators.asyncZeroExErrorHandler + public async getSignerAddressAsync(hash: string, signature: string): Promise { + assert.isHexString('hash', hash); + assert.isHexString('signature', signature); + const signerAddress = await this._contractInstance.getSignerAddress(hash, signature).callAsync(); + return signerAddress; + } + + private async _marketBuySellOrdersAsync( + exchangeFn: ExchangeFunctionName, + orders: Order[], + assetFillAmount: BigNumber, + signatures: string[], + txData: TxData, + sendTxOpts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + assert.doesConformToSchema('orders', orders, schemas.ordersSchema); + assert.isBigNumber('assetFillAmount', assetFillAmount); + return this._executeTxThroughCoordinatorAsync( + exchangeFn, + txData, + sendTxOpts, + orders, + orders, + assetFillAmount, + signatures, + ); + } + + private async _batchFillAsync( + exchangeFn: ExchangeFunctionName, + orders: Order[], + takerAssetFillAmounts: BigNumber[], + signatures: string[], + txData: TxData, + sendTxOpts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + assert.doesConformToSchema('orders', orders, schemas.ordersSchema); + takerAssetFillAmounts.forEach(takerAssetFillAmount => + assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount), + ); + return this._executeTxThroughCoordinatorAsync( + exchangeFn, + txData, + sendTxOpts, + orders, + orders, + takerAssetFillAmounts, + signatures, + ); + } + + private async _executeTxThroughCoordinatorAsync( + exchangeFn: ExchangeFunctionName, + txData: TxData, + sendTxOpts: Partial, + ordersNeedingApprovals: Order[], + ...args: any[] // tslint:disable-line:trailing-comma + ): Promise { + assert.isETHAddressHex('takerAddress', txData.from); + await assert.isSenderAddressAsync('takerAddress', txData.from, this._web3Wrapper); + + // get ABI encoded transaction data for the desired exchange method + const data = (this._exchangeInstance as any)[exchangeFn](...args).getABIEncodedTransactionData(); + + // generate and sign a ZeroExTransaction + const signedZrxTx = await this._generateSignedZeroExTransactionAsync(data, txData.from); + + // get approval signatures from registered coordinator operators + const approvalSignatures = await this._getApprovalsAsync(signedZrxTx, ordersNeedingApprovals, txData.from); + + // execute the transaction through the Coordinator Contract + const txDataWithDefaults = { + ...this._txDefaults, + ...txData, // override defaults + }; + const txHash = this._contractInstance + .executeTransaction(signedZrxTx, txData.from, signedZrxTx.signature, approvalSignatures) + .sendTransactionAsync(txDataWithDefaults, sendTxOpts); + return txHash; + } + + private async _generateSignedZeroExTransactionAsync( + data: string, + signerAddress: string, + ): Promise { + const transaction: ZeroExTransaction = { + salt: generatePseudoRandomSalt(), + signerAddress, + data, + domain: { + verifyingContract: this.exchangeAddress, + chainId: await this._web3Wrapper.getChainIdAsync(), + }, + expirationTimeSeconds: new BigNumber( + Math.floor(Date.now() / 1000) + + DEFAULT_APPROVAL_EXPIRATION_TIME_SECONDS - + DEFAULT_EXPIRATION_TIME_BUFFER_SECONDS, + ), + gasPrice: new BigNumber(1), + }; + const signedZrxTx = await signatureUtils.ecSignTransactionAsync( + this._web3Wrapper.getProvider(), + transaction, + transaction.signerAddress, + ); + return signedZrxTx; + } + + private async _getApprovalsAsync( + transaction: SignedZeroExTransaction, + orders: Order[], + txOrigin: string, + ): Promise { + const coordinatorOrders = orders.filter(o => o.senderAddress === this.address); + if (coordinatorOrders.length === 0) { + return []; + } + const serverEndpointsToOrders = await this._mapServerEndpointsToOrdersAsync(coordinatorOrders); + + // make server requests + const errorResponses: CoordinatorServerResponse[] = []; + const approvalResponses: CoordinatorServerResponse[] = []; + for (const endpoint of Object.keys(serverEndpointsToOrders)) { + const response = await this._executeServerRequestAsync(transaction, txOrigin, endpoint); + if (response.isError) { + errorResponses.push(response); + } else { + approvalResponses.push(response); + } + } + + // if no errors + if (errorResponses.length === 0) { + // concatenate all approval responses + return approvalResponses.reduce( + (accumulator, response) => + accumulator.concat((response.body as CoordinatorServerApprovalResponse).signatures), + [] as string[], + ); + } else { + // format errors and approvals + // concatenate approvals + const notCoordinatorOrders = orders.filter(o => o.senderAddress !== this.address); + const approvedOrdersNested = approvalResponses.map(resp => { + const endpoint = resp.coordinatorOperator; + return serverEndpointsToOrders[endpoint]; + }); + const approvedOrders = flatten(approvedOrdersNested.concat(notCoordinatorOrders)); + + // lookup orders with errors + const errorsWithOrders = errorResponses.map(resp => { + const endpoint = resp.coordinatorOperator; + return { + ...resp, + orders: serverEndpointsToOrders[endpoint], + }; + }); + + // throw informative error + const cancellations = new Array(); + throw new CoordinatorServerError( + CoordinatorServerErrorMsg.FillFailed, + approvedOrders, + cancellations, + errorsWithOrders, + ); + } + } + + private async _getServerEndpointOrThrowAsync(order: Order): Promise { + const cached = this._feeRecipientToEndpoint[order.feeRecipientAddress]; + const endpoint = + cached !== undefined + ? cached + : await _fetchServerEndpointOrThrowAsync(order.feeRecipientAddress, this._registryInstance); + return endpoint; + + async function _fetchServerEndpointOrThrowAsync( + feeRecipient: string, + registryInstance: CoordinatorRegistryContract, + ): Promise { + const coordinatorOperatorEndpoint = await registryInstance.getCoordinatorEndpoint(feeRecipient).callAsync(); + if (coordinatorOperatorEndpoint === '' || coordinatorOperatorEndpoint === undefined) { + throw new Error( + `No Coordinator server endpoint found in Coordinator Registry for feeRecipientAddress: ${feeRecipient}. Registry contract address: [${ + registryInstance.address + }] Order: [${JSON.stringify(order)}]`, + ); + } + return coordinatorOperatorEndpoint; + } + } + + private async _executeServerRequestAsync( + signedTransaction: SignedZeroExTransaction, + txOrigin: string, + endpoint: string, + ): Promise { + const requestPayload = { + signedTransaction, + txOrigin, + }; + const response = await fetchAsync(`${endpoint}/v2/request_transaction?chainId=${this.chainId}`, { + body: JSON.stringify(requestPayload), + method: 'POST', + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + }); + + const isError = response.status !== HttpStatus.OK; + const isValidationError = response.status === HttpStatus.BAD_REQUEST; + const json = isError && !isValidationError ? undefined : await response.json(); + + const result = { + isError, + status: response.status, + body: isError ? undefined : json, + error: isError ? json : undefined, + request: requestPayload, + coordinatorOperator: endpoint, + }; + + return result; + } + + private async _mapServerEndpointsToOrdersAsync( + coordinatorOrders: Order[], + ): Promise<{ [endpoint: string]: Order[] }> { + const groupByFeeRecipient: { [feeRecipient: string]: Order[] } = {}; + for (const order of coordinatorOrders) { + const feeRecipient = order.feeRecipientAddress; + if (groupByFeeRecipient[feeRecipient] === undefined) { + groupByFeeRecipient[feeRecipient] = [] as Order[]; + } + groupByFeeRecipient[feeRecipient].push(order); + } + const serverEndpointsToOrders: { [endpoint: string]: Order[] } = {}; + for (const orders of Object.values(groupByFeeRecipient)) { + const endpoint = await this._getServerEndpointOrThrowAsync(orders[0]); + if (serverEndpointsToOrders[endpoint] === undefined) { + serverEndpointsToOrders[endpoint] = []; + } + serverEndpointsToOrders[endpoint] = serverEndpointsToOrders[endpoint].concat(orders); + } + return serverEndpointsToOrders; + } +} + +function getMakerAddressOrThrow(orders: Array): string { + const uniqueMakerAddresses = new Set(orders.map(o => o.makerAddress)); + if (uniqueMakerAddresses.size > 1) { + throw new Error(`All orders in a batch must have the same makerAddress`); + } + return orders[0].makerAddress; +} + +// tslint:disable:max-file-line-count diff --git a/packages/contract-wrappers/src/utils/assert.ts b/contracts/coordinator/src/client/utils/assert.ts similarity index 97% rename from packages/contract-wrappers/src/utils/assert.ts rename to contracts/coordinator/src/client/utils/assert.ts index 85079b5f59..908bdc2d6f 100644 --- a/packages/contract-wrappers/src/utils/assert.ts +++ b/contracts/coordinator/src/client/utils/assert.ts @@ -4,7 +4,6 @@ import { Schema } from '@0x/json-schemas'; // tslint:disable-line:no-unused-vari import { Order } from '@0x/types'; // tslint:disable-line:no-unused-variable import { BigNumber } from '@0x/utils'; // tslint:disable-line:no-unused-variable import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as _ from 'lodash'; export const assert = { ...sharedAssert, diff --git a/packages/contract-wrappers/src/utils/coordinator_server_types.ts b/contracts/coordinator/src/client/utils/coordinator_server_types.ts similarity index 89% rename from packages/contract-wrappers/src/utils/coordinator_server_types.ts rename to contracts/coordinator/src/client/utils/coordinator_server_types.ts index 9fe360094b..dd3833aeeb 100644 --- a/packages/contract-wrappers/src/utils/coordinator_server_types.ts +++ b/contracts/coordinator/src/client/utils/coordinator_server_types.ts @@ -2,10 +2,6 @@ import { Order, SignedOrder, SignedZeroExTransaction } from '@0x/types'; import { BigNumber } from '@0x/utils'; export interface CoordinatorServerApprovalResponse { - signatures: string[]; - expirationTimeSeconds: BigNumber[]; -} -export interface CoordinatorServerApprovalRawResponse { signatures: string[]; expirationTimeSeconds: BigNumber; } @@ -24,7 +20,7 @@ export interface CoordinatorOutstandingFillSignatures { export interface CoordinatorServerResponse { isError: boolean; status: number; - body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalRawResponse; + body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalResponse; error?: any; request: CoordinatorServerRequest; coordinatorOperator: string; @@ -38,12 +34,12 @@ export interface CoordinatorServerRequest { export class CoordinatorServerError extends Error { public message: CoordinatorServerErrorMsg; - public approvedOrders?: SignedOrder[] = []; + public approvedOrders?: Order[] = []; public cancellations?: CoordinatorServerCancellationResponse[] = []; public errors: CoordinatorServerResponse[]; constructor( message: CoordinatorServerErrorMsg, - approvedOrders: SignedOrder[], + approvedOrders: Order[], cancellations: CoordinatorServerCancellationResponse[], errors: CoordinatorServerResponse[], ) { diff --git a/packages/contract-wrappers/src/utils/decorators.ts b/contracts/coordinator/src/client/utils/decorators.ts similarity index 100% rename from packages/contract-wrappers/src/utils/decorators.ts rename to contracts/coordinator/src/client/utils/decorators.ts diff --git a/contracts/coordinator/src/index.ts b/contracts/coordinator/src/index.ts index e104091c78..45a575245a 100644 --- a/contracts/coordinator/src/index.ts +++ b/contracts/coordinator/src/index.ts @@ -8,9 +8,18 @@ export { LibEIP712CoordinatorDomainContract, } from './wrappers'; export import CoordinatorRevertErrors = require('./revert_errors'); +export { CoordinatorServerCancellationResponse } from './client/index'; export { ApprovalFactory } from './approval_factory'; export { SignedCoordinatorApproval } from './types'; -export { SignatureType, SignedZeroExTransaction, EIP712DomainWithDefaultSchema } from '@0x/types'; +export { + Order, + SignedOrder, + SignatureType, + SignedZeroExTransaction, + EIP712DomainWithDefaultSchema, + ZeroExTransaction, +} from '@0x/types'; +export { AwaitTransactionSuccessOpts, SendTransactionOpts } from '@0x/base-contract'; export { ContractArtifact, ContractChains, @@ -38,4 +47,20 @@ export { ConstructorStateMutability, TupleDataItem, StateMutability, + SupportedProvider, + TxData, + TxDataPayable, + Web3JsProvider, + GanacheProvider, + EIP1193Provider, + ZeroExProvider, + EIP1193Event, + JSONRPCRequestPayload, + JSONRPCErrorCallback, + Web3JsV1Provider, + Web3JsV2Provider, + Web3JsV3Provider, + JSONRPCResponsePayload, + JSONRPCResponseError, } from 'ethereum-types'; +export { CoordinatorClient, CoordinatorServerErrorMsg } from './client/index'; diff --git a/contracts/coordinator/src/types.ts b/contracts/coordinator/src/types.ts index 4efc9b184d..6ff7ee7b3d 100644 --- a/contracts/coordinator/src/types.ts +++ b/contracts/coordinator/src/types.ts @@ -1,4 +1,5 @@ import { SignedZeroExTransaction } from '@0x/types'; +import { BigNumber } from '@0x/utils'; export interface CoordinatorApproval { transaction: SignedZeroExTransaction; @@ -8,3 +9,9 @@ export interface CoordinatorApproval { export interface SignedCoordinatorApproval extends CoordinatorApproval { signature: string; } + +export interface CoordinatorTransaction { + salt: BigNumber; + signerAddress: string; + data: string; +} diff --git a/contracts/integrations/package.json b/contracts/integrations/package.json index dfd203fb98..06a7bf0f12 100644 --- a/contracts/integrations/package.json +++ b/contracts/integrations/package.json @@ -51,13 +51,16 @@ "homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md", "devDependencies": { "@0x/abi-gen": "^4.4.0-beta.3", + "@0x/contract-addresses": "^3.3.0-beta.4", "@0x/contracts-coordinator": "^2.1.0-beta.3", "@0x/contracts-dev-utils": "^0.1.0-beta.3", "@0x/contracts-exchange-forwarder": "^3.1.0-beta.3", "@0x/contracts-exchange-libs": "^3.1.0-beta.3", "@0x/contracts-gen": "^1.1.0-beta.3", "@0x/contracts-utils": "^3.3.0-beta.3", + "@0x/coordinator-server": "^1.0.3", "@0x/dev-utils": "^2.4.0-beta.3", + "@0x/migrations": "^4.4.0-beta.3", "@0x/order-utils": "^8.5.0-beta.3", "@0x/sol-compiler": "^3.2.0-beta.3", "@0x/tslint-config": "^3.1.0-beta.2", @@ -73,6 +76,7 @@ "js-combinatorics": "^0.5.3", "make-promises-safe": "^1.1.0", "mocha": "^6.2.0", + "nock": "^10.0.6", "npm-run-all": "^4.1.2", "shx": "^0.2.2", "solhint": "^1.4.1", diff --git a/contracts/integrations/test/coordinator/client_test.ts b/contracts/integrations/test/coordinator/client_test.ts new file mode 100644 index 0000000000..162156e7ee --- /dev/null +++ b/contracts/integrations/test/coordinator/client_test.ts @@ -0,0 +1,634 @@ +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('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 diff --git a/contracts/test-utils/package.json b/contracts/test-utils/package.json index 03660a7f78..9bc6d21d05 100644 --- a/contracts/test-utils/package.json +++ b/contracts/test-utils/package.json @@ -44,6 +44,7 @@ "dependencies": { "@0x/assert": "^2.2.0-beta.2", "@0x/base-contract": "^5.5.0-beta.3", + "@0x/contract-addresses": "^3.3.0-beta.4", "@0x/dev-utils": "^2.4.0-beta.3", "@0x/json-schemas": "^4.1.0-beta.2", "@0x/order-utils": "^8.5.0-beta.3", diff --git a/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts b/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts index 87b74bd36d..27e67c6381 100644 --- a/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts +++ b/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts @@ -4,6 +4,7 @@ import { ERC20TokenContract } from '@0x/contracts-erc20'; import { ExchangeContract } from '@0x/contracts-exchange'; import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils'; import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; +import { migrateOnceAsync } from '@0x/migrations'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import 'mocha'; @@ -21,7 +22,6 @@ import { } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; -import { migrateOnceAsync } from './utils/migrate'; import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote'; import { provider, web3Wrapper } from './utils/web3_wrapper'; diff --git a/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts b/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts index d9f5343a62..174ec6d653 100644 --- a/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts +++ b/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts @@ -4,6 +4,7 @@ import { ERC20TokenContract } from '@0x/contracts-erc20'; import { ForwarderContract } from '@0x/contracts-exchange-forwarder'; import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils'; import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; +import { migrateOnceAsync } from '@0x/migrations'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import 'mocha'; @@ -20,7 +21,6 @@ import { } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; -import { migrateOnceAsync } from './utils/migrate'; import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote'; import { provider, web3Wrapper } from './utils/web3_wrapper'; diff --git a/packages/asset-swapper/test/order_prune_utils_test.ts b/packages/asset-swapper/test/order_prune_utils_test.ts index 6baf3d49e0..759a159443 100644 --- a/packages/asset-swapper/test/order_prune_utils_test.ts +++ b/packages/asset-swapper/test/order_prune_utils_test.ts @@ -4,6 +4,7 @@ import { ERC20TokenContract } from '@0x/contracts-erc20'; import { ExchangeContract } from '@0x/contracts-exchange'; import { constants as devConstants, getLatestBlockTimestampAsync, OrderFactory } from '@0x/contracts-test-utils'; import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; +import { migrateOnceAsync } from '@0x/migrations'; import { assetDataUtils } from '@0x/order-utils'; import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; @@ -15,7 +16,6 @@ import { OrderPrunerPermittedFeeTypes } from '../src/types'; import { OrderPruner } from '../src/utils/order_prune_utils'; import { chaiSetup } from './utils/chai_setup'; -import { migrateOnceAsync } from './utils/migrate'; import { provider, web3Wrapper } from './utils/web3_wrapper'; chaiSetup.configure(); diff --git a/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts b/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts index 24bf29a785..8bfd0ea1d2 100644 --- a/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts +++ b/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts @@ -3,6 +3,7 @@ import { DevUtilsContract } from '@0x/contracts-dev-utils'; import { WETH9Contract } from '@0x/contracts-erc20'; import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils'; import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; +import { migrateOnceAsync } from '@0x/migrations'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import 'mocha'; @@ -12,7 +13,6 @@ import { constants } from '../src/constants'; import { ExtensionContractType, MarketOperation, PrunedSignedOrder } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; -import { migrateOnceAsync } from './utils/migrate'; import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote'; import { provider, web3Wrapper } from './utils/web3_wrapper'; diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index dbaa9321b2..08836ae14b 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -64,7 +64,6 @@ "ethereum-types": "^2.2.0-beta.2", "lodash": "^4.17.11", "mocha": "^6.2.0", - "nock": "^10.0.6", "shx": "^0.2.2", "tslint": "5.11.0", "typedoc": "^0.15.0", @@ -76,8 +75,7 @@ "@0x/contract-addresses": "^3.3.0-beta.4", "@0x/contract-artifacts": "^2.3.0-beta.3", "@0x/order-utils": "^8.5.0-beta.3", - "ethers": "~4.0.4", - "http-status-codes": "^1.3.2" + "ethers": "~4.0.4" }, "publishConfig": { "access": "public" diff --git a/packages/contract-wrappers/src/contract_wrappers.ts b/packages/contract-wrappers/src/contract_wrappers.ts index e8bae8f4b6..725f4ff0fd 100644 --- a/packages/contract-wrappers/src/contract_wrappers.ts +++ b/packages/contract-wrappers/src/contract_wrappers.ts @@ -6,6 +6,7 @@ import { OrderValidatorContract, WETH9Contract, } from '@0x/abi-gen-wrappers'; +import { assert } from '@0x/assert'; import { ContractAddresses } from '@0x/contract-addresses'; import { Coordinator, @@ -23,9 +24,8 @@ import { Web3Wrapper } from '@0x/web3-wrapper'; import { SupportedProvider } from 'ethereum-types'; import * as _ from 'lodash'; -import { ContractWrappersConfigSchema } from './schemas/contract_wrappers_config_schema'; +import { ContractWrappersConfigSchema } from './contract_wrappers_config_schema'; import { ContractWrappersConfig } from './types'; -import { assert } from './utils/assert'; import { _getDefaultContractAddresses } from './utils/contract_addresses'; /** diff --git a/packages/contract-wrappers/src/schemas/contract_wrappers_config_schema.ts b/packages/contract-wrappers/src/contract_wrappers_config_schema.ts similarity index 100% rename from packages/contract-wrappers/src/schemas/contract_wrappers_config_schema.ts rename to packages/contract-wrappers/src/contract_wrappers_config_schema.ts diff --git a/packages/contract-wrappers/src/coordinator_wrapper.ts b/packages/contract-wrappers/src/coordinator_wrapper.ts deleted file mode 100644 index 184765c37d..0000000000 --- a/packages/contract-wrappers/src/coordinator_wrapper.ts +++ /dev/null @@ -1,787 +0,0 @@ -// import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; -// import { Coordinator } from '@0x/contract-artifacts'; -// import { schemas } from '@0x/json-schemas'; -// import { generatePseudoRandomSalt, signatureUtils } from '@0x/order-utils'; -// import { Order, SignedOrder, SignedZeroExTransaction, ZeroExTransaction } from '@0x/types'; -// import { BigNumber, fetchAsync } from '@0x/utils'; -// import { Web3Wrapper } from '@0x/web3-wrapper'; -// import { ContractAbi, SupportedProvider } from 'ethereum-types'; -// import * as HttpStatus from 'http-status-codes'; -// import { flatten } from 'lodash'; - -// import { CoordinatorContract, CoordinatorRegistryContract, ExchangeContract } from '@0x/abi-gen-wrappers'; - -// import { orderTxOptsSchema } from './schemas/order_tx_opts_schema'; -// import { txOptsSchema } from './schemas/tx_opts_schema'; -// import { CoordinatorTransaction, OrderTransactionOpts } from './types'; -// import { assert } from './utils/assert'; -// import { -// CoordinatorServerApprovalRawResponse, -// CoordinatorServerApprovalResponse, -// CoordinatorServerCancellationResponse, -// CoordinatorServerError, -// CoordinatorServerErrorMsg, -// CoordinatorServerResponse, -// } from './utils/coordinator_server_types'; -// import { decorators } from './utils/decorators'; - -// /** -// * This class includes all the functionality related to filling or cancelling orders through -// * the 0x V2 Coordinator extension contract. -// */ -// export class CoordinatorWrapper { -// public abi: ContractAbi = Coordinator.compilerOutput.abi; -// public chainId: number; -// public address: string; -// public exchangeAddress: string; -// public registryAddress: string; -// private readonly _web3Wrapper: Web3Wrapper; -// private readonly _contractInstance: CoordinatorContract; -// private readonly _registryInstance: CoordinatorRegistryContract; -// private readonly _exchangeInstance: ExchangeContract; -// private readonly _feeRecipientToEndpoint: { [feeRecipient: string]: string } = {}; - -// /** -// * Instantiate CoordinatorWrapper -// * @param web3Wrapper Web3Wrapper instance to use. -// * @param chainId Desired chainId. -// * @param address The address of the Coordinator contract. If undefined, will -// * default to the known address corresponding to the chainId. -// * @param exchangeAddress The address of the Exchange contract. If undefined, will -// * default to the known address corresponding to the chainId. -// * @param registryAddress The address of the CoordinatorRegistry contract. If undefined, will -// * default to the known address corresponding to the chainId. -// */ -// constructor( -// provider: SupportedProvider, -// chainId: number, -// address?: string, -// exchangeAddress?: string, -// registryAddress?: string, -// ) { -// this.chainId = chainId; -// const contractAddresses = getContractAddressesForChainOrThrow(chainId); -// this.address = address === undefined ? contractAddresses.coordinator : address; -// this.exchangeAddress = exchangeAddress === undefined ? contractAddresses.coordinator : exchangeAddress; -// this.registryAddress = registryAddress === undefined ? contractAddresses.coordinatorRegistry : registryAddress; -// this._web3Wrapper = new Web3Wrapper(provider); - -// this._contractInstance = new CoordinatorContract( -// this.address, -// this._web3Wrapper.getProvider(), -// this._web3Wrapper.getContractDefaults(), -// ); -// this._registryInstance = new CoordinatorRegistryContract( -// this.registryAddress, -// this._web3Wrapper.getProvider(), -// this._web3Wrapper.getContractDefaults(), -// ); -// this._exchangeInstance = new ExchangeContract( -// this.exchangeAddress, -// this._web3Wrapper.getProvider(), -// this._web3Wrapper.getContractDefaults(), -// ); -// } - -// /** -// * Fills a signed order with an amount denominated in baseUnits of the taker asset. Under-the-hood, this -// * method uses the `feeRecipientAddress` of the order to look up the coordinator server endpoint registered in the -// * coordinator registry contract. It requests a signature from that coordinator server before -// * submitting the order and signature as a 0x transaction to the coordinator extension contract. The coordinator extension -// * contract validates signatures and then fills the order via the Exchange contract. -// * @param signedOrder An object that conforms to the SignedOrder interface. -// * @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill. -// * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied -// * Provider provided at instantiation. -// * @param orderTransactionOpts Optional arguments this method accepts. -// * @return Transaction hash. -// */ -// @decorators.asyncZeroExErrorHandler -// public async fillOrderAsync( -// signedOrder: SignedOrder, -// takerAssetFillAmount: BigNumber, -// takerAddress: string, -// orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, -// ): Promise { -// assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); -// assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount); -// assert.isETHAddressHex('takerAddress', takerAddress); -// assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); -// await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - -// const data = this._exchangeInstance -// .fillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature) -// .getABIEncodedTransactionData(); -// const txHash = await this._handleFillsAsync(data, takerAddress, [signedOrder], orderTransactionOpts); -// return txHash; -// } - -// /** -// * Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled, -// * the fill order is abandoned. -// * @param signedOrder An object that conforms to the SignedOrder interface. -// * @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill. -// * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied -// * Provider provided at instantiation. -// * @param orderTransactionOpts Optional arguments this method accepts. -// * @return Transaction hash. -// */ -// @decorators.asyncZeroExErrorHandler -// public async fillOrKillOrderAsync( -// signedOrder: SignedOrder, -// takerAssetFillAmount: BigNumber, -// takerAddress: string, -// orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, -// ): Promise { -// assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); -// assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount); -// assert.isETHAddressHex('takerAddress', takerAddress); -// assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); -// await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - -// const data = this._exchangeInstance -// .fillOrKillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature) -// .getABIEncodedTransactionData(); -// const txHash = await this._handleFillsAsync(data, takerAddress, [signedOrder], orderTransactionOpts); -// return txHash; -// } - -// /** -// * Batch version of fillOrderAsync. Executes multiple fills atomically in a single transaction. -// * Under-the-hood, this method uses the `feeRecipientAddress`s of the orders to looks up the coordinator server endpoints -// * registered in the coordinator registry contract. It requests a signature from each coordinator server before -// * submitting the orders and signatures as a 0x transaction to the coordinator extension contract, which validates the -// * signatures and then fills the order through the Exchange contract. -// * If any `feeRecipientAddress` in the batch is not registered to a coordinator server, the whole batch fails. -// * @param signedOrders An array of signed orders to fill. -// * @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill. -// * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied -// * Provider provided at instantiation. -// * @param orderTransactionOpts Optional arguments this method accepts. -// * @return Transaction hash. -// */ -// @decorators.asyncZeroExErrorHandler -// public async batchFillOrdersAsync( -// signedOrders: SignedOrder[], -// takerAssetFillAmounts: BigNumber[], -// takerAddress: string, -// orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, -// ): Promise { -// assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); -// for (const takerAssetFillAmount of takerAssetFillAmounts) { -// assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount); -// } -// assert.isETHAddressHex('takerAddress', takerAddress); -// assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); -// await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - -// const signatures = signedOrders.map(o => o.signature); -// const data = this._exchangeInstance -// .batchFillOrders(signedOrders, takerAssetFillAmounts, signatures) -// .getABIEncodedTransactionData(); -// const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts); -// return txHash; -// } - -// /** -// * No throw version of batchFillOrdersAsync -// * @param signedOrders An array of signed orders to fill. -// * @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill. -// * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied -// * Provider provided at instantiation. -// * @param orderTransactionOpts Optional arguments this method accepts. -// * @return Transaction hash. -// */ -// @decorators.asyncZeroExErrorHandler -// public async batchFillOrdersNoThrowAsync( -// signedOrders: SignedOrder[], -// takerAssetFillAmounts: BigNumber[], -// takerAddress: string, -// orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, -// ): Promise { -// assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); -// for (const takerAssetFillAmount of takerAssetFillAmounts) { -// assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount); -// } -// assert.isETHAddressHex('takerAddress', takerAddress); -// assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); -// await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - -// const signatures = signedOrders.map(o => o.signature); -// const data = this._exchangeInstance -// .batchFillOrdersNoThrow(signedOrders, takerAssetFillAmounts, signatures) -// .getABIEncodedTransactionData(); -// const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts); -// return txHash; -// } - -// /** -// * Batch version of fillOrKillOrderAsync. Executes multiple fills atomically in a single transaction. -// * @param signedOrders An array of signed orders to fill. -// * @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill. -// * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied -// * Provider provided at instantiation. -// * @param orderTransactionOpts Optional arguments this method accepts. -// * @return Transaction hash. -// */ -// @decorators.asyncZeroExErrorHandler -// public async batchFillOrKillOrdersAsync( -// signedOrders: SignedOrder[], -// takerAssetFillAmounts: BigNumber[], -// takerAddress: string, -// orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, -// ): Promise { -// assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); -// for (const takerAssetFillAmount of takerAssetFillAmounts) { -// assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount); -// } -// assert.isETHAddressHex('takerAddress', takerAddress); -// assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); -// await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - -// const signatures = signedOrders.map(o => o.signature); -// const data = this._exchangeInstance -// .batchFillOrKillOrders(signedOrders, takerAssetFillAmounts, signatures) -// .getABIEncodedTransactionData(); -// const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts); -// return txHash; -// } - -// /** -// * No throw version of marketBuyOrdersAsync -// * @param signedOrders An array of signed orders to fill. -// * @param makerAssetFillAmount Maker asset fill amount. -// * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied -// * Provider provided at instantiation. -// * @param orderTransactionOpts Optional arguments this method accepts. -// * @return Transaction hash. -// */ -// @decorators.asyncZeroExErrorHandler -// public async marketBuyOrdersNoThrowAsync( -// signedOrders: SignedOrder[], -// makerAssetFillAmount: BigNumber, -// takerAddress: string, -// orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, -// ): Promise { -// assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); -// assert.isBigNumber('makerAssetFillAmount', makerAssetFillAmount); -// assert.isETHAddressHex('takerAddress', takerAddress); -// assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); -// await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - -// const signatures = signedOrders.map(o => o.signature); -// const data = this._exchangeInstance -// .marketBuyOrdersNoThrow(signedOrders, makerAssetFillAmount, signatures) -// .getABIEncodedTransactionData(); -// const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts); -// return txHash; -// } - -// /** -// * No throw version of marketSellOrdersAsync -// * @param signedOrders An array of signed orders to fill. -// * @param takerAssetFillAmount Taker asset fill amount. -// * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied -// * Provider provided at instantiation. -// * @param orderTransactionOpts Optional arguments this method accepts. -// * @return Transaction hash. -// */ -// @decorators.asyncZeroExErrorHandler -// public async marketSellOrdersNoThrowAsync( -// signedOrders: SignedOrder[], -// takerAssetFillAmount: BigNumber, -// takerAddress: string, -// orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, -// ): Promise { -// assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); -// assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount); -// assert.isETHAddressHex('takerAddress', takerAddress); -// assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); -// await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - -// const signatures = signedOrders.map(o => o.signature); -// const data = this._exchangeInstance -// .marketSellOrdersNoThrow(signedOrders, takerAssetFillAmount, signatures) -// .getABIEncodedTransactionData(); -// const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts); -// return txHash; -// } - -// /** -// * Soft cancel a given order. -// * Soft cancels are recorded only on coordinator operator servers and do not involve an Ethereum transaction. -// * See [soft cancels](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#soft-cancels). -// * @param order An object that conforms to the Order or SignedOrder interface. The order you would like to cancel. -// * @return CoordinatorServerCancellationResponse. See [Cancellation Response](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#response). -// */ -// public async softCancelOrderAsync(order: Order | SignedOrder): Promise { -// assert.doesConformToSchema('order', order, schemas.orderSchema); -// assert.isETHAddressHex('feeRecipientAddress', order.feeRecipientAddress); -// assert.isSenderAddressAsync('makerAddress', order.makerAddress, this._web3Wrapper); - -// const data = this._exchangeInstance.cancelOrder(order).getABIEncodedTransactionData(); -// const transaction = await this._generateSignedZeroExTransactionAsync(data, order.makerAddress); -// const endpoint = await this._getServerEndpointOrThrowAsync(order.feeRecipientAddress); - -// const response = await this._executeServerRequestAsync(transaction, order.makerAddress, endpoint); -// if (response.isError) { -// const approvedOrders = new Array(); -// const cancellations = new Array(); -// const errors = [ -// { -// ...response, -// orders: [order], -// }, -// ]; -// throw new CoordinatorServerError( -// CoordinatorServerErrorMsg.CancellationFailed, -// approvedOrders, -// cancellations, -// errors, -// ); -// } else { -// return response.body as CoordinatorServerCancellationResponse; -// } -// } - -// /** -// * Batch version of softCancelOrderAsync. Requests multiple soft cancels -// * @param orders An array of orders to cancel. -// * @return CoordinatorServerCancellationResponse. See [Cancellation Response](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#response). -// */ -// public async batchSoftCancelOrdersAsync(orders: SignedOrder[]): Promise { -// assert.doesConformToSchema('orders', orders, schemas.ordersSchema); -// const makerAddress = getMakerAddressOrThrow(orders); -// assert.isSenderAddressAsync('makerAddress', makerAddress, this._web3Wrapper); -// const data = this._exchangeInstance.batchCancelOrders(orders).getABIEncodedTransactionData(); - -// const serverEndpointsToOrders = await this._mapServerEndpointsToOrdersAsync(orders); - -// // make server requests -// const errorResponses: CoordinatorServerResponse[] = []; -// const successResponses: CoordinatorServerCancellationResponse[] = []; -// const transaction = await this._generateSignedZeroExTransactionAsync(data, makerAddress); -// for (const endpoint of Object.keys(serverEndpointsToOrders)) { -// const response = await this._executeServerRequestAsync(transaction, makerAddress, endpoint); -// if (response.isError) { -// errorResponses.push(response); -// } else { -// successResponses.push(response.body as CoordinatorServerCancellationResponse); -// } -// } - -// // if no errors -// if (errorResponses.length === 0) { -// return successResponses; -// } else { -// // lookup orders with errors -// const errorsWithOrders = errorResponses.map(resp => { -// const endpoint = resp.coordinatorOperator; -// const _orders = serverEndpointsToOrders[endpoint]; -// return { -// ...resp, -// orders: _orders, -// }; -// }); - -// const approvedOrders = new Array(); -// const cancellations = successResponses; -// // return errors and approvals -// throw new CoordinatorServerError( -// CoordinatorServerErrorMsg.CancellationFailed, -// approvedOrders, -// cancellations, -// errorsWithOrders, -// ); -// } -// } - -// /** -// * Cancels an order on-chain by submitting an Ethereum transaction. -// * @param order An object that conforms to the Order or SignedOrder interface. The order you would like to cancel. -// * @param orderTransactionOpts Optional arguments this method accepts. -// * @return Transaction hash. -// */ -// @decorators.asyncZeroExErrorHandler -// public async hardCancelOrderAsync( -// order: Order | SignedOrder, -// orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, -// ): Promise { -// assert.doesConformToSchema('order', order, schemas.orderSchema); -// assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); -// await assert.isSenderAddressAsync('makerAddress', order.makerAddress, this._web3Wrapper); - -// const data = this._exchangeInstance.cancelOrder(order).getABIEncodedTransactionData(); -// const transaction = await this._generateSignedZeroExTransactionAsync(data, order.makerAddress); - -// const approvalSignatures = new Array(); -// const approvalExpirationTimeSeconds = new Array(); -// const txHash = await this._submitCoordinatorTransactionAsync( -// transaction, -// order.makerAddress, -// transaction.signature, -// approvalExpirationTimeSeconds, -// approvalSignatures, -// orderTransactionOpts, -// ); -// return txHash; -// } - -// /** -// * Batch version of hardCancelOrderAsync. Cancels orders on-chain by submitting an Ethereum transaction. -// * Executes multiple cancels atomically in a single transaction. -// * @param orders An array of orders to cancel. -// * @param orderTransactionOpts Optional arguments this method accepts. -// * @return Transaction hash. -// */ -// @decorators.asyncZeroExErrorHandler -// public async batchHardCancelOrdersAsync( -// orders: SignedOrder[], -// orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, -// ): Promise { -// assert.doesConformToSchema('orders', orders, schemas.ordersSchema); -// const makerAddress = getMakerAddressOrThrow(orders); -// assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); -// await assert.isSenderAddressAsync('makerAddress', makerAddress, this._web3Wrapper); - -// const data = this._exchangeInstance.batchCancelOrders(orders).getABIEncodedTransactionData(); -// const transaction = await this._generateSignedZeroExTransactionAsync(data, makerAddress); - -// const approvalSignatures = new Array(); -// const approvalExpirationTimeSeconds = new Array(); -// const txHash = await this._submitCoordinatorTransactionAsync( -// transaction, -// makerAddress, -// transaction.signature, -// approvalExpirationTimeSeconds, -// approvalSignatures, -// orderTransactionOpts, -// ); -// return txHash; -// } - -// /** -// * Cancels orders on-chain by submitting an Ethereum transaction. -// * Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch -// * and senderAddress equal to coordinator extension contract address. -// * @param targetOrderEpoch Target order epoch. -// * @param senderAddress Address that should send the transaction. -// * @param orderTransactionOpts Optional arguments this method accepts. -// * @return Transaction hash. -// */ -// @decorators.asyncZeroExErrorHandler -// public async hardCancelOrdersUpToAsync( -// targetOrderEpoch: BigNumber, -// senderAddress: string, -// orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, -// ): Promise { -// assert.isBigNumber('targetOrderEpoch', targetOrderEpoch); -// assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); -// await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper); - -// const data = this._exchangeInstance.cancelOrdersUpTo(targetOrderEpoch).getABIEncodedTransactionData(); -// const transaction = await this._generateSignedZeroExTransactionAsync(data, senderAddress); - -// const approvalSignatures = new Array(); -// const approvalExpirationTimeSeconds = new Array(); -// const txHash = await this._submitCoordinatorTransactionAsync( -// transaction, -// senderAddress, -// transaction.signature, -// approvalExpirationTimeSeconds, -// approvalSignatures, -// orderTransactionOpts, -// ); -// return txHash; -// } - -// /** -// * Validates that the 0x transaction has been approved by all of the feeRecipients that correspond to each order in the transaction's Exchange calldata. -// * Throws an error if the transaction approvals are not valid. Will not detect failures that would occur when the transaction is executed on the Exchange contract. -// * @param transaction 0x transaction containing salt, signerAddress, and data. -// * @param txOrigin Required signer of Ethereum transaction calling this function. -// * @param transactionSignature Proof that the transaction has been signed by the signer. -// * @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires. -// * @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata. -// */ -// public async assertValidCoordinatorApprovalsOrThrowAsync( -// transaction: ZeroExTransaction, -// txOrigin: string, -// transactionSignature: string, -// approvalExpirationTimeSeconds: BigNumber[], -// approvalSignatures: string[], -// ): Promise { -// assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema); -// assert.isETHAddressHex('txOrigin', txOrigin); -// assert.isHexString('transactionSignature', transactionSignature); -// for (const expirationTime of approvalExpirationTimeSeconds) { -// assert.isBigNumber('expirationTime', expirationTime); -// } -// for (const approvalSignature of approvalSignatures) { -// assert.isHexString('approvalSignature', approvalSignature); -// } - -// await this._contractInstance -// .assertValidCoordinatorApprovals( -// transaction, -// txOrigin, -// transactionSignature, -// approvalExpirationTimeSeconds, -// approvalSignatures, -// ) -// .callAsync(); -// } - -// /** -// * Recovers the address of a signer given a hash and signature. -// * @param hash Any 32 byte hash. -// * @param signature Proof that the hash has been signed by signer. -// * @returns Signer address. -// */ -// public async getSignerAddressAsync(hash: string, signature: string): Promise { -// assert.isHexString('hash', hash); -// assert.isHexString('signature', signature); -// const signerAddress = await this._contractInstance.getSignerAddress(hash, signature).callAsync(); -// return signerAddress; -// } - -// private async _handleFillsAsync( -// data: string, -// takerAddress: string, -// signedOrders: SignedOrder[], -// orderTransactionOpts: OrderTransactionOpts, -// ): Promise { -// const coordinatorOrders = signedOrders.filter(o => o.senderAddress === this.address); -// const serverEndpointsToOrders = await this._mapServerEndpointsToOrdersAsync(coordinatorOrders); - -// // make server requests -// const errorResponses: CoordinatorServerResponse[] = []; -// const approvalResponses: CoordinatorServerResponse[] = []; -// const transaction = await this._generateSignedZeroExTransactionAsync(data, takerAddress); -// for (const endpoint of Object.keys(serverEndpointsToOrders)) { -// const response = await this._executeServerRequestAsync(transaction, takerAddress, endpoint); -// if (response.isError) { -// errorResponses.push(response); -// } else { -// approvalResponses.push(response); -// } -// } - -// // if no errors -// if (errorResponses.length === 0) { -// // concatenate all approval responses -// const allApprovals = approvalResponses.map(resp => -// formatRawResponse(resp.body as CoordinatorServerApprovalRawResponse), -// ); - -// const allSignatures = flatten(allApprovals.map(a => a.signatures)); -// const allExpirationTimes = flatten(allApprovals.map(a => a.expirationTimeSeconds)); - -// // submit transaction with approvals -// const txHash = await this._submitCoordinatorTransactionAsync( -// transaction, -// takerAddress, -// transaction.signature, -// allExpirationTimes, -// allSignatures, -// orderTransactionOpts, -// ); -// return txHash; -// } else { -// // format errors and approvals -// // concatenate approvals -// const notCoordinatorOrders = signedOrders.filter(o => o.senderAddress !== this.address); -// const approvedOrdersNested = approvalResponses.map(resp => { -// const endpoint = resp.coordinatorOperator; -// const orders = serverEndpointsToOrders[endpoint]; -// return orders; -// }); -// const approvedOrders = flatten(approvedOrdersNested.concat(notCoordinatorOrders)); - -// // lookup orders with errors -// const errorsWithOrders = errorResponses.map(resp => { -// const endpoint = resp.coordinatorOperator; -// const orders = serverEndpointsToOrders[endpoint]; -// return { -// ...resp, -// orders, -// }; -// }); - -// // throw informative error -// const cancellations = new Array(); -// throw new CoordinatorServerError( -// CoordinatorServerErrorMsg.FillFailed, -// approvedOrders, -// cancellations, -// errorsWithOrders, -// ); -// } -// function formatRawResponse( -// rawResponse: CoordinatorServerApprovalRawResponse, -// ): CoordinatorServerApprovalResponse { -// return { -// signatures: ([] as string[]).concat(rawResponse.signatures), -// expirationTimeSeconds: ([] as BigNumber[]).concat( -// Array(rawResponse.signatures.length).fill(rawResponse.expirationTimeSeconds), -// ), -// }; -// } -// } - -// private async _getServerEndpointOrThrowAsync(feeRecipientAddress: string): Promise { -// const cached = this._feeRecipientToEndpoint[feeRecipientAddress]; -// const endpoint = -// cached !== undefined -// ? cached -// : await _fetchServerEndpointOrThrowAsync(feeRecipientAddress, this._registryInstance); -// return endpoint; - -// async function _fetchServerEndpointOrThrowAsync( -// feeRecipient: string, -// registryInstance: CoordinatorRegistryContract, -// ): Promise { -// const coordinatorOperatorEndpoint = await registryInstance.getCoordinatorEndpoint(feeRecipient).callAsync(); -// if (coordinatorOperatorEndpoint === '' || coordinatorOperatorEndpoint === undefined) { -// throw new Error( -// `No Coordinator server endpoint found in Coordinator Registry for feeRecipientAddress: ${feeRecipient}. Registry contract address: ${ -// registryInstance.address -// }`, -// ); -// } -// return coordinatorOperatorEndpoint; -// } -// } - -// private async _generateSignedZeroExTransactionAsync( -// data: string, -// signerAddress: string, -// ): Promise { -// const transaction: ZeroExTransaction = { -// salt: generatePseudoRandomSalt(), -// signerAddress, -// data, -// domain: { -// verifyingContract: this.exchangeAddress, -// chainId: await this._web3Wrapper.getChainIdAsync(), -// }, -// // HACK (xianny): arbitrary numbers for now -// expirationTimeSeconds: new BigNumber(5), -// gasPrice: new BigNumber(1), -// }; -// const signedTransaction = await signatureUtils.ecSignTransactionAsync( -// this._web3Wrapper.getProvider(), -// transaction, -// transaction.signerAddress, -// ); - -// return signedTransaction; -// } - -// private async _executeServerRequestAsync( -// signedTransaction: SignedZeroExTransaction, -// txOrigin: string, -// endpoint: string, -// ): Promise { -// const requestPayload = { -// signedTransaction, -// txOrigin, -// }; -// const response = await fetchAsync(`${endpoint}/v1/request_transaction?chainId=${this.chainId}`, { -// body: JSON.stringify(requestPayload), -// method: 'POST', -// headers: { -// 'Content-Type': 'application/json; charset=utf-8', -// }, -// }); - -// const isError = response.status !== HttpStatus.OK; -// const isValidationError = response.status === HttpStatus.BAD_REQUEST; -// const json = isError && !isValidationError ? undefined : await response.json(); - -// const result = { -// isError, -// status: response.status, -// body: isError ? undefined : json, -// error: isError ? json : undefined, -// request: requestPayload, -// coordinatorOperator: endpoint, -// }; - -// return result; -// } - -// private async _submitCoordinatorTransactionAsync( -// transaction: CoordinatorTransaction, -// txOrigin: string, -// transactionSignature: string, -// approvalExpirationTimeSeconds: BigNumber[], -// approvalSignatures: string[], -// orderTransactionOpts: OrderTransactionOpts, -// ): Promise { -// if (orderTransactionOpts.shouldValidate) { -// await this._contractInstance -// .executeTransaction( -// transaction, -// txOrigin, -// transactionSignature, -// approvalExpirationTimeSeconds, -// approvalSignatures, -// ) -// .callAsync({ -// from: txOrigin, -// gas: orderTransactionOpts.gasLimit, -// gasPrice: orderTransactionOpts.gasPrice, -// nonce: orderTransactionOpts.nonce, -// }); -// } -// const txHash = await this._contractInstance -// .executeTransaction( -// transaction, -// txOrigin, -// transactionSignature, -// approvalExpirationTimeSeconds, -// approvalSignatures, -// ) -// .sendTransactionAsync({ -// from: txOrigin, -// gas: orderTransactionOpts.gasLimit, -// gasPrice: orderTransactionOpts.gasPrice, -// nonce: orderTransactionOpts.nonce, -// }); -// return txHash; -// } - -// private async _mapServerEndpointsToOrdersAsync( -// coordinatorOrders: SignedOrder[], -// ): Promise<{ [endpoint: string]: SignedOrder[] }> { -// const feeRecipientsToOrders: { [feeRecipient: string]: SignedOrder[] } = {}; -// for (const order of coordinatorOrders) { -// const feeRecipient = order.feeRecipientAddress; -// if (feeRecipientsToOrders[feeRecipient] === undefined) { -// feeRecipientsToOrders[feeRecipient] = [] as SignedOrder[]; -// } -// feeRecipientsToOrders[feeRecipient].push(order); -// } -// const serverEndpointsToOrders: { [endpoint: string]: SignedOrder[] } = {}; -// for (const feeRecipient of Object.keys(feeRecipientsToOrders)) { -// const endpoint = await this._getServerEndpointOrThrowAsync(feeRecipient); -// const orders = feeRecipientsToOrders[feeRecipient]; -// if (serverEndpointsToOrders[endpoint] === undefined) { -// serverEndpointsToOrders[endpoint] = []; -// } -// serverEndpointsToOrders[endpoint] = serverEndpointsToOrders[endpoint].concat(orders); -// } -// return serverEndpointsToOrders; -// } -// } - -// function getMakerAddressOrThrow(orders: Array): string { -// const uniqueMakerAddresses = new Set(orders.map(o => o.makerAddress)); -// if (uniqueMakerAddresses.size > 1) { -// throw new Error(`All orders in a batch must have the same makerAddress`); -// } -// return orders[0].makerAddress; -// } - -// tslint:disable:max-file-line-count diff --git a/packages/contract-wrappers/src/index.ts b/packages/contract-wrappers/src/index.ts index e9fda9231a..8134407d09 100644 --- a/packages/contract-wrappers/src/index.ts +++ b/packages/contract-wrappers/src/index.ts @@ -55,17 +55,7 @@ export { ExchangeTransactionExecutionEventArgs, } from '@0x/abi-gen-wrappers'; -export { - OrderStatus, - ContractError, - ForwarderError, - CoordinatorServerCancellationResponse, - CoordinatorServerError, - ContractWrappersConfig, - OrderTransactionOpts, - TransactionOpts, - OrderInfo, -} from './types'; +export { OrderStatus, ContractError, ForwarderError, ContractWrappersConfig, OrderInfo } from './types'; export { BlockRange, diff --git a/packages/contract-wrappers/src/schemas/order_tx_opts_schema.ts b/packages/contract-wrappers/src/schemas/order_tx_opts_schema.ts deleted file mode 100644 index 31ad759d5d..0000000000 --- a/packages/contract-wrappers/src/schemas/order_tx_opts_schema.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const orderTxOptsSchema = { - id: '/OrderTxOpts', - allOf: [{ $ref: '/TxOpts' }], - properties: { - shouldValidate: { type: 'boolean' }, - }, - type: 'object', -}; diff --git a/packages/contract-wrappers/src/schemas/tx_opts_schema.ts b/packages/contract-wrappers/src/schemas/tx_opts_schema.ts deleted file mode 100644 index 1c1588db78..0000000000 --- a/packages/contract-wrappers/src/schemas/tx_opts_schema.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const txOptsSchema = { - id: '/TxOpts', - properties: { - gasPrice: { $ref: '/numberSchema' }, - gasLimit: { type: 'number' }, - nonce: { type: 'number' }, - }, - type: 'object', -}; diff --git a/packages/contract-wrappers/src/types.ts b/packages/contract-wrappers/src/types.ts index 699e27233e..8e6d83b050 100644 --- a/packages/contract-wrappers/src/types.ts +++ b/packages/contract-wrappers/src/types.ts @@ -1,13 +1,6 @@ import { ContractAddresses } from '@0x/contract-addresses'; import { BigNumber } from '@0x/utils'; -export interface TxOpts { - from: string; - gas?: number; - value?: BigNumber; - gasPrice?: BigNumber; -} - export enum ForwarderError { CompleteFillFailed = 'COMPLETE_FILL_FAILED', } @@ -40,27 +33,6 @@ export interface ContractWrappersConfig { blockPollingIntervalMs?: number; } -// TODO(xianny): remove after refactoring coordinator wrapper -/** - * gasPrice: Gas price in Wei to use for a transaction - * gasLimit: The amount of gas to send with a transaction (in Gwei) - * nonce: The nonce to use for a transaction. If not specified, it defaults to the next incremented nonce. - */ -export interface TransactionOpts { - gasPrice?: BigNumber; - gasLimit?: number; - nonce?: number; -} - -// TODO(xianny): remove after refactoring coordinator wrapper -/** - * shouldValidate: Flag indicating whether the library should make attempts to validate a transaction before - * broadcasting it. For example, order has a valid signature, maker has sufficient funds, etc. Default=true. - */ -export interface OrderTransactionOpts extends TransactionOpts { - shouldValidate?: boolean; -} - export interface OrderInfo { orderStatus: OrderStatus; orderHash: string; @@ -92,11 +64,3 @@ export interface OrderAndTraderInfo { orderInfo: OrderInfo; traderInfo: TraderInfo; } - -export { CoordinatorServerCancellationResponse, CoordinatorServerError } from './utils/coordinator_server_types'; - -export interface CoordinatorTransaction { - salt: BigNumber; - signerAddress: string; - data: string; -} diff --git a/packages/contract-wrappers/test/calldata_decoder_test.ts b/packages/contract-wrappers/test/calldata_decoder_test.ts index 15f74a34e1..5091d8aeeb 100644 --- a/packages/contract-wrappers/test/calldata_decoder_test.ts +++ b/packages/contract-wrappers/test/calldata_decoder_test.ts @@ -1,5 +1,6 @@ import { constants, OrderFactory } from '@0x/contracts-test-utils'; import { BlockchainLifecycle } from '@0x/dev-utils'; +import { migrateOnceAsync } from '@0x/migrations'; import { SignedOrder } from '@0x/types'; import { addressUtils, BigNumber } from '@0x/utils'; import * as chai from 'chai'; @@ -9,7 +10,6 @@ import 'mocha'; import { ContractAddresses, ContractWrappers } from '../src'; import { chaiSetup } from './utils/chai_setup'; -import { migrateOnceAsync } from './utils/migrate'; import { provider, web3Wrapper } from './utils/web3_wrapper'; chaiSetup.configure(); diff --git a/packages/contract-wrappers/test/coordinator_wrapper_test.ts b/packages/contract-wrappers/test/coordinator_wrapper_test.ts deleted file mode 100644 index 7455cdcb64..0000000000 --- a/packages/contract-wrappers/test/coordinator_wrapper_test.ts +++ /dev/null @@ -1,675 +0,0 @@ -// import { constants, OrderFactory } from '@0x/contracts-test-utils'; -// import { defaultOrmConfig, getAppAsync } from '@0x/coordinator-server'; -// import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; -// import { SignedOrder } from '@0x/types'; -// import { BigNumber, fetchAsync, logUtils, providerUtils } from '@0x/utils'; -// import * as chai from 'chai'; -// import * as http from 'http'; -// import 'mocha'; -// import * as nock from 'nock'; - -// import { ContractWrappers } from '../src'; -// import { CoordinatorRegistryContract } from '../src/index'; -// import { CoordinatorServerErrorMsg } from '../src/utils/coordinator_server_types'; - -// import { chaiSetup } from './utils/chai_setup'; -// import { migrateOnceAsync } from './utils/migrate'; -// import { provider, web3Wrapper } from './utils/web3_wrapper'; - -// chaiSetup.configure(); -// const expect = chai.expect; -// const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -// const coordinatorPort = '3000'; -// const anotherCoordinatorPort = '4000'; -// const coordinatorEndpoint = 'http://localhost:'; - -// // tslint:disable:custom-no-magic-numbers -// // TODO (xianny): coordinator server must be updated to take new SignedOrder format. it returns all errors at the moment -// describe.skip('CoordinatorWrapper', () => { -// const takerTokenFillAmount = new BigNumber(5); - -// let chainId: number; -// let coordinatorServerApp: http.Server; -// let anotherCoordinatorServerApp: http.Server; -// let contractWrappers: ContractWrappers; -// let orderFactory: OrderFactory; -// let exchangeContractAddress: string; -// let userAddresses: string[]; -// let makerAddress: string; -// let takerAddress: string; -// let feeRecipientAddressOne: string; -// let feeRecipientAddressTwo: string; -// let feeRecipientAddressThree: string; -// let feeRecipientAddressFour: string; - -// let makerTokenAddress: string; -// let takerTokenAddress: string; -// let feeTokenAddress: string; -// let makerAssetData: string; -// let takerAssetData: string; -// let feeAssetData: string; -// let txHash: string; -// let signedOrder: SignedOrder; -// let anotherSignedOrder: SignedOrder; -// let signedOrderWithDifferentFeeRecipient: SignedOrder; -// let signedOrderWithDifferentCoordinatorOperator: SignedOrder; -// let coordinatorRegistryInstance: CoordinatorRegistryContract; - -// // for testing server error responses -// let serverValidationError: any; -// let serverCancellationSuccess: any; -// let serverApprovalSuccess: any; - -// before(async () => { -// const contractAddresses = await migrateOnceAsync(); -// await blockchainLifecycle.startAsync(); -// const config = { -// chainId: constants.TESTRPC_CHAIN_ID, -// contractAddresses, -// blockPollingIntervalMs: 10, -// }; -// contractWrappers = new ContractWrappers(provider, config); -// chainId = await providerUtils.getChainIdAsync(provider); -// exchangeContractAddress = contractWrappers.exchange.address; -// userAddresses = await web3Wrapper.getAvailableAddressesAsync(); -// [ -// , -// makerAddress, -// takerAddress, -// feeRecipientAddressOne, -// feeRecipientAddressTwo, -// feeRecipientAddressThree, -// feeRecipientAddressFour, -// ] = userAddresses.slice(0, 7); - -// [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); -// feeTokenAddress = contractAddresses.zrxToken; -// [makerAssetData, takerAssetData, feeAssetData] = [ -// await contractWrappers.devUtils.encodeERC20AssetData(makerTokenAddress).callAsync(), -// await contractWrappers.devUtils.encodeERC20AssetData(takerTokenAddress).callAsync(), -// await contractWrappers.devUtils.encodeERC20AssetData(feeTokenAddress).callAsync(), -// ]; - -// // Configure order defaults -// const defaultOrderParams = { -// ...constants.STATIC_ORDER_PARAMS, -// makerAddress, -// feeRecipientAddress: feeRecipientAddressOne, -// makerAssetData, -// takerAssetData, -// makerFeeAssetData: feeAssetData, -// takerFeeAssetData: feeAssetData, -// senderAddress: contractAddresses.coordinator, -// exchangeAddress: exchangeContractAddress, -// chainId, -// }; -// const privateKey = constants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)]; -// orderFactory = new OrderFactory(privateKey, defaultOrderParams); - -// // set up mock coordinator server -// const coordinatorServerConfigs = { -// HTTP_PORT: 3000, // Only used in default instantiation in 0x-coordinator-server/server.js; not used here -// NETWORK_ID_TO_SETTINGS: { -// // TODO: change to CHAIN_ID_TO_SETTINGS when @0x/coordinator-server is ready -// [config.chainId]: { -// FEE_RECIPIENTS: [ -// { -// ADDRESS: feeRecipientAddressOne, -// PRIVATE_KEY: constants.TESTRPC_PRIVATE_KEYS[ -// userAddresses.indexOf(feeRecipientAddressOne) -// ].toString('hex'), -// }, -// { -// ADDRESS: feeRecipientAddressTwo, -// PRIVATE_KEY: constants.TESTRPC_PRIVATE_KEYS[ -// userAddresses.indexOf(feeRecipientAddressTwo) -// ].toString('hex'), -// }, -// { -// ADDRESS: feeRecipientAddressThree, -// PRIVATE_KEY: constants.TESTRPC_PRIVATE_KEYS[ -// userAddresses.indexOf(feeRecipientAddressThree) -// ].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 -// [config.chainId]: contractAddresses, -// }, -// // Optional selective delay on fill requests -// SELECTIVE_DELAY_MS: 0, -// EXPIRATION_DURATION_SECONDS: 60, // 1 minute -// }; -// coordinatorServerApp = await getAppAsync( -// { -// [config.chainId]: provider, -// }, -// coordinatorServerConfigs, -// { -// name: 'coord_server_1', -// type: 'sqlite', -// database: ':memory:', -// entities: defaultOrmConfig.entities, -// cli: defaultOrmConfig.cli, -// logging: defaultOrmConfig.logging, -// synchronize: defaultOrmConfig.synchronize, -// }, -// ); - -// coordinatorServerApp.listen(coordinatorPort, () => { -// logUtils.log(`Coordinator SERVER API (HTTP) listening on port ${coordinatorPort}!`); -// }); - -// anotherCoordinatorServerApp = await getAppAsync( -// { -// [config.chainId]: provider, -// }, -// coordinatorServerConfigs, -// { -// type: 'sqlite', -// name: 'coord_server_2', -// database: ':memory:', -// entities: defaultOrmConfig.entities, -// cli: defaultOrmConfig.cli, -// logging: defaultOrmConfig.logging, -// synchronize: defaultOrmConfig.synchronize, -// }, -// ); - -// anotherCoordinatorServerApp.listen(anotherCoordinatorPort, () => { -// logUtils.log(`Coordinator SERVER API (HTTP) listening on port ${anotherCoordinatorPort}!`); -// }); - -// // setup coordinator registry -// coordinatorRegistryInstance = new CoordinatorRegistryContract(contractAddresses.coordinatorRegistry, provider); - -// // register coordinator server -// await web3Wrapper.awaitTransactionSuccessAsync( -// await coordinatorRegistryInstance -// .setCoordinatorEndpoint(`${coordinatorEndpoint}${coordinatorPort}`) -// .sendTransactionAsync({ -// from: feeRecipientAddressOne, -// }), -// constants.AWAIT_TRANSACTION_MINED_MS, -// ); -// await web3Wrapper.awaitTransactionSuccessAsync( -// await coordinatorRegistryInstance -// .setCoordinatorEndpoint(`${coordinatorEndpoint}${coordinatorPort}`) -// .sendTransactionAsync({ -// from: feeRecipientAddressTwo, -// }), -// constants.AWAIT_TRANSACTION_MINED_MS, -// ); - -// // register another coordinator server -// await web3Wrapper.awaitTransactionSuccessAsync( -// await coordinatorRegistryInstance -// .setCoordinatorEndpoint(`${coordinatorEndpoint}${anotherCoordinatorPort}`) -// .sendTransactionAsync({ -// from: feeRecipientAddressThree, -// }), -// constants.AWAIT_TRANSACTION_MINED_MS, -// ); -// }); -// after(async () => { -// await blockchainLifecycle.revertAsync(); -// }); -// beforeEach(async () => { -// await blockchainLifecycle.startAsync(); -// signedOrder = await orderFactory.newSignedOrderAsync(); -// anotherSignedOrder = await orderFactory.newSignedOrderAsync(); -// signedOrderWithDifferentFeeRecipient = await orderFactory.newSignedOrderAsync({ -// feeRecipientAddress: feeRecipientAddressTwo, -// }); -// signedOrderWithDifferentCoordinatorOperator = await orderFactory.newSignedOrderAsync({ -// feeRecipientAddress: feeRecipientAddressThree, -// }); -// }); -// afterEach(async () => { -// await blockchainLifecycle.revertAsync(); -// }); -// describe('test setup', () => { -// it('should have coordinator registry which returns an endpoint', async () => { -// const setCoordinatorEndpoint = await coordinatorRegistryInstance -// .getCoordinatorEndpoint(feeRecipientAddressOne) -// .callAsync(); -// const anotherSetCoordinatorEndpoint = await coordinatorRegistryInstance -// .getCoordinatorEndpoint(feeRecipientAddressThree) -// .callAsync(); -// expect(setCoordinatorEndpoint).to.be.equal(`${coordinatorEndpoint}${coordinatorPort}`); -// expect(anotherSetCoordinatorEndpoint).to.be.equal(`${coordinatorEndpoint}${anotherCoordinatorPort}`); -// }); -// it('should have coordinator server endpoints which respond to pings', async () => { -// let result = await fetchAsync(`${coordinatorEndpoint}${coordinatorPort}/v1/ping`); -// expect(result.status).to.be.equal(200); -// expect(await result.text()).to.be.equal('pong'); - -// result = await fetchAsync(`${coordinatorEndpoint}${anotherCoordinatorPort}/v1/ping`); -// expect(result.status).to.be.equal(200); -// expect(await result.text()).to.be.equal('pong'); -// }); -// }); -// // fill handling is the same for all fill methods so we can test them all through the fillOrder and batchFillOrders interfaces -// describe('fill order(s)', () => { -// describe('#fillOrderAsync', () => { -// it('should fill a valid order', async () => { -// txHash = await contractWrappers.coordinator.fillOrderAsync( -// signedOrder, -// takerTokenFillAmount, -// takerAddress, -// ); - -// await 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 contractWrappers.coordinator.batchFillOrdersAsync( -// signedOrders, -// takerAssetFillAmounts, -// takerAddress, -// ); - -// await 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 contractWrappers.coordinator.batchFillOrdersAsync( -// signedOrders, -// takerAssetFillAmounts, -// takerAddress, -// ); - -// await 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 contractWrappers.coordinator.batchFillOrdersAsync( -// signedOrders, -// takerAssetFillAmounts, -// takerAddress, -// ); - -// await 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 contractWrappers.coordinator.batchFillOrdersAsync( -// signedOrders, -// takerAssetFillAmounts, -// takerAddress, -// ); -// await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); -// }); -// }); -// }); -// describe('soft cancel order(s)', () => { -// describe('#softCancelOrderAsync', () => { -// it('should soft cancel a valid order', async () => { -// const response = await contractWrappers.coordinator.softCancelOrderAsync(signedOrder); -// expect(response.outstandingFillSignatures).to.have.lengthOf(0); -// expect(response.cancellationSignatures).to.have.lengthOf(1); -// }); -// }); -// describe('#batchSoftCancelOrdersAsync', () => { -// it('should soft cancel a batch of valid orders', async () => { -// const orders = [signedOrder, anotherSignedOrder]; -// const response = await contractWrappers.coordinator.batchSoftCancelOrdersAsync(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 contractWrappers.coordinator.batchSoftCancelOrdersAsync(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 contractWrappers.coordinator.batchSoftCancelOrdersAsync(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('hard cancel order(s)', () => { -// describe('#hardCancelOrderAsync', () => { -// it('should hard cancel a valid order', async () => { -// txHash = await contractWrappers.coordinator.hardCancelOrderAsync(signedOrder); -// await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); -// }); -// }); -// describe('#batchHardCancelOrdersAsync', () => { -// it('should hard cancel a batch of valid orders', async () => { -// const orders = [signedOrder, anotherSignedOrder]; -// txHash = await contractWrappers.coordinator.batchHardCancelOrdersAsync(orders); -// await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); -// }); -// }); -// describe('#cancelOrdersUpTo/getOrderEpochAsync', () => { -// it('should hard cancel orders up to target order epoch', async () => { -// const targetOrderEpoch = new BigNumber(42); -// txHash = await contractWrappers.coordinator.hardCancelOrdersUpToAsync(targetOrderEpoch, makerAddress); - -// await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); -// const orderEpoch = await contractWrappers.exchange -// .orderEpoch(makerAddress, contractWrappers.coordinator.address) -// .callAsync(); -// expect(orderEpoch).to.be.bignumber.equal(targetOrderEpoch.plus(1)); -// }); -// }); -// }); -// describe('coordinator edge cases', () => { -// it('should throw error when feeRecipientAddress is not in registry', async () => { -// const badOrder = await orderFactory.newSignedOrderAsync({ -// feeRecipientAddress: feeRecipientAddressFour, -// }); - -// expect( -// contractWrappers.coordinator.fillOrderAsync(badOrder, takerTokenFillAmount, takerAddress), -// ).to.be.rejected(); -// }); -// it('should throw error when coordinator endpoint is malformed', async () => { -// await web3Wrapper.awaitTransactionSuccessAsync( -// await coordinatorRegistryInstance.setCoordinatorEndpoint('localhost').sendTransactionAsync({ -// from: feeRecipientAddressFour, -// }), -// constants.AWAIT_TRANSACTION_MINED_MS, -// ); -// expect( -// contractWrappers.coordinator.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress), -// ).to.be.rejected(); -// }); -// }); -// describe('coordinator server errors', () => { -// beforeEach('setup', () => { -// serverValidationError = { -// code: 100, -// reason: 'Validation Failed', -// validationErrors: [ -// { -// field: 'signedTransaction', -// code: 1011, -// 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.', -// }, -// ], -// }; -// serverCancellationSuccess = { -// outstandingFillSignatures: [ -// { -// orderHash: '0xd1dc61f3e7e5f41d72beae7863487beea108971de678ca00d903756f842ef3ce', -// approvalSignatures: [ -// '0x1c7383ca8ebd6de8b5b20b1c2d49bea166df7dfe4af1932c9c52ec07334e859cf2176901da35f4480ceb3ab63d8d0339d851c31929c40d88752689b9a8a535671303', -// ], -// expirationTimeSeconds: 1552390380, -// takerAssetFillAmount: 100000000000000000000, -// }, -// ], -// cancellationSignatures: [ -// '0x2ea3117a8ebd6de8b5b20b1c2d49bea166df7dfe4af1932c9c52ec07334e859cf2176901da35f4480ceb3ab63d8d0339d851c31929c40d88752689b9a855b5a7b401', -// ], -// }; -// serverApprovalSuccess = { -// signatures: [ -// '0x1cc07d7ae39679690a91418d46491520f058e4fb14debdf2e98f2376b3970de8512ace44af0be6d1c65617f7aae8c2364ff63f241515ee1559c3eeecb0f671d9e903', -// ], -// expirationTimeSeconds: 1552390014, -// }; - -// nock(`${coordinatorEndpoint}${coordinatorPort}`) -// .post('/v1/request_transaction', () => true) -// .query({ -// chainId: 1337, -// }) -// .reply(400, serverValidationError); -// }); -// it('should throw error when softCancel fails', done => { -// contractWrappers.coordinator -// .softCancelOrderAsync(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}`); -// done(); -// }); -// }); -// it('should throw error when batch soft cancel fails with single coordinator operator', done => { -// const orders = [signedOrder, signedOrderWithDifferentFeeRecipient]; -// contractWrappers.coordinator -// .batchSoftCancelOrdersAsync(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}`); -// done(); -// }); -// }); -// it('should throw consolidated error when batch soft cancel partially fails with different coordinator operators', done => { -// nock(`${coordinatorEndpoint}${anotherCoordinatorPort}`) -// .post('/v1/request_transaction', () => true) -// .query({ -// chainId: 1337, -// }) -// .reply(200, serverCancellationSuccess); - -// const signedOrders = [signedOrder, signedOrderWithDifferentCoordinatorOperator]; -// contractWrappers.coordinator -// .batchSoftCancelOrdersAsync(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}`); -// done(); -// }); -// }); -// it('should throw consolidated error when batch soft cancel totally fails with different coordinator operators', done => { -// nock(`${coordinatorEndpoint}${anotherCoordinatorPort}`) -// .post('/v1/request_transaction', () => true) -// .query({ -// chainId: 1337, -// }) -// .reply(400, serverValidationError); - -// const signedOrders = [signedOrder, signedOrderWithDifferentCoordinatorOperator]; -// contractWrappers.coordinator -// .batchSoftCancelOrdersAsync(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}`, -// ); -// done(); -// }); -// }); -// it('should throw error when a fill fails', done => { -// contractWrappers.coordinator -// .fillOrderAsync(signedOrder, takerTokenFillAmount, 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}`); -// done(); -// }); -// }); -// it('should throw error when batch fill fails with single coordinator operator', done => { -// const signedOrders = [signedOrder, signedOrderWithDifferentFeeRecipient]; -// const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount, takerTokenFillAmount]; -// contractWrappers.coordinator -// .batchFillOrdersAsync(signedOrders, takerAssetFillAmounts, 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}`); -// done(); -// }); -// }); -// it('should throw consolidated error when batch fill partially fails with different coordinator operators', done => { -// nock(`${coordinatorEndpoint}${anotherCoordinatorPort}`) -// .post('/v1/request_transaction', () => true) -// .query({ -// chainId: 1337, -// }) -// .reply(200, serverApprovalSuccess); - -// const signedOrders = [signedOrder, signedOrderWithDifferentCoordinatorOperator]; -// const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount, takerTokenFillAmount]; -// contractWrappers.coordinator -// .batchFillOrdersAsync(signedOrders, takerAssetFillAmounts, 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}`); -// done(); -// }); -// }); -// it('should throw consolidated error when batch fill totally fails with different coordinator operators', done => { -// nock(`${coordinatorEndpoint}${anotherCoordinatorPort}`) -// .post('/v1/request_transaction', () => true) -// .query({ -// chainId: 1337, -// }) -// .reply(400, serverValidationError); - -// const signedOrders = [signedOrder, signedOrderWithDifferentCoordinatorOperator]; -// const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount, takerTokenFillAmount]; -// contractWrappers.coordinator -// .batchFillOrdersAsync(signedOrders, takerAssetFillAmounts, 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}`, -// ); -// done(); -// }); -// }); -// }); -// }); -// tslint:disable:max-file-line-count diff --git a/packages/contract-wrappers/test/utils/migrate.ts b/packages/contract-wrappers/test/utils/migrate.ts deleted file mode 100644 index 665ce0faaa..0000000000 --- a/packages/contract-wrappers/test/utils/migrate.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ContractAddresses } from '@0x/contract-addresses'; -import { devConstants } from '@0x/dev-utils'; -import { runMigrationsOnceAsync } from '@0x/migrations'; - -import { provider } from './web3_wrapper'; - -/** - * Configures and runs the migrations exactly once. Any subsequent times this is - * called, it returns the cached addresses. - * @returns The addresses of contracts that were deployed during the migrations. - */ -export async function migrateOnceAsync(): Promise { - const txDefaults = { - gas: devConstants.GAS_LIMIT, - from: devConstants.TESTRPC_FIRST_ADDRESS, - }; - return runMigrationsOnceAsync(provider, txDefaults); -} diff --git a/packages/migrations/src/index.ts b/packages/migrations/src/index.ts index 141aa76046..f1e2006d77 100644 --- a/packages/migrations/src/index.ts +++ b/packages/migrations/src/index.ts @@ -17,4 +17,5 @@ export { } from 'ethereum-types'; export { ContractAddresses } from '@0x/contract-addresses'; export { runMigrationsAsync, runMigrationsOnceAsync } from './migration'; +export { migrateOnceAsync } from './migrate_with_test_defaults'; export import Web3ProviderEngine = require('web3-provider-engine'); diff --git a/packages/asset-swapper/test/utils/migrate.ts b/packages/migrations/src/migrate_with_test_defaults.ts similarity index 66% rename from packages/asset-swapper/test/utils/migrate.ts rename to packages/migrations/src/migrate_with_test_defaults.ts index 665ce0faaa..fa00a1e8f8 100644 --- a/packages/asset-swapper/test/utils/migrate.ts +++ b/packages/migrations/src/migrate_with_test_defaults.ts @@ -1,8 +1,8 @@ import { ContractAddresses } from '@0x/contract-addresses'; -import { devConstants } from '@0x/dev-utils'; -import { runMigrationsOnceAsync } from '@0x/migrations'; +import { devConstants, web3Factory } from '@0x/dev-utils'; +import { Web3ProviderEngine } from '@0x/subproviders'; -import { provider } from './web3_wrapper'; +import { runMigrationsOnceAsync } from './index'; /** * Configures and runs the migrations exactly once. Any subsequent times this is @@ -14,5 +14,6 @@ export async function migrateOnceAsync(): Promise { gas: devConstants.GAS_LIMIT, from: devConstants.TESTRPC_FIRST_ADDRESS, }; + const provider: Web3ProviderEngine = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true }); return runMigrationsOnceAsync(provider, txDefaults); } diff --git a/packages/monorepo-scripts/src/utils/doc_generate_utils.ts b/packages/monorepo-scripts/src/utils/doc_generate_utils.ts index 436775e608..f9c9e49944 100644 --- a/packages/monorepo-scripts/src/utils/doc_generate_utils.ts +++ b/packages/monorepo-scripts/src/utils/doc_generate_utils.ts @@ -264,7 +264,7 @@ export class DocGenerateUtils { if (!_.isEmpty(missingReferences)) { throw new Error( `${this._packageName} package needs to export: \n${missingReferences.join( - '\n', + ',\n', )} \nFrom it\'s index.ts. If any are from external dependencies, then add them to the EXTERNAL_TYPE_MAP.`, ); } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index c52de62441..3af01e21fb 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -670,18 +670,6 @@ export interface Type { tupleElements?: Type[]; } -/** - * * shouldValidate: Flag indicating whether the library should make attempts to validate a transaction before - * broadcasting it. For example, order has a valid signature, maker has sufficient funds, etc. Default=true. - * * pollingIntervalMs: Used with `awaitTransactionSuccessAsync` to determine polling interval in milliseconds - * * timeoutMs: Used with `awaitTransactionSuccessAsync` to determine timeout in milliseconds - */ -export interface SendTransactionOpts { - shouldValidate?: boolean; - pollingIntervalMs?: number; - timeoutMs?: number; -} - export interface ElementType { name: string; typeDocType: TypeDocTypes; diff --git a/yarn.lock b/yarn.lock index 92e45f7189..a03bfa7873 100644 --- a/yarn.lock +++ b/yarn.lock @@ -713,7 +713,7 @@ uuid "^3.3.2" websocket "^1.0.26" -"@0x/contract-addresses@^3.0.1", "@0x/contract-addresses@^3.0.2", "@0x/contract-addresses@^3.2.0": +"@0x/contract-addresses@^3.0.2", "@0x/contract-addresses@^3.2.0": version "3.2.0" resolved "https://registry.npmjs.org/@0x/contract-addresses/-/contract-addresses-3.2.0.tgz#606307696d9622764220a34e9d4638b899093eec" dependencies: @@ -723,7 +723,7 @@ version "2.2.2" resolved "https://registry.npmjs.org/@0x/contract-artifacts/-/contract-artifacts-2.2.2.tgz#e6d771afb58d0b59c19c5364af5a42a3dfd17219" -"@0x/contract-wrappers@^9.1.6", "@0x/contract-wrappers@^9.1.7": +"@0x/contract-wrappers@^9.1.7": version "9.1.8" resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-9.1.8.tgz#5923d35af3e4b442a57d02f74e02620b2d5b1356" dependencies: @@ -747,49 +747,21 @@ lodash "^4.17.11" uuid "^3.3.2" -"@0x/contracts-erc20@^2.2.7": - version "2.2.14" - resolved "https://registry.npmjs.org/@0x/contracts-erc20/-/contracts-erc20-2.2.14.tgz#bac2528a590c0f9668811cfd23948a941ae0ad30" +"@0x/coordinator-server@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@0x/coordinator-server/-/coordinator-server-1.0.3.tgz#736640edc5960bd65674436f96050fd7b4da7ac3" dependencies: - "@0x/base-contract" "^5.4.0" - "@0x/contracts-utils" "^3.2.4" - "@0x/types" "^2.4.3" - "@0x/typescript-typings" "^4.3.0" - "@0x/utils" "^4.5.2" - "@0x/web3-wrapper" "^6.0.13" - ethereum-types "^2.1.6" - lodash "^4.17.11" - -"@0x/contracts-utils@^3.2.4": - version "3.2.4" - resolved "https://registry.npmjs.org/@0x/contracts-utils/-/contracts-utils-3.2.4.tgz#b5ae80684ac0542eb59925f52113ce2c8b1bdb2b" - dependencies: - "@0x/base-contract" "^5.4.0" - "@0x/order-utils" "^8.4.0" - "@0x/types" "^2.4.3" - "@0x/typescript-typings" "^4.3.0" - "@0x/utils" "^4.5.2" - "@0x/web3-wrapper" "^6.0.13" - bn.js "^4.11.8" - ethereum-types "^2.1.6" - ethereumjs-util "^5.1.1" - lodash "^4.17.11" - -"@0x/coordinator-server@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@0x/coordinator-server/-/coordinator-server-0.1.3.tgz#5fbb7c11bb641aa5386797769cab9a68a7d15b79" - dependencies: - "@0x/assert" "^2.1.0" - "@0x/contract-addresses" "^3.0.1" - "@0x/contract-wrappers" "^9.1.6" - "@0x/contracts-erc20" "^2.2.7" - "@0x/json-schemas" "^3.0.11" - "@0x/order-utils" "^8.2.1" - "@0x/subproviders" "^4.1.1" - "@0x/types" "^2.4.0" - "@0x/typescript-typings" "^4.2.3" - "@0x/utils" "^4.4.0" - "@0x/web3-wrapper" "^6.0.7" + "@0x/assert" "2.2.0-beta.2" + "@0x/contract-addresses" "3.3.0-beta.3" + "@0x/contract-wrappers" "12.2.0-beta.2" + "@0x/contracts-erc20" "2.3.0-beta.2" + "@0x/json-schemas" "4.1.0-beta.2" + "@0x/order-utils" "8.5.0-beta.2" + "@0x/subproviders" "5.1.0-beta.2" + "@0x/types" "2.5.0-beta.2" + "@0x/typescript-typings" "4.4.0-beta.2" + "@0x/utils" "4.6.0-beta.2" + "@0x/web3-wrapper" "6.1.0-beta.2" "@babel/polyfill" "^7.0.0" body-parser "^1.18.3" cors "^2.8.5" @@ -835,7 +807,7 @@ uuid "^3.3.2" websocket "^1.0.29" -"@0x/order-utils@^8.2.1", "@0x/order-utils@^8.2.2", "@0x/order-utils@^8.2.3", "@0x/order-utils@^8.4.0": +"@0x/order-utils@^8.2.2", "@0x/order-utils@^8.2.3", "@0x/order-utils@^8.4.0": version "8.4.0" resolved "https://registry.npmjs.org/@0x/order-utils/-/order-utils-8.4.0.tgz#f7fe9c73f9fd82ab05ec3c04951049e904aab46a" dependencies: