Add coordinator wrapper (#1792)
* wip first pass at coordinator wrapper * implement cancels, helper methods, and more unit tests * pin typeorm version in pipeline * prettier * add export to 0x.js * generate ZeroEx transaction using EIP712 * update Coordinator artifact * change OrderError -> TypedDataError
This commit is contained in:
parent
c2d3e5f052
commit
41cc5234c4
@ -10,6 +10,9 @@ export {
|
||||
|
||||
export {
|
||||
ContractWrappers,
|
||||
CoordinatorWrapper,
|
||||
CoordinatorServerCancellationResponse,
|
||||
CoordinatorServerError,
|
||||
DutchAuctionWrapper,
|
||||
ERC20TokenWrapper,
|
||||
ERC721TokenWrapper,
|
||||
|
162
packages/contract-artifacts/artifacts/Coordinator.json
generated
162
packages/contract-artifacts/artifacts/Coordinator.json
generated
File diff suppressed because one or more lines are too long
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "9.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added CoordinatorWrapper to support orders with the Coordinator extension contract",
|
||||
"pr": 1792
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "9.0.0",
|
||||
"changes": [
|
||||
|
@ -39,6 +39,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0x/contracts-test-utils": "^3.1.2",
|
||||
"@0x/coordinator-server": "0.1.1",
|
||||
"@0x/dev-utils": "^2.2.1",
|
||||
"@0x/fill-scenarios": "^3.0.5",
|
||||
"@0x/migrations": "^4.1.1",
|
||||
@ -46,6 +47,7 @@
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^2.2.42",
|
||||
"@types/nock": "^10.0.1",
|
||||
"@types/node": "*",
|
||||
"@types/sinon": "^2.2.2",
|
||||
"@types/uuid": "^3.4.3",
|
||||
@ -56,6 +58,7 @@
|
||||
"dirty-chai": "^2.0.1",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^4.1.0",
|
||||
"nock": "^10.0.6",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"nyc": "^11.0.1",
|
||||
"opn-cli": "^3.1.0",
|
||||
@ -83,6 +86,7 @@
|
||||
"ethereumjs-blockstream": "6.0.0",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"ethers": "~4.0.4",
|
||||
"http-status-codes": "^1.3.2",
|
||||
"js-sha3": "^0.7.0",
|
||||
"lodash": "^4.17.11",
|
||||
"uuid": "^3.3.2"
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {
|
||||
Coordinator,
|
||||
DutchAuction,
|
||||
ERC20Proxy,
|
||||
ERC20Token,
|
||||
@ -14,6 +15,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { SupportedProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { CoordinatorWrapper } from './contract_wrappers/coordinator_wrapper';
|
||||
import { DutchAuctionWrapper } from './contract_wrappers/dutch_auction_wrapper';
|
||||
import { ERC20ProxyWrapper } from './contract_wrappers/erc20_proxy_wrapper';
|
||||
import { ERC20TokenWrapper } from './contract_wrappers/erc20_token_wrapper';
|
||||
@ -73,6 +75,11 @@ export class ContractWrappers {
|
||||
*/
|
||||
public dutchAuction: DutchAuctionWrapper;
|
||||
|
||||
/**
|
||||
* An instance of the CoordinatorWrapper class containing methods for interacting with the Coordinator extension contract.
|
||||
*/
|
||||
public coordinator: CoordinatorWrapper;
|
||||
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
/**
|
||||
* Instantiates a new ContractWrappers instance.
|
||||
@ -88,6 +95,7 @@ export class ContractWrappers {
|
||||
};
|
||||
this._web3Wrapper = new Web3Wrapper(supportedProvider, txDefaults);
|
||||
const artifactsArray = [
|
||||
Coordinator,
|
||||
DutchAuction,
|
||||
ERC20Proxy,
|
||||
ERC20Token,
|
||||
@ -155,6 +163,13 @@ export class ContractWrappers {
|
||||
config.networkId,
|
||||
contractAddresses.dutchAuction,
|
||||
);
|
||||
this.coordinator = new CoordinatorWrapper(
|
||||
this._web3Wrapper,
|
||||
config.networkId,
|
||||
contractAddresses.coordinator,
|
||||
contractAddresses.exchange,
|
||||
contractAddresses.coordinatorRegistry,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Unsubscribes from all subscriptions for all contracts.
|
||||
|
@ -0,0 +1,856 @@
|
||||
import { CoordinatorContract, CoordinatorRegistryContract, ExchangeContract } from '@0x/abi-gen-wrappers';
|
||||
import { getContractAddressesForNetworkOrThrow } from '@0x/contract-addresses';
|
||||
import { Coordinator, CoordinatorRegistry, Exchange } 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 } from 'ethereum-types';
|
||||
import * as HttpStatus from 'http-status-codes';
|
||||
import { flatten } from 'lodash';
|
||||
|
||||
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';
|
||||
import { TransactionEncoder } from '../utils/transaction_encoder';
|
||||
|
||||
import { ContractWrapper } from './contract_wrapper';
|
||||
/**
|
||||
* This class includes all the functionality related to filling or cancelling orders through
|
||||
* the 0x V2 Coordinator extension contract.
|
||||
*/
|
||||
export class CoordinatorWrapper extends ContractWrapper {
|
||||
public abi: ContractAbi = Coordinator.compilerOutput.abi;
|
||||
public networkId: number;
|
||||
public address: string;
|
||||
public exchangeAddress: string;
|
||||
public registryAddress: string;
|
||||
private readonly _contractInstance: CoordinatorContract;
|
||||
private readonly _registryInstance: CoordinatorRegistryContract;
|
||||
private readonly _exchangeInstance: ExchangeContract;
|
||||
private readonly _transactionEncoder: TransactionEncoder;
|
||||
private readonly _feeRecipientToEndpoint: { [feeRecipient: string]: string } = {};
|
||||
|
||||
/**
|
||||
* Instantiate CoordinatorWrapper
|
||||
* @param web3Wrapper Web3Wrapper instance to use.
|
||||
* @param networkId Desired networkId.
|
||||
* @param address The address of the Coordinator contract. If undefined, will
|
||||
* default to the known address corresponding to the networkId.
|
||||
* @param exchangeAddress The address of the Exchange contract. If undefined, will
|
||||
* default to the known address corresponding to the networkId.
|
||||
* @param registryAddress The address of the CoordinatorRegistry contract. If undefined, will
|
||||
* default to the known address corresponding to the networkId.
|
||||
*/
|
||||
constructor(
|
||||
web3Wrapper: Web3Wrapper,
|
||||
networkId: number,
|
||||
address?: string,
|
||||
exchangeAddress?: string,
|
||||
registryAddress?: string,
|
||||
) {
|
||||
super(web3Wrapper, networkId);
|
||||
this.networkId = networkId;
|
||||
|
||||
const contractAddresses = getContractAddressesForNetworkOrThrow(networkId);
|
||||
this.address = address === undefined ? contractAddresses.coordinator : address;
|
||||
this.exchangeAddress = exchangeAddress === undefined ? contractAddresses.coordinator : exchangeAddress;
|
||||
this.registryAddress = registryAddress === undefined ? contractAddresses.coordinatorRegistry : registryAddress;
|
||||
|
||||
this._contractInstance = new CoordinatorContract(
|
||||
this.abi,
|
||||
this.address,
|
||||
this._web3Wrapper.getProvider(),
|
||||
this._web3Wrapper.getContractDefaults(),
|
||||
);
|
||||
this._registryInstance = new CoordinatorRegistryContract(
|
||||
CoordinatorRegistry.compilerOutput.abi,
|
||||
this.registryAddress,
|
||||
this._web3Wrapper.getProvider(),
|
||||
this._web3Wrapper.getContractDefaults(),
|
||||
);
|
||||
this._exchangeInstance = new ExchangeContract(
|
||||
Exchange.compilerOutput.abi,
|
||||
this.exchangeAddress,
|
||||
this._web3Wrapper.getProvider(),
|
||||
this._web3Wrapper.getContractDefaults(),
|
||||
);
|
||||
|
||||
this._transactionEncoder = new TransactionEncoder(this._exchangeInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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._transactionEncoder.fillOrderTx(signedOrder, takerAssetFillAmount);
|
||||
const txHash = await this._handleFillsAsync(data, takerAddress, [signedOrder], orderTransactionOpts);
|
||||
return txHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* No-throw version of fillOrderAsync. This version will not throw if the fill fails. This allows the caller to save gas at the expense of not knowing the reason the fill failed.
|
||||
* @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 fillOrderNoThrowAsync(
|
||||
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._transactionEncoder.fillOrderNoThrowTx(signedOrder, takerAssetFillAmount);
|
||||
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._transactionEncoder.fillOrKillOrderTx(signedOrder, takerAssetFillAmount);
|
||||
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 data = this._transactionEncoder.batchFillOrdersTx(signedOrders, takerAssetFillAmounts);
|
||||
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 data = this._transactionEncoder.batchFillOrdersNoThrowTx(signedOrders, takerAssetFillAmounts);
|
||||
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 data = this._transactionEncoder.batchFillOrKillOrdersTx(signedOrders, takerAssetFillAmounts);
|
||||
const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts);
|
||||
return txHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously executes multiple calls to fillOrder until total amount of makerAsset is bought by taker.
|
||||
* 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 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 marketBuyOrdersAsync(
|
||||
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 data = this._transactionEncoder.marketBuyOrdersTx(signedOrders, makerAssetFillAmount);
|
||||
const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts);
|
||||
return txHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously executes multiple calls to fillOrder until total amount of makerAsset is bought by taker.
|
||||
* 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 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 marketSellOrdersAsync(
|
||||
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 data = this._transactionEncoder.marketSellOrdersTx(signedOrders, takerAssetFillAmount);
|
||||
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 data = this._transactionEncoder.marketBuyOrdersNoThrowTx(signedOrders, makerAssetFillAmount);
|
||||
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 data = this._transactionEncoder.marketSellOrdersNoThrowTx(signedOrders, takerAssetFillAmount);
|
||||
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._transactionEncoder.cancelOrderTx(order);
|
||||
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._transactionEncoder.batchCancelOrdersTx(orders);
|
||||
|
||||
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._transactionEncoder.cancelOrderTx(order);
|
||||
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._transactionEncoder.batchCancelOrdersTx(orders);
|
||||
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._transactionEncoder.cancelOrdersUpToTx(targetOrderEpoch);
|
||||
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.callAsync(
|
||||
transaction,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.callAsync(hash, signature);
|
||||
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.callAsync(feeRecipient);
|
||||
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,
|
||||
verifyingContractAddress: this.exchangeAddress,
|
||||
};
|
||||
const signedTransaction = await signatureUtils.ecSignTypedDataTransactionAsync(
|
||||
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?networkId=${this.networkId}`, {
|
||||
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.callAsync(
|
||||
transaction,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures,
|
||||
{
|
||||
from: txOrigin,
|
||||
gas: orderTransactionOpts.gasLimit,
|
||||
gasPrice: orderTransactionOpts.gasPrice,
|
||||
nonce: orderTransactionOpts.nonce,
|
||||
},
|
||||
);
|
||||
}
|
||||
const txHash = await this._contractInstance.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures,
|
||||
{
|
||||
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
|
@ -26,6 +26,7 @@ export {
|
||||
} from '@0x/abi-gen-wrappers';
|
||||
|
||||
export { ContractWrappers } from './contract_wrappers';
|
||||
export { CoordinatorWrapper } from './contract_wrappers/coordinator_wrapper';
|
||||
export { ERC20TokenWrapper } from './contract_wrappers/erc20_token_wrapper';
|
||||
export { ERC721TokenWrapper } from './contract_wrappers/erc721_token_wrapper';
|
||||
export { EtherTokenWrapper } from './contract_wrappers/ether_token_wrapper';
|
||||
@ -58,6 +59,8 @@ export {
|
||||
TraderInfo,
|
||||
ValidateOrderFillableOpts,
|
||||
DutchAuctionData,
|
||||
CoordinatorServerCancellationResponse,
|
||||
CoordinatorServerError,
|
||||
} from './types';
|
||||
|
||||
export {
|
||||
@ -72,6 +75,8 @@ export {
|
||||
Order,
|
||||
SignedOrder,
|
||||
AssetProxyId,
|
||||
SignedZeroExTransaction,
|
||||
ZeroExTransaction,
|
||||
} from '@0x/types';
|
||||
|
||||
export {
|
||||
|
@ -225,3 +225,11 @@ export interface DutchAuctionData {
|
||||
beginTimeSeconds: BigNumber;
|
||||
beginAmount: BigNumber;
|
||||
}
|
||||
|
||||
export { CoordinatorServerCancellationResponse, CoordinatorServerError } from './utils/coordinator_server_types';
|
||||
|
||||
export interface CoordinatorTransaction {
|
||||
salt: BigNumber;
|
||||
signerAddress: string;
|
||||
data: string;
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
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;
|
||||
}
|
||||
|
||||
export interface CoordinatorServerCancellationResponse {
|
||||
outstandingFillSignatures: CoordinatorOutstandingFillSignatures[];
|
||||
cancellationSignatures: string[];
|
||||
}
|
||||
export interface CoordinatorOutstandingFillSignatures {
|
||||
orderHash: string;
|
||||
approvalSignatures: string[];
|
||||
expirationTimeSeconds: BigNumber;
|
||||
takerAssetFillAmount: BigNumber;
|
||||
}
|
||||
|
||||
export interface CoordinatorServerResponse {
|
||||
isError: boolean;
|
||||
status: number;
|
||||
body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalRawResponse;
|
||||
error?: any;
|
||||
request: CoordinatorServerRequest;
|
||||
coordinatorOperator: string;
|
||||
orders?: Array<SignedOrder | Order>;
|
||||
}
|
||||
|
||||
export interface CoordinatorServerRequest {
|
||||
signedTransaction: SignedZeroExTransaction;
|
||||
txOrigin: string;
|
||||
}
|
||||
|
||||
export class CoordinatorServerError extends Error {
|
||||
public message: CoordinatorServerErrorMsg;
|
||||
public approvedOrders?: SignedOrder[] = [];
|
||||
public cancellations?: CoordinatorServerCancellationResponse[] = [];
|
||||
public errors: CoordinatorServerResponse[];
|
||||
constructor(
|
||||
message: CoordinatorServerErrorMsg,
|
||||
approvedOrders: SignedOrder[],
|
||||
cancellations: CoordinatorServerCancellationResponse[],
|
||||
errors: CoordinatorServerResponse[],
|
||||
) {
|
||||
super();
|
||||
this.message = message;
|
||||
this.approvedOrders = approvedOrders;
|
||||
this.cancellations = cancellations;
|
||||
this.errors = errors;
|
||||
}
|
||||
}
|
||||
|
||||
export enum CoordinatorServerErrorMsg {
|
||||
CancellationFailed = 'Failed to cancel with some coordinator server(s). See errors for more info. See cancellations for successful cancellations.',
|
||||
FillFailed = 'Failed to obtain approval signatures from some coordinator server(s). See errors for more info. Current transaction has been abandoned but you may resubmit with only approvedOrders (a new ZeroEx transaction will have to be signed).',
|
||||
}
|
715
packages/contract-wrappers/test/coordinator_wrapper_test.ts
Normal file
715
packages/contract-wrappers/test/coordinator_wrapper_test.ts
Normal file
@ -0,0 +1,715 @@
|
||||
import { CoordinatorRegistryContract } from '@0x/abi-gen-wrappers';
|
||||
import { CoordinatorRegistry } from '@0x/contract-artifacts';
|
||||
import { constants } from '@0x/contracts-test-utils';
|
||||
import { defaultOrmConfig, getAppAsync } from '@0x/coordinator-server';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { FillScenarios } from '@0x/fill-scenarios';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber, fetchAsync, logUtils } 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 { CoordinatorServerErrorMsg } from '../src/utils/coordinator_server_types';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { migrateOnceAsync } from './utils/migrate';
|
||||
import { tokenUtils } from './utils/token_utils';
|
||||
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
|
||||
describe('CoordinatorWrapper', () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const takerTokenFillAmount = new BigNumber(5);
|
||||
let coordinatorServerApp: http.Server;
|
||||
let anotherCoordinatorServerApp: http.Server;
|
||||
let contractWrappers: ContractWrappers;
|
||||
let fillScenarios: FillScenarios;
|
||||
let exchangeContractAddress: string;
|
||||
let zrxTokenAddress: 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 makerAssetData: string;
|
||||
let takerAssetData: 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 = {
|
||||
networkId: constants.TESTRPC_NETWORK_ID,
|
||||
contractAddresses,
|
||||
blockPollingIntervalMs: 10,
|
||||
};
|
||||
contractWrappers = new ContractWrappers(provider, config);
|
||||
exchangeContractAddress = contractWrappers.exchange.address;
|
||||
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||
zrxTokenAddress = contractWrappers.exchange.zrxTokenAddress;
|
||||
fillScenarios = new FillScenarios(
|
||||
provider,
|
||||
userAddresses,
|
||||
zrxTokenAddress,
|
||||
exchangeContractAddress,
|
||||
contractWrappers.erc20Proxy.address,
|
||||
contractWrappers.erc721Proxy.address,
|
||||
);
|
||||
[
|
||||
,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
feeRecipientAddressOne,
|
||||
feeRecipientAddressTwo,
|
||||
feeRecipientAddressThree,
|
||||
feeRecipientAddressFour,
|
||||
] = userAddresses.slice(0, 7);
|
||||
[makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
|
||||
[makerAssetData, takerAssetData] = [
|
||||
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
|
||||
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
|
||||
];
|
||||
|
||||
// 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: {
|
||||
50: {
|
||||
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',
|
||||
},
|
||||
},
|
||||
// Optional selective delay on fill requests
|
||||
SELECTIVE_DELAY_MS: 0,
|
||||
EXPIRATION_DURATION_SECONDS: 60, // 1 minute
|
||||
};
|
||||
coordinatorServerApp = await getAppAsync(
|
||||
{
|
||||
[config.networkId]: provider,
|
||||
},
|
||||
coordinatorServerConfigs,
|
||||
defaultOrmConfig,
|
||||
);
|
||||
|
||||
coordinatorServerApp.listen(coordinatorPort, () => {
|
||||
logUtils.log(`Coordinator SERVER API (HTTP) listening on port ${coordinatorPort}!`);
|
||||
});
|
||||
|
||||
anotherCoordinatorServerApp = await getAppAsync(
|
||||
{
|
||||
[config.networkId]: provider,
|
||||
},
|
||||
coordinatorServerConfigs,
|
||||
{
|
||||
type: 'sqlite',
|
||||
database: 'database.sqlite_2',
|
||||
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(
|
||||
CoordinatorRegistry.compilerOutput.abi,
|
||||
contractAddresses.coordinatorRegistry,
|
||||
provider,
|
||||
);
|
||||
|
||||
// register coordinator server
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorRegistryInstance.setCoordinatorEndpoint.sendTransactionAsync(
|
||||
`${coordinatorEndpoint}${coordinatorPort}`,
|
||||
{
|
||||
from: feeRecipientAddressOne,
|
||||
},
|
||||
),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorRegistryInstance.setCoordinatorEndpoint.sendTransactionAsync(
|
||||
`${coordinatorEndpoint}${coordinatorPort}`,
|
||||
{
|
||||
from: feeRecipientAddressTwo,
|
||||
},
|
||||
),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// register another coordinator server
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorRegistryInstance.setCoordinatorEndpoint.sendTransactionAsync(
|
||||
`${coordinatorEndpoint}${anotherCoordinatorPort}`,
|
||||
{
|
||||
from: feeRecipientAddressThree,
|
||||
},
|
||||
),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
|
||||
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerAssetData,
|
||||
takerAssetData,
|
||||
new BigNumber(1),
|
||||
new BigNumber(1),
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
feeRecipientAddressOne,
|
||||
undefined,
|
||||
contractWrappers.coordinator.address,
|
||||
);
|
||||
anotherSignedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerAssetData,
|
||||
takerAssetData,
|
||||
new BigNumber(1),
|
||||
new BigNumber(1),
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
feeRecipientAddressOne,
|
||||
undefined,
|
||||
contractWrappers.coordinator.address,
|
||||
);
|
||||
signedOrderWithDifferentFeeRecipient = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerAssetData,
|
||||
takerAssetData,
|
||||
new BigNumber(1),
|
||||
new BigNumber(1),
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
feeRecipientAddressTwo,
|
||||
undefined,
|
||||
contractWrappers.coordinator.address,
|
||||
);
|
||||
signedOrderWithDifferentCoordinatorOperator = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerAssetData,
|
||||
takerAssetData,
|
||||
new BigNumber(1),
|
||||
new BigNumber(1),
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
feeRecipientAddressThree,
|
||||
undefined,
|
||||
contractWrappers.coordinator.address,
|
||||
);
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('test setup', () => {
|
||||
it('should have coordinator registry which returns an endpoint', async () => {
|
||||
const setCoordinatorEndpoint = await coordinatorRegistryInstance.getCoordinatorEndpoint.callAsync(
|
||||
feeRecipientAddressOne,
|
||||
);
|
||||
const anotherSetCoordinatorEndpoint = await coordinatorRegistryInstance.getCoordinatorEndpoint.callAsync(
|
||||
feeRecipientAddressThree,
|
||||
);
|
||||
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 fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerAssetData,
|
||||
takerAssetData,
|
||||
new BigNumber(1),
|
||||
new BigNumber(1),
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
feeRecipientAddressOne,
|
||||
undefined,
|
||||
);
|
||||
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.getOrderEpochAsync(
|
||||
makerAddress,
|
||||
contractWrappers.coordinator.address,
|
||||
);
|
||||
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 fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerAssetData,
|
||||
takerAssetData,
|
||||
new BigNumber(1),
|
||||
new BigNumber(1),
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
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.sendTransactionAsync('localhost', {
|
||||
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({
|
||||
networkId: 50,
|
||||
})
|
||||
.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({
|
||||
networkId: 50,
|
||||
})
|
||||
.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({
|
||||
networkId: 50,
|
||||
})
|
||||
.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({
|
||||
networkId: 50,
|
||||
})
|
||||
.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({
|
||||
networkId: 50,
|
||||
})
|
||||
.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
|
@ -62,6 +62,7 @@ export class FillScenarios {
|
||||
fillableAmount: BigNumber,
|
||||
feeRecipientAddress: string,
|
||||
expirationTimeSeconds?: BigNumber,
|
||||
senderAddress?: string,
|
||||
): Promise<SignedOrder> {
|
||||
return this._createAsymmetricFillableSignedOrderWithFeesAsync(
|
||||
makerAssetData,
|
||||
@ -74,6 +75,7 @@ export class FillScenarios {
|
||||
fillableAmount,
|
||||
feeRecipientAddress,
|
||||
expirationTimeSeconds,
|
||||
senderAddress,
|
||||
);
|
||||
}
|
||||
public async createAsymmetricFillableSignedOrderAsync(
|
||||
@ -149,6 +151,7 @@ export class FillScenarios {
|
||||
takerFillableAmount: BigNumber,
|
||||
feeRecipientAddress: string,
|
||||
expirationTimeSeconds?: BigNumber,
|
||||
senderAddress?: string,
|
||||
): Promise<SignedOrder> {
|
||||
await this._increaseBalanceAndAllowanceWithAssetDataAsync(makerAssetData, makerAddress, makerFillableAmount);
|
||||
await this._increaseBalanceAndAllowanceWithAssetDataAsync(takerAssetData, takerAddress, takerFillableAmount);
|
||||
@ -157,7 +160,7 @@ export class FillScenarios {
|
||||
this._increaseERC20BalanceAndAllowanceAsync(this._zrxTokenAddress, makerAddress, makerFee),
|
||||
this._increaseERC20BalanceAndAllowanceAsync(this._zrxTokenAddress, takerAddress, takerFee),
|
||||
]);
|
||||
const senderAddress = constants.NULL_ADDRESS;
|
||||
const _senderAddress = senderAddress ? senderAddress : constants.NULL_ADDRESS;
|
||||
|
||||
const signedOrder = await orderFactory.createSignedOrderAsync(
|
||||
this._web3Wrapper.getProvider(),
|
||||
@ -169,7 +172,7 @@ export class FillScenarios {
|
||||
this._exchangeAddress,
|
||||
{
|
||||
takerAddress,
|
||||
senderAddress,
|
||||
senderAddress: _senderAddress,
|
||||
makerFee,
|
||||
takerFee,
|
||||
feeRecipientAddress,
|
||||
|
@ -38,6 +38,7 @@ export const docGenConfigs: DocGenConfigs = {
|
||||
// and getting confused. Any class name in this list will not have it's constructor rendered in our docs.
|
||||
CLASSES_WITH_HIDDEN_CONSTRUCTORS: [
|
||||
'AssetBuyer',
|
||||
'CoordinatorWrapper',
|
||||
'DutchAuctionWrapper',
|
||||
'ERC20ProxyWrapper',
|
||||
'ERC20TokenWrapper',
|
||||
@ -56,9 +57,11 @@ export const docGenConfigs: DocGenConfigs = {
|
||||
'NonceSubproviderErrors',
|
||||
'Web3WrapperErrors',
|
||||
'ContractWrappersError',
|
||||
'OrderError',
|
||||
'TypedDataError',
|
||||
'AssetBuyerError',
|
||||
'ForwarderWrapperError',
|
||||
'CoordinatorServerError',
|
||||
'CoordinatorServerCancellationResponse',
|
||||
],
|
||||
// Some libraries only export types. In those cases, we cannot check if the exported types are part of the
|
||||
// "exported public interface". Thus we add them here and skip those checks.
|
||||
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "8.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Renamed `OrderError` to `TypedDataError`",
|
||||
"pr": 1792
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "7.2.0",
|
||||
"changes": [
|
||||
|
@ -68,7 +68,7 @@ export {
|
||||
} from '@0x/types';
|
||||
|
||||
export {
|
||||
OrderError,
|
||||
TypedDataError,
|
||||
TradeSide,
|
||||
TransferType,
|
||||
FindFeeOrdersThatCoverFeesForTargetOrdersOpts,
|
||||
|
@ -3,7 +3,7 @@ import { BigNumber, providerUtils } from '@0x/utils';
|
||||
import { SupportedProvider, ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { OrderError, TradeSide, TransferType } from './types';
|
||||
import { TradeSide, TransferType, TypedDataError } from './types';
|
||||
|
||||
import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
|
||||
import { constants } from './constants';
|
||||
@ -211,7 +211,7 @@ export class OrderValidationUtils {
|
||||
signedOrder.makerAddress,
|
||||
);
|
||||
if (!isValid) {
|
||||
throw new Error(OrderError.InvalidSignature);
|
||||
throw new Error(TypedDataError.InvalidSignature);
|
||||
}
|
||||
const filledTakerTokenAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
|
||||
if (signedOrder.takerAssetAmount.eq(filledTakerTokenAmount)) {
|
||||
|
@ -2,7 +2,15 @@ import { ExchangeContract, IValidatorContract, IWalletContract } from '@0x/abi-g
|
||||
import { getContractAddressesForNetworkOrThrow } from '@0x/contract-addresses';
|
||||
import * as artifacts from '@0x/contract-artifacts';
|
||||
import { schemas } from '@0x/json-schemas';
|
||||
import { ECSignature, Order, SignatureType, SignedOrder, ValidatorSignature } from '@0x/types';
|
||||
import {
|
||||
ECSignature,
|
||||
Order,
|
||||
SignatureType,
|
||||
SignedOrder,
|
||||
SignedZeroExTransaction,
|
||||
ValidatorSignature,
|
||||
ZeroExTransaction,
|
||||
} from '@0x/types';
|
||||
import { providerUtils } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { SupportedProvider } from 'ethereum-types';
|
||||
@ -12,7 +20,7 @@ import * as _ from 'lodash';
|
||||
import { assert } from './assert';
|
||||
import { eip712Utils } from './eip712_utils';
|
||||
import { orderHashUtils } from './order_hash';
|
||||
import { OrderError } from './types';
|
||||
import { TypedDataError } from './types';
|
||||
import { utils } from './utils';
|
||||
|
||||
export const signatureUtils = {
|
||||
@ -278,7 +286,50 @@ export const signatureUtils = {
|
||||
} catch (err) {
|
||||
// Detect if Metamask to transition users to the MetamaskSubprovider
|
||||
if ((provider as any).isMetaMask) {
|
||||
throw new Error(OrderError.InvalidMetamaskSigner);
|
||||
throw new Error(TypedDataError.InvalidMetamaskSigner);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Signs a ZeroExTransaction using `eth_signTypedData` and returns a SignedZeroExTransaction.
|
||||
* @param supportedProvider Web3 provider to use for all JSON RPC requests
|
||||
* @param transaction The ZeroEx Transaction to sign.
|
||||
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
|
||||
* must be available via the supplied Provider.
|
||||
* @return A SignedZeroExTransaction containing the ZeroExTransaction and Elliptic curve signature with Signature Type.
|
||||
*/
|
||||
async ecSignTypedDataTransactionAsync(
|
||||
supportedProvider: SupportedProvider,
|
||||
transaction: ZeroExTransaction,
|
||||
signerAddress: string,
|
||||
): Promise<SignedZeroExTransaction> {
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
assert.isETHAddressHex('signerAddress', signerAddress);
|
||||
assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema, [schemas.hexSchema]);
|
||||
const web3Wrapper = new Web3Wrapper(provider);
|
||||
await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
|
||||
const normalizedSignerAddress = signerAddress.toLowerCase();
|
||||
const typedData = eip712Utils.createZeroExTransactionTypedData(transaction);
|
||||
try {
|
||||
const signature = await web3Wrapper.signTypedDataAsync(normalizedSignerAddress, typedData);
|
||||
const ecSignatureRSV = parseSignatureHexAsRSV(signature);
|
||||
const signatureBuffer = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignatureRSV.v),
|
||||
ethUtil.toBuffer(ecSignatureRSV.r),
|
||||
ethUtil.toBuffer(ecSignatureRSV.s),
|
||||
ethUtil.toBuffer(SignatureType.EIP712),
|
||||
]);
|
||||
const signatureHex = `0x${signatureBuffer.toString('hex')}`;
|
||||
return {
|
||||
...transaction,
|
||||
signature: signatureHex,
|
||||
};
|
||||
} catch (err) {
|
||||
// Detect if Metamask to transition users to the MetamaskSubprovider
|
||||
if ((provider as any).isMetaMask) {
|
||||
throw new Error(TypedDataError.InvalidMetamaskSigner);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
@ -339,9 +390,9 @@ export const signatureUtils = {
|
||||
}
|
||||
// Detect if Metamask to transition users to the MetamaskSubprovider
|
||||
if ((provider as any).isMetaMask) {
|
||||
throw new Error(OrderError.InvalidMetamaskSigner);
|
||||
throw new Error(TypedDataError.InvalidMetamaskSigner);
|
||||
} else {
|
||||
throw new Error(OrderError.InvalidSignature);
|
||||
throw new Error(TypedDataError.InvalidSignature);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export enum OrderError {
|
||||
export enum TypedDataError {
|
||||
InvalidSignature = 'INVALID_SIGNATURE',
|
||||
InvalidMetamaskSigner = "MetaMask provider must be wrapped in a MetamaskSubprovider (from the '@0x/subproviders' package) in order to work with this method.",
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
"devDependencies": {
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@types/axios": "^0.14.0",
|
||||
"@types/ramda": "^0.25.38",
|
||||
"@types/ramda": "^0.26.8",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
@ -60,9 +60,9 @@
|
||||
"ethereum-types": "^2.1.2",
|
||||
"pg": "^7.5.0",
|
||||
"prettier": "^1.16.3",
|
||||
"ramda": "^0.25.0",
|
||||
"ramda": "^0.26.1",
|
||||
"reflect-metadata": "^0.1.12",
|
||||
"sqlite3": "^4.0.2",
|
||||
"typeorm": "^0.2.7"
|
||||
"typeorm": "0.2.7"
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ async function getOrderbookAsync(): Promise<void> {
|
||||
// Parse the sra orders, then add source url to each.
|
||||
const orders = R.pipe(
|
||||
parseSraOrders,
|
||||
R.map(setSourceUrl(RADAR_RELAY_URL)),
|
||||
R.map(o => R.set(sourceUrlProp, RADAR_RELAY_URL, o)),
|
||||
)(rawOrders);
|
||||
// Save all the orders and update the observed time stamps in a single
|
||||
// transaction.
|
||||
@ -50,13 +50,3 @@ async function getOrderbookAsync(): Promise<void> {
|
||||
}
|
||||
|
||||
const sourceUrlProp = R.lensProp('sourceUrl');
|
||||
|
||||
/**
|
||||
* Sets the source url for a single order. Returns a new order instead of
|
||||
* mutating the given one.
|
||||
*/
|
||||
const setSourceUrl = R.curry(
|
||||
(sourceURL: string, order: SraOrder): SraOrder => {
|
||||
return R.set(sourceUrlProp, sourceURL, order);
|
||||
},
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ContractWrappersError } from '@0x/contract-wrappers';
|
||||
import { assetDataUtils, OrderError } from '@0x/order-utils';
|
||||
import { assetDataUtils, TypedDataError } from '@0x/order-utils';
|
||||
import { constants as sharedConstants, Networks } from '@0x/react-shared';
|
||||
import { ExchangeContractErrs } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
@ -232,7 +232,7 @@ export const utils = {
|
||||
zeroExErrToHumanReadableErrMsg(error: ContractWrappersError | ExchangeContractErrs, takerAddress: string): string {
|
||||
const ContractWrappersErrorToHumanReadableError: { [error: string]: string } = {
|
||||
[BlockchainCallErrs.UserHasNoAssociatedAddresses]: 'User has no addresses available',
|
||||
[OrderError.InvalidSignature]: 'Order signature is not valid',
|
||||
[TypedDataError.InvalidSignature]: 'Order signature is not valid',
|
||||
[ContractWrappersError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network',
|
||||
[ContractWrappersError.InvalidJump]: 'Invalid jump occured while executing the transaction',
|
||||
[ContractWrappersError.OutOfGas]: 'Transaction ran out of gas',
|
||||
|
File diff suppressed because one or more lines are too long
520
yarn.lock
520
yarn.lock
@ -489,6 +489,30 @@
|
||||
ethers "~4.0.4"
|
||||
lodash "^4.17.11"
|
||||
|
||||
"@0x/contract-wrappers@^8.0.5":
|
||||
version "8.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-8.0.5.tgz#2bad814956625b740403a903d459a6e58fc77b92"
|
||||
integrity sha512-lz67pqZIN6nY0mAsIAeaii3k4JPtHRlfPl5hZAOyw3VzsrYPQ5u09rLWB04hRjSx4Ibmj6c1NLTpFLat/7bXlA==
|
||||
dependencies:
|
||||
"@0x/abi-gen-wrappers" "^4.1.0"
|
||||
"@0x/assert" "^2.0.8"
|
||||
"@0x/contract-addresses" "^2.3.0"
|
||||
"@0x/contract-artifacts" "^1.4.0"
|
||||
"@0x/json-schemas" "^3.0.8"
|
||||
"@0x/order-utils" "^7.1.1"
|
||||
"@0x/types" "^2.2.1"
|
||||
"@0x/typescript-typings" "^4.2.1"
|
||||
"@0x/utils" "^4.3.0"
|
||||
"@0x/web3-wrapper" "^6.0.4"
|
||||
ethereum-types "^2.1.1"
|
||||
ethereumjs-abi "0.6.5"
|
||||
ethereumjs-blockstream "6.0.0"
|
||||
ethereumjs-util "^5.1.1"
|
||||
ethers "~4.0.4"
|
||||
js-sha3 "^0.7.0"
|
||||
lodash "^4.17.11"
|
||||
uuid "^3.3.2"
|
||||
|
||||
"@0x/contracts-asset-proxy@^1.0.2":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.npmjs.org/@0x/contracts-asset-proxy/-/contracts-asset-proxy-1.0.9.tgz#3a48e64b93ddc642834bde1bd09cdd84ca688a2b"
|
||||
@ -505,20 +529,6 @@
|
||||
ethereum-types "^2.1.0"
|
||||
lodash "^4.17.11"
|
||||
|
||||
"@0x/contracts-erc20@1.0.8":
|
||||
version "1.0.8"
|
||||
resolved "https://registry.npmjs.org/@0x/contracts-erc20/-/contracts-erc20-1.0.8.tgz#882f3acf1a44148800d9bef692aa377627df9ace"
|
||||
dependencies:
|
||||
"@0x/base-contract" "^5.0.1"
|
||||
"@0x/contracts-exchange-libs" "1.0.2"
|
||||
"@0x/contracts-utils" "2.0.1"
|
||||
"@0x/types" "^2.1.1"
|
||||
"@0x/typescript-typings" "^4.1.0"
|
||||
"@0x/utils" "^4.2.1"
|
||||
"@0x/web3-wrapper" "^6.0.1"
|
||||
ethereum-types "^2.1.0"
|
||||
lodash "^4.17.11"
|
||||
|
||||
"@0x/contracts-erc20@^1.0.2", "@0x/contracts-erc20@^1.0.9":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.npmjs.org/@0x/contracts-erc20/-/contracts-erc20-1.0.9.tgz#366ce8222dcae5ade0ea7ca95332416a080f6abf"
|
||||
@ -533,19 +543,6 @@
|
||||
ethereum-types "^2.1.0"
|
||||
lodash "^4.17.11"
|
||||
|
||||
"@0x/contracts-erc721@1.0.8":
|
||||
version "1.0.8"
|
||||
resolved "https://registry.npmjs.org/@0x/contracts-erc721/-/contracts-erc721-1.0.8.tgz#d3746c26eec57662654557121601b5bb81085637"
|
||||
dependencies:
|
||||
"@0x/base-contract" "^5.0.1"
|
||||
"@0x/contracts-utils" "2.0.1"
|
||||
"@0x/types" "^2.1.1"
|
||||
"@0x/typescript-typings" "^4.1.0"
|
||||
"@0x/utils" "^4.2.1"
|
||||
"@0x/web3-wrapper" "^6.0.1"
|
||||
ethereum-types "^2.1.0"
|
||||
lodash "^4.17.11"
|
||||
|
||||
"@0x/contracts-erc721@^1.0.2", "@0x/contracts-erc721@^1.0.9":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.npmjs.org/@0x/contracts-erc721/-/contracts-erc721-1.0.9.tgz#3991858a3bf5a80dcd6e5fd65e938f8adc3b347c"
|
||||
@ -637,6 +634,36 @@
|
||||
ethereumjs-util "^5.1.1"
|
||||
lodash "^4.17.11"
|
||||
|
||||
"@0x/coordinator-server@0.1.1":
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@0x/coordinator-server/-/coordinator-server-0.1.1.tgz#7eeb74959dc72b76756b95ccd14fd9fa8d072ede"
|
||||
integrity sha512-KxdZO93TigDyVRC90DMrQDIxjNTq3zMgmW9j8AVzx78nWXiRrUknYnkPP2a1Fp53H6Ngilc33zFjysR+QgrZPA==
|
||||
dependencies:
|
||||
"@0x/assert" "^2.0.8"
|
||||
"@0x/contract-addresses" "^2.3.0"
|
||||
"@0x/contract-wrappers" "^8.0.5"
|
||||
"@0x/contracts-erc20" "^2.1.0"
|
||||
"@0x/json-schemas" "^3.0.8"
|
||||
"@0x/order-utils" "^7.2.0"
|
||||
"@0x/subproviders" "^4.0.4"
|
||||
"@0x/types" "^2.2.1"
|
||||
"@0x/typescript-typings" "^4.2.1"
|
||||
"@0x/utils" "^4.3.0"
|
||||
"@0x/web3-wrapper" "^6.0.4"
|
||||
"@babel/polyfill" "^7.0.0"
|
||||
body-parser "^1.18.3"
|
||||
cors "^2.8.5"
|
||||
express "^4.16.3"
|
||||
express-async-handler "^1.1.4"
|
||||
forever "^0.15.3"
|
||||
http-status-codes "^1.3.0"
|
||||
jsonschema "^1.2.4"
|
||||
lodash "^4.17.11"
|
||||
reflect-metadata "^0.1.10"
|
||||
sqlite3 "^4.0.2"
|
||||
typeorm "0.2.7"
|
||||
websocket "^1.0.25"
|
||||
|
||||
"@0x/order-utils@^5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@0x/order-utils/-/order-utils-5.0.0.tgz#7f43e0310ace31738895881501c8dda9c3a3aefa"
|
||||
@ -719,6 +746,14 @@
|
||||
esutils "^2.0.2"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/polyfill@^7.0.0":
|
||||
version "7.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.4.3.tgz#332dc6f57b718017c3a8b37b4eea8aa6eeac1187"
|
||||
integrity sha512-rkv8WIvJshA5Ev8iNMGgz5WZkRtgtiPexiT7w5qevGTuT7ZBfM3de9ox1y9JR5/OXb/sWGBbWlHNa7vQKqku3Q==
|
||||
dependencies:
|
||||
core-js "^2.6.5"
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
"@babel/runtime@7.0.0", "@babel/runtime@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz#adeb78fedfc855aa05bc041640f3f6f98e85424c"
|
||||
@ -1682,6 +1717,13 @@
|
||||
version "2.2.48"
|
||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.48.tgz#3523b126a0b049482e1c3c11877460f76622ffab"
|
||||
|
||||
"@types/nock@^10.0.1":
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/nock/-/nock-10.0.1.tgz#ca545bdd0c2559fe76e3cda1ba011a74fb940d45"
|
||||
integrity sha512-3Dbkj/f0HxuvyYfInbQCHLASFyjnNUcidabwrbhJDMZOXXznNyQpzsBgZnY2K+c43OekqWvZ+tDjGsGTKm1d5g==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/node@*", "@types/node@^10.3.2":
|
||||
version "10.9.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897"
|
||||
@ -1718,9 +1760,10 @@
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/query-string/-/query-string-5.1.0.tgz#7f40cdea49ddafa0ea4f3db35fb6c24d3bfd4dcc"
|
||||
|
||||
"@types/ramda@^0.25.38":
|
||||
version "0.25.46"
|
||||
resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.25.46.tgz#8399a35eb117f4a821deb9c4cfecc9b276fb7daf"
|
||||
"@types/ramda@^0.26.8":
|
||||
version "0.26.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.26.8.tgz#e002612cca52e52f9176d577f4d6229c8c72a10a"
|
||||
integrity sha512-PSMkNNhB900U2xcyndlkVjJeCXpVtf1yod0Kdq/ArsLDIE0tW8pLBChmQeJs4o4dfp3AEP+AGm6zIseZPU4ndA==
|
||||
|
||||
"@types/rc-slider@^8.6.0":
|
||||
version "8.6.0"
|
||||
@ -2566,7 +2609,7 @@ assert@^1.1.1:
|
||||
dependencies:
|
||||
util "0.10.3"
|
||||
|
||||
assertion-error@^1.0.1:
|
||||
assertion-error@^1.0.1, assertion-error@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
|
||||
|
||||
@ -2608,6 +2651,16 @@ async-parallel@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/async-parallel/-/async-parallel-1.2.3.tgz#0b90550aeffb7a365d8cee881eb0618f656a3450"
|
||||
|
||||
async@0.2.9:
|
||||
version "0.2.9"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-0.2.9.tgz#df63060fbf3d33286a76aaf6d55a2986d9ff8619"
|
||||
integrity sha1-32MGD789Myhqdqr21Vophtn/hhk=
|
||||
|
||||
async@0.2.x, async@~0.2.9:
|
||||
version "0.2.10"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
|
||||
integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E=
|
||||
|
||||
async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
|
||||
@ -3663,7 +3716,7 @@ body-parser@1.18.2, body-parser@^1.16.0, body-parser@^1.17.1:
|
||||
raw-body "2.3.2"
|
||||
type-is "~1.6.15"
|
||||
|
||||
body-parser@1.18.3:
|
||||
body-parser@1.18.3, body-parser@^1.18.3:
|
||||
version "1.18.3"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4"
|
||||
dependencies:
|
||||
@ -3769,6 +3822,17 @@ brcast@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/brcast/-/brcast-3.0.1.tgz#6256a8349b20de9eed44257a9b24d71493cd48dd"
|
||||
|
||||
broadway@~0.3.2, broadway@~0.3.6:
|
||||
version "0.3.6"
|
||||
resolved "https://registry.yarnpkg.com/broadway/-/broadway-0.3.6.tgz#7dbef068b954b7907925fd544963b578a902ba7a"
|
||||
integrity sha1-fb7waLlUt5B5Jf1USWO1eKkCuno=
|
||||
dependencies:
|
||||
cliff "0.1.9"
|
||||
eventemitter2 "0.4.14"
|
||||
nconf "0.6.9"
|
||||
utile "0.2.1"
|
||||
winston "0.8.0"
|
||||
|
||||
brorand@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||
@ -4083,6 +4147,13 @@ call-me-maybe@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
|
||||
|
||||
caller@~0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/caller/-/caller-0.0.1.tgz#f37a1d6ea10e829d94721ae29a90bb4fb52ab767"
|
||||
integrity sha1-83odbqEOgp2UchrimpC7T7Uqt2c=
|
||||
dependencies:
|
||||
tape "~2.3.2"
|
||||
|
||||
callsites@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
|
||||
@ -4129,10 +4200,6 @@ camelcase@^4.0.0, camelcase@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
|
||||
|
||||
camelcase@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
|
||||
|
||||
caniuse-api@^1.5.2:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
|
||||
@ -4196,6 +4263,18 @@ chai@^4.0.1:
|
||||
pathval "^1.0.0"
|
||||
type-detect "^4.0.0"
|
||||
|
||||
chai@^4.1.2:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
|
||||
integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==
|
||||
dependencies:
|
||||
assertion-error "^1.1.0"
|
||||
check-error "^1.0.2"
|
||||
deep-eql "^3.0.1"
|
||||
get-func-name "^2.0.0"
|
||||
pathval "^1.1.0"
|
||||
type-detect "^4.0.5"
|
||||
|
||||
chain-function@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc"
|
||||
@ -4466,6 +4545,24 @@ cli-width@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
|
||||
|
||||
cliff@0.1.9:
|
||||
version "0.1.9"
|
||||
resolved "https://registry.yarnpkg.com/cliff/-/cliff-0.1.9.tgz#a211e09c6a3de3ba1af27d049d301250d18812bc"
|
||||
integrity sha1-ohHgnGo947oa8n0EnTASUNGIErw=
|
||||
dependencies:
|
||||
colors "0.x.x"
|
||||
eyes "0.1.x"
|
||||
winston "0.8.x"
|
||||
|
||||
cliff@~0.1.9:
|
||||
version "0.1.10"
|
||||
resolved "https://registry.yarnpkg.com/cliff/-/cliff-0.1.10.tgz#53be33ea9f59bec85609ee300ac4207603e52013"
|
||||
integrity sha1-U74z6p9ZvshWCe4wCsQgdgPlIBM=
|
||||
dependencies:
|
||||
colors "~1.0.3"
|
||||
eyes "~0.1.8"
|
||||
winston "0.8.x"
|
||||
|
||||
clipboard@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/clipboard/-/clipboard-2.0.1.tgz#a12481e1c13d8a50f5f036b0560fe5d16d74e46a"
|
||||
@ -4609,7 +4706,12 @@ colors@0.5.x:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz#7d0023eaeb154e8ee9fce75dcb923d0ed1667774"
|
||||
|
||||
colors@1.0.x:
|
||||
colors@0.6.x, colors@0.x.x, colors@~0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc"
|
||||
integrity sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=
|
||||
|
||||
colors@1.0.x, colors@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
|
||||
|
||||
@ -5024,6 +5126,11 @@ core-js@^2.4.0, core-js@^2.5.0:
|
||||
version "2.5.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.5.tgz#b14dde936c640c0579a6b50cabcc132dd6127e3b"
|
||||
|
||||
core-js@^2.6.5:
|
||||
version "2.6.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895"
|
||||
integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==
|
||||
|
||||
core-util-is@1.0.2, core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
@ -5035,6 +5142,14 @@ cors@^2.8.1:
|
||||
object-assign "^4"
|
||||
vary "^1"
|
||||
|
||||
cors@^2.8.5:
|
||||
version "2.8.5"
|
||||
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
|
||||
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
|
||||
dependencies:
|
||||
object-assign "^4"
|
||||
vary "^1"
|
||||
|
||||
cosmiconfig@^5.0.2:
|
||||
version "5.0.5"
|
||||
resolved "http://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.5.tgz#a809e3c2306891ce17ab70359dc8bdf661fe2cd0"
|
||||
@ -5415,7 +5530,7 @@ debug@^3.2.6:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^4.0.1:
|
||||
debug@^4.0.1, debug@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
|
||||
dependencies:
|
||||
@ -5438,7 +5553,7 @@ decamelize-keys@^1.0.0:
|
||||
decamelize "^1.1.0"
|
||||
map-obj "^1.0.0"
|
||||
|
||||
decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
|
||||
decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
|
||||
@ -5514,16 +5629,21 @@ dedent@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "http://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
|
||||
|
||||
deep-eql@^3.0.0:
|
||||
deep-eql@^3.0.0, deep-eql@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
|
||||
dependencies:
|
||||
type-detect "^4.0.0"
|
||||
|
||||
deep-equal@^1.0.1, deep-equal@~1.0.1:
|
||||
deep-equal@*, deep-equal@^1.0.0, deep-equal@^1.0.1, deep-equal@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
|
||||
|
||||
deep-equal@~0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-0.1.2.tgz#b246c2b80a570a47c11be1d9bd1070ec878b87ce"
|
||||
integrity sha1-skbCuApXCkfBG+HZvRBw7IeLh84=
|
||||
|
||||
deep-equal@~0.2.1:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-0.2.2.tgz#84b745896f34c684e98f2ce0e42abaf43bba017d"
|
||||
@ -5606,6 +5726,11 @@ defined@^1.0.0, defined@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
|
||||
|
||||
defined@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/defined/-/defined-0.0.0.tgz#f35eea7d705e933baf13b2f03b3f83d921403b3e"
|
||||
integrity sha1-817qfXBekzuvE7LwOz+D2SFAOz4=
|
||||
|
||||
del@^2.0.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
|
||||
@ -5748,6 +5873,11 @@ dir-glob@^2.0.0:
|
||||
arrify "^1.0.1"
|
||||
path-type "^3.0.0"
|
||||
|
||||
director@1.2.7:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/director/-/director-1.2.7.tgz#bfd3741075fd7fb1a5b2e13658c5f4bec77736f3"
|
||||
integrity sha1-v9N0EHX9f7GlsuE2WMX0vsd3NvM=
|
||||
|
||||
dirty-chai@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dirty-chai/-/dirty-chai-2.0.1.tgz#6b2162ef17f7943589da840abc96e75bda01aff3"
|
||||
@ -6754,6 +6884,13 @@ ev-emitter@^1.0.0, ev-emitter@^1.0.1, ev-emitter@^1.0.2:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ev-emitter/-/ev-emitter-1.1.1.tgz#8f18b0ce5c76a5d18017f71c0a795c65b9138f2a"
|
||||
|
||||
event-stream@~0.5:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-0.5.3.tgz#b77b9309f7107addfeab63f0c0eafd8db0bd8c1c"
|
||||
integrity sha1-t3uTCfcQet3+q2PwwOr9jbC9jBw=
|
||||
dependencies:
|
||||
optimist "0.2"
|
||||
|
||||
event-stream@~3.3.0:
|
||||
version "3.3.4"
|
||||
resolved "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
|
||||
@ -6766,6 +6903,11 @@ event-stream@~3.3.0:
|
||||
stream-combiner "~0.0.4"
|
||||
through "~2.3.1"
|
||||
|
||||
eventemitter2@0.4.14, eventemitter2@~0.4.14:
|
||||
version "0.4.14"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab"
|
||||
integrity sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=
|
||||
|
||||
eventemitter3@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.1.1.tgz#47786bdaa087caf7b1b75e73abc5c7d540158cd0"
|
||||
@ -6914,6 +7056,11 @@ expect@^23.6.0:
|
||||
jest-message-util "^23.4.0"
|
||||
jest-regex-util "^23.3.0"
|
||||
|
||||
express-async-handler@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/express-async-handler/-/express-async-handler-1.1.4.tgz#225a84908df63b35ae9df94b6f0f1af061266426"
|
||||
integrity sha512-HdmbVF4V4w1q/iz++RV7bUxIeepTukWewiJGkoCKQMtvPF11MLTa7It9PRc/reysXXZSEyD4Pthchju+IUbMiQ==
|
||||
|
||||
express@^4.14.0, express@^4.15.2, express@^4.16.2:
|
||||
version "4.16.3"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53"
|
||||
@ -7055,7 +7202,7 @@ extsprintf@^1.2.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
|
||||
|
||||
eyes@0.1.x:
|
||||
eyes@0.1.x, eyes@~0.1.8:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
|
||||
|
||||
@ -7363,6 +7510,16 @@ flat-cache@^1.2.1:
|
||||
graceful-fs "^4.1.2"
|
||||
write "^0.2.1"
|
||||
|
||||
flatiron@~0.4.2:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/flatiron/-/flatiron-0.4.3.tgz#248cf79a3da7d7dc379e2a11c92a2719cbb540f6"
|
||||
integrity sha1-JIz3mj2n19w3nioRySonGcu1QPY=
|
||||
dependencies:
|
||||
broadway "~0.3.2"
|
||||
director "1.2.7"
|
||||
optimist "0.6.0"
|
||||
prompt "0.2.14"
|
||||
|
||||
flatten@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
|
||||
@ -7435,6 +7592,38 @@ forever-agent@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
||||
|
||||
forever-monitor@~1.7.0:
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/forever-monitor/-/forever-monitor-1.7.1.tgz#5d820f4a3a78db2d81ae2671f158b9e86a091bb8"
|
||||
integrity sha1-XYIPSjp42y2BriZx8Vi56GoJG7g=
|
||||
dependencies:
|
||||
broadway "~0.3.6"
|
||||
chokidar "^1.0.1"
|
||||
minimatch "~3.0.2"
|
||||
ps-tree "0.0.x"
|
||||
utile "~0.2.1"
|
||||
|
||||
forever@^0.15.3:
|
||||
version "0.15.3"
|
||||
resolved "https://registry.yarnpkg.com/forever/-/forever-0.15.3.tgz#77d9d7e15fd2f511ad9d84a110c7dd8fc8ecebc2"
|
||||
integrity sha1-d9nX4V/S9RGtnYShEMfdj8js68I=
|
||||
dependencies:
|
||||
cliff "~0.1.9"
|
||||
clone "^1.0.2"
|
||||
colors "~0.6.2"
|
||||
flatiron "~0.4.2"
|
||||
forever-monitor "~1.7.0"
|
||||
nconf "~0.6.9"
|
||||
nssocket "~0.5.1"
|
||||
object-assign "^3.0.0"
|
||||
optimist "~0.6.0"
|
||||
path-is-absolute "~1.0.0"
|
||||
prettyjson "^1.1.2"
|
||||
shush "^1.0.0"
|
||||
timespan "~2.3.0"
|
||||
utile "~0.2.1"
|
||||
winston "~0.8.1"
|
||||
|
||||
form-data@~2.1.1:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
|
||||
@ -8058,6 +8247,7 @@ got@^6.7.1:
|
||||
graceful-fs@4.1.15, graceful-fs@^3.0.0, graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@~1.2.0:
|
||||
version "4.1.15"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||
|
||||
"graceful-readlink@>= 1.0.0":
|
||||
version "1.0.1"
|
||||
@ -8573,6 +8763,11 @@ http-signature@~1.2.0:
|
||||
jsprim "^1.2.2"
|
||||
sshpk "^1.7.0"
|
||||
|
||||
http-status-codes@^1.3.0, http-status-codes@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-1.3.2.tgz#181dfa4455ef454e5e4d827718fca3936680d10d"
|
||||
integrity sha512-nDUtj0ltIt08tGi2VWSpSzNNFye0v3YSe9lX3lIqLTuVvvRiYCvs4QQBSHo0eomFYw1wlUuofurUAlTm+vHnXg==
|
||||
|
||||
https-browserify@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
||||
@ -8751,7 +8946,7 @@ inherits@2.0.1, inherits@=2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
||||
|
||||
ini@^1.3.2, ini@^1.3.4, ini@~1.3.0:
|
||||
ini@1.x.x, ini@^1.3.2, ini@^1.3.4, ini@~1.3.0:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||
|
||||
@ -9990,7 +10185,7 @@ jsonschema-draft4@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/jsonschema-draft4/-/jsonschema-draft4-1.0.0.tgz#f0af2005054f0f0ade7ea2118614b69dc512d865"
|
||||
|
||||
jsonschema@*, jsonschema@1.2.4, jsonschema@^1.2.0:
|
||||
jsonschema@*, jsonschema@1.2.4, jsonschema@^1.2.0, jsonschema@^1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.4.tgz#a46bac5d3506a254465bc548876e267c6d0d6464"
|
||||
|
||||
@ -10150,6 +10345,11 @@ lazy-cache@^1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
|
||||
|
||||
lazy@~1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/lazy/-/lazy-1.0.11.tgz#daa068206282542c088288e975c297c1ae77b690"
|
||||
integrity sha1-2qBoIGKCVCwIgojpdcKXwa53tpA=
|
||||
|
||||
lazystream@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4"
|
||||
@ -10730,7 +10930,8 @@ lodash.words@^3.0.0:
|
||||
|
||||
lodash@4.17.11, lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.3:
|
||||
version "4.17.11"
|
||||
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
|
||||
|
||||
lodash@=4.17.4:
|
||||
version "4.17.4"
|
||||
@ -11261,7 +11462,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
|
||||
|
||||
"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
|
||||
"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
dependencies:
|
||||
@ -11513,6 +11714,20 @@ natural-compare@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
|
||||
nconf@0.6.9, nconf@~0.6.9:
|
||||
version "0.6.9"
|
||||
resolved "https://registry.yarnpkg.com/nconf/-/nconf-0.6.9.tgz#9570ef15ed6f9ae6b2b3c8d5e71b66d3193cd661"
|
||||
integrity sha1-lXDvFe1vmuays8jV5xtm0xk81mE=
|
||||
dependencies:
|
||||
async "0.2.9"
|
||||
ini "1.x.x"
|
||||
optimist "0.6.0"
|
||||
|
||||
ncp@0.4.x:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/ncp/-/ncp-0.4.2.tgz#abcc6cbd3ec2ed2a729ff6e7c1fa8f01784a8574"
|
||||
integrity sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=
|
||||
|
||||
ncp@1.0.x:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ncp/-/ncp-1.0.1.tgz#d15367e5cb87432ba117d2bf80fdf45aecfb4246"
|
||||
@ -11567,6 +11782,21 @@ no-case@^2.2.0, no-case@^2.3.2:
|
||||
dependencies:
|
||||
lower-case "^1.1.1"
|
||||
|
||||
nock@^10.0.6:
|
||||
version "10.0.6"
|
||||
resolved "https://registry.yarnpkg.com/nock/-/nock-10.0.6.tgz#e6d90ee7a68b8cfc2ab7f6127e7d99aa7d13d111"
|
||||
integrity sha512-b47OWj1qf/LqSQYnmokNWM8D88KvUl2y7jT0567NB3ZBAZFz2bWp2PC81Xn7u8F2/vJxzkzNZybnemeFa7AZ2w==
|
||||
dependencies:
|
||||
chai "^4.1.2"
|
||||
debug "^4.1.0"
|
||||
deep-equal "^1.0.0"
|
||||
json-stringify-safe "^5.0.1"
|
||||
lodash "^4.17.5"
|
||||
mkdirp "^0.5.0"
|
||||
propagate "^1.0.0"
|
||||
qs "^6.5.1"
|
||||
semver "^5.5.0"
|
||||
|
||||
node-abi@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.3.0.tgz#f3d554d6ac72a9ee16f0f4dc9548db7c08de4986"
|
||||
@ -11904,6 +12134,14 @@ npmlog@~2.0.0:
|
||||
are-we-there-yet "~1.1.2"
|
||||
gauge "~1.2.5"
|
||||
|
||||
nssocket@~0.5.1:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/nssocket/-/nssocket-0.5.3.tgz#883ca2ec605f5ed64a4d5190b2625401928f8f8d"
|
||||
integrity sha1-iDyi7GBfXtZKTVGQsmJUAZKPj40=
|
||||
dependencies:
|
||||
eventemitter2 "~0.4.14"
|
||||
lazy "~1.0.11"
|
||||
|
||||
nth-check@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4"
|
||||
@ -12171,7 +12409,22 @@ opn@^5.1.0, opn@^5.3.0:
|
||||
dependencies:
|
||||
is-wsl "^1.1.0"
|
||||
|
||||
optimist@^0.6.1:
|
||||
optimist@0.2:
|
||||
version "0.2.8"
|
||||
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.2.8.tgz#e981ab7e268b457948593b55674c099a815cac31"
|
||||
integrity sha1-6YGrfiaLRXlIWTtVZ0wJmoFcrDE=
|
||||
dependencies:
|
||||
wordwrap ">=0.0.1 <0.1.0"
|
||||
|
||||
optimist@0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.0.tgz#69424826f3405f79f142e6fc3d9ae58d4dbb9200"
|
||||
integrity sha1-aUJIJvNAX3nxQub8PZrljU27kgA=
|
||||
dependencies:
|
||||
minimist "~0.0.1"
|
||||
wordwrap "~0.0.2"
|
||||
|
||||
optimist@^0.6.1, optimist@~0.6.0:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
|
||||
dependencies:
|
||||
@ -12550,7 +12803,7 @@ path-exists@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
|
||||
|
||||
path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
|
||||
path-is-absolute@^1.0.0, path-is-absolute@^1.0.1, path-is-absolute@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
|
||||
@ -12606,7 +12859,7 @@ path-type@^3.0.0:
|
||||
dependencies:
|
||||
pify "^3.0.0"
|
||||
|
||||
pathval@^1.0.0:
|
||||
pathval@^1.0.0, pathval@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
|
||||
|
||||
@ -13107,6 +13360,14 @@ pretty-hrtime@^1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
|
||||
|
||||
prettyjson@^1.1.2:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prettyjson/-/prettyjson-1.2.1.tgz#fcffab41d19cab4dfae5e575e64246619b12d289"
|
||||
integrity sha1-/P+rQdGcq0365eV15kJGYZsS0ok=
|
||||
dependencies:
|
||||
colors "^1.1.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
prismjs@^1.15.0, prismjs@^1.8.4, prismjs@~1.15.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.15.0.tgz#8801d332e472091ba8def94976c8877ad60398d9"
|
||||
@ -13168,6 +13429,17 @@ promisify-child-process@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/promisify-child-process/-/promisify-child-process-1.0.5.tgz#817ad1aec92c013d83bb37e1f143e9b4033d9669"
|
||||
|
||||
prompt@0.2.14:
|
||||
version "0.2.14"
|
||||
resolved "https://registry.yarnpkg.com/prompt/-/prompt-0.2.14.tgz#57754f64f543fd7b0845707c818ece618f05ffdc"
|
||||
integrity sha1-V3VPZPVD/XsIRXB8gY7OYY8F/9w=
|
||||
dependencies:
|
||||
pkginfo "0.x.x"
|
||||
read "1.0.x"
|
||||
revalidator "0.1.x"
|
||||
utile "0.2.x"
|
||||
winston "0.8.x"
|
||||
|
||||
prompt@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/prompt/-/prompt-1.0.0.tgz#8e57123c396ab988897fb327fd3aedc3e735e4fe"
|
||||
@ -13207,6 +13479,11 @@ prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8,
|
||||
loose-envify "^1.3.1"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
propagate@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/propagate/-/propagate-1.0.0.tgz#00c2daeedda20e87e3782b344adba1cddd6ad709"
|
||||
integrity sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=
|
||||
|
||||
property-information@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.0.1.tgz#c3b09f4f5750b1634c0b24205adbf78f18bdf94f"
|
||||
@ -13241,6 +13518,13 @@ prr@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
|
||||
|
||||
ps-tree@0.0.x:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-0.0.3.tgz#dbf8d752a7fe22fa7d58635689499610e9276ddc"
|
||||
integrity sha1-2/jXUqf+Ivp9WGNWiUmWEOknbdw=
|
||||
dependencies:
|
||||
event-stream "~0.5"
|
||||
|
||||
ps-tree@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014"
|
||||
@ -13395,6 +13679,11 @@ qs@6.5.2, qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
|
||||
qs@^6.5.1:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
|
||||
qs@~6.4.0:
|
||||
version "6.4.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
|
||||
@ -13461,13 +13750,10 @@ ramda@0.21.0:
|
||||
version "0.21.0"
|
||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35"
|
||||
|
||||
ramda@^0.25.0:
|
||||
version "0.25.0"
|
||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.25.0.tgz#8fdf68231cffa90bc2f9460390a0cb74a29b29a9"
|
||||
|
||||
ramda@^0.26:
|
||||
ramda@^0.26, ramda@^0.26.1:
|
||||
version "0.26.1"
|
||||
resolved "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
|
||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
|
||||
integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
|
||||
|
||||
randexp@0.4.6:
|
||||
version "0.4.6"
|
||||
@ -13677,8 +13963,6 @@ react-highlight@0xproject/react-highlight#react-peer-deps:
|
||||
dependencies:
|
||||
highlight.js "^9.11.0"
|
||||
highlightjs-solidity "^1.0.5"
|
||||
react "^16.5.2"
|
||||
react-dom "^16.5.2"
|
||||
|
||||
react-hot-loader@^4.3.3:
|
||||
version "4.3.4"
|
||||
@ -14199,6 +14483,11 @@ redux@^3.6.0:
|
||||
loose-envify "^1.1.0"
|
||||
symbol-observable "^1.0.3"
|
||||
|
||||
reflect-metadata@^0.1.10:
|
||||
version "0.1.13"
|
||||
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
|
||||
integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
|
||||
|
||||
reflect-metadata@^0.1.12:
|
||||
version "0.1.12"
|
||||
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.12.tgz#311bf0c6b63cd782f228a81abe146a2bfa9c56f2"
|
||||
@ -14223,6 +14512,11 @@ regenerator-runtime@^0.12.0:
|
||||
version "0.12.1"
|
||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
|
||||
|
||||
regenerator-runtime@^0.13.2:
|
||||
version "0.13.2"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447"
|
||||
integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==
|
||||
|
||||
regenerator-transform@^0.10.0:
|
||||
version "0.10.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
|
||||
@ -15105,6 +15399,14 @@ shellwords@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||
|
||||
shush@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shush/-/shush-1.0.0.tgz#c27415a9e458f2fed39b27cf8eb37c003782b431"
|
||||
integrity sha1-wnQVqeRY8v7TmyfPjrN8ADeCtDE=
|
||||
dependencies:
|
||||
caller "~0.0.1"
|
||||
strip-json-comments "~0.1.1"
|
||||
|
||||
shx@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/shx/-/shx-0.2.2.tgz#0a304d020b0edf1306ad81570e80f0346df58a39"
|
||||
@ -15787,6 +16089,11 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
|
||||
strip-json-comments@~0.1.1:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-0.1.3.tgz#164c64e370a8a3cc00c9e01b539e569823f0ee54"
|
||||
integrity sha1-Fkxk43Coo8wAyeAbU55WmCPw7lQ=
|
||||
|
||||
strong-log-transformer@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "http://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-1.0.6.tgz#f7fb93758a69a571140181277eea0c2eb1301fa3"
|
||||
@ -16022,6 +16329,18 @@ tape@^4.4.0, tape@^4.6.3, tape@^4.8.0:
|
||||
string.prototype.trim "~1.1.2"
|
||||
through "~2.3.8"
|
||||
|
||||
tape@~2.3.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/tape/-/tape-2.3.3.tgz#2e7ce0a31df09f8d6851664a71842e0ca5057af7"
|
||||
integrity sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=
|
||||
dependencies:
|
||||
deep-equal "~0.1.0"
|
||||
defined "~0.0.0"
|
||||
inherits "~2.0.1"
|
||||
jsonify "~0.0.0"
|
||||
resumer "~0.0.0"
|
||||
through "~2.3.4"
|
||||
|
||||
tar-fs@^1.13.0:
|
||||
version "1.16.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.0.tgz#e877a25acbcc51d8c790da1c57c9cf439817b896"
|
||||
@ -16262,6 +16581,11 @@ timers-browserify@^2.0.4:
|
||||
dependencies:
|
||||
setimmediate "^1.0.4"
|
||||
|
||||
timespan@~2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/timespan/-/timespan-2.3.0.tgz#4902ce040bd13d845c8f59b27e9d59bad6f39929"
|
||||
integrity sha1-SQLOBAvRPYRcj1myfp1ZutbzmSk=
|
||||
|
||||
tiny-emitter@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c"
|
||||
@ -16614,9 +16938,10 @@ typemoq@^2.1.0:
|
||||
lodash "^4.17.4"
|
||||
postinstall-build "^5.0.1"
|
||||
|
||||
typeorm@^0.2.7:
|
||||
version "0.2.11"
|
||||
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.11.tgz#d81a295ed822e05043f2920cd539f52a963896b0"
|
||||
typeorm@0.2.7:
|
||||
version "0.2.7"
|
||||
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.7.tgz#4bbbace80dc91b1303be13f42d44ebf01d1b2558"
|
||||
integrity sha512-D7JxOBSqBiLAPu/M/4v15J++3klAWcv5WvYgrfl0iaaGObZJ/8UXm3oTpOtQUHfwJO9Cja8JMiwT9G7dyvwrxg==
|
||||
dependencies:
|
||||
app-root-path "^2.0.1"
|
||||
buffer "^5.1.0"
|
||||
@ -16630,7 +16955,7 @@ typeorm@^0.2.7:
|
||||
reflect-metadata "^0.1.12"
|
||||
xml2js "^0.4.17"
|
||||
yargonaut "^1.1.2"
|
||||
yargs "^12.0.5"
|
||||
yargs "^11.1.0"
|
||||
|
||||
types-bn@^0.0.1:
|
||||
version "0.0.1"
|
||||
@ -17026,6 +17351,18 @@ utila@^0.4.0, utila@~0.4:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
|
||||
|
||||
utile@0.2.1, utile@0.2.x, utile@~0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/utile/-/utile-0.2.1.tgz#930c88e99098d6220834c356cbd9a770522d90d7"
|
||||
integrity sha1-kwyI6ZCY1iIINMNWy9mncFItkNc=
|
||||
dependencies:
|
||||
async "~0.2.9"
|
||||
deep-equal "*"
|
||||
i "0.3.x"
|
||||
mkdirp "0.x.x"
|
||||
ncp "0.4.x"
|
||||
rimraf "2.x.x"
|
||||
|
||||
utile@0.3.x:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/utile/-/utile-0.3.0.tgz#1352c340eb820e4d8ddba039a4fbfaa32ed4ef3a"
|
||||
@ -17766,7 +18103,7 @@ websocket@1.0.26:
|
||||
typedarray-to-buffer "^3.1.2"
|
||||
yaeti "^0.0.6"
|
||||
|
||||
websocket@^1.0.26:
|
||||
websocket@^1.0.25, websocket@^1.0.26:
|
||||
version "1.0.28"
|
||||
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.28.tgz#9e5f6fdc8a3fe01d4422647ef93abdd8d45a78d3"
|
||||
dependencies:
|
||||
@ -17866,6 +18203,31 @@ window-size@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
|
||||
|
||||
winston@0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/winston/-/winston-0.8.0.tgz#61d0830fa699706212206b0a2b5ca69a93043668"
|
||||
integrity sha1-YdCDD6aZcGISIGsKK1ymmpMENmg=
|
||||
dependencies:
|
||||
async "0.2.x"
|
||||
colors "0.6.x"
|
||||
cycle "1.0.x"
|
||||
eyes "0.1.x"
|
||||
pkginfo "0.3.x"
|
||||
stack-trace "0.0.x"
|
||||
|
||||
winston@0.8.x, winston@~0.8.1:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/winston/-/winston-0.8.3.tgz#64b6abf4cd01adcaefd5009393b1d8e8bec19db0"
|
||||
integrity sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=
|
||||
dependencies:
|
||||
async "0.2.x"
|
||||
colors "0.6.x"
|
||||
cycle "1.0.x"
|
||||
eyes "0.1.x"
|
||||
isstream "0.1.x"
|
||||
pkginfo "0.3.x"
|
||||
stack-trace "0.0.x"
|
||||
|
||||
winston@2.1.x:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/winston/-/winston-2.1.1.tgz#3c9349d196207fd1bdff9d4bc43ef72510e3a12e"
|
||||
@ -17882,14 +18244,14 @@ wordwrap@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
|
||||
|
||||
"wordwrap@>=0.0.1 <0.1.0", wordwrap@~0.0.2:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
|
||||
|
||||
wordwrap@^1.0.0, wordwrap@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
|
||||
|
||||
wordwrap@~0.0.2:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
|
||||
|
||||
worker-farm@^1.5.2:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0"
|
||||
@ -18120,13 +18482,6 @@ yargs-parser@10.x, yargs-parser@^10.0.0, yargs-parser@^10.1.0:
|
||||
dependencies:
|
||||
camelcase "^4.1.0"
|
||||
|
||||
yargs-parser@^11.1.1:
|
||||
version "11.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
yargs-parser@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4"
|
||||
@ -18152,7 +18507,7 @@ yargs-parser@^9.0.2:
|
||||
dependencies:
|
||||
camelcase "^4.1.0"
|
||||
|
||||
yargs@11.1.0:
|
||||
yargs@11.1.0, yargs@^11.1.0:
|
||||
version "11.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77"
|
||||
dependencies:
|
||||
@ -18237,23 +18592,6 @@ yargs@^12.0.1:
|
||||
y18n "^3.2.1 || ^4.0.0"
|
||||
yargs-parser "^10.1.0"
|
||||
|
||||
yargs@^12.0.5:
|
||||
version "12.0.5"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
|
||||
dependencies:
|
||||
cliui "^4.0.0"
|
||||
decamelize "^1.2.0"
|
||||
find-up "^3.0.0"
|
||||
get-caller-file "^1.0.1"
|
||||
os-locale "^3.0.0"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^1.0.1"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^2.0.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^3.2.1 || ^4.0.0"
|
||||
yargs-parser "^11.1.1"
|
||||
|
||||
yargs@^3.7.2:
|
||||
version "3.32.0"
|
||||
resolved "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995"
|
||||
|
Loading…
x
Reference in New Issue
Block a user