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"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^2.2.0-beta.2",
|
||||
"@0x/base-contract": "^5.5.0-beta.3",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.3",
|
||||
"@0x/types": "^2.5.0-beta.2",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.2",
|
||||
"@0x/utils": "^4.6.0-beta.2",
|
||||
"ethereum-types": "^2.2.0-beta.2"
|
||||
"ethereum-types": "^2.2.0-beta.2",
|
||||
"@0x/contract-addresses": "^3.3.0-beta.4",
|
||||
"@0x/json-schemas": "^4.1.0-beta.2",
|
||||
"http-status-codes": "^1.3.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
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 { BigNumber } from '@0x/utils'; // tslint:disable-line:no-unused-variable
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export const assert = {
|
||||
...sharedAssert,
|
@ -2,10 +2,6 @@ import { Order, SignedOrder, SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface CoordinatorServerApprovalResponse {
|
||||
signatures: string[];
|
||||
expirationTimeSeconds: BigNumber[];
|
||||
}
|
||||
export interface CoordinatorServerApprovalRawResponse {
|
||||
signatures: string[];
|
||||
expirationTimeSeconds: BigNumber;
|
||||
}
|
||||
@ -24,7 +20,7 @@ export interface CoordinatorOutstandingFillSignatures {
|
||||
export interface CoordinatorServerResponse {
|
||||
isError: boolean;
|
||||
status: number;
|
||||
body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalRawResponse;
|
||||
body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalResponse;
|
||||
error?: any;
|
||||
request: CoordinatorServerRequest;
|
||||
coordinatorOperator: string;
|
||||
@ -38,12 +34,12 @@ export interface CoordinatorServerRequest {
|
||||
|
||||
export class CoordinatorServerError extends Error {
|
||||
public message: CoordinatorServerErrorMsg;
|
||||
public approvedOrders?: SignedOrder[] = [];
|
||||
public approvedOrders?: Order[] = [];
|
||||
public cancellations?: CoordinatorServerCancellationResponse[] = [];
|
||||
public errors: CoordinatorServerResponse[];
|
||||
constructor(
|
||||
message: CoordinatorServerErrorMsg,
|
||||
approvedOrders: SignedOrder[],
|
||||
approvedOrders: Order[],
|
||||
cancellations: CoordinatorServerCancellationResponse[],
|
||||
errors: CoordinatorServerResponse[],
|
||||
) {
|
@ -8,9 +8,18 @@ export {
|
||||
LibEIP712CoordinatorDomainContract,
|
||||
} from './wrappers';
|
||||
export import CoordinatorRevertErrors = require('./revert_errors');
|
||||
export { CoordinatorServerCancellationResponse } from './client/index';
|
||||
export { ApprovalFactory } from './approval_factory';
|
||||
export { SignedCoordinatorApproval } from './types';
|
||||
export { SignatureType, SignedZeroExTransaction, EIP712DomainWithDefaultSchema } from '@0x/types';
|
||||
export {
|
||||
Order,
|
||||
SignedOrder,
|
||||
SignatureType,
|
||||
SignedZeroExTransaction,
|
||||
EIP712DomainWithDefaultSchema,
|
||||
ZeroExTransaction,
|
||||
} from '@0x/types';
|
||||
export { AwaitTransactionSuccessOpts, SendTransactionOpts } from '@0x/base-contract';
|
||||
export {
|
||||
ContractArtifact,
|
||||
ContractChains,
|
||||
@ -38,4 +47,20 @@ export {
|
||||
ConstructorStateMutability,
|
||||
TupleDataItem,
|
||||
StateMutability,
|
||||
SupportedProvider,
|
||||
TxData,
|
||||
TxDataPayable,
|
||||
Web3JsProvider,
|
||||
GanacheProvider,
|
||||
EIP1193Provider,
|
||||
ZeroExProvider,
|
||||
EIP1193Event,
|
||||
JSONRPCRequestPayload,
|
||||
JSONRPCErrorCallback,
|
||||
Web3JsV1Provider,
|
||||
Web3JsV2Provider,
|
||||
Web3JsV3Provider,
|
||||
JSONRPCResponsePayload,
|
||||
JSONRPCResponseError,
|
||||
} from 'ethereum-types';
|
||||
export { CoordinatorClient, CoordinatorServerErrorMsg } from './client/index';
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface CoordinatorApproval {
|
||||
transaction: SignedZeroExTransaction;
|
||||
@ -8,3 +9,9 @@ export interface CoordinatorApproval {
|
||||
export interface SignedCoordinatorApproval extends CoordinatorApproval {
|
||||
signature: string;
|
||||
}
|
||||
|
||||
export interface CoordinatorTransaction {
|
||||
salt: BigNumber;
|
||||
signerAddress: string;
|
||||
data: string;
|
||||
}
|
||||
|
@ -51,13 +51,16 @@
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.4.0-beta.3",
|
||||
"@0x/contract-addresses": "^3.3.0-beta.4",
|
||||
"@0x/contracts-coordinator": "^2.1.0-beta.3",
|
||||
"@0x/contracts-dev-utils": "^0.1.0-beta.3",
|
||||
"@0x/contracts-exchange-forwarder": "^3.1.0-beta.3",
|
||||
"@0x/contracts-exchange-libs": "^3.1.0-beta.3",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.3",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.3",
|
||||
"@0x/coordinator-server": "^1.0.3",
|
||||
"@0x/dev-utils": "^2.4.0-beta.3",
|
||||
"@0x/migrations": "^4.4.0-beta.3",
|
||||
"@0x/order-utils": "^8.5.0-beta.3",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.3",
|
||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
||||
@ -73,6 +76,7 @@
|
||||
"js-combinatorics": "^0.5.3",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
"nock": "^10.0.6",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"solhint": "^1.4.1",
|
||||
|
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": {
|
||||
"@0x/assert": "^2.2.0-beta.2",
|
||||
"@0x/base-contract": "^5.5.0-beta.3",
|
||||
"@0x/contract-addresses": "^3.3.0-beta.4",
|
||||
"@0x/dev-utils": "^2.4.0-beta.3",
|
||||
"@0x/json-schemas": "^4.1.0-beta.2",
|
||||
"@0x/order-utils": "^8.5.0-beta.3",
|
||||
|
@ -4,6 +4,7 @@ import { ERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { ExchangeContract } from '@0x/contracts-exchange';
|
||||
import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
|
||||
import { migrateOnceAsync } from '@0x/migrations';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import 'mocha';
|
||||
@ -21,7 +22,6 @@ import {
|
||||
} from '../src/types';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { migrateOnceAsync } from './utils/migrate';
|
||||
import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote';
|
||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { ERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { ForwarderContract } from '@0x/contracts-exchange-forwarder';
|
||||
import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
|
||||
import { migrateOnceAsync } from '@0x/migrations';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import 'mocha';
|
||||
@ -20,7 +21,6 @@ import {
|
||||
} from '../src/types';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { migrateOnceAsync } from './utils/migrate';
|
||||
import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote';
|
||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { ERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { ExchangeContract } from '@0x/contracts-exchange';
|
||||
import { constants as devConstants, getLatestBlockTimestampAsync, OrderFactory } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
|
||||
import { migrateOnceAsync } from '@0x/migrations';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
@ -15,7 +16,6 @@ import { OrderPrunerPermittedFeeTypes } from '../src/types';
|
||||
import { OrderPruner } from '../src/utils/order_prune_utils';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { migrateOnceAsync } from './utils/migrate';
|
||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
|
@ -3,6 +3,7 @@ import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { WETH9Contract } from '@0x/contracts-erc20';
|
||||
import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
|
||||
import { migrateOnceAsync } from '@0x/migrations';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import 'mocha';
|
||||
@ -12,7 +13,6 @@ import { constants } from '../src/constants';
|
||||
import { ExtensionContractType, MarketOperation, PrunedSignedOrder } from '../src/types';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { migrateOnceAsync } from './utils/migrate';
|
||||
import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote';
|
||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
|
@ -64,7 +64,6 @@
|
||||
"ethereum-types": "^2.2.0-beta.2",
|
||||
"lodash": "^4.17.11",
|
||||
"mocha": "^6.2.0",
|
||||
"nock": "^10.0.6",
|
||||
"shx": "^0.2.2",
|
||||
"tslint": "5.11.0",
|
||||
"typedoc": "^0.15.0",
|
||||
@ -76,8 +75,7 @@
|
||||
"@0x/contract-addresses": "^3.3.0-beta.4",
|
||||
"@0x/contract-artifacts": "^2.3.0-beta.3",
|
||||
"@0x/order-utils": "^8.5.0-beta.3",
|
||||
"ethers": "~4.0.4",
|
||||
"http-status-codes": "^1.3.2"
|
||||
"ethers": "~4.0.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
OrderValidatorContract,
|
||||
WETH9Contract,
|
||||
} from '@0x/abi-gen-wrappers';
|
||||
import { assert } from '@0x/assert';
|
||||
import { ContractAddresses } from '@0x/contract-addresses';
|
||||
import {
|
||||
Coordinator,
|
||||
@ -23,9 +24,8 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { SupportedProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ContractWrappersConfigSchema } from './schemas/contract_wrappers_config_schema';
|
||||
import { ContractWrappersConfigSchema } from './contract_wrappers_config_schema';
|
||||
import { ContractWrappersConfig } from './types';
|
||||
import { assert } from './utils/assert';
|
||||
import { _getDefaultContractAddresses } from './utils/contract_addresses';
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
} from '@0x/abi-gen-wrappers';
|
||||
|
||||
export {
|
||||
OrderStatus,
|
||||
ContractError,
|
||||
ForwarderError,
|
||||
CoordinatorServerCancellationResponse,
|
||||
CoordinatorServerError,
|
||||
ContractWrappersConfig,
|
||||
OrderTransactionOpts,
|
||||
TransactionOpts,
|
||||
OrderInfo,
|
||||
} from './types';
|
||||
export { OrderStatus, ContractError, ForwarderError, ContractWrappersConfig, OrderInfo } from './types';
|
||||
|
||||
export {
|
||||
BlockRange,
|
||||
|
@ -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 { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface TxOpts {
|
||||
from: string;
|
||||
gas?: number;
|
||||
value?: BigNumber;
|
||||
gasPrice?: BigNumber;
|
||||
}
|
||||
|
||||
export enum ForwarderError {
|
||||
CompleteFillFailed = 'COMPLETE_FILL_FAILED',
|
||||
}
|
||||
@ -40,27 +33,6 @@ export interface ContractWrappersConfig {
|
||||
blockPollingIntervalMs?: number;
|
||||
}
|
||||
|
||||
// TODO(xianny): remove after refactoring coordinator wrapper
|
||||
/**
|
||||
* gasPrice: Gas price in Wei to use for a transaction
|
||||
* gasLimit: The amount of gas to send with a transaction (in Gwei)
|
||||
* nonce: The nonce to use for a transaction. If not specified, it defaults to the next incremented nonce.
|
||||
*/
|
||||
export interface TransactionOpts {
|
||||
gasPrice?: BigNumber;
|
||||
gasLimit?: number;
|
||||
nonce?: number;
|
||||
}
|
||||
|
||||
// TODO(xianny): remove after refactoring coordinator wrapper
|
||||
/**
|
||||
* shouldValidate: Flag indicating whether the library should make attempts to validate a transaction before
|
||||
* broadcasting it. For example, order has a valid signature, maker has sufficient funds, etc. Default=true.
|
||||
*/
|
||||
export interface OrderTransactionOpts extends TransactionOpts {
|
||||
shouldValidate?: boolean;
|
||||
}
|
||||
|
||||
export interface OrderInfo {
|
||||
orderStatus: OrderStatus;
|
||||
orderHash: string;
|
||||
@ -92,11 +64,3 @@ export interface OrderAndTraderInfo {
|
||||
orderInfo: OrderInfo;
|
||||
traderInfo: TraderInfo;
|
||||
}
|
||||
|
||||
export { CoordinatorServerCancellationResponse, CoordinatorServerError } from './utils/coordinator_server_types';
|
||||
|
||||
export interface CoordinatorTransaction {
|
||||
salt: BigNumber;
|
||||
signerAddress: string;
|
||||
data: string;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { constants, OrderFactory } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { migrateOnceAsync } from '@0x/migrations';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { addressUtils, BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
@ -9,7 +10,6 @@ import 'mocha';
|
||||
import { ContractAddresses, ContractWrappers } from '../src';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { migrateOnceAsync } from './utils/migrate';
|
||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
|
@ -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';
|
||||
export { ContractAddresses } from '@0x/contract-addresses';
|
||||
export { runMigrationsAsync, runMigrationsOnceAsync } from './migration';
|
||||
export { migrateOnceAsync } from './migrate_with_test_defaults';
|
||||
export import Web3ProviderEngine = require('web3-provider-engine');
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { ContractAddresses } from '@0x/contract-addresses';
|
||||
import { devConstants } from '@0x/dev-utils';
|
||||
import { runMigrationsOnceAsync } from '@0x/migrations';
|
||||
import { devConstants, web3Factory } from '@0x/dev-utils';
|
||||
import { Web3ProviderEngine } from '@0x/subproviders';
|
||||
|
||||
import { provider } from './web3_wrapper';
|
||||
import { runMigrationsOnceAsync } from './index';
|
||||
|
||||
/**
|
||||
* Configures and runs the migrations exactly once. Any subsequent times this is
|
||||
@ -14,5 +14,6 @@ export async function migrateOnceAsync(): Promise<ContractAddresses> {
|
||||
gas: devConstants.GAS_LIMIT,
|
||||
from: devConstants.TESTRPC_FIRST_ADDRESS,
|
||||
};
|
||||
const provider: Web3ProviderEngine = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true });
|
||||
return runMigrationsOnceAsync(provider, txDefaults);
|
||||
}
|
@ -264,7 +264,7 @@ export class DocGenerateUtils {
|
||||
if (!_.isEmpty(missingReferences)) {
|
||||
throw new Error(
|
||||
`${this._packageName} package needs to export: \n${missingReferences.join(
|
||||
'\n',
|
||||
',\n',
|
||||
)} \nFrom it\'s index.ts. If any are from external dependencies, then add them to the EXTERNAL_TYPE_MAP.`,
|
||||
);
|
||||
}
|
||||
|
@ -670,18 +670,6 @@ export interface Type {
|
||||
tupleElements?: Type[];
|
||||
}
|
||||
|
||||
/**
|
||||
* * shouldValidate: Flag indicating whether the library should make attempts to validate a transaction before
|
||||
* broadcasting it. For example, order has a valid signature, maker has sufficient funds, etc. Default=true.
|
||||
* * pollingIntervalMs: Used with `awaitTransactionSuccessAsync` to determine polling interval in milliseconds
|
||||
* * timeoutMs: Used with `awaitTransactionSuccessAsync` to determine timeout in milliseconds
|
||||
*/
|
||||
export interface SendTransactionOpts {
|
||||
shouldValidate?: boolean;
|
||||
pollingIntervalMs?: number;
|
||||
timeoutMs?: number;
|
||||
}
|
||||
|
||||
export interface ElementType {
|
||||
name: string;
|
||||
typeDocType: TypeDocTypes;
|
||||
|
62
yarn.lock
62
yarn.lock
@ -713,7 +713,7 @@
|
||||
uuid "^3.3.2"
|
||||
websocket "^1.0.26"
|
||||
|
||||
"@0x/contract-addresses@^3.0.1", "@0x/contract-addresses@^3.0.2", "@0x/contract-addresses@^3.2.0":
|
||||
"@0x/contract-addresses@^3.0.2", "@0x/contract-addresses@^3.2.0":
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmjs.org/@0x/contract-addresses/-/contract-addresses-3.2.0.tgz#606307696d9622764220a34e9d4638b899093eec"
|
||||
dependencies:
|
||||
@ -723,7 +723,7 @@
|
||||
version "2.2.2"
|
||||
resolved "https://registry.npmjs.org/@0x/contract-artifacts/-/contract-artifacts-2.2.2.tgz#e6d771afb58d0b59c19c5364af5a42a3dfd17219"
|
||||
|
||||
"@0x/contract-wrappers@^9.1.6", "@0x/contract-wrappers@^9.1.7":
|
||||
"@0x/contract-wrappers@^9.1.7":
|
||||
version "9.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-9.1.8.tgz#5923d35af3e4b442a57d02f74e02620b2d5b1356"
|
||||
dependencies:
|
||||
@ -747,49 +747,21 @@
|
||||
lodash "^4.17.11"
|
||||
uuid "^3.3.2"
|
||||
|
||||
"@0x/contracts-erc20@^2.2.7":
|
||||
version "2.2.14"
|
||||
resolved "https://registry.npmjs.org/@0x/contracts-erc20/-/contracts-erc20-2.2.14.tgz#bac2528a590c0f9668811cfd23948a941ae0ad30"
|
||||
"@0x/coordinator-server@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@0x/coordinator-server/-/coordinator-server-1.0.3.tgz#736640edc5960bd65674436f96050fd7b4da7ac3"
|
||||
dependencies:
|
||||
"@0x/base-contract" "^5.4.0"
|
||||
"@0x/contracts-utils" "^3.2.4"
|
||||
"@0x/types" "^2.4.3"
|
||||
"@0x/typescript-typings" "^4.3.0"
|
||||
"@0x/utils" "^4.5.2"
|
||||
"@0x/web3-wrapper" "^6.0.13"
|
||||
ethereum-types "^2.1.6"
|
||||
lodash "^4.17.11"
|
||||
|
||||
"@0x/contracts-utils@^3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.npmjs.org/@0x/contracts-utils/-/contracts-utils-3.2.4.tgz#b5ae80684ac0542eb59925f52113ce2c8b1bdb2b"
|
||||
dependencies:
|
||||
"@0x/base-contract" "^5.4.0"
|
||||
"@0x/order-utils" "^8.4.0"
|
||||
"@0x/types" "^2.4.3"
|
||||
"@0x/typescript-typings" "^4.3.0"
|
||||
"@0x/utils" "^4.5.2"
|
||||
"@0x/web3-wrapper" "^6.0.13"
|
||||
bn.js "^4.11.8"
|
||||
ethereum-types "^2.1.6"
|
||||
ethereumjs-util "^5.1.1"
|
||||
lodash "^4.17.11"
|
||||
|
||||
"@0x/coordinator-server@^0.1.3":
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@0x/coordinator-server/-/coordinator-server-0.1.3.tgz#5fbb7c11bb641aa5386797769cab9a68a7d15b79"
|
||||
dependencies:
|
||||
"@0x/assert" "^2.1.0"
|
||||
"@0x/contract-addresses" "^3.0.1"
|
||||
"@0x/contract-wrappers" "^9.1.6"
|
||||
"@0x/contracts-erc20" "^2.2.7"
|
||||
"@0x/json-schemas" "^3.0.11"
|
||||
"@0x/order-utils" "^8.2.1"
|
||||
"@0x/subproviders" "^4.1.1"
|
||||
"@0x/types" "^2.4.0"
|
||||
"@0x/typescript-typings" "^4.2.3"
|
||||
"@0x/utils" "^4.4.0"
|
||||
"@0x/web3-wrapper" "^6.0.7"
|
||||
"@0x/assert" "2.2.0-beta.2"
|
||||
"@0x/contract-addresses" "3.3.0-beta.3"
|
||||
"@0x/contract-wrappers" "12.2.0-beta.2"
|
||||
"@0x/contracts-erc20" "2.3.0-beta.2"
|
||||
"@0x/json-schemas" "4.1.0-beta.2"
|
||||
"@0x/order-utils" "8.5.0-beta.2"
|
||||
"@0x/subproviders" "5.1.0-beta.2"
|
||||
"@0x/types" "2.5.0-beta.2"
|
||||
"@0x/typescript-typings" "4.4.0-beta.2"
|
||||
"@0x/utils" "4.6.0-beta.2"
|
||||
"@0x/web3-wrapper" "6.1.0-beta.2"
|
||||
"@babel/polyfill" "^7.0.0"
|
||||
body-parser "^1.18.3"
|
||||
cors "^2.8.5"
|
||||
@ -835,7 +807,7 @@
|
||||
uuid "^3.3.2"
|
||||
websocket "^1.0.29"
|
||||
|
||||
"@0x/order-utils@^8.2.1", "@0x/order-utils@^8.2.2", "@0x/order-utils@^8.2.3", "@0x/order-utils@^8.4.0":
|
||||
"@0x/order-utils@^8.2.2", "@0x/order-utils@^8.2.3", "@0x/order-utils@^8.4.0":
|
||||
version "8.4.0"
|
||||
resolved "https://registry.npmjs.org/@0x/order-utils/-/order-utils-8.4.0.tgz#f7fe9c73f9fd82ab05ec3c04951049e904aab46a"
|
||||
dependencies:
|
||||
|
Loading…
x
Reference in New Issue
Block a user