Refactor/3.0/coordinator client (#2348)

* deduplicate migrateOnceAsync() test helper

* move and rename coordinator client to @0x/contracts-coordinator
This commit is contained in:
Xianny 2019-11-22 12:19:00 -08:00 committed by GitHub
parent f6d26392fb
commit 50d5b4fa37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1604 additions and 1625 deletions

View File

@ -84,12 +84,16 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0x/assert": "^2.2.0-beta.2",
"@0x/base-contract": "^5.5.0-beta.3", "@0x/base-contract": "^5.5.0-beta.3",
"@0x/contracts-utils": "^3.3.0-beta.3", "@0x/contracts-utils": "^3.3.0-beta.3",
"@0x/types": "^2.5.0-beta.2", "@0x/types": "^2.5.0-beta.2",
"@0x/typescript-typings": "^4.4.0-beta.2", "@0x/typescript-typings": "^4.4.0-beta.2",
"@0x/utils": "^4.6.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": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -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<void> {
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<TxData>,
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<SendTransactionOpts> = { shouldValidate: true },
): Promise<string> {
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<SendTransactionOpts> = { shouldValidate: true },
): Promise<string> {
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<SendTransactionOpts>,
): Promise<string> {
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<SendTransactionOpts>,
): Promise<string> {
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<SendTransactionOpts>,
): Promise<string> {
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<string> {
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<string> {
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<string> {
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<string> {
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<string> {
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<string> {
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<string> {
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<string> {
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<string> {
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<CoordinatorServerCancellationResponse> {
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<CoordinatorServerCancellationResponse[]> {
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<string> {
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<string> {
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<string> {
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<SendTransactionOpts>,
ordersNeedingApprovals: Order[],
...args: any[] // tslint:disable-line:trailing-comma
): Promise<string> {
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<SignedZeroExTransaction> {
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<string[]> {
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<string> {
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<string> {
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<CoordinatorServerResponse> {
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<Order | SignedOrder>): 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

View File

@ -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 { Order } from '@0x/types'; // tslint:disable-line:no-unused-variable
import { BigNumber } from '@0x/utils'; // tslint:disable-line:no-unused-variable import { BigNumber } from '@0x/utils'; // tslint:disable-line:no-unused-variable
import { Web3Wrapper } from '@0x/web3-wrapper'; import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
export const assert = { export const assert = {
...sharedAssert, ...sharedAssert,

View File

@ -2,10 +2,6 @@ import { Order, SignedOrder, SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
export interface CoordinatorServerApprovalResponse { export interface CoordinatorServerApprovalResponse {
signatures: string[];
expirationTimeSeconds: BigNumber[];
}
export interface CoordinatorServerApprovalRawResponse {
signatures: string[]; signatures: string[];
expirationTimeSeconds: BigNumber; expirationTimeSeconds: BigNumber;
} }
@ -24,7 +20,7 @@ export interface CoordinatorOutstandingFillSignatures {
export interface CoordinatorServerResponse { export interface CoordinatorServerResponse {
isError: boolean; isError: boolean;
status: number; status: number;
body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalRawResponse; body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalResponse;
error?: any; error?: any;
request: CoordinatorServerRequest; request: CoordinatorServerRequest;
coordinatorOperator: string; coordinatorOperator: string;
@ -38,12 +34,12 @@ export interface CoordinatorServerRequest {
export class CoordinatorServerError extends Error { export class CoordinatorServerError extends Error {
public message: CoordinatorServerErrorMsg; public message: CoordinatorServerErrorMsg;
public approvedOrders?: SignedOrder[] = []; public approvedOrders?: Order[] = [];
public cancellations?: CoordinatorServerCancellationResponse[] = []; public cancellations?: CoordinatorServerCancellationResponse[] = [];
public errors: CoordinatorServerResponse[]; public errors: CoordinatorServerResponse[];
constructor( constructor(
message: CoordinatorServerErrorMsg, message: CoordinatorServerErrorMsg,
approvedOrders: SignedOrder[], approvedOrders: Order[],
cancellations: CoordinatorServerCancellationResponse[], cancellations: CoordinatorServerCancellationResponse[],
errors: CoordinatorServerResponse[], errors: CoordinatorServerResponse[],
) { ) {

View File

@ -8,9 +8,18 @@ export {
LibEIP712CoordinatorDomainContract, LibEIP712CoordinatorDomainContract,
} from './wrappers'; } from './wrappers';
export import CoordinatorRevertErrors = require('./revert_errors'); export import CoordinatorRevertErrors = require('./revert_errors');
export { CoordinatorServerCancellationResponse } from './client/index';
export { ApprovalFactory } from './approval_factory'; export { ApprovalFactory } from './approval_factory';
export { SignedCoordinatorApproval } from './types'; 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 { export {
ContractArtifact, ContractArtifact,
ContractChains, ContractChains,
@ -38,4 +47,20 @@ export {
ConstructorStateMutability, ConstructorStateMutability,
TupleDataItem, TupleDataItem,
StateMutability, StateMutability,
SupportedProvider,
TxData,
TxDataPayable,
Web3JsProvider,
GanacheProvider,
EIP1193Provider,
ZeroExProvider,
EIP1193Event,
JSONRPCRequestPayload,
JSONRPCErrorCallback,
Web3JsV1Provider,
Web3JsV2Provider,
Web3JsV3Provider,
JSONRPCResponsePayload,
JSONRPCResponseError,
} from 'ethereum-types'; } from 'ethereum-types';
export { CoordinatorClient, CoordinatorServerErrorMsg } from './client/index';

View File

@ -1,4 +1,5 @@
import { SignedZeroExTransaction } from '@0x/types'; import { SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
export interface CoordinatorApproval { export interface CoordinatorApproval {
transaction: SignedZeroExTransaction; transaction: SignedZeroExTransaction;
@ -8,3 +9,9 @@ export interface CoordinatorApproval {
export interface SignedCoordinatorApproval extends CoordinatorApproval { export interface SignedCoordinatorApproval extends CoordinatorApproval {
signature: string; signature: string;
} }
export interface CoordinatorTransaction {
salt: BigNumber;
signerAddress: string;
data: string;
}

View File

@ -51,13 +51,16 @@
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^4.4.0-beta.3", "@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-coordinator": "^2.1.0-beta.3",
"@0x/contracts-dev-utils": "^0.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-forwarder": "^3.1.0-beta.3",
"@0x/contracts-exchange-libs": "^3.1.0-beta.3", "@0x/contracts-exchange-libs": "^3.1.0-beta.3",
"@0x/contracts-gen": "^1.1.0-beta.3", "@0x/contracts-gen": "^1.1.0-beta.3",
"@0x/contracts-utils": "^3.3.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/dev-utils": "^2.4.0-beta.3",
"@0x/migrations": "^4.4.0-beta.3",
"@0x/order-utils": "^8.5.0-beta.3", "@0x/order-utils": "^8.5.0-beta.3",
"@0x/sol-compiler": "^3.2.0-beta.3", "@0x/sol-compiler": "^3.2.0-beta.3",
"@0x/tslint-config": "^3.1.0-beta.2", "@0x/tslint-config": "^3.1.0-beta.2",
@ -73,6 +76,7 @@
"js-combinatorics": "^0.5.3", "js-combinatorics": "^0.5.3",
"make-promises-safe": "^1.1.0", "make-promises-safe": "^1.1.0",
"mocha": "^6.2.0", "mocha": "^6.2.0",
"nock": "^10.0.6",
"npm-run-all": "^4.1.2", "npm-run-all": "^4.1.2",
"shx": "^0.2.2", "shx": "^0.2.2",
"solhint": "^1.4.1", "solhint": "^1.4.1",

View File

@ -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

View File

@ -44,6 +44,7 @@
"dependencies": { "dependencies": {
"@0x/assert": "^2.2.0-beta.2", "@0x/assert": "^2.2.0-beta.2",
"@0x/base-contract": "^5.5.0-beta.3", "@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/dev-utils": "^2.4.0-beta.3",
"@0x/json-schemas": "^4.1.0-beta.2", "@0x/json-schemas": "^4.1.0-beta.2",
"@0x/order-utils": "^8.5.0-beta.3", "@0x/order-utils": "^8.5.0-beta.3",

View File

@ -4,6 +4,7 @@ import { ERC20TokenContract } from '@0x/contracts-erc20';
import { ExchangeContract } from '@0x/contracts-exchange'; import { ExchangeContract } from '@0x/contracts-exchange';
import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils'; import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils';
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
import { migrateOnceAsync } from '@0x/migrations';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import 'mocha'; import 'mocha';
@ -21,7 +22,6 @@ import {
} from '../src/types'; } from '../src/types';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate';
import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote'; import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote';
import { provider, web3Wrapper } from './utils/web3_wrapper'; import { provider, web3Wrapper } from './utils/web3_wrapper';

View File

@ -4,6 +4,7 @@ import { ERC20TokenContract } from '@0x/contracts-erc20';
import { ForwarderContract } from '@0x/contracts-exchange-forwarder'; import { ForwarderContract } from '@0x/contracts-exchange-forwarder';
import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils'; import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils';
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
import { migrateOnceAsync } from '@0x/migrations';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import 'mocha'; import 'mocha';
@ -20,7 +21,6 @@ import {
} from '../src/types'; } from '../src/types';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate';
import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote'; import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote';
import { provider, web3Wrapper } from './utils/web3_wrapper'; import { provider, web3Wrapper } from './utils/web3_wrapper';

View File

@ -4,6 +4,7 @@ import { ERC20TokenContract } from '@0x/contracts-erc20';
import { ExchangeContract } from '@0x/contracts-exchange'; import { ExchangeContract } from '@0x/contracts-exchange';
import { constants as devConstants, getLatestBlockTimestampAsync, OrderFactory } from '@0x/contracts-test-utils'; import { constants as devConstants, getLatestBlockTimestampAsync, OrderFactory } from '@0x/contracts-test-utils';
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
import { migrateOnceAsync } from '@0x/migrations';
import { assetDataUtils } from '@0x/order-utils'; import { assetDataUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types'; import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
@ -15,7 +16,6 @@ import { OrderPrunerPermittedFeeTypes } from '../src/types';
import { OrderPruner } from '../src/utils/order_prune_utils'; import { OrderPruner } from '../src/utils/order_prune_utils';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate';
import { provider, web3Wrapper } from './utils/web3_wrapper'; import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure(); chaiSetup.configure();

View File

@ -3,6 +3,7 @@ import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { WETH9Contract } from '@0x/contracts-erc20'; import { WETH9Contract } from '@0x/contracts-erc20';
import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils'; import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils';
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
import { migrateOnceAsync } from '@0x/migrations';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import 'mocha'; import 'mocha';
@ -12,7 +13,6 @@ import { constants } from '../src/constants';
import { ExtensionContractType, MarketOperation, PrunedSignedOrder } from '../src/types'; import { ExtensionContractType, MarketOperation, PrunedSignedOrder } from '../src/types';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate';
import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote'; import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote';
import { provider, web3Wrapper } from './utils/web3_wrapper'; import { provider, web3Wrapper } from './utils/web3_wrapper';

View File

@ -64,7 +64,6 @@
"ethereum-types": "^2.2.0-beta.2", "ethereum-types": "^2.2.0-beta.2",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"mocha": "^6.2.0", "mocha": "^6.2.0",
"nock": "^10.0.6",
"shx": "^0.2.2", "shx": "^0.2.2",
"tslint": "5.11.0", "tslint": "5.11.0",
"typedoc": "^0.15.0", "typedoc": "^0.15.0",
@ -76,8 +75,7 @@
"@0x/contract-addresses": "^3.3.0-beta.4", "@0x/contract-addresses": "^3.3.0-beta.4",
"@0x/contract-artifacts": "^2.3.0-beta.3", "@0x/contract-artifacts": "^2.3.0-beta.3",
"@0x/order-utils": "^8.5.0-beta.3", "@0x/order-utils": "^8.5.0-beta.3",
"ethers": "~4.0.4", "ethers": "~4.0.4"
"http-status-codes": "^1.3.2"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -6,6 +6,7 @@ import {
OrderValidatorContract, OrderValidatorContract,
WETH9Contract, WETH9Contract,
} from '@0x/abi-gen-wrappers'; } from '@0x/abi-gen-wrappers';
import { assert } from '@0x/assert';
import { ContractAddresses } from '@0x/contract-addresses'; import { ContractAddresses } from '@0x/contract-addresses';
import { import {
Coordinator, Coordinator,
@ -23,9 +24,8 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
import { SupportedProvider } from 'ethereum-types'; import { SupportedProvider } from 'ethereum-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { ContractWrappersConfigSchema } from './schemas/contract_wrappers_config_schema'; import { ContractWrappersConfigSchema } from './contract_wrappers_config_schema';
import { ContractWrappersConfig } from './types'; import { ContractWrappersConfig } from './types';
import { assert } from './utils/assert';
import { _getDefaultContractAddresses } from './utils/contract_addresses'; import { _getDefaultContractAddresses } from './utils/contract_addresses';
/** /**

View File

@ -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<string> {
// 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<string> {
// 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<string> {
// 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<string> {
// 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<string> {
// 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<string> {
// 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<string> {
// 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<CoordinatorServerCancellationResponse> {
// 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<CoordinatorServerCancellationResponse[]> {
// 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<string> {
// 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<string> {
// 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<string> {
// 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<void> {
// 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<string> {
// 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<string> {
// 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<string> {
// 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<string> {
// 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<SignedZeroExTransaction> {
// 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<CoordinatorServerResponse> {
// 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<string> {
// 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<Order | SignedOrder>): 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

View File

@ -55,17 +55,7 @@ export {
ExchangeTransactionExecutionEventArgs, ExchangeTransactionExecutionEventArgs,
} from '@0x/abi-gen-wrappers'; } from '@0x/abi-gen-wrappers';
export { export { OrderStatus, ContractError, ForwarderError, ContractWrappersConfig, OrderInfo } from './types';
OrderStatus,
ContractError,
ForwarderError,
CoordinatorServerCancellationResponse,
CoordinatorServerError,
ContractWrappersConfig,
OrderTransactionOpts,
TransactionOpts,
OrderInfo,
} from './types';
export { export {
BlockRange, BlockRange,

View File

@ -1,8 +0,0 @@
export const orderTxOptsSchema = {
id: '/OrderTxOpts',
allOf: [{ $ref: '/TxOpts' }],
properties: {
shouldValidate: { type: 'boolean' },
},
type: 'object',
};

View File

@ -1,9 +0,0 @@
export const txOptsSchema = {
id: '/TxOpts',
properties: {
gasPrice: { $ref: '/numberSchema' },
gasLimit: { type: 'number' },
nonce: { type: 'number' },
},
type: 'object',
};

View File

@ -1,13 +1,6 @@
import { ContractAddresses } from '@0x/contract-addresses'; import { ContractAddresses } from '@0x/contract-addresses';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
export interface TxOpts {
from: string;
gas?: number;
value?: BigNumber;
gasPrice?: BigNumber;
}
export enum ForwarderError { export enum ForwarderError {
CompleteFillFailed = 'COMPLETE_FILL_FAILED', CompleteFillFailed = 'COMPLETE_FILL_FAILED',
} }
@ -40,27 +33,6 @@ export interface ContractWrappersConfig {
blockPollingIntervalMs?: number; 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 { export interface OrderInfo {
orderStatus: OrderStatus; orderStatus: OrderStatus;
orderHash: string; orderHash: string;
@ -92,11 +64,3 @@ export interface OrderAndTraderInfo {
orderInfo: OrderInfo; orderInfo: OrderInfo;
traderInfo: TraderInfo; traderInfo: TraderInfo;
} }
export { CoordinatorServerCancellationResponse, CoordinatorServerError } from './utils/coordinator_server_types';
export interface CoordinatorTransaction {
salt: BigNumber;
signerAddress: string;
data: string;
}

View File

@ -1,5 +1,6 @@
import { constants, OrderFactory } from '@0x/contracts-test-utils'; import { constants, OrderFactory } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils'; import { BlockchainLifecycle } from '@0x/dev-utils';
import { migrateOnceAsync } from '@0x/migrations';
import { SignedOrder } from '@0x/types'; import { SignedOrder } from '@0x/types';
import { addressUtils, BigNumber } from '@0x/utils'; import { addressUtils, BigNumber } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
@ -9,7 +10,6 @@ import 'mocha';
import { ContractAddresses, ContractWrappers } from '../src'; import { ContractAddresses, ContractWrappers } from '../src';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate';
import { provider, web3Wrapper } from './utils/web3_wrapper'; import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure(); chaiSetup.configure();

View File

@ -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

View File

@ -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<ContractAddresses> {
const txDefaults = {
gas: devConstants.GAS_LIMIT,
from: devConstants.TESTRPC_FIRST_ADDRESS,
};
return runMigrationsOnceAsync(provider, txDefaults);
}

View File

@ -17,4 +17,5 @@ export {
} from 'ethereum-types'; } from 'ethereum-types';
export { ContractAddresses } from '@0x/contract-addresses'; export { ContractAddresses } from '@0x/contract-addresses';
export { runMigrationsAsync, runMigrationsOnceAsync } from './migration'; export { runMigrationsAsync, runMigrationsOnceAsync } from './migration';
export { migrateOnceAsync } from './migrate_with_test_defaults';
export import Web3ProviderEngine = require('web3-provider-engine'); export import Web3ProviderEngine = require('web3-provider-engine');

View File

@ -1,8 +1,8 @@
import { ContractAddresses } from '@0x/contract-addresses'; import { ContractAddresses } from '@0x/contract-addresses';
import { devConstants } from '@0x/dev-utils'; import { devConstants, web3Factory } from '@0x/dev-utils';
import { runMigrationsOnceAsync } from '@0x/migrations'; 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 * Configures and runs the migrations exactly once. Any subsequent times this is
@ -14,5 +14,6 @@ export async function migrateOnceAsync(): Promise<ContractAddresses> {
gas: devConstants.GAS_LIMIT, gas: devConstants.GAS_LIMIT,
from: devConstants.TESTRPC_FIRST_ADDRESS, from: devConstants.TESTRPC_FIRST_ADDRESS,
}; };
const provider: Web3ProviderEngine = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true });
return runMigrationsOnceAsync(provider, txDefaults); return runMigrationsOnceAsync(provider, txDefaults);
} }

View File

@ -264,7 +264,7 @@ export class DocGenerateUtils {
if (!_.isEmpty(missingReferences)) { if (!_.isEmpty(missingReferences)) {
throw new Error( throw new Error(
`${this._packageName} package needs to export: \n${missingReferences.join( `${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.`, )} \nFrom it\'s index.ts. If any are from external dependencies, then add them to the EXTERNAL_TYPE_MAP.`,
); );
} }

View File

@ -670,18 +670,6 @@ export interface Type {
tupleElements?: 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 { export interface ElementType {
name: string; name: string;
typeDocType: TypeDocTypes; typeDocType: TypeDocTypes;

View File

@ -713,7 +713,7 @@
uuid "^3.3.2" uuid "^3.3.2"
websocket "^1.0.26" 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" version "3.2.0"
resolved "https://registry.npmjs.org/@0x/contract-addresses/-/contract-addresses-3.2.0.tgz#606307696d9622764220a34e9d4638b899093eec" resolved "https://registry.npmjs.org/@0x/contract-addresses/-/contract-addresses-3.2.0.tgz#606307696d9622764220a34e9d4638b899093eec"
dependencies: dependencies:
@ -723,7 +723,7 @@
version "2.2.2" version "2.2.2"
resolved "https://registry.npmjs.org/@0x/contract-artifacts/-/contract-artifacts-2.2.2.tgz#e6d771afb58d0b59c19c5364af5a42a3dfd17219" 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" version "9.1.8"
resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-9.1.8.tgz#5923d35af3e4b442a57d02f74e02620b2d5b1356" resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-9.1.8.tgz#5923d35af3e4b442a57d02f74e02620b2d5b1356"
dependencies: dependencies:
@ -747,49 +747,21 @@
lodash "^4.17.11" lodash "^4.17.11"
uuid "^3.3.2" uuid "^3.3.2"
"@0x/contracts-erc20@^2.2.7": "@0x/coordinator-server@^1.0.3":
version "2.2.14" version "1.0.3"
resolved "https://registry.npmjs.org/@0x/contracts-erc20/-/contracts-erc20-2.2.14.tgz#bac2528a590c0f9668811cfd23948a941ae0ad30" resolved "https://registry.yarnpkg.com/@0x/coordinator-server/-/coordinator-server-1.0.3.tgz#736640edc5960bd65674436f96050fd7b4da7ac3"
dependencies: dependencies:
"@0x/base-contract" "^5.4.0" "@0x/assert" "2.2.0-beta.2"
"@0x/contracts-utils" "^3.2.4" "@0x/contract-addresses" "3.3.0-beta.3"
"@0x/types" "^2.4.3" "@0x/contract-wrappers" "12.2.0-beta.2"
"@0x/typescript-typings" "^4.3.0" "@0x/contracts-erc20" "2.3.0-beta.2"
"@0x/utils" "^4.5.2" "@0x/json-schemas" "4.1.0-beta.2"
"@0x/web3-wrapper" "^6.0.13" "@0x/order-utils" "8.5.0-beta.2"
ethereum-types "^2.1.6" "@0x/subproviders" "5.1.0-beta.2"
lodash "^4.17.11" "@0x/types" "2.5.0-beta.2"
"@0x/typescript-typings" "4.4.0-beta.2"
"@0x/contracts-utils@^3.2.4": "@0x/utils" "4.6.0-beta.2"
version "3.2.4" "@0x/web3-wrapper" "6.1.0-beta.2"
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"
"@babel/polyfill" "^7.0.0" "@babel/polyfill" "^7.0.0"
body-parser "^1.18.3" body-parser "^1.18.3"
cors "^2.8.5" cors "^2.8.5"
@ -835,7 +807,7 @@
uuid "^3.3.2" uuid "^3.3.2"
websocket "^1.0.29" 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" version "8.4.0"
resolved "https://registry.npmjs.org/@0x/order-utils/-/order-utils-8.4.0.tgz#f7fe9c73f9fd82ab05ec3c04951049e904aab46a" resolved "https://registry.npmjs.org/@0x/order-utils/-/order-utils-8.4.0.tgz#f7fe9c73f9fd82ab05ec3c04951049e904aab46a"
dependencies: dependencies: