Refactor/3.0/coordinator client (#2348)
* deduplicate migrateOnceAsync() test helper * move and rename coordinator client to @0x/contracts-coordinator
This commit is contained in:
parent
f6d26392fb
commit
50d5b4fa37
@ -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"
|
||||||
|
892
contracts/coordinator/src/client/index.ts
Normal file
892
contracts/coordinator/src/client/index.ts
Normal 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
|
@ -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,
|
@ -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[],
|
||||||
) {
|
) {
|
@ -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';
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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",
|
||||||
|
634
contracts/integrations/test/coordinator/client_test.ts
Normal file
634
contracts/integrations/test/coordinator/client_test.ts
Normal 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
|
@ -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",
|
||||||
|
@ -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';
|
||||||
|
|
||||||
|
@ -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';
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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';
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
|
@ -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,
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
export const orderTxOptsSchema = {
|
|
||||||
id: '/OrderTxOpts',
|
|
||||||
allOf: [{ $ref: '/TxOpts' }],
|
|
||||||
properties: {
|
|
||||||
shouldValidate: { type: 'boolean' },
|
|
||||||
},
|
|
||||||
type: 'object',
|
|
||||||
};
|
|
@ -1,9 +0,0 @@
|
|||||||
export const txOptsSchema = {
|
|
||||||
id: '/TxOpts',
|
|
||||||
properties: {
|
|
||||||
gasPrice: { $ref: '/numberSchema' },
|
|
||||||
gasLimit: { type: 'number' },
|
|
||||||
nonce: { type: 'number' },
|
|
||||||
},
|
|
||||||
type: 'object',
|
|
||||||
};
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
|
@ -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);
|
|
||||||
}
|
|
@ -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');
|
||||||
|
@ -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);
|
||||||
}
|
}
|
@ -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.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
62
yarn.lock
62
yarn.lock
@ -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:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user