From 8687b9533c67a551df928b28a776bfdbe6e3e84f Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 24 Aug 2018 11:11:29 -0700 Subject: [PATCH 01/47] Add getForwarderHelperForMakerAssetDataAsync method to forwarderHelperFactory --- packages/connect/src/http_client.ts | 2 +- packages/forwarder-helper/package.json | 5 +- .../src/forwarder_helper_factory.ts | 229 +++++++++++++++++- packages/forwarder-helper/src/types.ts | 5 + .../forwarder-helper/src/utils/order_utils.ts | 16 ++ 5 files changed, 253 insertions(+), 4 deletions(-) create mode 100644 packages/forwarder-helper/src/utils/order_utils.ts diff --git a/packages/connect/src/http_client.ts b/packages/connect/src/http_client.ts index 87d5c30be2..bdcfdd8d23 100644 --- a/packages/connect/src/http_client.ts +++ b/packages/connect/src/http_client.ts @@ -29,7 +29,7 @@ const TRAILING_SLASHES_REGEX = /\/+$/; /** * This class includes all the functionality related to interacting with a set of HTTP endpoints - * that implement the standard relayer API v0 + * that implement the standard relayer API v2 */ export class HttpClient implements Client { private readonly _apiEndpointUrl: string; diff --git a/packages/forwarder-helper/package.json b/packages/forwarder-helper/package.json index 410323f768..f849fb4ed9 100644 --- a/packages/forwarder-helper/package.json +++ b/packages/forwarder-helper/package.json @@ -40,18 +40,21 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/forwarder-helper/README.md", "dependencies": { "@0xproject/assert": "^1.0.8", + "@0xproject/connect": "^2.0.0", + "@0xproject/contract-wrappers": "^1.0.1", "@0xproject/json-schemas": "^1.0.1", "@0xproject/order-utils": "^1.0.1", + "@0xproject/subproviders": "^2.0.2", "@0xproject/types": "^1.0.1", "@0xproject/typescript-typings": "^2.0.0", "@0xproject/utils": "^1.0.8", - "@types/node": "^8.0.53", "lodash": "^4.17.10" }, "devDependencies": { "@0xproject/tslint-config": "^1.0.7", "@types/lodash": "^4.14.116", "@types/mocha": "^2.2.42", + "@types/node": "^8.0.53", "chai": "^4.0.1", "chai-as-promised": "^7.1.0", "chai-bignumber": "^2.0.1", diff --git a/packages/forwarder-helper/src/forwarder_helper_factory.ts b/packages/forwarder-helper/src/forwarder_helper_factory.ts index 95f11f555a..e3ef593887 100644 --- a/packages/forwarder-helper/src/forwarder_helper_factory.ts +++ b/packages/forwarder-helper/src/forwarder_helper_factory.ts @@ -1,13 +1,22 @@ import { assert } from '@0xproject/assert'; +import { APIOrder, HttpClient } from '@0xproject/connect'; +import { ContractWrappers, OrderAndTraderInfo, OrderStatus } from '@0xproject/contract-wrappers'; import { schemas } from '@0xproject/json-schemas'; +import { assetDataUtils } from '@0xproject/order-utils'; +import { RemainingFillableCalculator } from '@0xproject/order-utils/lib/src/remaining_fillable_calculator'; +import { RPCSubprovider, Web3ProviderEngine } from '@0xproject/subproviders'; import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import { constants } from './constants'; import { ForwarderHelperImpl, ForwarderHelperImplConfig } from './forwarder_helper_impl'; -import { ForwarderHelper } from './types'; +import { ForwarderHelper, ForwarderHelperFactoryError } from './types'; +import { orderUtils } from './utils/order_utils'; export const forwarderHelperFactory = { /** - * Given an array of orders and an array of feeOrders + * Given an array of orders and an array of feeOrders, get a ForwarderHelper * @param orders An array of objects conforming to SignedOrder. Each order should specify the same makerAssetData and takerAssetData * @param feeOrders An array of objects conforming to SignedOrder. Each order should specify ZRX as makerAssetData WETH as takerAssetData * @return A ForwarderHelper, see type for definition @@ -22,4 +31,220 @@ export const forwarderHelperFactory = { const helper = new ForwarderHelperImpl(config); return helper; }, + /** + * Given a desired makerAsset and SRA url, get a ForwarderHelper + * @param makerAssetData An array of objects conforming to SignedOrder. Each order should specify the same makerAssetData and takerAssetData + * @param sraUrl A url pointing to an SRA v2 compliant endpoint. + * @param rpcUrl A url pointing to an ethereum node. + * @param networkId The ethereum networkId, defaults to 1 (mainnet). + * @return A ForwarderHelper, see type for definition + */ + async getForwarderHelperForMakerAssetDataAsync( + makerAssetData: string, + takerAddress: string, + sraUrl: string, + rpcUrl?: string, + networkId: number = 1, + ): Promise { + assert.isHexString('makerAssetData', makerAssetData); + assert.isETHAddressHex('takerAddress', takerAddress); + assert.isWebUri('sraUrl', sraUrl); + if (!_.isUndefined(rpcUrl)) { + assert.isWebUri('rpcUrl', rpcUrl); + } + assert.isNumber('networkId', networkId); + // create provider + const providerEngine = new Web3ProviderEngine(); + if (!_.isUndefined(rpcUrl)) { + providerEngine.addProvider(new RPCSubprovider(rpcUrl)); + } + providerEngine.start(); + // create contract wrappers given provider and networkId + const contractWrappers = new ContractWrappers(providerEngine, { networkId }); + // find ether token asset data + const etherTokenAddressIfExists = contractWrappers.etherToken.getContractAddressIfExists(); + if (_.isUndefined(etherTokenAddressIfExists)) { + throw new Error(ForwarderHelperFactoryError.NoEtherTokenContractFound); + } + const etherTokenAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists); + // find zrx token asset data + let zrxTokenAssetData: string; + try { + zrxTokenAssetData = contractWrappers.exchange.getZRXAssetData(); + } catch (err) { + throw new Error(ForwarderHelperFactoryError.NoZrxTokenContractFound); + } + // get orderbooks for makerAsset/WETH and ZRX/WETH + const sraClient = new HttpClient(sraUrl); + const orderbookRequests = [ + { baseAssetData: makerAssetData, quoteAssetData: etherTokenAssetData }, + { baseAssetData: zrxTokenAssetData, quoteAssetData: etherTokenAssetData }, + ]; + const requestOpts = { networkId }; + // TODO: try catch these requests and throw a more domain specific error + const [makerAssetOrderbook, zrxOrderbook] = await Promise.all( + _.map(orderbookRequests, request => sraClient.getOrderbookAsync(request, requestOpts)), + ); + // validate orders and find remaining fillable from on chain state or sra api + let ordersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; + let feeOrdersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; + if (!_.isUndefined(rpcUrl)) { + // if we do have an rpc url, get on-chain orders and traders info via the OrderValidatorWrapper + const ordersFromSra = _.map(makerAssetOrderbook.asks.records, apiOrder => apiOrder.order); + const feeOrdersFromSra = _.map(zrxOrderbook.asks.records, apiOrder => apiOrder.order); + // TODO: try catch these requests and throw a more domain specific error + const [makerAssetOrdersAndTradersInfo, feeOrdersAndTradersInfo] = await Promise.all( + _.map([ordersFromSra, feeOrdersFromSra], ordersToBeValidated => { + const takerAddresses = _.map(ordersToBeValidated, () => takerAddress); + return contractWrappers.orderValidator.getOrdersAndTradersInfoAsync( + ordersToBeValidated, + takerAddresses, + ); + }), + ); + // take maker asset orders from SRA + on chain information and find the valid orders and remaining fillable maker asset amounts + ordersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( + ordersFromSra, + makerAssetOrdersAndTradersInfo, + zrxTokenAssetData, + ); + // take fee orders from SRA + on chain information and find the valid orders and remaining fillable maker asset amounts + feeOrdersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( + feeOrdersFromSra, + feeOrdersAndTradersInfo, + zrxTokenAssetData, + ); + } else { + // if we don't have an rpc url, assume all orders are valid and fallback to optional fill amounts from SRA + // if fill amounts are not available from the SRA, assume all orders are completely fillable + const apiOrdersFromSra = makerAssetOrderbook.asks.records; + const feeApiOrdersFromSra = zrxOrderbook.asks.records; + // take maker asset orders from SRA and the valid orders and remaining fillable maker asset amounts + ordersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( + apiOrdersFromSra, + ); + // take fee orders from SRA and find the valid orders and remaining fillable maker asset amounts + feeOrdersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( + feeApiOrdersFromSra, + ); + } + // compile final config + const config: ForwarderHelperImplConfig = { + orders: ordersAndRemainingFillableMakerAssetAmounts.orders, + feeOrders: feeOrdersAndRemainingFillableMakerAssetAmounts.orders, + remainingFillableMakerAssetAmounts: + ordersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts, + remainingFillableFeeAmounts: + feeOrdersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts, + }; + const helper = new ForwarderHelperImpl(config); + return helper; + }, }; + +interface OrdersAndRemainingFillableMakerAssetAmounts { + orders: SignedOrder[]; + remainingFillableMakerAssetAmounts: BigNumber[]; +} + +/** + * Given an array of APIOrder objects from a standard relayer api, return an array + * of fillable orders with their corresponding remainingFillableMakerAssetAmounts + */ +function getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( + apiOrders: APIOrder[], +): OrdersAndRemainingFillableMakerAssetAmounts { + const result = _.reduce( + apiOrders, + (acc, apiOrder, index) => { + // get current accumulations + const { orders, remainingFillableMakerAssetAmounts } = acc; + // get order and metadata + const { order, metaData } = apiOrder; + // if the order is expired, move on + if (orderUtils.isOrderExpired(order)) { + return acc; + } + // calculate remainingFillableMakerAssetAmount from api metadata + const remainingFillableTakerAssetAmount = _.get( + metaData, + 'remainingTakerAssetAmount', + order.takerAssetAmount, + ); + const remainingFillableMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( + order, + remainingFillableTakerAssetAmount, + ); + // if there is some amount of maker asset left to fill and add the order and remaining amount to the accumulations + // if there is not any maker asset left to fill, do not add + if (remainingFillableMakerAssetAmount.gt(constants.ZERO_AMOUNT)) { + return { + orders: _.concat(orders, order), + remainingFillableMakerAssetAmounts: _.concat( + remainingFillableMakerAssetAmounts, + remainingFillableMakerAssetAmount, + ), + }; + } else { + return acc; + } + }, + { orders: [] as SignedOrder[], remainingFillableMakerAssetAmounts: [] as BigNumber[] }, + ); + return result; +} + +/** + * Given an array of orders and corresponding on-chain infos, return a subset of the orders + * that are still fillable orders with their corresponding remainingFillableMakerAssetAmounts + */ +function getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( + inputOrders: SignedOrder[], + ordersAndTradersInfo: OrderAndTraderInfo[], + zrxAssetData: string, +): OrdersAndRemainingFillableMakerAssetAmounts { + // iterate through the input orders and find the ones that are still fillable + // for the orders that are still fillable, calculate the remaining fillable maker asset amount + const result = _.reduce( + inputOrders, + (acc, order, index) => { + // get current accumulations + const { orders, remainingFillableMakerAssetAmounts } = acc; + // get corresponding on-chain state for the order + const { orderInfo, traderInfo } = ordersAndTradersInfo[index]; + // if the order IS NOT fillable, do not add anything and continue iterating + if (orderInfo.orderStatus !== OrderStatus.FILLABLE) { + return acc; + } + // if the order IS fillable, add the order and calculate the remaining fillable amount + const transferrableAssetAmount = BigNumber.min([traderInfo.makerAllowance, traderInfo.makerBalance]); + const transferrableFeeAssetAmount = BigNumber.min([ + traderInfo.makerZrxAllowance, + traderInfo.makerZrxBalance, + ]); + const remainingTakerAssetAmount = order.takerAssetAmount.minus(orderInfo.orderTakerAssetFilledAmount); + const remainingMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( + order, + remainingTakerAssetAmount, + ); + const remainingFillableCalculator = new RemainingFillableCalculator( + order.makerFee, + order.makerAssetAmount, + order.makerAssetData === zrxAssetData, + transferrableAssetAmount, + transferrableFeeAssetAmount, + remainingMakerAssetAmount, + ); + const remainingFillableAmount = remainingFillableCalculator.computeRemainingFillable(); + return { + orders: _.concat(orders, order), + remainingFillableMakerAssetAmounts: _.concat( + remainingFillableMakerAssetAmounts, + remainingFillableAmount, + ), + }; + }, + { orders: [] as SignedOrder[], remainingFillableMakerAssetAmounts: [] as BigNumber[] }, + ); + return result; +} diff --git a/packages/forwarder-helper/src/types.ts b/packages/forwarder-helper/src/types.ts index fb171cc902..5a84392571 100644 --- a/packages/forwarder-helper/src/types.ts +++ b/packages/forwarder-helper/src/types.ts @@ -1,6 +1,11 @@ import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; +export enum ForwarderHelperFactoryError { + NoEtherTokenContractFound = 'NO_ETHER_TOKEN_CONTRACT_FOUND', + NoZrxTokenContractFound = 'NO_ZRX_TOKEN_CONTRACT_FOUND', +} + export interface ForwarderHelper { /** * Given a MarketBuyOrdersInfoRequest, returns a MarketBuyOrdersInfo containing all information relevant to fulfilling the request diff --git a/packages/forwarder-helper/src/utils/order_utils.ts b/packages/forwarder-helper/src/utils/order_utils.ts new file mode 100644 index 0000000000..d14c6a6d7d --- /dev/null +++ b/packages/forwarder-helper/src/utils/order_utils.ts @@ -0,0 +1,16 @@ +import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; + +export const orderUtils = { + isOrderExpired(order: SignedOrder): boolean { + const millisecondsInSecond = 1000; + const currentUnixTimestampSec = new BigNumber(Date.now() / millisecondsInSecond).round(); + return order.expirationTimeSeconds.lessThan(currentUnixTimestampSec); + }, + calculateRemainingMakerAssetAmount(order: SignedOrder, remainingTakerAssetAmount: BigNumber): BigNumber { + const result = remainingTakerAssetAmount.eq(0) + ? new BigNumber(0) + : remainingTakerAssetAmount.times(order.makerAssetAmount).dividedToIntegerBy(order.takerAssetAmount); + return result; + }, +}; From 8d6132736bfd57d08448cbde538c13072df80256 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 13 Sep 2018 14:39:06 +0200 Subject: [PATCH 02/47] Remove taker address as param and filter out non-open orders --- packages/forwarder-helper/src/constants.ts | 1 + .../src/forwarder_helper_factory.ts | 20 +++++++++++-------- .../forwarder-helper/src/utils/order_utils.ts | 5 +++++ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/forwarder-helper/src/constants.ts b/packages/forwarder-helper/src/constants.ts index 0ad30e4c0a..c0a1b090e0 100644 --- a/packages/forwarder-helper/src/constants.ts +++ b/packages/forwarder-helper/src/constants.ts @@ -2,4 +2,5 @@ import { BigNumber } from '@0xproject/utils'; export const constants = { ZERO_AMOUNT: new BigNumber(0), + NULL_ADDRESS: '0x0000000000000000000000000000000000000000', }; diff --git a/packages/forwarder-helper/src/forwarder_helper_factory.ts b/packages/forwarder-helper/src/forwarder_helper_factory.ts index e3ef593887..ab8eaeeaf0 100644 --- a/packages/forwarder-helper/src/forwarder_helper_factory.ts +++ b/packages/forwarder-helper/src/forwarder_helper_factory.ts @@ -1,5 +1,5 @@ import { assert } from '@0xproject/assert'; -import { APIOrder, HttpClient } from '@0xproject/connect'; +import { APIOrder, HttpClient, OrderbookResponse } from '@0xproject/connect'; import { ContractWrappers, OrderAndTraderInfo, OrderStatus } from '@0xproject/contract-wrappers'; import { schemas } from '@0xproject/json-schemas'; import { assetDataUtils } from '@0xproject/order-utils'; @@ -41,13 +41,11 @@ export const forwarderHelperFactory = { */ async getForwarderHelperForMakerAssetDataAsync( makerAssetData: string, - takerAddress: string, sraUrl: string, rpcUrl?: string, networkId: number = 1, ): Promise { assert.isHexString('makerAssetData', makerAssetData); - assert.isETHAddressHex('takerAddress', takerAddress); assert.isWebUri('sraUrl', sraUrl); if (!_.isUndefined(rpcUrl)) { assert.isWebUri('rpcUrl', rpcUrl); @@ -90,12 +88,12 @@ export const forwarderHelperFactory = { let feeOrdersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; if (!_.isUndefined(rpcUrl)) { // if we do have an rpc url, get on-chain orders and traders info via the OrderValidatorWrapper - const ordersFromSra = _.map(makerAssetOrderbook.asks.records, apiOrder => apiOrder.order); - const feeOrdersFromSra = _.map(zrxOrderbook.asks.records, apiOrder => apiOrder.order); + const ordersFromSra = getOpenAsksFromOrderbook(makerAssetOrderbook); + const feeOrdersFromSra = getOpenAsksFromOrderbook(zrxOrderbook); // TODO: try catch these requests and throw a more domain specific error const [makerAssetOrdersAndTradersInfo, feeOrdersAndTradersInfo] = await Promise.all( _.map([ordersFromSra, feeOrdersFromSra], ordersToBeValidated => { - const takerAddresses = _.map(ordersToBeValidated, () => takerAddress); + const takerAddresses = _.map(ordersToBeValidated, () => constants.NULL_ADDRESS); return contractWrappers.orderValidator.getOrdersAndTradersInfoAsync( ordersToBeValidated, takerAddresses, @@ -161,8 +159,8 @@ function getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( const { orders, remainingFillableMakerAssetAmounts } = acc; // get order and metadata const { order, metaData } = apiOrder; - // if the order is expired, move on - if (orderUtils.isOrderExpired(order)) { + // if the order is expired or not open, move on + if (orderUtils.isOrderExpired(order) || !orderUtils.isOpenOrder(order)) { return acc; } // calculate remainingFillableMakerAssetAmount from api metadata @@ -248,3 +246,9 @@ function getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( ); return result; } + +function getOpenAsksFromOrderbook(orderbookResponse: OrderbookResponse): SignedOrder[] { + const asks = _.map(orderbookResponse.asks.records, apiOrder => apiOrder.order); + const result = _.filter(asks, ask => orderUtils.isOpenOrder(ask)); + return result; +} diff --git a/packages/forwarder-helper/src/utils/order_utils.ts b/packages/forwarder-helper/src/utils/order_utils.ts index d14c6a6d7d..482006bf18 100644 --- a/packages/forwarder-helper/src/utils/order_utils.ts +++ b/packages/forwarder-helper/src/utils/order_utils.ts @@ -1,6 +1,8 @@ import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; +import { constants } from '../constants'; + export const orderUtils = { isOrderExpired(order: SignedOrder): boolean { const millisecondsInSecond = 1000; @@ -13,4 +15,7 @@ export const orderUtils = { : remainingTakerAssetAmount.times(order.makerAssetAmount).dividedToIntegerBy(order.takerAssetAmount); return result; }, + isOpenOrder(order: SignedOrder): boolean { + return order.takerAddress === constants.NULL_ADDRESS; + }, }; From c0a14a4a4110f3fedea6624376eae4c86c02cda8 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 13 Sep 2018 14:44:53 +0200 Subject: [PATCH 03/47] Catch standard relayer api errors --- .../src/forwarder_helper_factory.ts | 13 +++++++++---- packages/forwarder-helper/src/types.ts | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/forwarder-helper/src/forwarder_helper_factory.ts b/packages/forwarder-helper/src/forwarder_helper_factory.ts index ab8eaeeaf0..be38f2bd51 100644 --- a/packages/forwarder-helper/src/forwarder_helper_factory.ts +++ b/packages/forwarder-helper/src/forwarder_helper_factory.ts @@ -79,10 +79,15 @@ export const forwarderHelperFactory = { { baseAssetData: zrxTokenAssetData, quoteAssetData: etherTokenAssetData }, ]; const requestOpts = { networkId }; - // TODO: try catch these requests and throw a more domain specific error - const [makerAssetOrderbook, zrxOrderbook] = await Promise.all( - _.map(orderbookRequests, request => sraClient.getOrderbookAsync(request, requestOpts)), - ); + let makerAssetOrderbook: OrderbookResponse; + let zrxOrderbook: OrderbookResponse; + try { + [makerAssetOrderbook, zrxOrderbook] = await Promise.all( + _.map(orderbookRequests, request => sraClient.getOrderbookAsync(request, requestOpts)), + ); + } catch (err) { + throw new Error(ForwarderHelperFactoryError.StandardRelayerApiError); + } // validate orders and find remaining fillable from on chain state or sra api let ordersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; let feeOrdersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; diff --git a/packages/forwarder-helper/src/types.ts b/packages/forwarder-helper/src/types.ts index 5a84392571..a7f02ff8d3 100644 --- a/packages/forwarder-helper/src/types.ts +++ b/packages/forwarder-helper/src/types.ts @@ -4,6 +4,7 @@ import { BigNumber } from '@0xproject/utils'; export enum ForwarderHelperFactoryError { NoEtherTokenContractFound = 'NO_ETHER_TOKEN_CONTRACT_FOUND', NoZrxTokenContractFound = 'NO_ZRX_TOKEN_CONTRACT_FOUND', + StandardRelayerApiError = 'STANDARD_RELAYER_API_ERROR', } export interface ForwarderHelper { From 90674d9038b45644e82545558d9937b9eec498f9 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 14 Sep 2018 13:42:37 +0200 Subject: [PATCH 04/47] Various clean up --- packages/forwarder-helper/src/forwarder_helper_factory.ts | 8 +++++--- packages/forwarder-helper/src/utils/order_utils.ts | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/forwarder-helper/src/forwarder_helper_factory.ts b/packages/forwarder-helper/src/forwarder_helper_factory.ts index be38f2bd51..2b37ac98fd 100644 --- a/packages/forwarder-helper/src/forwarder_helper_factory.ts +++ b/packages/forwarder-helper/src/forwarder_helper_factory.ts @@ -24,6 +24,7 @@ export const forwarderHelperFactory = { getForwarderHelperForOrders(orders: SignedOrder[], feeOrders: SignedOrder[] = []): ForwarderHelper { assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); assert.doesConformToSchema('feeOrders', orders, schemas.signedOrdersSchema); + // TODO: Add assertion here for orders all having the same makerAsset and takerAsset const config: ForwarderHelperImplConfig = { orders, feeOrders, @@ -96,6 +97,7 @@ export const forwarderHelperFactory = { const ordersFromSra = getOpenAsksFromOrderbook(makerAssetOrderbook); const feeOrdersFromSra = getOpenAsksFromOrderbook(zrxOrderbook); // TODO: try catch these requests and throw a more domain specific error + // TODO: optimization, reduce this to once RPC call buy combining orders into one array and then splitting up the response const [makerAssetOrdersAndTradersInfo, feeOrdersAndTradersInfo] = await Promise.all( _.map([ordersFromSra, feeOrdersFromSra], ordersToBeValidated => { const takerAddresses = _.map(ordersToBeValidated, () => constants.NULL_ADDRESS); @@ -159,7 +161,7 @@ function getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( ): OrdersAndRemainingFillableMakerAssetAmounts { const result = _.reduce( apiOrders, - (acc, apiOrder, index) => { + (acc, apiOrder) => { // get current accumulations const { orders, remainingFillableMakerAssetAmounts } = acc; // get order and metadata @@ -168,7 +170,7 @@ function getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( if (orderUtils.isOrderExpired(order) || !orderUtils.isOpenOrder(order)) { return acc; } - // calculate remainingFillableMakerAssetAmount from api metadata + // calculate remainingFillableMakerAssetAmount from api metadata, else assume order is completely fillable const remainingFillableTakerAssetAmount = _.get( metaData, 'remainingTakerAssetAmount', @@ -215,7 +217,7 @@ function getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( const { orders, remainingFillableMakerAssetAmounts } = acc; // get corresponding on-chain state for the order const { orderInfo, traderInfo } = ordersAndTradersInfo[index]; - // if the order IS NOT fillable, do not add anything and continue iterating + // if the order IS NOT fillable, do not add anything to the accumulations and continue iterating if (orderInfo.orderStatus !== OrderStatus.FILLABLE) { return acc; } diff --git a/packages/forwarder-helper/src/utils/order_utils.ts b/packages/forwarder-helper/src/utils/order_utils.ts index 482006bf18..bb0bdb80f7 100644 --- a/packages/forwarder-helper/src/utils/order_utils.ts +++ b/packages/forwarder-helper/src/utils/order_utils.ts @@ -11,7 +11,7 @@ export const orderUtils = { }, calculateRemainingMakerAssetAmount(order: SignedOrder, remainingTakerAssetAmount: BigNumber): BigNumber { const result = remainingTakerAssetAmount.eq(0) - ? new BigNumber(0) + ? constants.ZERO_AMOUNT : remainingTakerAssetAmount.times(order.makerAssetAmount).dividedToIntegerBy(order.takerAssetAmount); return result; }, From 91702bbae214f56e7ff66156fa799fcfb83f6e42 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 14 Sep 2018 13:45:14 +0200 Subject: [PATCH 05/47] Move packages/forwarder-helper into packages/asset-buyer --- packages/{forwarder-helper => asset-buyer}/.npmignore | 0 packages/{forwarder-helper => asset-buyer}/CHANGELOG.json | 0 packages/{forwarder-helper => asset-buyer}/CHANGELOG.md | 0 packages/{forwarder-helper => asset-buyer}/README.md | 0 packages/{forwarder-helper => asset-buyer}/package.json | 0 packages/{forwarder-helper => asset-buyer}/src/constants.ts | 0 .../src/forwarder_helper_factory.ts | 0 .../src/forwarder_helper_impl.ts | 0 packages/{forwarder-helper => asset-buyer}/src/globals.d.ts | 0 packages/{forwarder-helper => asset-buyer}/src/index.ts | 0 packages/{forwarder-helper => asset-buyer}/src/types.ts | 0 .../src/utils/forwarder_helper_impl_config_utils.ts | 0 .../{forwarder-helper => asset-buyer}/src/utils/order_utils.ts | 0 .../test/forwarder_helper_impl_test.ts | 0 .../{forwarder-helper => asset-buyer}/test/utils/chai_setup.ts | 0 packages/{forwarder-helper => asset-buyer}/tsconfig.json | 0 packages/{forwarder-helper => asset-buyer}/tslint.json | 0 packages/{forwarder-helper => asset-buyer}/typedoc-tsconfig.json | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename packages/{forwarder-helper => asset-buyer}/.npmignore (100%) rename packages/{forwarder-helper => asset-buyer}/CHANGELOG.json (100%) rename packages/{forwarder-helper => asset-buyer}/CHANGELOG.md (100%) rename packages/{forwarder-helper => asset-buyer}/README.md (100%) rename packages/{forwarder-helper => asset-buyer}/package.json (100%) rename packages/{forwarder-helper => asset-buyer}/src/constants.ts (100%) rename packages/{forwarder-helper => asset-buyer}/src/forwarder_helper_factory.ts (100%) rename packages/{forwarder-helper => asset-buyer}/src/forwarder_helper_impl.ts (100%) rename packages/{forwarder-helper => asset-buyer}/src/globals.d.ts (100%) rename packages/{forwarder-helper => asset-buyer}/src/index.ts (100%) rename packages/{forwarder-helper => asset-buyer}/src/types.ts (100%) rename packages/{forwarder-helper => asset-buyer}/src/utils/forwarder_helper_impl_config_utils.ts (100%) rename packages/{forwarder-helper => asset-buyer}/src/utils/order_utils.ts (100%) rename packages/{forwarder-helper => asset-buyer}/test/forwarder_helper_impl_test.ts (100%) rename packages/{forwarder-helper => asset-buyer}/test/utils/chai_setup.ts (100%) rename packages/{forwarder-helper => asset-buyer}/tsconfig.json (100%) rename packages/{forwarder-helper => asset-buyer}/tslint.json (100%) rename packages/{forwarder-helper => asset-buyer}/typedoc-tsconfig.json (100%) diff --git a/packages/forwarder-helper/.npmignore b/packages/asset-buyer/.npmignore similarity index 100% rename from packages/forwarder-helper/.npmignore rename to packages/asset-buyer/.npmignore diff --git a/packages/forwarder-helper/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json similarity index 100% rename from packages/forwarder-helper/CHANGELOG.json rename to packages/asset-buyer/CHANGELOG.json diff --git a/packages/forwarder-helper/CHANGELOG.md b/packages/asset-buyer/CHANGELOG.md similarity index 100% rename from packages/forwarder-helper/CHANGELOG.md rename to packages/asset-buyer/CHANGELOG.md diff --git a/packages/forwarder-helper/README.md b/packages/asset-buyer/README.md similarity index 100% rename from packages/forwarder-helper/README.md rename to packages/asset-buyer/README.md diff --git a/packages/forwarder-helper/package.json b/packages/asset-buyer/package.json similarity index 100% rename from packages/forwarder-helper/package.json rename to packages/asset-buyer/package.json diff --git a/packages/forwarder-helper/src/constants.ts b/packages/asset-buyer/src/constants.ts similarity index 100% rename from packages/forwarder-helper/src/constants.ts rename to packages/asset-buyer/src/constants.ts diff --git a/packages/forwarder-helper/src/forwarder_helper_factory.ts b/packages/asset-buyer/src/forwarder_helper_factory.ts similarity index 100% rename from packages/forwarder-helper/src/forwarder_helper_factory.ts rename to packages/asset-buyer/src/forwarder_helper_factory.ts diff --git a/packages/forwarder-helper/src/forwarder_helper_impl.ts b/packages/asset-buyer/src/forwarder_helper_impl.ts similarity index 100% rename from packages/forwarder-helper/src/forwarder_helper_impl.ts rename to packages/asset-buyer/src/forwarder_helper_impl.ts diff --git a/packages/forwarder-helper/src/globals.d.ts b/packages/asset-buyer/src/globals.d.ts similarity index 100% rename from packages/forwarder-helper/src/globals.d.ts rename to packages/asset-buyer/src/globals.d.ts diff --git a/packages/forwarder-helper/src/index.ts b/packages/asset-buyer/src/index.ts similarity index 100% rename from packages/forwarder-helper/src/index.ts rename to packages/asset-buyer/src/index.ts diff --git a/packages/forwarder-helper/src/types.ts b/packages/asset-buyer/src/types.ts similarity index 100% rename from packages/forwarder-helper/src/types.ts rename to packages/asset-buyer/src/types.ts diff --git a/packages/forwarder-helper/src/utils/forwarder_helper_impl_config_utils.ts b/packages/asset-buyer/src/utils/forwarder_helper_impl_config_utils.ts similarity index 100% rename from packages/forwarder-helper/src/utils/forwarder_helper_impl_config_utils.ts rename to packages/asset-buyer/src/utils/forwarder_helper_impl_config_utils.ts diff --git a/packages/forwarder-helper/src/utils/order_utils.ts b/packages/asset-buyer/src/utils/order_utils.ts similarity index 100% rename from packages/forwarder-helper/src/utils/order_utils.ts rename to packages/asset-buyer/src/utils/order_utils.ts diff --git a/packages/forwarder-helper/test/forwarder_helper_impl_test.ts b/packages/asset-buyer/test/forwarder_helper_impl_test.ts similarity index 100% rename from packages/forwarder-helper/test/forwarder_helper_impl_test.ts rename to packages/asset-buyer/test/forwarder_helper_impl_test.ts diff --git a/packages/forwarder-helper/test/utils/chai_setup.ts b/packages/asset-buyer/test/utils/chai_setup.ts similarity index 100% rename from packages/forwarder-helper/test/utils/chai_setup.ts rename to packages/asset-buyer/test/utils/chai_setup.ts diff --git a/packages/forwarder-helper/tsconfig.json b/packages/asset-buyer/tsconfig.json similarity index 100% rename from packages/forwarder-helper/tsconfig.json rename to packages/asset-buyer/tsconfig.json diff --git a/packages/forwarder-helper/tslint.json b/packages/asset-buyer/tslint.json similarity index 100% rename from packages/forwarder-helper/tslint.json rename to packages/asset-buyer/tslint.json diff --git a/packages/forwarder-helper/typedoc-tsconfig.json b/packages/asset-buyer/typedoc-tsconfig.json similarity index 100% rename from packages/forwarder-helper/typedoc-tsconfig.json rename to packages/asset-buyer/typedoc-tsconfig.json From 8da7d399981472ff9cbfa28fb0957a530de72b2d Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 14 Sep 2018 13:54:06 +0200 Subject: [PATCH 06/47] Update readme, changelog, and package.json with rename --- packages/asset-buyer/CHANGELOG.json | 24 ++---------------------- packages/asset-buyer/CHANGELOG.md | 17 ----------------- packages/asset-buyer/README.md | 16 ++++++++-------- packages/asset-buyer/package.json | 8 ++++---- 4 files changed, 14 insertions(+), 51 deletions(-) diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index f0e6467005..2e4db1f6e8 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -1,30 +1,10 @@ [ { - "timestamp": 1536142250, - "version": "1.0.1", + "version": "1.0.0-rc.1", "changes": [ { - "note": "Dependencies updated" + "note": "Init" } ] - }, - { - "version": "1.0.1-rc.2", - "changes": [ - { - "note": "Dependencies updated" - } - ], - "timestamp": 1535377027 - }, - { - "version": "1.0.1-rc.1", - "changes": [ - { - "note": "Add initial forwarderHelperFactory", - "pr": 997 - } - ], - "timestamp": 1535133899 } ] diff --git a/packages/asset-buyer/CHANGELOG.md b/packages/asset-buyer/CHANGELOG.md index d6b67aa1bc..8b13789179 100644 --- a/packages/asset-buyer/CHANGELOG.md +++ b/packages/asset-buyer/CHANGELOG.md @@ -1,18 +1 @@ - -CHANGELOG - -## v1.0.1 - _September 5, 2018_ - - * Dependencies updated - -## v1.0.1-rc.2 - _August 27, 2018_ - - * Dependencies updated - -## v1.0.1-rc.1 - _August 24, 2018_ - - * Add initial forwarderHelperFactory (#997) diff --git a/packages/asset-buyer/README.md b/packages/asset-buyer/README.md index c745269102..717849bf2e 100644 --- a/packages/asset-buyer/README.md +++ b/packages/asset-buyer/README.md @@ -1,25 +1,25 @@ -## @0xproject/forwarder-helper +## @0xproject/asset-buyer -Provides convenience objects to help work with the Forwarder Contract +Convenience package for buying assets -### Read the [Documentation](https://0xproject.com/docs/forwarder-helper). +### Read the [Documentation](https://0xproject.com/docs/asset-buyer). ## Installation ```bash -yarn add @0xproject/forwarder-helper +yarn add @0xproject/asset-buyer ``` **Import** ```typescript -import { forwarderHelperFactory } from '@0xproject/forwarder-helper'; +import { AssetBuyer } from '@0xproject/asset-buyer'; ``` or ```javascript -var forwarderHelperFactory = require('@0xproject/forwarder-helper').forwarderHelperFactory; +var AssetBuyer = require('@0xproject/asset-buyer').AssetBuyer; ``` If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: @@ -55,13 +55,13 @@ yarn install To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory: ```bash -PKG=@0xproject/forwarder-helper yarn build +PKG=@0xproject/asset-buyer yarn build ``` Or continuously rebuild on change: ```bash -PKG=@0xproject/forwarder-helper yarn watch +PKG=@0xproject/asset-buyer yarn watch ``` ### Clean diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json index f849fb4ed9..e4d3d0701a 100644 --- a/packages/asset-buyer/package.json +++ b/packages/asset-buyer/package.json @@ -1,10 +1,10 @@ { - "name": "@0xproject/forwarder-helper", - "version": "1.0.1", + "name": "@0xproject/asset-buyer", + "version": "1.0.0-rc.1", "engines": { "node": ">=6.12" }, - "description": "Convenience object for working with the forwarder contract", + "description": "Convenience package for buying assets", "main": "lib/src/index.js", "types": "lib/src/index.d.ts", "scripts": { @@ -37,7 +37,7 @@ "bugs": { "url": "https://github.com/0xProject/0x-monorepo/issues" }, - "homepage": "https://github.com/0xProject/0x-monorepo/packages/forwarder-helper/README.md", + "homepage": "https://github.com/0xProject/0x-monorepo/packages/asset-buyer/README.md", "dependencies": { "@0xproject/assert": "^1.0.8", "@0xproject/connect": "^2.0.0", From 7b46cef83dca0a743bd598a70076004983cbf294 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 14 Sep 2018 16:18:16 +0200 Subject: [PATCH 07/47] Create initial AssetBuyer class --- packages/asset-buyer/package.json | 2 + .../src/asset_buyers/asset_buyer.ts | 129 +++++ packages/asset-buyer/src/constants.ts | 1 + .../src/forwarder_helper_factory.ts | 510 +++++++++--------- .../asset-buyer/src/forwarder_helper_impl.ts | 64 --- packages/asset-buyer/src/index.ts | 4 +- packages/asset-buyer/src/types.ts | 63 +-- .../forwarder_helper_impl_config_utils.ts | 170 +++--- .../test/forwarder_helper_impl_test.ts | 264 ++++----- tsconfig.json | 2 +- 10 files changed, 635 insertions(+), 574 deletions(-) create mode 100644 packages/asset-buyer/src/asset_buyers/asset_buyer.ts delete mode 100644 packages/asset-buyer/src/forwarder_helper_impl.ts diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json index e4d3d0701a..0d6d1f52ba 100644 --- a/packages/asset-buyer/package.json +++ b/packages/asset-buyer/package.json @@ -48,6 +48,8 @@ "@0xproject/types": "^1.0.1", "@0xproject/typescript-typings": "^2.0.0", "@0xproject/utils": "^1.0.8", + "@0xproject/web3-wrapper": "^2.0.2", + "ethereum-types": "^1.0.6", "lodash": "^4.17.10" }, "devDependencies": { diff --git a/packages/asset-buyer/src/asset_buyers/asset_buyer.ts b/packages/asset-buyer/src/asset_buyers/asset_buyer.ts new file mode 100644 index 0000000000..eb7f85e2b6 --- /dev/null +++ b/packages/asset-buyer/src/asset_buyers/asset_buyer.ts @@ -0,0 +1,129 @@ +import { ContractWrappers } from '@0xproject/contract-wrappers'; +import { marketUtils } from '@0xproject/order-utils'; +import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as _ from 'lodash'; +import { Provider } from 'ethereum-types'; + +import { constants } from '../constants'; +import { AssetBuyerError, BuyQuote, BuyQuoteRequest } from '../types'; + +const SLIPPAGE_PERCENTAGE = new BigNumber(0.2); // 20% slippage protection, possibly move this into request interface + +export interface AssetBuyerConfig { + orders: SignedOrder[]; + feeOrders: SignedOrder[]; + remainingFillableMakerAssetAmounts?: BigNumber[]; + remainingFillableFeeAmounts?: BigNumber[]; + networkId?: number; +} + +export class AssetBuyer { + public readonly provider: Provider; + public readonly config: AssetBuyerConfig; + private _contractWrappers: ContractWrappers; + constructor(provider: Provider, config: AssetBuyerConfig) { + this.provider = provider; + this.config = config; + const networkId = this.config.networkId || constants.MAINNET_NETWORK_ID; + this._contractWrappers = new ContractWrappers(this.provider, { + networkId, + }); + } + /** + * Given a BuyQuoteRequest, returns a BuyQuote containing all information relevant to fulfilling the buy. Pass the BuyQuote + * to executeBuyQuoteAsync to execute the buy. + * @param buyQuoteRequest An object that conforms to BuyQuoteRequest. See type definition for more information. + * @return An object that conforms to BuyQuote that satisfies the request. See type definition for more information. + */ + public getBuyQuote(buyQuoteRequest: BuyQuoteRequest): BuyQuote { + const { assetBuyAmount, feePercentage } = buyQuoteRequest; + const { orders, feeOrders, remainingFillableMakerAssetAmounts, remainingFillableFeeAmounts } = this.config; + // TODO: optimization + // make the slippage percentage customizable + const slippageBufferAmount = assetBuyAmount.mul(SLIPPAGE_PERCENTAGE).round(); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + orders, + assetBuyAmount, + { + remainingFillableMakerAssetAmounts, + slippageBufferAmount, + }, + ); + if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { + throw new Error(AssetBuyerError.InsufficientAssetLiquidity); + } + // TODO: optimization + // update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to + // finding order that cover all fees, this will help with estimating ETH and minimizing gas usage + const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( + resultOrders, + feeOrders, + { + remainingFillableMakerAssetAmounts, + remainingFillableFeeAmounts, + }, + ); + if (remainingFeeAmount.gt(constants.ZERO_AMOUNT)) { + throw new Error(AssetBuyerError.InsufficientZrxLiquidity); + } + const assetData = orders[0].makerAssetData; + // TODO: critical + // calculate minRate and maxRate by calculating min and max eth usage and then dividing into + // assetBuyAmount to get assetData / WETH + return { + assetData, + orders: resultOrders, + feeOrders: resultFeeOrders, + minRate: constants.ZERO_AMOUNT, + maxRate: constants.ZERO_AMOUNT, + assetBuyAmount, + feePercentage, + }; + } + /** + * Given a BuyQuote and desired rate, attempt to execute the buy. + * @param buyQuote An object that conforms to BuyQuote. See type definition for more information. + * @param rate The desired rate to execute the buy at. Affects the amount of ETH sent with the transaction, defaults to buyQuote.maxRate. + * @param takerAddress The address to perform the buy. Defaults to the first available address from the provider. + * @param feeRecipient The address where affiliate fees are sent. Defaults to null address (0x000...000). + * @return A promise of the txHash. + */ + public async executeBuyQuoteAsync( + buyQuote: BuyQuote, + rate?: BigNumber, + takerAddress?: string, + feeRecipient: string = constants.NULL_ADDRESS, + ): Promise { + const { orders, feeOrders, feePercentage, assetBuyAmount, maxRate } = buyQuote; + // if no takerAddress is provided, try to get one from the provider + let finalTakerAddress; + if (!_.isUndefined(takerAddress)) { + finalTakerAddress = takerAddress; + } else { + const web3Wrapper = new Web3Wrapper(this.provider); + const availableAddresses = await web3Wrapper.getAvailableAddressesAsync(); + const firstAvailableAddress = _.head(availableAddresses); + if (!_.isUndefined(firstAvailableAddress)) { + finalTakerAddress = firstAvailableAddress; + } else { + throw new Error(AssetBuyerError.NoAddressAvailable); + } + } + // if no rate is provided, default to the maxRate from buyQuote + const desiredRate = rate || maxRate; + // calculate how much eth is required to buy assetBuyAmount at the desired rate + const ethAmount = assetBuyAmount.dividedToIntegerBy(desiredRate); + const txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync( + orders, + assetBuyAmount, + finalTakerAddress, + ethAmount, + feeOrders, + feePercentage, + feeRecipient, + ); + return txHash; + } +} diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts index c0a1b090e0..5785e705b3 100644 --- a/packages/asset-buyer/src/constants.ts +++ b/packages/asset-buyer/src/constants.ts @@ -3,4 +3,5 @@ import { BigNumber } from '@0xproject/utils'; export const constants = { ZERO_AMOUNT: new BigNumber(0), NULL_ADDRESS: '0x0000000000000000000000000000000000000000', + MAINNET_NETWORK_ID: 1, }; diff --git a/packages/asset-buyer/src/forwarder_helper_factory.ts b/packages/asset-buyer/src/forwarder_helper_factory.ts index 2b37ac98fd..9a3832e81e 100644 --- a/packages/asset-buyer/src/forwarder_helper_factory.ts +++ b/packages/asset-buyer/src/forwarder_helper_factory.ts @@ -1,261 +1,261 @@ -import { assert } from '@0xproject/assert'; -import { APIOrder, HttpClient, OrderbookResponse } from '@0xproject/connect'; -import { ContractWrappers, OrderAndTraderInfo, OrderStatus } from '@0xproject/contract-wrappers'; -import { schemas } from '@0xproject/json-schemas'; -import { assetDataUtils } from '@0xproject/order-utils'; -import { RemainingFillableCalculator } from '@0xproject/order-utils/lib/src/remaining_fillable_calculator'; -import { RPCSubprovider, Web3ProviderEngine } from '@0xproject/subproviders'; -import { SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; +// import { assert } from '@0xproject/assert'; +// import { APIOrder, HttpClient, OrderbookResponse } from '@0xproject/connect'; +// import { ContractWrappers, OrderAndTraderInfo, OrderStatus } from '@0xproject/contract-wrappers'; +// import { schemas } from '@0xproject/json-schemas'; +// import { assetDataUtils } from '@0xproject/order-utils'; +// import { RemainingFillableCalculator } from '@0xproject/order-utils/lib/src/remaining_fillable_calculator'; +// import { RPCSubprovider, Web3ProviderEngine } from '@0xproject/subproviders'; +// import { SignedOrder } from '@0xproject/types'; +// import { BigNumber } from '@0xproject/utils'; +// import * as _ from 'lodash'; -import { constants } from './constants'; -import { ForwarderHelperImpl, ForwarderHelperImplConfig } from './forwarder_helper_impl'; -import { ForwarderHelper, ForwarderHelperFactoryError } from './types'; -import { orderUtils } from './utils/order_utils'; +// import { constants } from './constants'; +// import { ForwarderHelperImpl, ForwarderHelperImplConfig } from '@0xproject/asset-buyer/src/asset_buyer'; +// import { ForwarderHelper, ForwarderHelperFactoryError } from './types'; +// import { orderUtils } from './utils/order_utils'; -export const forwarderHelperFactory = { - /** - * Given an array of orders and an array of feeOrders, get a ForwarderHelper - * @param orders An array of objects conforming to SignedOrder. Each order should specify the same makerAssetData and takerAssetData - * @param feeOrders An array of objects conforming to SignedOrder. Each order should specify ZRX as makerAssetData WETH as takerAssetData - * @return A ForwarderHelper, see type for definition - */ - getForwarderHelperForOrders(orders: SignedOrder[], feeOrders: SignedOrder[] = []): ForwarderHelper { - assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); - assert.doesConformToSchema('feeOrders', orders, schemas.signedOrdersSchema); - // TODO: Add assertion here for orders all having the same makerAsset and takerAsset - const config: ForwarderHelperImplConfig = { - orders, - feeOrders, - }; - const helper = new ForwarderHelperImpl(config); - return helper; - }, - /** - * Given a desired makerAsset and SRA url, get a ForwarderHelper - * @param makerAssetData An array of objects conforming to SignedOrder. Each order should specify the same makerAssetData and takerAssetData - * @param sraUrl A url pointing to an SRA v2 compliant endpoint. - * @param rpcUrl A url pointing to an ethereum node. - * @param networkId The ethereum networkId, defaults to 1 (mainnet). - * @return A ForwarderHelper, see type for definition - */ - async getForwarderHelperForMakerAssetDataAsync( - makerAssetData: string, - sraUrl: string, - rpcUrl?: string, - networkId: number = 1, - ): Promise { - assert.isHexString('makerAssetData', makerAssetData); - assert.isWebUri('sraUrl', sraUrl); - if (!_.isUndefined(rpcUrl)) { - assert.isWebUri('rpcUrl', rpcUrl); - } - assert.isNumber('networkId', networkId); - // create provider - const providerEngine = new Web3ProviderEngine(); - if (!_.isUndefined(rpcUrl)) { - providerEngine.addProvider(new RPCSubprovider(rpcUrl)); - } - providerEngine.start(); - // create contract wrappers given provider and networkId - const contractWrappers = new ContractWrappers(providerEngine, { networkId }); - // find ether token asset data - const etherTokenAddressIfExists = contractWrappers.etherToken.getContractAddressIfExists(); - if (_.isUndefined(etherTokenAddressIfExists)) { - throw new Error(ForwarderHelperFactoryError.NoEtherTokenContractFound); - } - const etherTokenAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists); - // find zrx token asset data - let zrxTokenAssetData: string; - try { - zrxTokenAssetData = contractWrappers.exchange.getZRXAssetData(); - } catch (err) { - throw new Error(ForwarderHelperFactoryError.NoZrxTokenContractFound); - } - // get orderbooks for makerAsset/WETH and ZRX/WETH - const sraClient = new HttpClient(sraUrl); - const orderbookRequests = [ - { baseAssetData: makerAssetData, quoteAssetData: etherTokenAssetData }, - { baseAssetData: zrxTokenAssetData, quoteAssetData: etherTokenAssetData }, - ]; - const requestOpts = { networkId }; - let makerAssetOrderbook: OrderbookResponse; - let zrxOrderbook: OrderbookResponse; - try { - [makerAssetOrderbook, zrxOrderbook] = await Promise.all( - _.map(orderbookRequests, request => sraClient.getOrderbookAsync(request, requestOpts)), - ); - } catch (err) { - throw new Error(ForwarderHelperFactoryError.StandardRelayerApiError); - } - // validate orders and find remaining fillable from on chain state or sra api - let ordersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; - let feeOrdersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; - if (!_.isUndefined(rpcUrl)) { - // if we do have an rpc url, get on-chain orders and traders info via the OrderValidatorWrapper - const ordersFromSra = getOpenAsksFromOrderbook(makerAssetOrderbook); - const feeOrdersFromSra = getOpenAsksFromOrderbook(zrxOrderbook); - // TODO: try catch these requests and throw a more domain specific error - // TODO: optimization, reduce this to once RPC call buy combining orders into one array and then splitting up the response - const [makerAssetOrdersAndTradersInfo, feeOrdersAndTradersInfo] = await Promise.all( - _.map([ordersFromSra, feeOrdersFromSra], ordersToBeValidated => { - const takerAddresses = _.map(ordersToBeValidated, () => constants.NULL_ADDRESS); - return contractWrappers.orderValidator.getOrdersAndTradersInfoAsync( - ordersToBeValidated, - takerAddresses, - ); - }), - ); - // take maker asset orders from SRA + on chain information and find the valid orders and remaining fillable maker asset amounts - ordersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( - ordersFromSra, - makerAssetOrdersAndTradersInfo, - zrxTokenAssetData, - ); - // take fee orders from SRA + on chain information and find the valid orders and remaining fillable maker asset amounts - feeOrdersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( - feeOrdersFromSra, - feeOrdersAndTradersInfo, - zrxTokenAssetData, - ); - } else { - // if we don't have an rpc url, assume all orders are valid and fallback to optional fill amounts from SRA - // if fill amounts are not available from the SRA, assume all orders are completely fillable - const apiOrdersFromSra = makerAssetOrderbook.asks.records; - const feeApiOrdersFromSra = zrxOrderbook.asks.records; - // take maker asset orders from SRA and the valid orders and remaining fillable maker asset amounts - ordersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( - apiOrdersFromSra, - ); - // take fee orders from SRA and find the valid orders and remaining fillable maker asset amounts - feeOrdersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( - feeApiOrdersFromSra, - ); - } - // compile final config - const config: ForwarderHelperImplConfig = { - orders: ordersAndRemainingFillableMakerAssetAmounts.orders, - feeOrders: feeOrdersAndRemainingFillableMakerAssetAmounts.orders, - remainingFillableMakerAssetAmounts: - ordersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts, - remainingFillableFeeAmounts: - feeOrdersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts, - }; - const helper = new ForwarderHelperImpl(config); - return helper; - }, -}; +// export const forwarderHelperFactory = { +// /** +// * Given an array of orders and an array of feeOrders, get a ForwarderHelper +// * @param orders An array of objects conforming to SignedOrder. Each order should specify the same makerAssetData and takerAssetData +// * @param feeOrders An array of objects conforming to SignedOrder. Each order should specify ZRX as makerAssetData WETH as takerAssetData +// * @return A ForwarderHelper, see type for definition +// */ +// getForwarderHelperForOrders(orders: SignedOrder[], feeOrders: SignedOrder[] = []): ForwarderHelper { +// assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); +// assert.doesConformToSchema('feeOrders', orders, schemas.signedOrdersSchema); +// // TODO: Add assertion here for orders all having the same makerAsset and takerAsset +// const config: ForwarderHelperImplConfig = { +// orders, +// feeOrders, +// }; +// const helper = new ForwarderHelperImpl(config); +// return helper; +// }, +// /** +// * Given a desired makerAsset and SRA url, get a ForwarderHelper +// * @param makerAssetData An array of objects conforming to SignedOrder. Each order should specify the same makerAssetData and takerAssetData +// * @param sraUrl A url pointing to an SRA v2 compliant endpoint. +// * @param rpcUrl A url pointing to an ethereum node. +// * @param networkId The ethereum networkId, defaults to 1 (mainnet). +// * @return A ForwarderHelper, see type for definition +// */ +// async getForwarderHelperForMakerAssetDataAsync( +// makerAssetData: string, +// sraUrl: string, +// rpcUrl?: string, +// networkId: number = 1, +// ): Promise { +// assert.isHexString('makerAssetData', makerAssetData); +// assert.isWebUri('sraUrl', sraUrl); +// if (!_.isUndefined(rpcUrl)) { +// assert.isWebUri('rpcUrl', rpcUrl); +// } +// assert.isNumber('networkId', networkId); +// // create provider +// const providerEngine = new Web3ProviderEngine(); +// if (!_.isUndefined(rpcUrl)) { +// providerEngine.addProvider(new RPCSubprovider(rpcUrl)); +// } +// providerEngine.start(); +// // create contract wrappers given provider and networkId +// const contractWrappers = new ContractWrappers(providerEngine, { networkId }); +// // find ether token asset data +// const etherTokenAddressIfExists = contractWrappers.etherToken.getContractAddressIfExists(); +// if (_.isUndefined(etherTokenAddressIfExists)) { +// throw new Error(ForwarderHelperFactoryError.NoEtherTokenContractFound); +// } +// const etherTokenAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists); +// // find zrx token asset data +// let zrxTokenAssetData: string; +// try { +// zrxTokenAssetData = contractWrappers.exchange.getZRXAssetData(); +// } catch (err) { +// throw new Error(ForwarderHelperFactoryError.NoZrxTokenContractFound); +// } +// // get orderbooks for makerAsset/WETH and ZRX/WETH +// const sraClient = new HttpClient(sraUrl); +// const orderbookRequests = [ +// { baseAssetData: makerAssetData, quoteAssetData: etherTokenAssetData }, +// { baseAssetData: zrxTokenAssetData, quoteAssetData: etherTokenAssetData }, +// ]; +// const requestOpts = { networkId }; +// let makerAssetOrderbook: OrderbookResponse; +// let zrxOrderbook: OrderbookResponse; +// try { +// [makerAssetOrderbook, zrxOrderbook] = await Promise.all( +// _.map(orderbookRequests, request => sraClient.getOrderbookAsync(request, requestOpts)), +// ); +// } catch (err) { +// throw new Error(ForwarderHelperFactoryError.StandardRelayerApiError); +// } +// // validate orders and find remaining fillable from on chain state or sra api +// let ordersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; +// let feeOrdersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; +// if (!_.isUndefined(rpcUrl)) { +// // if we do have an rpc url, get on-chain orders and traders info via the OrderValidatorWrapper +// const ordersFromSra = getOpenAsksFromOrderbook(makerAssetOrderbook); +// const feeOrdersFromSra = getOpenAsksFromOrderbook(zrxOrderbook); +// // TODO: try catch these requests and throw a more domain specific error +// // TODO: optimization, reduce this to once RPC call buy combining orders into one array and then splitting up the response +// const [makerAssetOrdersAndTradersInfo, feeOrdersAndTradersInfo] = await Promise.all( +// _.map([ordersFromSra, feeOrdersFromSra], ordersToBeValidated => { +// const takerAddresses = _.map(ordersToBeValidated, () => constants.NULL_ADDRESS); +// return contractWrappers.orderValidator.getOrdersAndTradersInfoAsync( +// ordersToBeValidated, +// takerAddresses, +// ); +// }), +// ); +// // take maker asset orders from SRA + on chain information and find the valid orders and remaining fillable maker asset amounts +// ordersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( +// ordersFromSra, +// makerAssetOrdersAndTradersInfo, +// zrxTokenAssetData, +// ); +// // take fee orders from SRA + on chain information and find the valid orders and remaining fillable maker asset amounts +// feeOrdersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( +// feeOrdersFromSra, +// feeOrdersAndTradersInfo, +// zrxTokenAssetData, +// ); +// } else { +// // if we don't have an rpc url, assume all orders are valid and fallback to optional fill amounts from SRA +// // if fill amounts are not available from the SRA, assume all orders are completely fillable +// const apiOrdersFromSra = makerAssetOrderbook.asks.records; +// const feeApiOrdersFromSra = zrxOrderbook.asks.records; +// // take maker asset orders from SRA and the valid orders and remaining fillable maker asset amounts +// ordersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( +// apiOrdersFromSra, +// ); +// // take fee orders from SRA and find the valid orders and remaining fillable maker asset amounts +// feeOrdersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( +// feeApiOrdersFromSra, +// ); +// } +// // compile final config +// const config: ForwarderHelperImplConfig = { +// orders: ordersAndRemainingFillableMakerAssetAmounts.orders, +// feeOrders: feeOrdersAndRemainingFillableMakerAssetAmounts.orders, +// remainingFillableMakerAssetAmounts: +// ordersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts, +// remainingFillableFeeAmounts: +// feeOrdersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts, +// }; +// const helper = new ForwarderHelperImpl(config); +// return helper; +// }, +// }; -interface OrdersAndRemainingFillableMakerAssetAmounts { - orders: SignedOrder[]; - remainingFillableMakerAssetAmounts: BigNumber[]; -} +// interface OrdersAndRemainingFillableMakerAssetAmounts { +// orders: SignedOrder[]; +// remainingFillableMakerAssetAmounts: BigNumber[]; +// } -/** - * Given an array of APIOrder objects from a standard relayer api, return an array - * of fillable orders with their corresponding remainingFillableMakerAssetAmounts - */ -function getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( - apiOrders: APIOrder[], -): OrdersAndRemainingFillableMakerAssetAmounts { - const result = _.reduce( - apiOrders, - (acc, apiOrder) => { - // get current accumulations - const { orders, remainingFillableMakerAssetAmounts } = acc; - // get order and metadata - const { order, metaData } = apiOrder; - // if the order is expired or not open, move on - if (orderUtils.isOrderExpired(order) || !orderUtils.isOpenOrder(order)) { - return acc; - } - // calculate remainingFillableMakerAssetAmount from api metadata, else assume order is completely fillable - const remainingFillableTakerAssetAmount = _.get( - metaData, - 'remainingTakerAssetAmount', - order.takerAssetAmount, - ); - const remainingFillableMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( - order, - remainingFillableTakerAssetAmount, - ); - // if there is some amount of maker asset left to fill and add the order and remaining amount to the accumulations - // if there is not any maker asset left to fill, do not add - if (remainingFillableMakerAssetAmount.gt(constants.ZERO_AMOUNT)) { - return { - orders: _.concat(orders, order), - remainingFillableMakerAssetAmounts: _.concat( - remainingFillableMakerAssetAmounts, - remainingFillableMakerAssetAmount, - ), - }; - } else { - return acc; - } - }, - { orders: [] as SignedOrder[], remainingFillableMakerAssetAmounts: [] as BigNumber[] }, - ); - return result; -} +// /** +// * Given an array of APIOrder objects from a standard relayer api, return an array +// * of fillable orders with their corresponding remainingFillableMakerAssetAmounts +// */ +// function getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( +// apiOrders: APIOrder[], +// ): OrdersAndRemainingFillableMakerAssetAmounts { +// const result = _.reduce( +// apiOrders, +// (acc, apiOrder) => { +// // get current accumulations +// const { orders, remainingFillableMakerAssetAmounts } = acc; +// // get order and metadata +// const { order, metaData } = apiOrder; +// // if the order is expired or not open, move on +// if (orderUtils.isOrderExpired(order) || !orderUtils.isOpenOrder(order)) { +// return acc; +// } +// // calculate remainingFillableMakerAssetAmount from api metadata, else assume order is completely fillable +// const remainingFillableTakerAssetAmount = _.get( +// metaData, +// 'remainingTakerAssetAmount', +// order.takerAssetAmount, +// ); +// const remainingFillableMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( +// order, +// remainingFillableTakerAssetAmount, +// ); +// // if there is some amount of maker asset left to fill and add the order and remaining amount to the accumulations +// // if there is not any maker asset left to fill, do not add +// if (remainingFillableMakerAssetAmount.gt(constants.ZERO_AMOUNT)) { +// return { +// orders: _.concat(orders, order), +// remainingFillableMakerAssetAmounts: _.concat( +// remainingFillableMakerAssetAmounts, +// remainingFillableMakerAssetAmount, +// ), +// }; +// } else { +// return acc; +// } +// }, +// { orders: [] as SignedOrder[], remainingFillableMakerAssetAmounts: [] as BigNumber[] }, +// ); +// return result; +// } -/** - * Given an array of orders and corresponding on-chain infos, return a subset of the orders - * that are still fillable orders with their corresponding remainingFillableMakerAssetAmounts - */ -function getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( - inputOrders: SignedOrder[], - ordersAndTradersInfo: OrderAndTraderInfo[], - zrxAssetData: string, -): OrdersAndRemainingFillableMakerAssetAmounts { - // iterate through the input orders and find the ones that are still fillable - // for the orders that are still fillable, calculate the remaining fillable maker asset amount - const result = _.reduce( - inputOrders, - (acc, order, index) => { - // get current accumulations - const { orders, remainingFillableMakerAssetAmounts } = acc; - // get corresponding on-chain state for the order - const { orderInfo, traderInfo } = ordersAndTradersInfo[index]; - // if the order IS NOT fillable, do not add anything to the accumulations and continue iterating - if (orderInfo.orderStatus !== OrderStatus.FILLABLE) { - return acc; - } - // if the order IS fillable, add the order and calculate the remaining fillable amount - const transferrableAssetAmount = BigNumber.min([traderInfo.makerAllowance, traderInfo.makerBalance]); - const transferrableFeeAssetAmount = BigNumber.min([ - traderInfo.makerZrxAllowance, - traderInfo.makerZrxBalance, - ]); - const remainingTakerAssetAmount = order.takerAssetAmount.minus(orderInfo.orderTakerAssetFilledAmount); - const remainingMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( - order, - remainingTakerAssetAmount, - ); - const remainingFillableCalculator = new RemainingFillableCalculator( - order.makerFee, - order.makerAssetAmount, - order.makerAssetData === zrxAssetData, - transferrableAssetAmount, - transferrableFeeAssetAmount, - remainingMakerAssetAmount, - ); - const remainingFillableAmount = remainingFillableCalculator.computeRemainingFillable(); - return { - orders: _.concat(orders, order), - remainingFillableMakerAssetAmounts: _.concat( - remainingFillableMakerAssetAmounts, - remainingFillableAmount, - ), - }; - }, - { orders: [] as SignedOrder[], remainingFillableMakerAssetAmounts: [] as BigNumber[] }, - ); - return result; -} +// /** +// * Given an array of orders and corresponding on-chain infos, return a subset of the orders +// * that are still fillable orders with their corresponding remainingFillableMakerAssetAmounts +// */ +// function getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( +// inputOrders: SignedOrder[], +// ordersAndTradersInfo: OrderAndTraderInfo[], +// zrxAssetData: string, +// ): OrdersAndRemainingFillableMakerAssetAmounts { +// // iterate through the input orders and find the ones that are still fillable +// // for the orders that are still fillable, calculate the remaining fillable maker asset amount +// const result = _.reduce( +// inputOrders, +// (acc, order, index) => { +// // get current accumulations +// const { orders, remainingFillableMakerAssetAmounts } = acc; +// // get corresponding on-chain state for the order +// const { orderInfo, traderInfo } = ordersAndTradersInfo[index]; +// // if the order IS NOT fillable, do not add anything to the accumulations and continue iterating +// if (orderInfo.orderStatus !== OrderStatus.FILLABLE) { +// return acc; +// } +// // if the order IS fillable, add the order and calculate the remaining fillable amount +// const transferrableAssetAmount = BigNumber.min([traderInfo.makerAllowance, traderInfo.makerBalance]); +// const transferrableFeeAssetAmount = BigNumber.min([ +// traderInfo.makerZrxAllowance, +// traderInfo.makerZrxBalance, +// ]); +// const remainingTakerAssetAmount = order.takerAssetAmount.minus(orderInfo.orderTakerAssetFilledAmount); +// const remainingMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( +// order, +// remainingTakerAssetAmount, +// ); +// const remainingFillableCalculator = new RemainingFillableCalculator( +// order.makerFee, +// order.makerAssetAmount, +// order.makerAssetData === zrxAssetData, +// transferrableAssetAmount, +// transferrableFeeAssetAmount, +// remainingMakerAssetAmount, +// ); +// const remainingFillableAmount = remainingFillableCalculator.computeRemainingFillable(); +// return { +// orders: _.concat(orders, order), +// remainingFillableMakerAssetAmounts: _.concat( +// remainingFillableMakerAssetAmounts, +// remainingFillableAmount, +// ), +// }; +// }, +// { orders: [] as SignedOrder[], remainingFillableMakerAssetAmounts: [] as BigNumber[] }, +// ); +// return result; +// } -function getOpenAsksFromOrderbook(orderbookResponse: OrderbookResponse): SignedOrder[] { - const asks = _.map(orderbookResponse.asks.records, apiOrder => apiOrder.order); - const result = _.filter(asks, ask => orderUtils.isOpenOrder(ask)); - return result; -} +// function getOpenAsksFromOrderbook(orderbookResponse: OrderbookResponse): SignedOrder[] { +// const asks = _.map(orderbookResponse.asks.records, apiOrder => apiOrder.order); +// const result = _.filter(asks, ask => orderUtils.isOpenOrder(ask)); +// return result; +// } diff --git a/packages/asset-buyer/src/forwarder_helper_impl.ts b/packages/asset-buyer/src/forwarder_helper_impl.ts deleted file mode 100644 index a90edb0bba..0000000000 --- a/packages/asset-buyer/src/forwarder_helper_impl.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { marketUtils } from '@0xproject/order-utils'; -import { SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; - -import { constants } from './constants'; -import { ForwarderHelper, ForwarderHelperError, MarketBuyOrdersInfo, MarketBuyOrdersInfoRequest } from './types'; -import { forwarderHelperImplConfigUtils } from './utils/forwarder_helper_impl_config_utils'; - -const SLIPPAGE_PERCENTAGE = new BigNumber(0.2); // 20% slippage protection, possibly move this into request interface - -export interface ForwarderHelperImplConfig { - orders: SignedOrder[]; - feeOrders: SignedOrder[]; - remainingFillableMakerAssetAmounts?: BigNumber[]; - remainingFillableFeeAmounts?: BigNumber[]; -} - -export class ForwarderHelperImpl implements ForwarderHelper { - public readonly config: ForwarderHelperImplConfig; - constructor(config: ForwarderHelperImplConfig) { - this.config = forwarderHelperImplConfigUtils.sortedConfig(config); - } - public getMarketBuyOrdersInfo(request: MarketBuyOrdersInfoRequest): MarketBuyOrdersInfo { - const { makerAssetFillAmount, feePercentage } = request; - const { orders, feeOrders, remainingFillableMakerAssetAmounts, remainingFillableFeeAmounts } = this.config; - // TODO: make the slippage percentage customizable - const slippageBufferAmount = makerAssetFillAmount.mul(SLIPPAGE_PERCENTAGE).round(); - const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( - orders, - makerAssetFillAmount, - { - remainingFillableMakerAssetAmounts, - slippageBufferAmount, - }, - ); - if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { - throw new Error(ForwarderHelperError.InsufficientMakerAssetLiquidity); - } - // TODO: update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to - // finding order that cover all fees, this will help with estimating ETH and minimizing gas usage - const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( - resultOrders, - feeOrders, - { - remainingFillableMakerAssetAmounts, - remainingFillableFeeAmounts, - }, - ); - if (remainingFeeAmount.gt(constants.ZERO_AMOUNT)) { - throw new Error(ForwarderHelperError.InsufficientZrxLiquidity); - } - // TODO: calculate min and max eth usage - // TODO: optimize orders call data - return { - makerAssetFillAmount, - orders: resultOrders, - feeOrders: resultFeeOrders, - minEthAmount: constants.ZERO_AMOUNT, - maxEthAmount: constants.ZERO_AMOUNT, - feePercentage, - }; - } -} diff --git a/packages/asset-buyer/src/index.ts b/packages/asset-buyer/src/index.ts index eb3a34bd54..299b32edd0 100644 --- a/packages/asset-buyer/src/index.ts +++ b/packages/asset-buyer/src/index.ts @@ -1,2 +1,2 @@ -export { forwarderHelperFactory } from './forwarder_helper_factory'; -export { ForwarderHelper, ForwarderHelperError, MarketBuyOrdersInfoRequest, MarketBuyOrdersInfo } from './types'; +export { AssetBuyerError, BuyQuote, BuyQuoteRequest } from './types'; +export { AssetBuyer } from './asset_buyers/asset_buyer'; diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index a7f02ff8d3..8a12d0cf83 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -1,49 +1,42 @@ import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; -export enum ForwarderHelperFactoryError { - NoEtherTokenContractFound = 'NO_ETHER_TOKEN_CONTRACT_FOUND', - NoZrxTokenContractFound = 'NO_ZRX_TOKEN_CONTRACT_FOUND', - StandardRelayerApiError = 'STANDARD_RELAYER_API_ERROR', -} - -export interface ForwarderHelper { - /** - * Given a MarketBuyOrdersInfoRequest, returns a MarketBuyOrdersInfo containing all information relevant to fulfilling the request - * using the ForwarderContract marketBuyOrdersWithEth function. - * @param request An object that conforms to MarketBuyOrdersInfoRequest. See type definition for more information. - * @return An object that conforms to MarketBuyOrdersInfo that satisfies the request. See type definition for more information. - */ - getMarketBuyOrdersInfo: (request: MarketBuyOrdersInfoRequest) => MarketBuyOrdersInfo; -} - -export enum ForwarderHelperError { - InsufficientMakerAssetLiquidity = 'INSUFFICIENT_MAKER_ASSET_LIQUIDITY', - InsufficientZrxLiquidity = 'INSUFFICIENT_ZRX_LIQUIDITY', -} - /** - * makerAssetFillAmount: The amount of makerAsset requesting to be filled - * feePercentage: Optional affiliate percentage amount factoring into eth amount calculations + * assetBuyAmount: The amount of asset to buy. + * feePercentage: Optional affiliate percentage amount factoring into eth amount calculations. */ -export interface MarketBuyOrdersInfoRequest { - makerAssetFillAmount: BigNumber; +export interface BuyQuoteRequest { + assetBuyAmount: BigNumber; feePercentage?: BigNumber; } /** - * makerAssetFillAmount: The amount of makerAsset requesting to be filled - * orders: An array of objects conforming to SignedOrder. These orders can be used to cover the requested makerAssetFillAmount plus slippage - * feeOrders: An array of objects conforming to SignedOrder. These orders can be used to cover the fees for the orders param above - * minEthAmount: Amount of eth in wei to send with the tx for the most optimistic case - * maxEthAmount: Amount of eth in wei to send with the tx for the worst case - * feePercentage: Affiliate fee percentage used to calculate the eth amounts above. Passed thru directly from the request + * assetData: The asset information. + * orders: An array of objects conforming to SignedOrder. These orders can be used to cover the requested assetBuyAmount plus slippage. + * feeOrders: An array of objects conforming to SignedOrder. These orders can be used to cover the fees for the orders param above. + * minRate: Min rate that needs to be paid in order to execute the buy. + * maxRate: Max rate that can be paid in order to execute the buy. + * assetBuyAmount: The amount of asset to buy. Passed through directly from the request. + * feePercentage: Affiliate fee percentage used to calculate the eth amounts above. Passed through directly from the request. */ -export interface MarketBuyOrdersInfo { - makerAssetFillAmount: BigNumber; +export interface BuyQuote { + assetData: string; orders: SignedOrder[]; feeOrders: SignedOrder[]; - minEthAmount: BigNumber; - maxEthAmount: BigNumber; + minRate: BigNumber; + maxRate: BigNumber; + assetBuyAmount: BigNumber; feePercentage?: BigNumber; } + +/** + * Possible errors thrown by an AssetBuyer instance or associated static methods + */ +export enum AssetBuyerError { + NoEtherTokenContractFound = 'NO_ETHER_TOKEN_CONTRACT_FOUND', + NoZrxTokenContractFound = 'NO_ZRX_TOKEN_CONTRACT_FOUND', + StandardRelayerApiError = 'STANDARD_RELAYER_API_ERROR', + InsufficientAssetLiquidity = 'INSUFFICIENT_ASSET_LIQUIDITY', + InsufficientZrxLiquidity = 'INSUFFICIENT_ZRX_LIQUIDITY', + NoAddressAvailable = 'NO_ADDRESS_AVAILABLE', +} diff --git a/packages/asset-buyer/src/utils/forwarder_helper_impl_config_utils.ts b/packages/asset-buyer/src/utils/forwarder_helper_impl_config_utils.ts index 253384f655..d3cbb651a1 100644 --- a/packages/asset-buyer/src/utils/forwarder_helper_impl_config_utils.ts +++ b/packages/asset-buyer/src/utils/forwarder_helper_impl_config_utils.ts @@ -1,92 +1,92 @@ -import { sortingUtils } from '@0xproject/order-utils'; -import { SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; +// import { sortingUtils } from '@0xproject/order-utils'; +// import { SignedOrder } from '@0xproject/types'; +// import { BigNumber } from '@0xproject/utils'; +// import * as _ from 'lodash'; -import { ForwarderHelperImplConfig } from '../forwarder_helper_impl'; +// import { ForwarderHelperImplConfig } from '@0xproject/asset-buyer/src/asset_buyer'; -interface SignedOrderWithAmount extends SignedOrder { - remainingFillAmount: BigNumber; -} +// interface SignedOrderWithAmount extends SignedOrder { +// remainingFillAmount: BigNumber; +// } -export const forwarderHelperImplConfigUtils = { - sortedConfig(config: ForwarderHelperImplConfig): ForwarderHelperImplConfig { - const { orders, feeOrders, remainingFillableMakerAssetAmounts, remainingFillableFeeAmounts } = config; - // TODO: provide a feeRate to the sorting function to more accurately sort based on the current market for ZRX tokens - const orderSorter = (ordersToSort: SignedOrder[]) => { - return sortingUtils.sortOrdersByFeeAdjustedRate(ordersToSort); - }; - const sortOrdersResult = sortOrdersAndRemainingFillAmounts( - orderSorter, - orders, - remainingFillableMakerAssetAmounts, - ); - const feeOrderSorter = (ordersToSort: SignedOrder[]) => { - return sortingUtils.sortFeeOrdersByFeeAdjustedRate(ordersToSort); - }; - const sortFeeOrdersResult = sortOrdersAndRemainingFillAmounts( - feeOrderSorter, - feeOrders, - remainingFillableFeeAmounts, - ); - return { - orders: sortOrdersResult.orders, - feeOrders: sortFeeOrdersResult.orders, - remainingFillableMakerAssetAmounts: sortOrdersResult.remainingFillAmounts, - remainingFillableFeeAmounts: sortFeeOrdersResult.remainingFillAmounts, - }; - }, -}; +// export const forwarderHelperImplConfigUtils = { +// sortedConfig(config: ForwarderHelperImplConfig): ForwarderHelperImplConfig { +// const { orders, feeOrders, remainingFillableMakerAssetAmounts, remainingFillableFeeAmounts } = config; +// // TODO: provide a feeRate to the sorting function to more accurately sort based on the current market for ZRX tokens +// const orderSorter = (ordersToSort: SignedOrder[]) => { +// return sortingUtils.sortOrdersByFeeAdjustedRate(ordersToSort); +// }; +// const sortOrdersResult = sortOrdersAndRemainingFillAmounts( +// orderSorter, +// orders, +// remainingFillableMakerAssetAmounts, +// ); +// const feeOrderSorter = (ordersToSort: SignedOrder[]) => { +// return sortingUtils.sortFeeOrdersByFeeAdjustedRate(ordersToSort); +// }; +// const sortFeeOrdersResult = sortOrdersAndRemainingFillAmounts( +// feeOrderSorter, +// feeOrders, +// remainingFillableFeeAmounts, +// ); +// return { +// orders: sortOrdersResult.orders, +// feeOrders: sortFeeOrdersResult.orders, +// remainingFillableMakerAssetAmounts: sortOrdersResult.remainingFillAmounts, +// remainingFillableFeeAmounts: sortFeeOrdersResult.remainingFillAmounts, +// }; +// }, +// }; -type OrderSorter = (orders: SignedOrder[]) => SignedOrder[]; +// type OrderSorter = (orders: SignedOrder[]) => SignedOrder[]; -function sortOrdersAndRemainingFillAmounts( - orderSorter: OrderSorter, - orders: SignedOrder[], - remainingFillAmounts?: BigNumber[], -): { orders: SignedOrder[]; remainingFillAmounts?: BigNumber[] } { - if (!_.isUndefined(remainingFillAmounts)) { - // Bundle orders together with their remainingFillAmounts so that we can sort them together - const orderWithAmounts = bundleSignedOrderWithAmounts(orders, remainingFillAmounts); - // Sort - const sortedOrderWithAmounts = orderSorter(orderWithAmounts) as SignedOrderWithAmount[]; - // Unbundle after sorting - const unbundledSortedOrderWithAmounts = unbundleSignedOrderWithAmounts(sortedOrderWithAmounts); - return { - orders: unbundledSortedOrderWithAmounts.orders, - remainingFillAmounts: unbundledSortedOrderWithAmounts.amounts, - }; - } else { - const sortedOrders = orderSorter(orders); - return { - orders: sortedOrders, - }; - } -} +// function sortOrdersAndRemainingFillAmounts( +// orderSorter: OrderSorter, +// orders: SignedOrder[], +// remainingFillAmounts?: BigNumber[], +// ): { orders: SignedOrder[]; remainingFillAmounts?: BigNumber[] } { +// if (!_.isUndefined(remainingFillAmounts)) { +// // Bundle orders together with their remainingFillAmounts so that we can sort them together +// const orderWithAmounts = bundleSignedOrderWithAmounts(orders, remainingFillAmounts); +// // Sort +// const sortedOrderWithAmounts = orderSorter(orderWithAmounts) as SignedOrderWithAmount[]; +// // Unbundle after sorting +// const unbundledSortedOrderWithAmounts = unbundleSignedOrderWithAmounts(sortedOrderWithAmounts); +// return { +// orders: unbundledSortedOrderWithAmounts.orders, +// remainingFillAmounts: unbundledSortedOrderWithAmounts.amounts, +// }; +// } else { +// const sortedOrders = orderSorter(orders); +// return { +// orders: sortedOrders, +// }; +// } +// } -function bundleSignedOrderWithAmounts(orders: SignedOrder[], amounts: BigNumber[]): SignedOrderWithAmount[] { - const ordersAndAmounts = _.map(orders, (order, index) => { - return { - ...order, - remainingFillAmount: amounts[index], - }; - }); - return ordersAndAmounts; -} +// function bundleSignedOrderWithAmounts(orders: SignedOrder[], amounts: BigNumber[]): SignedOrderWithAmount[] { +// const ordersAndAmounts = _.map(orders, (order, index) => { +// return { +// ...order, +// remainingFillAmount: amounts[index], +// }; +// }); +// return ordersAndAmounts; +// } -function unbundleSignedOrderWithAmounts( - signedOrderWithAmounts: SignedOrderWithAmount[], -): { orders: SignedOrder[]; amounts: BigNumber[] } { - const orders = _.map(signedOrderWithAmounts, order => { - const { remainingFillAmount, ...rest } = order; - return rest; - }); - const amounts = _.map(signedOrderWithAmounts, order => { - const { remainingFillAmount } = order; - return remainingFillAmount; - }); - return { - orders, - amounts, - }; -} +// function unbundleSignedOrderWithAmounts( +// signedOrderWithAmounts: SignedOrderWithAmount[], +// ): { orders: SignedOrder[]; amounts: BigNumber[] } { +// const orders = _.map(signedOrderWithAmounts, order => { +// const { remainingFillAmount, ...rest } = order; +// return rest; +// }); +// const amounts = _.map(signedOrderWithAmounts, order => { +// const { remainingFillAmount } = order; +// return remainingFillAmount; +// }); +// return { +// orders, +// amounts, +// }; +// } diff --git a/packages/asset-buyer/test/forwarder_helper_impl_test.ts b/packages/asset-buyer/test/forwarder_helper_impl_test.ts index 3c3b6db921..b7c014402f 100644 --- a/packages/asset-buyer/test/forwarder_helper_impl_test.ts +++ b/packages/asset-buyer/test/forwarder_helper_impl_test.ts @@ -1,136 +1,136 @@ -import { testOrderFactory } from '@0xproject/order-utils/lib/test/utils/test_order_factory'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import * as _ from 'lodash'; -import 'mocha'; +// import { testOrderFactory } from '@0xproject/order-utils/lib/test/utils/test_order_factory'; +// import { BigNumber } from '@0xproject/utils'; +// import * as chai from 'chai'; +// import * as _ from 'lodash'; +// import 'mocha'; -import { ForwarderHelperImpl, ForwarderHelperImplConfig } from '../src/forwarder_helper_impl'; -import { ForwarderHelperError } from '../src/types'; +// import { ForwarderHelperImpl, ForwarderHelperImplConfig } from '@0xproject/asset-buyer/src/asset_buyer'; +// import { ForwarderHelperError } from '../src/types'; -import { chaiSetup } from './utils/chai_setup'; +// import { chaiSetup } from './utils/chai_setup'; -chaiSetup.configure(); -const expect = chai.expect; +// chaiSetup.configure(); +// const expect = chai.expect; -describe('ForwarderHelperImpl', () => { - // rate: 2 takerAsset / makerAsset - const testOrder1 = testOrderFactory.generateTestSignedOrder({ - makerAssetAmount: new BigNumber(100), - takerAssetAmount: new BigNumber(200), - }); - // rate: 1 takerAsset / makerAsset - const testOrder2 = testOrderFactory.generateTestSignedOrder({ - makerAssetAmount: new BigNumber(100), - takerAssetAmount: new BigNumber(100), - }); - // rate: 3 takerAsset / makerAsset - const testOrder3 = testOrderFactory.generateTestSignedOrder({ - makerAssetAmount: new BigNumber(100), - takerAssetAmount: new BigNumber(300), - takerFee: new BigNumber(1), - }); - // rate: 3 WETH / ZRX - const testFeeOrder1 = testOrderFactory.generateTestSignedOrder({ - makerAssetAmount: new BigNumber(100), - takerAssetAmount: new BigNumber(300), - }); - // rate: 2 WETH / ZRX - const testFeeOrder2 = testOrderFactory.generateTestSignedOrder({ - makerAssetAmount: new BigNumber(100), - takerAssetAmount: new BigNumber(200), - }); - // rate: 1 WETH / ZRX - const testFeeOrder3 = testOrderFactory.generateTestSignedOrder({ - makerAssetAmount: new BigNumber(100), - takerAssetAmount: new BigNumber(100), - }); - const inputForwarderHelperConfig: ForwarderHelperImplConfig = { - orders: [testOrder1, testOrder2, testOrder3], - feeOrders: [testFeeOrder1, testFeeOrder2, testFeeOrder3], - remainingFillableMakerAssetAmounts: [new BigNumber(1), new BigNumber(2), new BigNumber(3)], - remainingFillableFeeAmounts: [new BigNumber(4), new BigNumber(5), new BigNumber(6)], - }; - describe('#constructor', () => { - const inputForwarderHelperConfigNoRemainingAmounts: ForwarderHelperImplConfig = { - orders: [testOrder1, testOrder2, testOrder3], - feeOrders: [testFeeOrder1, testFeeOrder2, testFeeOrder3], - }; - it('sorts orders', () => { - const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); - expect(forwarderHelper.config.orders).deep.equals([testOrder2, testOrder1, testOrder3]); - }); - it('sorts fee orders', () => { - const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); - expect(forwarderHelper.config.feeOrders).deep.equals([testFeeOrder3, testFeeOrder2, testFeeOrder1]); - }); - it('sorts remainingFillableMakerAssetAmounts', () => { - const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); - expect(forwarderHelper.config.remainingFillableMakerAssetAmounts).to.be.not.undefined(); - expect(_.nth(forwarderHelper.config.remainingFillableMakerAssetAmounts, 0)).to.bignumber.equal( - new BigNumber(2), - ); - expect(_.nth(forwarderHelper.config.remainingFillableMakerAssetAmounts, 1)).to.bignumber.equal( - new BigNumber(1), - ); - expect(_.nth(forwarderHelper.config.remainingFillableMakerAssetAmounts, 2)).to.bignumber.equal( - new BigNumber(3), - ); - }); - it('sorts remainingFillableFeeAmounts', () => { - const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); - expect(forwarderHelper.config.remainingFillableFeeAmounts).to.be.not.undefined(); - expect(_.nth(forwarderHelper.config.remainingFillableFeeAmounts, 0)).to.bignumber.equal(new BigNumber(6)); - expect(_.nth(forwarderHelper.config.remainingFillableFeeAmounts, 1)).to.bignumber.equal(new BigNumber(5)); - expect(_.nth(forwarderHelper.config.remainingFillableFeeAmounts, 2)).to.bignumber.equal(new BigNumber(4)); - }); - it('remainingFillableMakerAssetAmounts is undefined if none provided', () => { - const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfigNoRemainingAmounts); - expect(forwarderHelper.config.remainingFillableMakerAssetAmounts).to.be.undefined(); - }); - it('remainingFillableFeeAmounts is undefined if none provided', () => { - const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfigNoRemainingAmounts); - expect(forwarderHelper.config.remainingFillableFeeAmounts).to.be.undefined(); - }); - }); - describe('#getMarketBuyOrdersInfo', () => { - it('throws if not enough makerAsset liquidity', () => { - const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); - expect(() => { - // request for 6 makerAsset units, because we have exactly 6 available we should throw because there is a built in slippage buffer - forwarderHelper.getMarketBuyOrdersInfo({ - makerAssetFillAmount: new BigNumber(6), - }); - }).to.throw(ForwarderHelperError.InsufficientMakerAssetLiquidity); - }); - it('throws if not enough ZRX liquidity', () => { - const inputForwarderHelperConfigNoFees: ForwarderHelperImplConfig = { - orders: [testOrder1, testOrder2, testOrder3], - feeOrders: [], - }; - const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfigNoFees); - expect(() => { - // request for 4 makerAsset units, we need fees but no fee orders exist, show we should throw - forwarderHelper.getMarketBuyOrdersInfo({ - makerAssetFillAmount: new BigNumber(250), - }); - }).to.throw(ForwarderHelperError.InsufficientZrxLiquidity); - }); - it('passes the makerAssetFillAmount from the request to the info response', () => { - const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); - const makerAssetFillAmount = new BigNumber(4); - const info = forwarderHelper.getMarketBuyOrdersInfo({ - makerAssetFillAmount, - }); - expect(info.makerAssetFillAmount).to.bignumber.equal(makerAssetFillAmount); - }); - it('passes the feePercentage from the request to the info response', () => { - const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); - const feePercentage = new BigNumber(0.2); - const info = forwarderHelper.getMarketBuyOrdersInfo({ - makerAssetFillAmount: new BigNumber(4), - feePercentage, - }); - expect(info.feePercentage).to.bignumber.equal(feePercentage); - }); - }); -}); +// describe('ForwarderHelperImpl', () => { +// // rate: 2 takerAsset / makerAsset +// const testOrder1 = testOrderFactory.generateTestSignedOrder({ +// makerAssetAmount: new BigNumber(100), +// takerAssetAmount: new BigNumber(200), +// }); +// // rate: 1 takerAsset / makerAsset +// const testOrder2 = testOrderFactory.generateTestSignedOrder({ +// makerAssetAmount: new BigNumber(100), +// takerAssetAmount: new BigNumber(100), +// }); +// // rate: 3 takerAsset / makerAsset +// const testOrder3 = testOrderFactory.generateTestSignedOrder({ +// makerAssetAmount: new BigNumber(100), +// takerAssetAmount: new BigNumber(300), +// takerFee: new BigNumber(1), +// }); +// // rate: 3 WETH / ZRX +// const testFeeOrder1 = testOrderFactory.generateTestSignedOrder({ +// makerAssetAmount: new BigNumber(100), +// takerAssetAmount: new BigNumber(300), +// }); +// // rate: 2 WETH / ZRX +// const testFeeOrder2 = testOrderFactory.generateTestSignedOrder({ +// makerAssetAmount: new BigNumber(100), +// takerAssetAmount: new BigNumber(200), +// }); +// // rate: 1 WETH / ZRX +// const testFeeOrder3 = testOrderFactory.generateTestSignedOrder({ +// makerAssetAmount: new BigNumber(100), +// takerAssetAmount: new BigNumber(100), +// }); +// const inputForwarderHelperConfig: ForwarderHelperImplConfig = { +// orders: [testOrder1, testOrder2, testOrder3], +// feeOrders: [testFeeOrder1, testFeeOrder2, testFeeOrder3], +// remainingFillableMakerAssetAmounts: [new BigNumber(1), new BigNumber(2), new BigNumber(3)], +// remainingFillableFeeAmounts: [new BigNumber(4), new BigNumber(5), new BigNumber(6)], +// }; +// describe('#constructor', () => { +// const inputForwarderHelperConfigNoRemainingAmounts: ForwarderHelperImplConfig = { +// orders: [testOrder1, testOrder2, testOrder3], +// feeOrders: [testFeeOrder1, testFeeOrder2, testFeeOrder3], +// }; +// it('sorts orders', () => { +// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); +// expect(forwarderHelper.config.orders).deep.equals([testOrder2, testOrder1, testOrder3]); +// }); +// it('sorts fee orders', () => { +// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); +// expect(forwarderHelper.config.feeOrders).deep.equals([testFeeOrder3, testFeeOrder2, testFeeOrder1]); +// }); +// it('sorts remainingFillableMakerAssetAmounts', () => { +// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); +// expect(forwarderHelper.config.remainingFillableMakerAssetAmounts).to.be.not.undefined(); +// expect(_.nth(forwarderHelper.config.remainingFillableMakerAssetAmounts, 0)).to.bignumber.equal( +// new BigNumber(2), +// ); +// expect(_.nth(forwarderHelper.config.remainingFillableMakerAssetAmounts, 1)).to.bignumber.equal( +// new BigNumber(1), +// ); +// expect(_.nth(forwarderHelper.config.remainingFillableMakerAssetAmounts, 2)).to.bignumber.equal( +// new BigNumber(3), +// ); +// }); +// it('sorts remainingFillableFeeAmounts', () => { +// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); +// expect(forwarderHelper.config.remainingFillableFeeAmounts).to.be.not.undefined(); +// expect(_.nth(forwarderHelper.config.remainingFillableFeeAmounts, 0)).to.bignumber.equal(new BigNumber(6)); +// expect(_.nth(forwarderHelper.config.remainingFillableFeeAmounts, 1)).to.bignumber.equal(new BigNumber(5)); +// expect(_.nth(forwarderHelper.config.remainingFillableFeeAmounts, 2)).to.bignumber.equal(new BigNumber(4)); +// }); +// it('remainingFillableMakerAssetAmounts is undefined if none provided', () => { +// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfigNoRemainingAmounts); +// expect(forwarderHelper.config.remainingFillableMakerAssetAmounts).to.be.undefined(); +// }); +// it('remainingFillableFeeAmounts is undefined if none provided', () => { +// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfigNoRemainingAmounts); +// expect(forwarderHelper.config.remainingFillableFeeAmounts).to.be.undefined(); +// }); +// }); +// describe('#getMarketBuyOrdersInfo', () => { +// it('throws if not enough makerAsset liquidity', () => { +// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); +// expect(() => { +// // request for 6 makerAsset units, because we have exactly 6 available we should throw because there is a built in slippage buffer +// forwarderHelper.getMarketBuyOrdersInfo({ +// makerAssetFillAmount: new BigNumber(6), +// }); +// }).to.throw(ForwarderHelperError.InsufficientMakerAssetLiquidity); +// }); +// it('throws if not enough ZRX liquidity', () => { +// const inputForwarderHelperConfigNoFees: ForwarderHelperImplConfig = { +// orders: [testOrder1, testOrder2, testOrder3], +// feeOrders: [], +// }; +// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfigNoFees); +// expect(() => { +// // request for 4 makerAsset units, we need fees but no fee orders exist, show we should throw +// forwarderHelper.getMarketBuyOrdersInfo({ +// makerAssetFillAmount: new BigNumber(250), +// }); +// }).to.throw(ForwarderHelperError.InsufficientZrxLiquidity); +// }); +// it('passes the makerAssetFillAmount from the request to the info response', () => { +// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); +// const makerAssetFillAmount = new BigNumber(4); +// const info = forwarderHelper.getMarketBuyOrdersInfo({ +// makerAssetFillAmount, +// }); +// expect(info.makerAssetFillAmount).to.bignumber.equal(makerAssetFillAmount); +// }); +// it('passes the feePercentage from the request to the info response', () => { +// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); +// const feePercentage = new BigNumber(0.2); +// const info = forwarderHelper.getMarketBuyOrdersInfo({ +// makerAssetFillAmount: new BigNumber(4), +// feePercentage, +// }); +// expect(info.feePercentage).to.bignumber.equal(feePercentage); +// }); +// }); +// }); diff --git a/tsconfig.json b/tsconfig.json index a931b28b9f..bc71dfb703 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,7 @@ { "path": "./packages/0x.js" }, { "path": "./packages/abi-gen" }, { "path": "./packages/assert" }, + { "path": "./packages/asset-buyer" }, { "path": "./packages/base-contract" }, { "path": "./packages/connect" }, { "path": "./packages/contract-wrappers" }, @@ -30,7 +31,6 @@ { "path": "./packages/dev-utils" }, { "path": "./packages/ethereum-types" }, { "path": "./packages/fill-scenarios" }, - { "path": "./packages/forwarder-helper" }, { "path": "./packages/json-schemas" }, { "path": "./packages/metacoin" }, { "path": "./packages/migrations" }, From f1a22e9bd7943bc9cb8d8308daca0c60af6e0039 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sat, 15 Sep 2018 12:59:23 +0200 Subject: [PATCH 08/47] Flesh out the AssetBuyer class --- packages/asset-buyer/src/asset_buyer.ts | 227 ++++++++++++++++++ .../src/asset_buyers/asset_buyer.ts | 129 ---------- .../src/forwarder_helper_factory.ts | 1 + packages/asset-buyer/src/index.ts | 15 +- packages/asset-buyer/src/types.ts | 50 +++- packages/asset-buyer/src/utils/assert.ts | 23 ++ .../src/utils/buy_quote_calculator.ts | 60 +++++ .../forwarder_helper_impl_config_utils.ts | 92 ------- .../utils/order_fetcher_response_processor.ts | 180 ++++++++++++++ 9 files changed, 544 insertions(+), 233 deletions(-) create mode 100644 packages/asset-buyer/src/asset_buyer.ts delete mode 100644 packages/asset-buyer/src/asset_buyers/asset_buyer.ts create mode 100644 packages/asset-buyer/src/utils/assert.ts create mode 100644 packages/asset-buyer/src/utils/buy_quote_calculator.ts delete mode 100644 packages/asset-buyer/src/utils/forwarder_helper_impl_config_utils.ts create mode 100644 packages/asset-buyer/src/utils/order_fetcher_response_processor.ts diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts new file mode 100644 index 0000000000..9cbdd8df63 --- /dev/null +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -0,0 +1,227 @@ +import { ContractWrappers } from '@0xproject/contract-wrappers'; +import { assetDataUtils, marketUtils } from '@0xproject/order-utils'; +import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { Provider } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { constants } from './constants'; +import { + AssetBuyerError, + AssetBuyerOrdersAndFillableAmounts, + BuyQuote, + OrderFetcher, + OrderFetcherResponse, +} from './types'; +import { assert } from './utils/assert'; +import { buyQuoteCalculator } from './utils/buy_quote_calculator'; +import { orderFetcherResponseProcessor } from './utils/order_fetcher_response_processor'; + +const SLIPPAGE_PERCENTAGE = 0.2; // 20% slippage protection, possibly move this into request interface +const DEFAULT_ORDER_REFRESH_INTERVAL_MS = 10000; // 10 seconds +const DEFAULT_FEE_PERCENTAGE = 0; +const ETHER_TOKEN_DECIMALS = 18; + +export class AssetBuyer { + public readonly provider: Provider; + public readonly assetData: string; + public readonly orderFetcher: OrderFetcher; + public readonly networkId: number; + public readonly orderRefreshIntervalMs: number; + private _contractWrappers: ContractWrappers; + private _lastRefreshTimeIfExists?: number; + private _currentOrdersAndFillableAmountsIfExists?: AssetBuyerOrdersAndFillableAmounts; + /** + * Instantiates a new AssetBuyer instance + * @param provider The Provider instance you would like to use for interacting with the Ethereum network. + * @param assetData The assetData of the desired asset to buy (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). + * @param orderFetcher An object that conforms to OrderFetcher, see type for definition. + * @param networkId The ethereum network id. Defaults to 1 (mainnet). + * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. + * Defaults to 10000ms (10s). + * @return An instance of AssetBuyer + */ + constructor( + provider: Provider, + assetData: string, + orderFetcher: OrderFetcher, + networkId: number = constants.MAINNET_NETWORK_ID, + orderRefreshIntervalMs: number = DEFAULT_ORDER_REFRESH_INTERVAL_MS, + ) { + assert.isWeb3Provider('provider', provider); + assert.isString('assetData', assetData); + assert.isValidOrderFetcher('orderFetcher', orderFetcher); + assert.isNumber('networkId', networkId); + assert.isNumber('orderRefreshIntervalMs', orderRefreshIntervalMs); + this.provider = provider; + this.assetData = assetData; + this.orderFetcher = orderFetcher; + this.networkId = networkId; + this.orderRefreshIntervalMs = orderRefreshIntervalMs; + this._contractWrappers = new ContractWrappers(this.provider, { + networkId, + }); + } + /** + * Get a BuyQuote containing all information relevant to fulfilling a buy. + * Pass the BuyQuote to executeBuyQuoteAsync to execute the buy. + * @param assetBuyAmount The amount of asset to buy. + * @param feePercentage The affiliate fee percentage. Defaults to 0. + * @param forceOrderRefresh If set to true, new orders and state will be fetched instead of waiting for + * the next orderRefreshIntervalMs. Defaults to false. + * @return An object that conforms to BuyQuote that satisfies the request. See type definition for more information. + */ + public async getBuyQuoteAsync( + assetBuyAmount: BigNumber, + feePercentage: number = DEFAULT_FEE_PERCENTAGE, + forceOrderRefresh: boolean = false, + ): Promise { + assert.isBigNumber('assetBuyAmount', assetBuyAmount); + assert.isNumber('feePercentage', feePercentage); + assert.isBoolean('forceOrderRefresh', forceOrderRefresh); + // we should refresh if: + // we do not have any orders OR + // we are forced to OR + // we have some last refresh time AND that time was sufficiently long ago + const shouldRefresh = + _.isUndefined(this._currentOrdersAndFillableAmountsIfExists) || + forceOrderRefresh || + (!_.isUndefined(this._lastRefreshTimeIfExists) && + this._lastRefreshTimeIfExists + this.orderRefreshIntervalMs < Date.now()); + let ordersAndFillableAmounts: AssetBuyerOrdersAndFillableAmounts; + if (shouldRefresh) { + ordersAndFillableAmounts = await this._getLatestOrdersAndFillableAmountsAsync(); + this._lastRefreshTimeIfExists = Date.now(); + this._currentOrdersAndFillableAmountsIfExists = ordersAndFillableAmounts; + } else { + // it is safe to cast to AssetBuyerOrdersAndFillableAmounts because shouldRefresh catches the undefined case above + ordersAndFillableAmounts = this + ._currentOrdersAndFillableAmountsIfExists as AssetBuyerOrdersAndFillableAmounts; + } + // TODO: optimization + // make the slippage percentage customizable by integrator + const buyQuote = buyQuoteCalculator.calculate( + ordersAndFillableAmounts, + assetBuyAmount, + feePercentage, + SLIPPAGE_PERCENTAGE, + ); + return buyQuote; + } + /** + * Given a BuyQuote and desired rate, attempt to execute the buy. + * @param buyQuote An object that conforms to BuyQuote. See type definition for more information. + * @param rate The desired rate to execute the buy at. Affects the amount of ETH sent with the transaction, defaults to buyQuote.maxRate. + * @param takerAddress The address to perform the buy. Defaults to the first available address from the provider. + * @param feeRecipient The address where affiliate fees are sent. Defaults to null address (0x000...000). + * @return A promise of the txHash. + */ + public async executeBuyQuoteAsync( + buyQuote: BuyQuote, + rate?: BigNumber, + takerAddress?: string, + feeRecipient: string = constants.NULL_ADDRESS, + ): Promise { + assert.isValidBuyQuote('buyQuote', buyQuote); + if (!_.isUndefined(rate)) { + assert.isBigNumber('rate', rate); + } + if (!_.isUndefined(takerAddress)) { + assert.isETHAddressHex('takerAddress', takerAddress); + } + assert.isETHAddressHex('feeRecipient', feeRecipient); + const { orders, feeOrders, feePercentage, assetBuyAmount, maxRate } = buyQuote; + // if no takerAddress is provided, try to get one from the provider + let finalTakerAddress; + if (!_.isUndefined(takerAddress)) { + finalTakerAddress = takerAddress; + } else { + const web3Wrapper = new Web3Wrapper(this.provider); + const availableAddresses = await web3Wrapper.getAvailableAddressesAsync(); + const firstAvailableAddress = _.head(availableAddresses); + if (!_.isUndefined(firstAvailableAddress)) { + finalTakerAddress = firstAvailableAddress; + } else { + throw new Error(AssetBuyerError.NoAddressAvailable); + } + } + // if no rate is provided, default to the maxRate from buyQuote + const desiredRate = rate || maxRate; + // calculate how much eth is required to buy assetBuyAmount at the desired rate + const ethAmount = assetBuyAmount.dividedToIntegerBy(desiredRate); + // TODO: critical + // update the forwarder wrapper to take in feePercentage as a number instead of a BigNumber, verify with Amir that this is being done correctly + const feePercentageBigNumber = !_.isUndefined(feePercentage) + ? Web3Wrapper.toBaseUnitAmount(new BigNumber(1), ETHER_TOKEN_DECIMALS).mul(feePercentage) + : constants.ZERO_AMOUNT; + const txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync( + orders, + assetBuyAmount, + finalTakerAddress, + ethAmount, + feeOrders, + feePercentageBigNumber, + feeRecipient, + ); + return txHash; + } + /** + * Ask the order fetcher for orders and process them. + */ + private async _getLatestOrdersAndFillableAmountsAsync(): Promise { + // find ether token asset data + const etherTokenAssetData = this._getEtherTokenAssetData(); + // find zrx token asset data + const zrxTokenAssetData = this._getZrxTokenAssetData(); + // construct order fetcher requests + const targetOrderFetcherRequest = { + makerAssetData: this.assetData, + takerAssetData: etherTokenAssetData, + networkId: this.networkId, + }; + const feeOrderFetcherRequest = { + makerAssetData: zrxTokenAssetData, + takerAssetData: etherTokenAssetData, + networkId: this.networkId, + }; + const requests = [targetOrderFetcherRequest, feeOrderFetcherRequest]; + // fetch orders and possible fillable amounts + const [targetOrderFetcherResponse, feeOrderFetcherResponse] = await Promise.all( + _.map(requests, request => this.orderFetcher.fetchOrdersAsync(request)), + ); + // process the responses into one object + const ordersAndFillableAmounts = await orderFetcherResponseProcessor.processAsync( + targetOrderFetcherResponse, + feeOrderFetcherResponse, + zrxTokenAssetData, + this._contractWrappers.orderValidator, + ); + return ordersAndFillableAmounts; + } + /** + * Get the assetData that represents the WETH token. + * Will throw if WETH does not exist for the current network. + */ + private _getEtherTokenAssetData(): string { + const etherTokenAddressIfExists = this._contractWrappers.etherToken.getContractAddressIfExists(); + if (_.isUndefined(etherTokenAddressIfExists)) { + throw new Error(AssetBuyerError.NoEtherTokenContractFound); + } + const etherTokenAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists); + return etherTokenAssetData; + } + /** + * Get the assetData that represents the ZRX token. + * Will throw if ZRX does not exist for the current network. + */ + private _getZrxTokenAssetData(): string { + let zrxTokenAssetData: string; + try { + zrxTokenAssetData = this._contractWrappers.exchange.getZRXAssetData(); + } catch (err) { + throw new Error(AssetBuyerError.NoZrxTokenContractFound); + } + return zrxTokenAssetData; + } +} diff --git a/packages/asset-buyer/src/asset_buyers/asset_buyer.ts b/packages/asset-buyer/src/asset_buyers/asset_buyer.ts deleted file mode 100644 index eb7f85e2b6..0000000000 --- a/packages/asset-buyer/src/asset_buyers/asset_buyer.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { ContractWrappers } from '@0xproject/contract-wrappers'; -import { marketUtils } from '@0xproject/order-utils'; -import { SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import * as _ from 'lodash'; -import { Provider } from 'ethereum-types'; - -import { constants } from '../constants'; -import { AssetBuyerError, BuyQuote, BuyQuoteRequest } from '../types'; - -const SLIPPAGE_PERCENTAGE = new BigNumber(0.2); // 20% slippage protection, possibly move this into request interface - -export interface AssetBuyerConfig { - orders: SignedOrder[]; - feeOrders: SignedOrder[]; - remainingFillableMakerAssetAmounts?: BigNumber[]; - remainingFillableFeeAmounts?: BigNumber[]; - networkId?: number; -} - -export class AssetBuyer { - public readonly provider: Provider; - public readonly config: AssetBuyerConfig; - private _contractWrappers: ContractWrappers; - constructor(provider: Provider, config: AssetBuyerConfig) { - this.provider = provider; - this.config = config; - const networkId = this.config.networkId || constants.MAINNET_NETWORK_ID; - this._contractWrappers = new ContractWrappers(this.provider, { - networkId, - }); - } - /** - * Given a BuyQuoteRequest, returns a BuyQuote containing all information relevant to fulfilling the buy. Pass the BuyQuote - * to executeBuyQuoteAsync to execute the buy. - * @param buyQuoteRequest An object that conforms to BuyQuoteRequest. See type definition for more information. - * @return An object that conforms to BuyQuote that satisfies the request. See type definition for more information. - */ - public getBuyQuote(buyQuoteRequest: BuyQuoteRequest): BuyQuote { - const { assetBuyAmount, feePercentage } = buyQuoteRequest; - const { orders, feeOrders, remainingFillableMakerAssetAmounts, remainingFillableFeeAmounts } = this.config; - // TODO: optimization - // make the slippage percentage customizable - const slippageBufferAmount = assetBuyAmount.mul(SLIPPAGE_PERCENTAGE).round(); - const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( - orders, - assetBuyAmount, - { - remainingFillableMakerAssetAmounts, - slippageBufferAmount, - }, - ); - if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { - throw new Error(AssetBuyerError.InsufficientAssetLiquidity); - } - // TODO: optimization - // update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to - // finding order that cover all fees, this will help with estimating ETH and minimizing gas usage - const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( - resultOrders, - feeOrders, - { - remainingFillableMakerAssetAmounts, - remainingFillableFeeAmounts, - }, - ); - if (remainingFeeAmount.gt(constants.ZERO_AMOUNT)) { - throw new Error(AssetBuyerError.InsufficientZrxLiquidity); - } - const assetData = orders[0].makerAssetData; - // TODO: critical - // calculate minRate and maxRate by calculating min and max eth usage and then dividing into - // assetBuyAmount to get assetData / WETH - return { - assetData, - orders: resultOrders, - feeOrders: resultFeeOrders, - minRate: constants.ZERO_AMOUNT, - maxRate: constants.ZERO_AMOUNT, - assetBuyAmount, - feePercentage, - }; - } - /** - * Given a BuyQuote and desired rate, attempt to execute the buy. - * @param buyQuote An object that conforms to BuyQuote. See type definition for more information. - * @param rate The desired rate to execute the buy at. Affects the amount of ETH sent with the transaction, defaults to buyQuote.maxRate. - * @param takerAddress The address to perform the buy. Defaults to the first available address from the provider. - * @param feeRecipient The address where affiliate fees are sent. Defaults to null address (0x000...000). - * @return A promise of the txHash. - */ - public async executeBuyQuoteAsync( - buyQuote: BuyQuote, - rate?: BigNumber, - takerAddress?: string, - feeRecipient: string = constants.NULL_ADDRESS, - ): Promise { - const { orders, feeOrders, feePercentage, assetBuyAmount, maxRate } = buyQuote; - // if no takerAddress is provided, try to get one from the provider - let finalTakerAddress; - if (!_.isUndefined(takerAddress)) { - finalTakerAddress = takerAddress; - } else { - const web3Wrapper = new Web3Wrapper(this.provider); - const availableAddresses = await web3Wrapper.getAvailableAddressesAsync(); - const firstAvailableAddress = _.head(availableAddresses); - if (!_.isUndefined(firstAvailableAddress)) { - finalTakerAddress = firstAvailableAddress; - } else { - throw new Error(AssetBuyerError.NoAddressAvailable); - } - } - // if no rate is provided, default to the maxRate from buyQuote - const desiredRate = rate || maxRate; - // calculate how much eth is required to buy assetBuyAmount at the desired rate - const ethAmount = assetBuyAmount.dividedToIntegerBy(desiredRate); - const txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync( - orders, - assetBuyAmount, - finalTakerAddress, - ethAmount, - feeOrders, - feePercentage, - feeRecipient, - ); - return txHash; - } -} diff --git a/packages/asset-buyer/src/forwarder_helper_factory.ts b/packages/asset-buyer/src/forwarder_helper_factory.ts index 9a3832e81e..4c4adfda07 100644 --- a/packages/asset-buyer/src/forwarder_helper_factory.ts +++ b/packages/asset-buyer/src/forwarder_helper_factory.ts @@ -89,6 +89,7 @@ // } catch (err) { // throw new Error(ForwarderHelperFactoryError.StandardRelayerApiError); // } + // // validate orders and find remaining fillable from on chain state or sra api // let ordersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; // let feeOrdersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; diff --git a/packages/asset-buyer/src/index.ts b/packages/asset-buyer/src/index.ts index 299b32edd0..67ad06084b 100644 --- a/packages/asset-buyer/src/index.ts +++ b/packages/asset-buyer/src/index.ts @@ -1,2 +1,13 @@ -export { AssetBuyerError, BuyQuote, BuyQuoteRequest } from './types'; -export { AssetBuyer } from './asset_buyers/asset_buyer'; +export { Provider } from 'ethereum-types'; +export { SignedOrder } from '@0xproject/types'; +export { BigNumber } from '@0xproject/utils'; + +export { AssetBuyer } from './asset_buyer'; +export { + AssetBuyerError, + BuyQuote, + OrderFetcher, + OrderFetcherRequest, + OrderFetcherResponse, + SignedOrderWithRemainingFillableMakerAssetAmount, +} from './types'; diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index 8a12d0cf83..0da30f48d5 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -2,22 +2,45 @@ import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; /** - * assetBuyAmount: The amount of asset to buy. - * feePercentage: Optional affiliate percentage amount factoring into eth amount calculations. + * makerAssetData: The assetData representing the desired makerAsset. + * takerAssetData: The assetData representing the desired takerAsset. + * networkId: The networkId that the desired orders should be for. */ -export interface BuyQuoteRequest { - assetBuyAmount: BigNumber; - feePercentage?: BigNumber; +export interface OrderFetcherRequest { + makerAssetData: string; + takerAssetData: string; + networkId: number; +} + +/** + * orders: An array of orders with optional remaining fillable makerAsset amounts. See type for more info. + */ +export interface OrderFetcherResponse { + orders: SignedOrderWithRemainingFillableMakerAssetAmount[]; +} + +/** + * A normal SignedOrder with one extra optional property `remainingFillableMakerAssetAmount` + * remainingFillableMakerAssetAmount: The amount of the makerAsset that is available to be filled + */ +export interface SignedOrderWithRemainingFillableMakerAssetAmount extends SignedOrder { + remainingFillableMakerAssetAmount?: BigNumber; +} +/** + * Given an OrderFetchRequest, get an OrderFetchResponse. + */ +export interface OrderFetcher { + fetchOrdersAsync: (orderFetchRequest: OrderFetcherRequest) => Promise; } /** - * assetData: The asset information. + * assetData: String that represents a specific asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * orders: An array of objects conforming to SignedOrder. These orders can be used to cover the requested assetBuyAmount plus slippage. * feeOrders: An array of objects conforming to SignedOrder. These orders can be used to cover the fees for the orders param above. * minRate: Min rate that needs to be paid in order to execute the buy. * maxRate: Max rate that can be paid in order to execute the buy. - * assetBuyAmount: The amount of asset to buy. Passed through directly from the request. - * feePercentage: Affiliate fee percentage used to calculate the eth amounts above. Passed through directly from the request. + * assetBuyAmount: The amount of asset to buy. + * feePercentage: Optional affiliate fee percentage used to calculate the eth amounts above. */ export interface BuyQuote { assetData: string; @@ -26,11 +49,11 @@ export interface BuyQuote { minRate: BigNumber; maxRate: BigNumber; assetBuyAmount: BigNumber; - feePercentage?: BigNumber; + feePercentage?: number; } /** - * Possible errors thrown by an AssetBuyer instance or associated static methods + * Possible errors thrown by an AssetBuyer instance or associated static methods. */ export enum AssetBuyerError { NoEtherTokenContractFound = 'NO_ETHER_TOKEN_CONTRACT_FOUND', @@ -40,3 +63,10 @@ export enum AssetBuyerError { InsufficientZrxLiquidity = 'INSUFFICIENT_ZRX_LIQUIDITY', NoAddressAvailable = 'NO_ADDRESS_AVAILABLE', } + +export interface AssetBuyerOrdersAndFillableAmounts { + orders: SignedOrderWithRemainingFillableMakerAssetAmount[]; + feeOrders: SignedOrderWithRemainingFillableMakerAssetAmount[]; + remainingFillableMakerAssetAmounts: BigNumber[]; + remainingFillableFeeAmounts: BigNumber[]; +} diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts new file mode 100644 index 0000000000..c4d6114771 --- /dev/null +++ b/packages/asset-buyer/src/utils/assert.ts @@ -0,0 +1,23 @@ +import { assert as sharedAssert } from '@0xproject/assert'; +import { schemas } from '@0xproject/json-schemas'; +import * as _ from 'lodash'; + +import { BuyQuote, OrderFetcher } from '../types'; + +export const assert = { + ...sharedAssert, + isValidBuyQuote(variableName: string, buyQuote: BuyQuote): void { + sharedAssert.isHexString(`${variableName}.assetData`, buyQuote.assetData); + sharedAssert.doesConformToSchema(`${variableName}.orders`, buyQuote.orders, schemas.signedOrdersSchema); + sharedAssert.doesConformToSchema(`${variableName}.feeOrders`, buyQuote.feeOrders, schemas.signedOrdersSchema); + sharedAssert.isBigNumber(`${variableName}.minRate`, buyQuote.minRate); + sharedAssert.isBigNumber(`${variableName}.maxRate`, buyQuote.maxRate); + sharedAssert.isBigNumber(`${variableName}.assetBuyAmount`, buyQuote.assetBuyAmount); + if (!_.isUndefined(buyQuote.feePercentage)) { + sharedAssert.isNumber(`${variableName}.feePercentage`, buyQuote.feePercentage); + } + }, + isValidOrderFetcher(variableName: string, orderFetcher: OrderFetcher): void { + sharedAssert.isFunction(`${variableName}.fetchOrdersAsync`, orderFetcher.fetchOrdersAsync); + }, +}; diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts new file mode 100644 index 0000000000..e05ab1e550 --- /dev/null +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -0,0 +1,60 @@ +import { marketUtils } from '@0xproject/order-utils'; +import { BigNumber } from '@0xproject/utils'; + +import { constants } from '../constants'; +import { AssetBuyerError, AssetBuyerOrdersAndFillableAmounts, BuyQuote } from '../types'; + +export const buyQuoteCalculator = { + calculate( + ordersAndFillableAmounts: AssetBuyerOrdersAndFillableAmounts, + assetBuyAmount: BigNumber, + feePercentage: number, + slippagePercentage: number, + ): BuyQuote { + const { + orders, + feeOrders, + remainingFillableMakerAssetAmounts, + remainingFillableFeeAmounts, + } = ordersAndFillableAmounts; + const slippageBufferAmount = assetBuyAmount.mul(slippagePercentage).round(); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + orders, + assetBuyAmount, + { + remainingFillableMakerAssetAmounts, + slippageBufferAmount, + }, + ); + if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { + throw new Error(AssetBuyerError.InsufficientAssetLiquidity); + } + // TODO: optimization + // update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to + // finding order that cover all fees, this will help with estimating ETH and minimizing gas usage + const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( + resultOrders, + feeOrders, + { + remainingFillableMakerAssetAmounts, + remainingFillableFeeAmounts, + }, + ); + if (remainingFeeAmount.gt(constants.ZERO_AMOUNT)) { + throw new Error(AssetBuyerError.InsufficientZrxLiquidity); + } + const assetData = orders[0].makerAssetData; + // TODO: critical + // calculate minRate and maxRate by calculating min and max eth usage and then dividing into + // assetBuyAmount to get assetData / WETH, needs to take into account feePercentage as well + return { + assetData, + orders: resultOrders, + feeOrders: resultFeeOrders, + minRate: constants.ZERO_AMOUNT, + maxRate: constants.ZERO_AMOUNT, + assetBuyAmount, + feePercentage, + }; + }, +}; diff --git a/packages/asset-buyer/src/utils/forwarder_helper_impl_config_utils.ts b/packages/asset-buyer/src/utils/forwarder_helper_impl_config_utils.ts deleted file mode 100644 index d3cbb651a1..0000000000 --- a/packages/asset-buyer/src/utils/forwarder_helper_impl_config_utils.ts +++ /dev/null @@ -1,92 +0,0 @@ -// import { sortingUtils } from '@0xproject/order-utils'; -// import { SignedOrder } from '@0xproject/types'; -// import { BigNumber } from '@0xproject/utils'; -// import * as _ from 'lodash'; - -// import { ForwarderHelperImplConfig } from '@0xproject/asset-buyer/src/asset_buyer'; - -// interface SignedOrderWithAmount extends SignedOrder { -// remainingFillAmount: BigNumber; -// } - -// export const forwarderHelperImplConfigUtils = { -// sortedConfig(config: ForwarderHelperImplConfig): ForwarderHelperImplConfig { -// const { orders, feeOrders, remainingFillableMakerAssetAmounts, remainingFillableFeeAmounts } = config; -// // TODO: provide a feeRate to the sorting function to more accurately sort based on the current market for ZRX tokens -// const orderSorter = (ordersToSort: SignedOrder[]) => { -// return sortingUtils.sortOrdersByFeeAdjustedRate(ordersToSort); -// }; -// const sortOrdersResult = sortOrdersAndRemainingFillAmounts( -// orderSorter, -// orders, -// remainingFillableMakerAssetAmounts, -// ); -// const feeOrderSorter = (ordersToSort: SignedOrder[]) => { -// return sortingUtils.sortFeeOrdersByFeeAdjustedRate(ordersToSort); -// }; -// const sortFeeOrdersResult = sortOrdersAndRemainingFillAmounts( -// feeOrderSorter, -// feeOrders, -// remainingFillableFeeAmounts, -// ); -// return { -// orders: sortOrdersResult.orders, -// feeOrders: sortFeeOrdersResult.orders, -// remainingFillableMakerAssetAmounts: sortOrdersResult.remainingFillAmounts, -// remainingFillableFeeAmounts: sortFeeOrdersResult.remainingFillAmounts, -// }; -// }, -// }; - -// type OrderSorter = (orders: SignedOrder[]) => SignedOrder[]; - -// function sortOrdersAndRemainingFillAmounts( -// orderSorter: OrderSorter, -// orders: SignedOrder[], -// remainingFillAmounts?: BigNumber[], -// ): { orders: SignedOrder[]; remainingFillAmounts?: BigNumber[] } { -// if (!_.isUndefined(remainingFillAmounts)) { -// // Bundle orders together with their remainingFillAmounts so that we can sort them together -// const orderWithAmounts = bundleSignedOrderWithAmounts(orders, remainingFillAmounts); -// // Sort -// const sortedOrderWithAmounts = orderSorter(orderWithAmounts) as SignedOrderWithAmount[]; -// // Unbundle after sorting -// const unbundledSortedOrderWithAmounts = unbundleSignedOrderWithAmounts(sortedOrderWithAmounts); -// return { -// orders: unbundledSortedOrderWithAmounts.orders, -// remainingFillAmounts: unbundledSortedOrderWithAmounts.amounts, -// }; -// } else { -// const sortedOrders = orderSorter(orders); -// return { -// orders: sortedOrders, -// }; -// } -// } - -// function bundleSignedOrderWithAmounts(orders: SignedOrder[], amounts: BigNumber[]): SignedOrderWithAmount[] { -// const ordersAndAmounts = _.map(orders, (order, index) => { -// return { -// ...order, -// remainingFillAmount: amounts[index], -// }; -// }); -// return ordersAndAmounts; -// } - -// function unbundleSignedOrderWithAmounts( -// signedOrderWithAmounts: SignedOrderWithAmount[], -// ): { orders: SignedOrder[]; amounts: BigNumber[] } { -// const orders = _.map(signedOrderWithAmounts, order => { -// const { remainingFillAmount, ...rest } = order; -// return rest; -// }); -// const amounts = _.map(signedOrderWithAmounts, order => { -// const { remainingFillAmount } = order; -// return remainingFillAmount; -// }); -// return { -// orders, -// amounts, -// }; -// } diff --git a/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts b/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts new file mode 100644 index 0000000000..04c5355eb1 --- /dev/null +++ b/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts @@ -0,0 +1,180 @@ +import { OrderAndTraderInfo, OrderStatus, OrderValidatorWrapper } from '@0xproject/contract-wrappers'; +import { sortingUtils } from '@0xproject/order-utils'; +import { RemainingFillableCalculator } from '@0xproject/order-utils/lib/src/remaining_fillable_calculator'; +import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { constants } from '../constants'; +import { + AssetBuyerOrdersAndFillableAmounts, + OrderFetcherResponse, + SignedOrderWithRemainingFillableMakerAssetAmount, +} from '../types'; + +import { orderUtils } from './order_utils'; + +interface OrdersAndRemainingFillableMakerAssetAmounts { + orders: SignedOrder[]; + remainingFillableMakerAssetAmounts: BigNumber[]; +} + +export const orderFetcherResponseProcessor = { + /** + * Take the responses for the target orders to buy and fee orders and process them. + * Processing includes: + * - Drop orders that are expired or not open orders (null taker address) + * - If shouldValidateOnChain, attempt to grab fillable amounts from on-chain otherwise assume completely fillable + * - Sort by rate + */ + async processAsync( + targetOrderFetcherResponse: OrderFetcherResponse, + feeOrderFetcherResponse: OrderFetcherResponse, + zrxTokenAssetData: string, + orderValidator?: OrderValidatorWrapper, + ): Promise { + // drop orders that are expired or not open + const filteredTargetOrders = filterOutExpiredAndNonOpenOrders(targetOrderFetcherResponse.orders); + const filteredFeeOrders = filterOutExpiredAndNonOpenOrders(feeOrderFetcherResponse.orders); + // set the orders to be sorted equal to the filtered orders + let unsortedTargetOrders = filteredTargetOrders; + let unsortedFeeOrders = filteredFeeOrders; + // if an orderValidator is provided, use on chain information to calculate remaining fillable makerAsset amounts + if (!_.isUndefined(orderValidator)) { + // TODO: critical + // try catch these requests and throw a more domain specific error + // TODO: optimization + // reduce this to once RPC call buy combining orders into one array and then splitting up the response + const [targetOrdersAndTradersInfo, feeOrdersAndTradersInfo] = await Promise.all( + _.map([filteredTargetOrders, filteredFeeOrders], ordersToBeValidated => { + const takerAddresses = _.map(ordersToBeValidated, () => constants.NULL_ADDRESS); + return orderValidator.getOrdersAndTradersInfoAsync(ordersToBeValidated, takerAddresses); + }), + ); + // take orders + on chain information and find the valid orders and remaining fillable maker asset amounts + unsortedTargetOrders = getValidOrdersWithRemainingFillableMakerAssetAmountsFromOnChain( + filteredTargetOrders, + targetOrdersAndTradersInfo, + zrxTokenAssetData, + ); + // take orders + on chain information and find the valid orders and remaining fillable maker asset amounts + unsortedFeeOrders = getValidOrdersWithRemainingFillableMakerAssetAmountsFromOnChain( + filteredFeeOrders, + feeOrdersAndTradersInfo, + zrxTokenAssetData, + ); + } + // sort orders by rate + // TODO: optimization + // provide a feeRate to the sorting function to more accurately sort based on the current market for ZRX tokens + const sortedTargetOrders = sortingUtils.sortOrdersByFeeAdjustedRate(unsortedTargetOrders); + const sortedFeeOrders = sortingUtils.sortFeeOrdersByFeeAdjustedRate(unsortedFeeOrders); + // unbundle orders and fillable amounts and compile final result + const targetOrdersAndRemainingFillableMakerAssetAmounts = unbundleOrdersWithAmounts(sortedTargetOrders); + const feeOrdersAndRemainingFillableMakerAssetAmounts = unbundleOrdersWithAmounts(sortedFeeOrders); + return { + orders: targetOrdersAndRemainingFillableMakerAssetAmounts.orders, + feeOrders: feeOrdersAndRemainingFillableMakerAssetAmounts.orders, + remainingFillableMakerAssetAmounts: + targetOrdersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts, + remainingFillableFeeAmounts: + feeOrdersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts, + }; + }, +}; + +/** + * Given an array of orders, return a new array with expired and non open orders filtered out. + */ +function filterOutExpiredAndNonOpenOrders( + orders: SignedOrderWithRemainingFillableMakerAssetAmount[], +): SignedOrderWithRemainingFillableMakerAssetAmount[] { + const result = _.filter(orders, order => { + return orderUtils.isOpenOrder(order) && orderUtils.isOrderExpired(order); + }); + return result; +} + +/** + * Given an array of orders and corresponding on-chain infos, return a subset of the orders + * that are still fillable orders with their corresponding remainingFillableMakerAssetAmounts. + */ +function getValidOrdersWithRemainingFillableMakerAssetAmountsFromOnChain( + inputOrders: SignedOrder[], + ordersAndTradersInfo: OrderAndTraderInfo[], + zrxAssetData: string, +): SignedOrderWithRemainingFillableMakerAssetAmount[] { + // iterate through the input orders and find the ones that are still fillable + // for the orders that are still fillable, calculate the remaining fillable maker asset amount + const result = _.reduce( + inputOrders, + (accOrders, order, index) => { + // get corresponding on-chain state for the order + const { orderInfo, traderInfo } = ordersAndTradersInfo[index]; + // if the order IS NOT fillable, do not add anything to the accumulations and continue iterating + if (orderInfo.orderStatus !== OrderStatus.FILLABLE) { + return accOrders; + } + // if the order IS fillable, add the order and calculate the remaining fillable amount + const transferrableAssetAmount = BigNumber.min([traderInfo.makerAllowance, traderInfo.makerBalance]); + const transferrableFeeAssetAmount = BigNumber.min([ + traderInfo.makerZrxAllowance, + traderInfo.makerZrxBalance, + ]); + const remainingTakerAssetAmount = order.takerAssetAmount.minus(orderInfo.orderTakerAssetFilledAmount); + const remainingMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( + order, + remainingTakerAssetAmount, + ); + const remainingFillableCalculator = new RemainingFillableCalculator( + order.makerFee, + order.makerAssetAmount, + order.makerAssetData === zrxAssetData, + transferrableAssetAmount, + transferrableFeeAssetAmount, + remainingMakerAssetAmount, + ); + const remainingFillableAmount = remainingFillableCalculator.computeRemainingFillable(); + // if the order does not have any remaining fillable makerAsset, do not add anything to the accumulations and continue iterating + if (remainingFillableAmount.lte(constants.ZERO_AMOUNT)) { + return accOrders; + } + const orderWithRemainingFillableMakerAssetAmount = { + ...order, + remainingFillableMakerAssetAmount: remainingFillableAmount, + }; + const newAccOrders = _.concat(accOrders, orderWithRemainingFillableMakerAssetAmount); + return newAccOrders; + }, + [] as SignedOrderWithRemainingFillableMakerAssetAmount[], + ); + return result; +} + +/** + * Given an array of orders with remaining fillable maker asset amounts. Unbundle into an instance of OrdersAndRemainingFillableMakerAssetAmounts. + * If an order is missing a corresponding remainingFillableMakerAssetAmount, assume it is completely fillable. + */ +function unbundleOrdersWithAmounts( + ordersWithAmounts: SignedOrderWithRemainingFillableMakerAssetAmount[], +): OrdersAndRemainingFillableMakerAssetAmounts { + const result = _.reduce( + ordersWithAmounts, + (acc, orderWithAmount) => { + const { orders, remainingFillableMakerAssetAmounts } = acc; + const { remainingFillableMakerAssetAmount, ...order } = orderWithAmount; + // if we are still missing a remainingFillableMakerAssetAmount, assume the order is completely fillable + const newRemainingAmount = remainingFillableMakerAssetAmount || order.makerAssetAmount; + const newAcc = { + orders: _.concat(orders, order), + remainingFillableMakerAssetAmounts: _.concat(remainingFillableMakerAssetAmounts, newRemainingAmount), + }; + return newAcc; + }, + { + orders: [] as SignedOrder[], + remainingFillableMakerAssetAmounts: [] as BigNumber[], + }, + ); + return result; +} From 190bf2599c1327fffd03d8a9d50bc7568190cf33 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sat, 15 Sep 2018 13:56:20 +0200 Subject: [PATCH 09/47] Implement StandardRelayerAPIOrderFetcher --- .../src/forwarder_helper_factory.ts | 262 ------------------ .../standard_relayer_api_order_fetcher.ts | 80 ++++++ .../utils/order_fetcher_response_processor.ts | 4 + 3 files changed, 84 insertions(+), 262 deletions(-) delete mode 100644 packages/asset-buyer/src/forwarder_helper_factory.ts create mode 100644 packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts diff --git a/packages/asset-buyer/src/forwarder_helper_factory.ts b/packages/asset-buyer/src/forwarder_helper_factory.ts deleted file mode 100644 index 4c4adfda07..0000000000 --- a/packages/asset-buyer/src/forwarder_helper_factory.ts +++ /dev/null @@ -1,262 +0,0 @@ -// import { assert } from '@0xproject/assert'; -// import { APIOrder, HttpClient, OrderbookResponse } from '@0xproject/connect'; -// import { ContractWrappers, OrderAndTraderInfo, OrderStatus } from '@0xproject/contract-wrappers'; -// import { schemas } from '@0xproject/json-schemas'; -// import { assetDataUtils } from '@0xproject/order-utils'; -// import { RemainingFillableCalculator } from '@0xproject/order-utils/lib/src/remaining_fillable_calculator'; -// import { RPCSubprovider, Web3ProviderEngine } from '@0xproject/subproviders'; -// import { SignedOrder } from '@0xproject/types'; -// import { BigNumber } from '@0xproject/utils'; -// import * as _ from 'lodash'; - -// import { constants } from './constants'; -// import { ForwarderHelperImpl, ForwarderHelperImplConfig } from '@0xproject/asset-buyer/src/asset_buyer'; -// import { ForwarderHelper, ForwarderHelperFactoryError } from './types'; -// import { orderUtils } from './utils/order_utils'; - -// export const forwarderHelperFactory = { -// /** -// * Given an array of orders and an array of feeOrders, get a ForwarderHelper -// * @param orders An array of objects conforming to SignedOrder. Each order should specify the same makerAssetData and takerAssetData -// * @param feeOrders An array of objects conforming to SignedOrder. Each order should specify ZRX as makerAssetData WETH as takerAssetData -// * @return A ForwarderHelper, see type for definition -// */ -// getForwarderHelperForOrders(orders: SignedOrder[], feeOrders: SignedOrder[] = []): ForwarderHelper { -// assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); -// assert.doesConformToSchema('feeOrders', orders, schemas.signedOrdersSchema); -// // TODO: Add assertion here for orders all having the same makerAsset and takerAsset -// const config: ForwarderHelperImplConfig = { -// orders, -// feeOrders, -// }; -// const helper = new ForwarderHelperImpl(config); -// return helper; -// }, -// /** -// * Given a desired makerAsset and SRA url, get a ForwarderHelper -// * @param makerAssetData An array of objects conforming to SignedOrder. Each order should specify the same makerAssetData and takerAssetData -// * @param sraUrl A url pointing to an SRA v2 compliant endpoint. -// * @param rpcUrl A url pointing to an ethereum node. -// * @param networkId The ethereum networkId, defaults to 1 (mainnet). -// * @return A ForwarderHelper, see type for definition -// */ -// async getForwarderHelperForMakerAssetDataAsync( -// makerAssetData: string, -// sraUrl: string, -// rpcUrl?: string, -// networkId: number = 1, -// ): Promise { -// assert.isHexString('makerAssetData', makerAssetData); -// assert.isWebUri('sraUrl', sraUrl); -// if (!_.isUndefined(rpcUrl)) { -// assert.isWebUri('rpcUrl', rpcUrl); -// } -// assert.isNumber('networkId', networkId); -// // create provider -// const providerEngine = new Web3ProviderEngine(); -// if (!_.isUndefined(rpcUrl)) { -// providerEngine.addProvider(new RPCSubprovider(rpcUrl)); -// } -// providerEngine.start(); -// // create contract wrappers given provider and networkId -// const contractWrappers = new ContractWrappers(providerEngine, { networkId }); -// // find ether token asset data -// const etherTokenAddressIfExists = contractWrappers.etherToken.getContractAddressIfExists(); -// if (_.isUndefined(etherTokenAddressIfExists)) { -// throw new Error(ForwarderHelperFactoryError.NoEtherTokenContractFound); -// } -// const etherTokenAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists); -// // find zrx token asset data -// let zrxTokenAssetData: string; -// try { -// zrxTokenAssetData = contractWrappers.exchange.getZRXAssetData(); -// } catch (err) { -// throw new Error(ForwarderHelperFactoryError.NoZrxTokenContractFound); -// } -// // get orderbooks for makerAsset/WETH and ZRX/WETH -// const sraClient = new HttpClient(sraUrl); -// const orderbookRequests = [ -// { baseAssetData: makerAssetData, quoteAssetData: etherTokenAssetData }, -// { baseAssetData: zrxTokenAssetData, quoteAssetData: etherTokenAssetData }, -// ]; -// const requestOpts = { networkId }; -// let makerAssetOrderbook: OrderbookResponse; -// let zrxOrderbook: OrderbookResponse; -// try { -// [makerAssetOrderbook, zrxOrderbook] = await Promise.all( -// _.map(orderbookRequests, request => sraClient.getOrderbookAsync(request, requestOpts)), -// ); -// } catch (err) { -// throw new Error(ForwarderHelperFactoryError.StandardRelayerApiError); -// } - -// // validate orders and find remaining fillable from on chain state or sra api -// let ordersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; -// let feeOrdersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts; -// if (!_.isUndefined(rpcUrl)) { -// // if we do have an rpc url, get on-chain orders and traders info via the OrderValidatorWrapper -// const ordersFromSra = getOpenAsksFromOrderbook(makerAssetOrderbook); -// const feeOrdersFromSra = getOpenAsksFromOrderbook(zrxOrderbook); -// // TODO: try catch these requests and throw a more domain specific error -// // TODO: optimization, reduce this to once RPC call buy combining orders into one array and then splitting up the response -// const [makerAssetOrdersAndTradersInfo, feeOrdersAndTradersInfo] = await Promise.all( -// _.map([ordersFromSra, feeOrdersFromSra], ordersToBeValidated => { -// const takerAddresses = _.map(ordersToBeValidated, () => constants.NULL_ADDRESS); -// return contractWrappers.orderValidator.getOrdersAndTradersInfoAsync( -// ordersToBeValidated, -// takerAddresses, -// ); -// }), -// ); -// // take maker asset orders from SRA + on chain information and find the valid orders and remaining fillable maker asset amounts -// ordersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( -// ordersFromSra, -// makerAssetOrdersAndTradersInfo, -// zrxTokenAssetData, -// ); -// // take fee orders from SRA + on chain information and find the valid orders and remaining fillable maker asset amounts -// feeOrdersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( -// feeOrdersFromSra, -// feeOrdersAndTradersInfo, -// zrxTokenAssetData, -// ); -// } else { -// // if we don't have an rpc url, assume all orders are valid and fallback to optional fill amounts from SRA -// // if fill amounts are not available from the SRA, assume all orders are completely fillable -// const apiOrdersFromSra = makerAssetOrderbook.asks.records; -// const feeApiOrdersFromSra = zrxOrderbook.asks.records; -// // take maker asset orders from SRA and the valid orders and remaining fillable maker asset amounts -// ordersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( -// apiOrdersFromSra, -// ); -// // take fee orders from SRA and find the valid orders and remaining fillable maker asset amounts -// feeOrdersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( -// feeApiOrdersFromSra, -// ); -// } -// // compile final config -// const config: ForwarderHelperImplConfig = { -// orders: ordersAndRemainingFillableMakerAssetAmounts.orders, -// feeOrders: feeOrdersAndRemainingFillableMakerAssetAmounts.orders, -// remainingFillableMakerAssetAmounts: -// ordersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts, -// remainingFillableFeeAmounts: -// feeOrdersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts, -// }; -// const helper = new ForwarderHelperImpl(config); -// return helper; -// }, -// }; - -// interface OrdersAndRemainingFillableMakerAssetAmounts { -// orders: SignedOrder[]; -// remainingFillableMakerAssetAmounts: BigNumber[]; -// } - -// /** -// * Given an array of APIOrder objects from a standard relayer api, return an array -// * of fillable orders with their corresponding remainingFillableMakerAssetAmounts -// */ -// function getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi( -// apiOrders: APIOrder[], -// ): OrdersAndRemainingFillableMakerAssetAmounts { -// const result = _.reduce( -// apiOrders, -// (acc, apiOrder) => { -// // get current accumulations -// const { orders, remainingFillableMakerAssetAmounts } = acc; -// // get order and metadata -// const { order, metaData } = apiOrder; -// // if the order is expired or not open, move on -// if (orderUtils.isOrderExpired(order) || !orderUtils.isOpenOrder(order)) { -// return acc; -// } -// // calculate remainingFillableMakerAssetAmount from api metadata, else assume order is completely fillable -// const remainingFillableTakerAssetAmount = _.get( -// metaData, -// 'remainingTakerAssetAmount', -// order.takerAssetAmount, -// ); -// const remainingFillableMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( -// order, -// remainingFillableTakerAssetAmount, -// ); -// // if there is some amount of maker asset left to fill and add the order and remaining amount to the accumulations -// // if there is not any maker asset left to fill, do not add -// if (remainingFillableMakerAssetAmount.gt(constants.ZERO_AMOUNT)) { -// return { -// orders: _.concat(orders, order), -// remainingFillableMakerAssetAmounts: _.concat( -// remainingFillableMakerAssetAmounts, -// remainingFillableMakerAssetAmount, -// ), -// }; -// } else { -// return acc; -// } -// }, -// { orders: [] as SignedOrder[], remainingFillableMakerAssetAmounts: [] as BigNumber[] }, -// ); -// return result; -// } - -// /** -// * Given an array of orders and corresponding on-chain infos, return a subset of the orders -// * that are still fillable orders with their corresponding remainingFillableMakerAssetAmounts -// */ -// function getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain( -// inputOrders: SignedOrder[], -// ordersAndTradersInfo: OrderAndTraderInfo[], -// zrxAssetData: string, -// ): OrdersAndRemainingFillableMakerAssetAmounts { -// // iterate through the input orders and find the ones that are still fillable -// // for the orders that are still fillable, calculate the remaining fillable maker asset amount -// const result = _.reduce( -// inputOrders, -// (acc, order, index) => { -// // get current accumulations -// const { orders, remainingFillableMakerAssetAmounts } = acc; -// // get corresponding on-chain state for the order -// const { orderInfo, traderInfo } = ordersAndTradersInfo[index]; -// // if the order IS NOT fillable, do not add anything to the accumulations and continue iterating -// if (orderInfo.orderStatus !== OrderStatus.FILLABLE) { -// return acc; -// } -// // if the order IS fillable, add the order and calculate the remaining fillable amount -// const transferrableAssetAmount = BigNumber.min([traderInfo.makerAllowance, traderInfo.makerBalance]); -// const transferrableFeeAssetAmount = BigNumber.min([ -// traderInfo.makerZrxAllowance, -// traderInfo.makerZrxBalance, -// ]); -// const remainingTakerAssetAmount = order.takerAssetAmount.minus(orderInfo.orderTakerAssetFilledAmount); -// const remainingMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( -// order, -// remainingTakerAssetAmount, -// ); -// const remainingFillableCalculator = new RemainingFillableCalculator( -// order.makerFee, -// order.makerAssetAmount, -// order.makerAssetData === zrxAssetData, -// transferrableAssetAmount, -// transferrableFeeAssetAmount, -// remainingMakerAssetAmount, -// ); -// const remainingFillableAmount = remainingFillableCalculator.computeRemainingFillable(); -// return { -// orders: _.concat(orders, order), -// remainingFillableMakerAssetAmounts: _.concat( -// remainingFillableMakerAssetAmounts, -// remainingFillableAmount, -// ), -// }; -// }, -// { orders: [] as SignedOrder[], remainingFillableMakerAssetAmounts: [] as BigNumber[] }, -// ); -// return result; -// } - -// function getOpenAsksFromOrderbook(orderbookResponse: OrderbookResponse): SignedOrder[] { -// const asks = _.map(orderbookResponse.asks.records, apiOrder => apiOrder.order); -// const result = _.filter(asks, ask => orderUtils.isOpenOrder(ask)); -// return result; -// } diff --git a/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts b/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts new file mode 100644 index 0000000000..bf177b93b1 --- /dev/null +++ b/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts @@ -0,0 +1,80 @@ +import { APIOrder, HttpClient, OrderbookResponse } from '@0xproject/connect'; +import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { constants } from '../constants'; +import { + AssetBuyerError, + OrderFetcher, + OrderFetcherRequest, + OrderFetcherResponse, + SignedOrderWithRemainingFillableMakerAssetAmount, +} from '../types'; +import { assert } from '../utils/assert'; +import { orderUtils } from '../utils/order_utils'; + +export class StandardRelayerAPIOrderFetcher implements OrderFetcher { + public readonly apiUrl: string; + private _sraClient: HttpClient; + /** + * Given an array of APIOrder objects from a standard relayer api, return an array + * of SignedOrderWithRemainingFillableMakerAssetAmounts + */ + private static _getSignedOrderWithRemainingFillableMakerAssetAmountFromApi( + apiOrders: APIOrder[], + ): SignedOrderWithRemainingFillableMakerAssetAmount[] { + const result = _.map(apiOrders, apiOrder => { + const { order, metaData } = apiOrder; + // calculate remainingFillableMakerAssetAmount from api metadata, else assume order is completely fillable + const remainingFillableTakerAssetAmount = _.get( + metaData, + 'remainingTakerAssetAmount', + order.takerAssetAmount, + ); + const remainingFillableMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( + order, + remainingFillableTakerAssetAmount, + ); + const newOrder = { + ...order, + remainingFillableMakerAssetAmount, + }; + return newOrder; + }); + return result; + } + /** + * Instantiates a new StandardRelayerAPIOrderFetcher instance + * @param apiUrl The relayer API base HTTP url you would like to interact with. + * @return An instance of StandardRelayerAPIOrderFetcher + */ + constructor(apiUrl: string) { + assert.isWebUri('apiUrl', apiUrl); + this.apiUrl = apiUrl; + this._sraClient = new HttpClient(apiUrl); + } + /** + * Given an object that conforms to OrderFetcherRequest, return the corresponding OrderFetcherResponse that satisfies the request. + * @param orderFetchRequest An instance of OrderFetcherRequest. See type for more information. + * @return An instance of OrderFetcherResponse. See type for more information. + */ + public async fetchOrdersAsync(orderFetchRequest: OrderFetcherRequest): Promise { + const { makerAssetData, takerAssetData, networkId } = orderFetchRequest; + const orderbookRequest = { baseAssetData: makerAssetData, quoteAssetData: takerAssetData }; + const requestOpts = { networkId }; + let orderbook: OrderbookResponse; + try { + orderbook = await this._sraClient.getOrderbookAsync(orderbookRequest, requestOpts); + } catch (err) { + throw new Error(AssetBuyerError.StandardRelayerApiError); + } + const apiOrders = orderbook.asks.records; + const orders = StandardRelayerAPIOrderFetcher._getSignedOrderWithRemainingFillableMakerAssetAmountFromApi( + apiOrders, + ); + return { + orders, + }; + } +} diff --git a/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts b/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts index 04c5355eb1..f1116a80fa 100644 --- a/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts +++ b/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts @@ -165,6 +165,10 @@ function unbundleOrdersWithAmounts( const { remainingFillableMakerAssetAmount, ...order } = orderWithAmount; // if we are still missing a remainingFillableMakerAssetAmount, assume the order is completely fillable const newRemainingAmount = remainingFillableMakerAssetAmount || order.makerAssetAmount; + // if remaining amount is less than or equal to zero, do not add it + if (newRemainingAmount.lte(constants.ZERO_AMOUNT)) { + return acc; + } const newAcc = { orders: _.concat(orders, order), remainingFillableMakerAssetAmounts: _.concat(remainingFillableMakerAssetAmounts, newRemainingAmount), From 4e59be9afce49fca8b396c305d5271c8299c85c1 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sat, 15 Sep 2018 14:08:19 +0200 Subject: [PATCH 10/47] Implement ProvidedOrderFetcher --- .../order_fetchers/provided_order_fetcher.ts | 41 +++++++++++++++++++ .../standard_relayer_api_order_fetcher.ts | 1 + packages/asset-buyer/src/utils/assert.ts | 7 +++- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts diff --git a/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts b/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts new file mode 100644 index 0000000000..29b03f1c41 --- /dev/null +++ b/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts @@ -0,0 +1,41 @@ +import { schemas } from '@0xproject/json-schemas'; +import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { constants } from '../constants'; +import { + AssetBuyerError, + OrderFetcher, + OrderFetcherRequest, + OrderFetcherResponse, + SignedOrderWithRemainingFillableMakerAssetAmount, +} from '../types'; +import { assert } from '../utils/assert'; +import { orderUtils } from '../utils/order_utils'; + +export class ProvidedOrderFetcher implements OrderFetcher { + public readonly providedOrders: SignedOrder[]; + /** + * Instantiates a new ProvidedOrderFetcher instance + * @param providedOrders An array of objects that conform to SignedOrder to fetch from. + * @return An instance of ProvidedOrderFetcher + */ + constructor(providedOrders: SignedOrder[]) { + assert.doesConformToSchema('providedOrders', providedOrders, schemas.signedOrdersSchema); + this.providedOrders = providedOrders; + } + /** + * Given an object that conforms to OrderFetcherRequest, return the corresponding OrderFetcherResponse that satisfies the request. + * @param orderFetchRequest An instance of OrderFetcherRequest. See type for more information. + * @return An instance of OrderFetcherResponse. See type for more information. + */ + public async fetchOrdersAsync(orderFetchRequest: OrderFetcherRequest): Promise { + assert.isValidOrderFetcherRequest('orderFetchRequest', orderFetchRequest); + const { makerAssetData, takerAssetData } = orderFetchRequest; + const orders = _.filter(this.providedOrders, order => { + return order.makerAssetData === makerAssetData && order.takerAssetData === takerAssetData; + }); + return { orders }; + } +} diff --git a/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts b/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts index bf177b93b1..5688daf69e 100644 --- a/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts +++ b/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts @@ -60,6 +60,7 @@ export class StandardRelayerAPIOrderFetcher implements OrderFetcher { * @return An instance of OrderFetcherResponse. See type for more information. */ public async fetchOrdersAsync(orderFetchRequest: OrderFetcherRequest): Promise { + assert.isValidOrderFetcherRequest('orderFetchRequest', orderFetchRequest); const { makerAssetData, takerAssetData, networkId } = orderFetchRequest; const orderbookRequest = { baseAssetData: makerAssetData, quoteAssetData: takerAssetData }; const requestOpts = { networkId }; diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts index c4d6114771..0085ca41e8 100644 --- a/packages/asset-buyer/src/utils/assert.ts +++ b/packages/asset-buyer/src/utils/assert.ts @@ -2,7 +2,7 @@ import { assert as sharedAssert } from '@0xproject/assert'; import { schemas } from '@0xproject/json-schemas'; import * as _ from 'lodash'; -import { BuyQuote, OrderFetcher } from '../types'; +import { BuyQuote, OrderFetcher, OrderFetcherRequest } from '../types'; export const assert = { ...sharedAssert, @@ -20,4 +20,9 @@ export const assert = { isValidOrderFetcher(variableName: string, orderFetcher: OrderFetcher): void { sharedAssert.isFunction(`${variableName}.fetchOrdersAsync`, orderFetcher.fetchOrdersAsync); }, + isValidOrderFetcherRequest(variableName: string, orderFetcherRequest: OrderFetcherRequest): void { + sharedAssert.isHexString(`${variableName}.makerAssetData`, orderFetcherRequest.makerAssetData); + sharedAssert.isHexString(`${variableName}.takerAssetData`, orderFetcherRequest.takerAssetData); + sharedAssert.isNumber(`${variableName}.networkId`, orderFetcherRequest.networkId); + }, }; From dfe58e4975e0ad3edf08fdc6d3441e83171096a9 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sat, 15 Sep 2018 14:08:34 +0200 Subject: [PATCH 11/47] Delete old test --- .../test/forwarder_helper_impl_test.ts | 136 ------------------ 1 file changed, 136 deletions(-) delete mode 100644 packages/asset-buyer/test/forwarder_helper_impl_test.ts diff --git a/packages/asset-buyer/test/forwarder_helper_impl_test.ts b/packages/asset-buyer/test/forwarder_helper_impl_test.ts deleted file mode 100644 index b7c014402f..0000000000 --- a/packages/asset-buyer/test/forwarder_helper_impl_test.ts +++ /dev/null @@ -1,136 +0,0 @@ -// import { testOrderFactory } from '@0xproject/order-utils/lib/test/utils/test_order_factory'; -// import { BigNumber } from '@0xproject/utils'; -// import * as chai from 'chai'; -// import * as _ from 'lodash'; -// import 'mocha'; - -// import { ForwarderHelperImpl, ForwarderHelperImplConfig } from '@0xproject/asset-buyer/src/asset_buyer'; -// import { ForwarderHelperError } from '../src/types'; - -// import { chaiSetup } from './utils/chai_setup'; - -// chaiSetup.configure(); -// const expect = chai.expect; - -// describe('ForwarderHelperImpl', () => { -// // rate: 2 takerAsset / makerAsset -// const testOrder1 = testOrderFactory.generateTestSignedOrder({ -// makerAssetAmount: new BigNumber(100), -// takerAssetAmount: new BigNumber(200), -// }); -// // rate: 1 takerAsset / makerAsset -// const testOrder2 = testOrderFactory.generateTestSignedOrder({ -// makerAssetAmount: new BigNumber(100), -// takerAssetAmount: new BigNumber(100), -// }); -// // rate: 3 takerAsset / makerAsset -// const testOrder3 = testOrderFactory.generateTestSignedOrder({ -// makerAssetAmount: new BigNumber(100), -// takerAssetAmount: new BigNumber(300), -// takerFee: new BigNumber(1), -// }); -// // rate: 3 WETH / ZRX -// const testFeeOrder1 = testOrderFactory.generateTestSignedOrder({ -// makerAssetAmount: new BigNumber(100), -// takerAssetAmount: new BigNumber(300), -// }); -// // rate: 2 WETH / ZRX -// const testFeeOrder2 = testOrderFactory.generateTestSignedOrder({ -// makerAssetAmount: new BigNumber(100), -// takerAssetAmount: new BigNumber(200), -// }); -// // rate: 1 WETH / ZRX -// const testFeeOrder3 = testOrderFactory.generateTestSignedOrder({ -// makerAssetAmount: new BigNumber(100), -// takerAssetAmount: new BigNumber(100), -// }); -// const inputForwarderHelperConfig: ForwarderHelperImplConfig = { -// orders: [testOrder1, testOrder2, testOrder3], -// feeOrders: [testFeeOrder1, testFeeOrder2, testFeeOrder3], -// remainingFillableMakerAssetAmounts: [new BigNumber(1), new BigNumber(2), new BigNumber(3)], -// remainingFillableFeeAmounts: [new BigNumber(4), new BigNumber(5), new BigNumber(6)], -// }; -// describe('#constructor', () => { -// const inputForwarderHelperConfigNoRemainingAmounts: ForwarderHelperImplConfig = { -// orders: [testOrder1, testOrder2, testOrder3], -// feeOrders: [testFeeOrder1, testFeeOrder2, testFeeOrder3], -// }; -// it('sorts orders', () => { -// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); -// expect(forwarderHelper.config.orders).deep.equals([testOrder2, testOrder1, testOrder3]); -// }); -// it('sorts fee orders', () => { -// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); -// expect(forwarderHelper.config.feeOrders).deep.equals([testFeeOrder3, testFeeOrder2, testFeeOrder1]); -// }); -// it('sorts remainingFillableMakerAssetAmounts', () => { -// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); -// expect(forwarderHelper.config.remainingFillableMakerAssetAmounts).to.be.not.undefined(); -// expect(_.nth(forwarderHelper.config.remainingFillableMakerAssetAmounts, 0)).to.bignumber.equal( -// new BigNumber(2), -// ); -// expect(_.nth(forwarderHelper.config.remainingFillableMakerAssetAmounts, 1)).to.bignumber.equal( -// new BigNumber(1), -// ); -// expect(_.nth(forwarderHelper.config.remainingFillableMakerAssetAmounts, 2)).to.bignumber.equal( -// new BigNumber(3), -// ); -// }); -// it('sorts remainingFillableFeeAmounts', () => { -// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); -// expect(forwarderHelper.config.remainingFillableFeeAmounts).to.be.not.undefined(); -// expect(_.nth(forwarderHelper.config.remainingFillableFeeAmounts, 0)).to.bignumber.equal(new BigNumber(6)); -// expect(_.nth(forwarderHelper.config.remainingFillableFeeAmounts, 1)).to.bignumber.equal(new BigNumber(5)); -// expect(_.nth(forwarderHelper.config.remainingFillableFeeAmounts, 2)).to.bignumber.equal(new BigNumber(4)); -// }); -// it('remainingFillableMakerAssetAmounts is undefined if none provided', () => { -// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfigNoRemainingAmounts); -// expect(forwarderHelper.config.remainingFillableMakerAssetAmounts).to.be.undefined(); -// }); -// it('remainingFillableFeeAmounts is undefined if none provided', () => { -// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfigNoRemainingAmounts); -// expect(forwarderHelper.config.remainingFillableFeeAmounts).to.be.undefined(); -// }); -// }); -// describe('#getMarketBuyOrdersInfo', () => { -// it('throws if not enough makerAsset liquidity', () => { -// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); -// expect(() => { -// // request for 6 makerAsset units, because we have exactly 6 available we should throw because there is a built in slippage buffer -// forwarderHelper.getMarketBuyOrdersInfo({ -// makerAssetFillAmount: new BigNumber(6), -// }); -// }).to.throw(ForwarderHelperError.InsufficientMakerAssetLiquidity); -// }); -// it('throws if not enough ZRX liquidity', () => { -// const inputForwarderHelperConfigNoFees: ForwarderHelperImplConfig = { -// orders: [testOrder1, testOrder2, testOrder3], -// feeOrders: [], -// }; -// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfigNoFees); -// expect(() => { -// // request for 4 makerAsset units, we need fees but no fee orders exist, show we should throw -// forwarderHelper.getMarketBuyOrdersInfo({ -// makerAssetFillAmount: new BigNumber(250), -// }); -// }).to.throw(ForwarderHelperError.InsufficientZrxLiquidity); -// }); -// it('passes the makerAssetFillAmount from the request to the info response', () => { -// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); -// const makerAssetFillAmount = new BigNumber(4); -// const info = forwarderHelper.getMarketBuyOrdersInfo({ -// makerAssetFillAmount, -// }); -// expect(info.makerAssetFillAmount).to.bignumber.equal(makerAssetFillAmount); -// }); -// it('passes the feePercentage from the request to the info response', () => { -// const forwarderHelper = new ForwarderHelperImpl(inputForwarderHelperConfig); -// const feePercentage = new BigNumber(0.2); -// const info = forwarderHelper.getMarketBuyOrdersInfo({ -// makerAssetFillAmount: new BigNumber(4), -// feePercentage, -// }); -// expect(info.feePercentage).to.bignumber.equal(feePercentage); -// }); -// }); -// }); From c692115cdcbc761c0c4074db79f8c127b92da7e0 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sat, 15 Sep 2018 14:14:19 +0200 Subject: [PATCH 12/47] Fix lint errors --- packages/asset-buyer/src/asset_buyer.ts | 7 +++---- .../src/order_fetchers/provided_order_fetcher.ts | 11 +---------- .../standard_relayer_api_order_fetcher.ts | 5 +---- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 9cbdd8df63..3b603d9291 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -1,6 +1,5 @@ import { ContractWrappers } from '@0xproject/contract-wrappers'; -import { assetDataUtils, marketUtils } from '@0xproject/order-utils'; -import { SignedOrder } from '@0xproject/types'; +import { assetDataUtils } from '@0xproject/order-utils'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Provider } from 'ethereum-types'; @@ -29,7 +28,7 @@ export class AssetBuyer { public readonly orderFetcher: OrderFetcher; public readonly networkId: number; public readonly orderRefreshIntervalMs: number; - private _contractWrappers: ContractWrappers; + private readonly _contractWrappers: ContractWrappers; private _lastRefreshTimeIfExists?: number; private _currentOrdersAndFillableAmountsIfExists?: AssetBuyerOrdersAndFillableAmounts; /** @@ -188,7 +187,7 @@ export class AssetBuyer { const requests = [targetOrderFetcherRequest, feeOrderFetcherRequest]; // fetch orders and possible fillable amounts const [targetOrderFetcherResponse, feeOrderFetcherResponse] = await Promise.all( - _.map(requests, request => this.orderFetcher.fetchOrdersAsync(request)), + _.map(requests, async request => this.orderFetcher.fetchOrdersAsync(request)), ); // process the responses into one object const ordersAndFillableAmounts = await orderFetcherResponseProcessor.processAsync( diff --git a/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts b/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts index 29b03f1c41..7a7ffcdfe6 100644 --- a/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts +++ b/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts @@ -1,18 +1,9 @@ import { schemas } from '@0xproject/json-schemas'; import { SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; -import { constants } from '../constants'; -import { - AssetBuyerError, - OrderFetcher, - OrderFetcherRequest, - OrderFetcherResponse, - SignedOrderWithRemainingFillableMakerAssetAmount, -} from '../types'; +import { OrderFetcher, OrderFetcherRequest, OrderFetcherResponse } from '../types'; import { assert } from '../utils/assert'; -import { orderUtils } from '../utils/order_utils'; export class ProvidedOrderFetcher implements OrderFetcher { public readonly providedOrders: SignedOrder[]; diff --git a/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts b/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts index 5688daf69e..9fcd16ca19 100644 --- a/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts +++ b/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts @@ -1,9 +1,6 @@ import { APIOrder, HttpClient, OrderbookResponse } from '@0xproject/connect'; -import { SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; -import { constants } from '../constants'; import { AssetBuyerError, OrderFetcher, @@ -16,7 +13,7 @@ import { orderUtils } from '../utils/order_utils'; export class StandardRelayerAPIOrderFetcher implements OrderFetcher { public readonly apiUrl: string; - private _sraClient: HttpClient; + private readonly _sraClient: HttpClient; /** * Given an array of APIOrder objects from a standard relayer api, return an array * of SignedOrderWithRemainingFillableMakerAssetAmounts From d57619b5db8d4b5a8d31f095d8dfef04faef44c9 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sat, 15 Sep 2018 14:16:08 +0200 Subject: [PATCH 13/47] Export the order fetchers --- packages/asset-buyer/src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/asset-buyer/src/index.ts b/packages/asset-buyer/src/index.ts index 67ad06084b..efd3523fd9 100644 --- a/packages/asset-buyer/src/index.ts +++ b/packages/asset-buyer/src/index.ts @@ -3,6 +3,8 @@ export { SignedOrder } from '@0xproject/types'; export { BigNumber } from '@0xproject/utils'; export { AssetBuyer } from './asset_buyer'; +export { ProvidedOrderFetcher } from './order_fetchers/provided_order_fetcher'; +export { StandardRelayerAPIOrderFetcher } from './order_fetchers/standard_relayer_api_order_fetcher'; export { AssetBuyerError, BuyQuote, From af40989f5f6b606172370d8e878c51ce2e7382eb Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sat, 15 Sep 2018 14:47:44 +0200 Subject: [PATCH 14/47] Add factory method on AssetBuyer for provided orders --- packages/asset-buyer/src/asset_buyer.ts | 24 +++++++++++++++++++++++- packages/asset-buyer/src/utils/assert.ts | 16 ++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 3b603d9291..a68658d22c 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -1,5 +1,6 @@ import { ContractWrappers } from '@0xproject/contract-wrappers'; -import { assetDataUtils } from '@0xproject/order-utils'; +import { schemas } from '@0xproject/json-schemas'; +import { assetDataUtils, SignedOrder } from '@0xproject/order-utils'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Provider } from 'ethereum-types'; @@ -13,6 +14,7 @@ import { OrderFetcher, OrderFetcherResponse, } from './types'; +import { ProvidedOrderFetcher } from './order_fetchers/provided_order_fetcher'; import { assert } from './utils/assert'; import { buyQuoteCalculator } from './utils/buy_quote_calculator'; import { orderFetcherResponseProcessor } from './utils/order_fetcher_response_processor'; @@ -31,6 +33,26 @@ export class AssetBuyer { private readonly _contractWrappers: ContractWrappers; private _lastRefreshTimeIfExists?: number; private _currentOrdersAndFillableAmountsIfExists?: AssetBuyerOrdersAndFillableAmounts; + public static getAssetBuyerForProvidedOrders( + provider: Provider, + orders: SignedOrder[], + feeOrders: SignedOrder[] = [], + networkId: number = constants.MAINNET_NETWORK_ID, + orderRefreshIntervalMs: number = DEFAULT_ORDER_REFRESH_INTERVAL_MS, + ): AssetBuyer { + assert.isWeb3Provider('provider', provider); + assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); + assert.doesConformToSchema('feeOrders', feeOrders, schemas.signedOrdersSchema); + assert.isNumber('networkId', networkId); + assert.isNumber('orderRefreshIntervalMs', orderRefreshIntervalMs); + assert.areValidProvidedOrders('orders', orders); + assert.areValidProvidedOrders('feeOrders', feeOrders); + assert.assert(orders.length !== 0, `Expected orders to contain at least one order`); + const assetData = orders[0].makerAssetData; + const orderFetcher = new ProvidedOrderFetcher(_.concat(orders, feeOrders)); + const assetBuyer = new AssetBuyer(provider, assetData, orderFetcher, networkId, orderRefreshIntervalMs); + return assetBuyer; + } /** * Instantiates a new AssetBuyer instance * @param provider The Provider instance you would like to use for interacting with the Ethereum network. diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts index 0085ca41e8..edc90608ce 100644 --- a/packages/asset-buyer/src/utils/assert.ts +++ b/packages/asset-buyer/src/utils/assert.ts @@ -1,5 +1,6 @@ import { assert as sharedAssert } from '@0xproject/assert'; import { schemas } from '@0xproject/json-schemas'; +import { SignedOrder } from '@0xproject/types'; import * as _ from 'lodash'; import { BuyQuote, OrderFetcher, OrderFetcherRequest } from '../types'; @@ -25,4 +26,19 @@ export const assert = { sharedAssert.isHexString(`${variableName}.takerAssetData`, orderFetcherRequest.takerAssetData); sharedAssert.isNumber(`${variableName}.networkId`, orderFetcherRequest.networkId); }, + areValidProvidedOrders(variableName: string, orders: SignedOrder[]): void { + if (orders.length === 0) { + return; + } + const makerAssetData = orders[0].makerAssetData; + const takerAssetData = orders[0].takerAssetData; + const filteredOrders = _.filter( + orders, + order => order.makerAssetData === makerAssetData && order.takerAssetData === takerAssetData, + ); + sharedAssert.assert( + orders.length === filteredOrders.length, + `Expected all orders in ${variableName} to have the same makerAssetData and takerAssetData.`, + ); + }, }; From 9ec2b5a2d53bad2820b297f90bd16528f87537eb Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sat, 15 Sep 2018 14:53:04 +0200 Subject: [PATCH 15/47] Add factory method on AssetBuyer for specific token address --- packages/asset-buyer/src/asset_buyer.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index a68658d22c..bb3d6d77c6 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -7,6 +7,8 @@ import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; import { constants } from './constants'; +import { ProvidedOrderFetcher } from './order_fetchers/provided_order_fetcher'; +import { StandardRelayerAPIOrderFetcher } from './order_fetchers/standard_relayer_api_order_fetcher'; import { AssetBuyerError, AssetBuyerOrdersAndFillableAmounts, @@ -14,7 +16,7 @@ import { OrderFetcher, OrderFetcherResponse, } from './types'; -import { ProvidedOrderFetcher } from './order_fetchers/provided_order_fetcher'; + import { assert } from './utils/assert'; import { buyQuoteCalculator } from './utils/buy_quote_calculator'; import { orderFetcherResponseProcessor } from './utils/order_fetcher_response_processor'; @@ -53,6 +55,23 @@ export class AssetBuyer { const assetBuyer = new AssetBuyer(provider, assetData, orderFetcher, networkId, orderRefreshIntervalMs); return assetBuyer; } + public static getAssetBuyerForERC20TokenAddress( + provider: Provider, + tokenAddress: string, + sraApiUrl: string, + networkId: number = constants.MAINNET_NETWORK_ID, + orderRefreshIntervalMs: number = DEFAULT_ORDER_REFRESH_INTERVAL_MS, + ): AssetBuyer { + assert.isWeb3Provider('provider', provider); + assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.isWebUri('sraApiUrl', sraApiUrl); + assert.isNumber('networkId', networkId); + assert.isNumber('orderRefreshIntervalMs', orderRefreshIntervalMs); + const assetData = assetDataUtils.encodeERC20AssetData(tokenAddress); + const orderFetcher = new StandardRelayerAPIOrderFetcher(sraApiUrl); + const assetBuyer = new AssetBuyer(provider, assetData, orderFetcher, networkId, orderRefreshIntervalMs); + return assetBuyer; + } /** * Instantiates a new AssetBuyer instance * @param provider The Provider instance you would like to use for interacting with the Ethereum network. From a22d2dc7ee3a1f354074009f3ff117b14050f5c6 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sat, 15 Sep 2018 15:05:27 +0200 Subject: [PATCH 16/47] Add factory method for specific assetData to buy and add comments --- packages/asset-buyer/src/asset_buyer.ts | 55 ++++++++++++++++++- .../standard_relayer_api_order_fetcher.ts | 2 +- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index bb3d6d77c6..fa1118e181 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -35,6 +35,16 @@ export class AssetBuyer { private readonly _contractWrappers: ContractWrappers; private _lastRefreshTimeIfExists?: number; private _currentOrdersAndFillableAmountsIfExists?: AssetBuyerOrdersAndFillableAmounts; + /** + * Instantiates a new AssetBuyer instance + * @param provider The Provider instance you would like to use for interacting with the Ethereum network. + * @param orders A non-empty array of objects that conform to SignedOrder. All orders must have the same makerAssetData and takerAssetData (WETH). + * @param feeOrders A array of objects that conform to SignedOrder. All orders must have the same makerAssetData (ZRX) and takerAssetData (WETH). Defaults to an empty array. + * @param networkId The ethereum network id. Defaults to 1 (mainnet). + * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. + * Defaults to 10000ms (10s). + * @return An instance of AssetBuyer + */ public static getAssetBuyerForProvidedOrders( provider: Provider, orders: SignedOrder[], @@ -55,6 +65,42 @@ export class AssetBuyer { const assetBuyer = new AssetBuyer(provider, assetData, orderFetcher, networkId, orderRefreshIntervalMs); return assetBuyer; } + /** + * Instantiates a new AssetBuyer instance + * @param provider The Provider instance you would like to use for interacting with the Ethereum network. + * @param assetData The assetData that identifies the desired asset to buy. + * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from. + * @param networkId The ethereum network id. Defaults to 1 (mainnet). + * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. + * Defaults to 10000ms (10s). + * @return An instance of AssetBuyer + */ + public static getAssetBuyerForAssetData( + provider: Provider, + assetData: string, + sraApiUrl: string, + networkId: number = constants.MAINNET_NETWORK_ID, + orderRefreshIntervalMs: number = DEFAULT_ORDER_REFRESH_INTERVAL_MS, + ): AssetBuyer { + assert.isWeb3Provider('provider', provider); + assert.isHexString('assetData', assetData); + assert.isWebUri('sraApiUrl', sraApiUrl); + assert.isNumber('networkId', networkId); + assert.isNumber('orderRefreshIntervalMs', orderRefreshIntervalMs); + const orderFetcher = new StandardRelayerAPIOrderFetcher(sraApiUrl); + const assetBuyer = new AssetBuyer(provider, assetData, orderFetcher, networkId, orderRefreshIntervalMs); + return assetBuyer; + } + /** + * Instantiates a new AssetBuyer instance + * @param provider The Provider instance you would like to use for interacting with the Ethereum network. + * @param tokenAddress The ERC20 token address that identifies the desired asset to buy. + * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from. + * @param networkId The ethereum network id. Defaults to 1 (mainnet). + * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. + * Defaults to 10000ms (10s). + * @return An instance of AssetBuyer + */ public static getAssetBuyerForERC20TokenAddress( provider: Provider, tokenAddress: string, @@ -68,8 +114,13 @@ export class AssetBuyer { assert.isNumber('networkId', networkId); assert.isNumber('orderRefreshIntervalMs', orderRefreshIntervalMs); const assetData = assetDataUtils.encodeERC20AssetData(tokenAddress); - const orderFetcher = new StandardRelayerAPIOrderFetcher(sraApiUrl); - const assetBuyer = new AssetBuyer(provider, assetData, orderFetcher, networkId, orderRefreshIntervalMs); + const assetBuyer = AssetBuyer.getAssetBuyerForAssetData( + provider, + assetData, + sraApiUrl, + networkId, + orderRefreshIntervalMs, + ); return assetBuyer; } /** diff --git a/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts b/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts index 9fcd16ca19..3b082b68d6 100644 --- a/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts +++ b/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts @@ -43,7 +43,7 @@ export class StandardRelayerAPIOrderFetcher implements OrderFetcher { } /** * Instantiates a new StandardRelayerAPIOrderFetcher instance - * @param apiUrl The relayer API base HTTP url you would like to interact with. + * @param apiUrl The standard relayer API base HTTP url you would like to source orders from. * @return An instance of StandardRelayerAPIOrderFetcher */ constructor(apiUrl: string) { From e46807c28b0c915130b1b5e49d1c9f39f12751ae Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 18 Sep 2018 13:55:31 +0200 Subject: [PATCH 17/47] Fix order creation and fill flow in Portal --- packages/website/package.json | 1 + packages/website/ts/blockchain.ts | 2 +- .../website/ts/components/inputs/token_amount_input.tsx | 2 +- yarn.lock | 6 +++--- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/website/package.json b/packages/website/package.json index b602a31164..f0a44b97bd 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -20,6 +20,7 @@ "dependencies": { "0x.js": "^0.38.6", "@0xproject/contract-wrappers": "^1.0.1", + "@0xproject/json-schemas": "^1.0.1", "@0xproject/order-utils": "^1.0.1", "@0xproject/react-docs": "^1.0.8", "@0xproject/react-shared": "^1.0.9", diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index d3f2f3f7b8..632a630162 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -228,7 +228,7 @@ export class Blockchain { const tokenSymbolToAddressOverrides = tokenAddressOverrides[this.networkId]; let isTokenAddressInOverrides = false; if (!_.isUndefined(tokenSymbolToAddressOverrides)) { - isTokenAddressInOverrides = _.keys(tokenSymbolToAddressOverrides).includes(tokenAddress); + isTokenAddressInOverrides = _.values(tokenSymbolToAddressOverrides).includes(tokenAddress); } return !_.isUndefined(tokenIfExists) || isTokenAddressInOverrides; } diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx index 93ef516cfc..db093fb68c 100644 --- a/packages/website/ts/components/inputs/token_amount_input.tsx +++ b/packages/website/ts/components/inputs/token_amount_input.tsx @@ -111,7 +111,7 @@ export class TokenAmountInput extends React.Component Insufficient allowance.{' '} Set allowance diff --git a/yarn.lock b/yarn.lock index aa30946a86..7464a9c5d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5296,9 +5296,9 @@ ethereumjs-wallet@~0.6.0: utf8 "^3.0.0" uuid "^3.3.2" -ethers@0xproject/ethers.js#eip-838-reasons, ethers@3.0.22: - version "3.0.18" - resolved "https://codeload.github.com/0xproject/ethers.js/tar.gz/b91342bd200d142af0165d6befddf783c8ae8447" +ethers@3.0.22: + version "3.0.22" + resolved "https://registry.npmjs.org/ethers/-/ethers-3.0.22.tgz#7fab1ea16521705837aa43c15831877b2716b436" dependencies: aes-js "3.0.0" bn.js "^4.4.0" From f6e1bf78c8c73221e06b3356c37a269265221275 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 18 Sep 2018 14:33:47 +0200 Subject: [PATCH 18/47] Add yarn.lock --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7464a9c5d7..aa30946a86 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5296,9 +5296,9 @@ ethereumjs-wallet@~0.6.0: utf8 "^3.0.0" uuid "^3.3.2" -ethers@3.0.22: - version "3.0.22" - resolved "https://registry.npmjs.org/ethers/-/ethers-3.0.22.tgz#7fab1ea16521705837aa43c15831877b2716b436" +ethers@0xproject/ethers.js#eip-838-reasons, ethers@3.0.22: + version "3.0.18" + resolved "https://codeload.github.com/0xproject/ethers.js/tar.gz/b91342bd200d142af0165d6befddf783c8ae8447" dependencies: aes-js "3.0.0" bn.js "^4.4.0" From 65f709d50ae559ec1b17f3c44f3d87e5d3cc4da7 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 18 Sep 2018 16:07:08 +0200 Subject: [PATCH 19/47] Move all constants to own file --- packages/asset-buyer/src/asset_buyer.ts | 19 +++++++------------ packages/asset-buyer/src/constants.ts | 4 ++++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index fa1118e181..76b96427e9 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -21,11 +21,6 @@ import { assert } from './utils/assert'; import { buyQuoteCalculator } from './utils/buy_quote_calculator'; import { orderFetcherResponseProcessor } from './utils/order_fetcher_response_processor'; -const SLIPPAGE_PERCENTAGE = 0.2; // 20% slippage protection, possibly move this into request interface -const DEFAULT_ORDER_REFRESH_INTERVAL_MS = 10000; // 10 seconds -const DEFAULT_FEE_PERCENTAGE = 0; -const ETHER_TOKEN_DECIMALS = 18; - export class AssetBuyer { public readonly provider: Provider; public readonly assetData: string; @@ -50,7 +45,7 @@ export class AssetBuyer { orders: SignedOrder[], feeOrders: SignedOrder[] = [], networkId: number = constants.MAINNET_NETWORK_ID, - orderRefreshIntervalMs: number = DEFAULT_ORDER_REFRESH_INTERVAL_MS, + orderRefreshIntervalMs: number = constants.DEFAULT_ORDER_REFRESH_INTERVAL_MS, ): AssetBuyer { assert.isWeb3Provider('provider', provider); assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); @@ -80,7 +75,7 @@ export class AssetBuyer { assetData: string, sraApiUrl: string, networkId: number = constants.MAINNET_NETWORK_ID, - orderRefreshIntervalMs: number = DEFAULT_ORDER_REFRESH_INTERVAL_MS, + orderRefreshIntervalMs: number = constants.DEFAULT_ORDER_REFRESH_INTERVAL_MS, ): AssetBuyer { assert.isWeb3Provider('provider', provider); assert.isHexString('assetData', assetData); @@ -106,7 +101,7 @@ export class AssetBuyer { tokenAddress: string, sraApiUrl: string, networkId: number = constants.MAINNET_NETWORK_ID, - orderRefreshIntervalMs: number = DEFAULT_ORDER_REFRESH_INTERVAL_MS, + orderRefreshIntervalMs: number = constants.DEFAULT_ORDER_REFRESH_INTERVAL_MS, ): AssetBuyer { assert.isWeb3Provider('provider', provider); assert.isETHAddressHex('tokenAddress', tokenAddress); @@ -138,7 +133,7 @@ export class AssetBuyer { assetData: string, orderFetcher: OrderFetcher, networkId: number = constants.MAINNET_NETWORK_ID, - orderRefreshIntervalMs: number = DEFAULT_ORDER_REFRESH_INTERVAL_MS, + orderRefreshIntervalMs: number = constants.DEFAULT_ORDER_REFRESH_INTERVAL_MS, ) { assert.isWeb3Provider('provider', provider); assert.isString('assetData', assetData); @@ -165,7 +160,7 @@ export class AssetBuyer { */ public async getBuyQuoteAsync( assetBuyAmount: BigNumber, - feePercentage: number = DEFAULT_FEE_PERCENTAGE, + feePercentage: number = constants.DEFAULT_FEE_PERCENTAGE, forceOrderRefresh: boolean = false, ): Promise { assert.isBigNumber('assetBuyAmount', assetBuyAmount); @@ -196,7 +191,7 @@ export class AssetBuyer { ordersAndFillableAmounts, assetBuyAmount, feePercentage, - SLIPPAGE_PERCENTAGE, + constants.DEFAULT_SLIPPAGE_PERCENTAGE, ); return buyQuote; } @@ -244,7 +239,7 @@ export class AssetBuyer { // TODO: critical // update the forwarder wrapper to take in feePercentage as a number instead of a BigNumber, verify with Amir that this is being done correctly const feePercentageBigNumber = !_.isUndefined(feePercentage) - ? Web3Wrapper.toBaseUnitAmount(new BigNumber(1), ETHER_TOKEN_DECIMALS).mul(feePercentage) + ? Web3Wrapper.toBaseUnitAmount(new BigNumber(1), constants.ETHER_TOKEN_DECIMALS).mul(feePercentage) : constants.ZERO_AMOUNT; const txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync( orders, diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts index 5785e705b3..e20bcc6ed9 100644 --- a/packages/asset-buyer/src/constants.ts +++ b/packages/asset-buyer/src/constants.ts @@ -4,4 +4,8 @@ export const constants = { ZERO_AMOUNT: new BigNumber(0), NULL_ADDRESS: '0x0000000000000000000000000000000000000000', MAINNET_NETWORK_ID: 1, + DEFAULT_SLIPPAGE_PERCENTAGE: 0.2, // 20% slippage protection + DEFAULT_ORDER_REFRESH_INTERVAL_MS: 10000, // 10 seconds + DEFAULT_FEE_PERCENTAGE: 0, + ETHER_TOKEN_DECIMALS: 18, }; From 38afd108f8e1dbb56655260a415050f44754c630 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 18 Sep 2018 16:07:47 +0200 Subject: [PATCH 20/47] Fix order expired bug --- .../asset-buyer/src/utils/order_fetcher_response_processor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts b/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts index f1116a80fa..f700cadf9b 100644 --- a/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts +++ b/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts @@ -90,7 +90,7 @@ function filterOutExpiredAndNonOpenOrders( orders: SignedOrderWithRemainingFillableMakerAssetAmount[], ): SignedOrderWithRemainingFillableMakerAssetAmount[] { const result = _.filter(orders, order => { - return orderUtils.isOpenOrder(order) && orderUtils.isOrderExpired(order); + return orderUtils.isOpenOrder(order) && !orderUtils.isOrderExpired(order); }); return result; } From b947609ff18f518ac36435083a97604b983a3081 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 18 Sep 2018 16:26:50 +0200 Subject: [PATCH 21/47] Restrict orders field of AssetBuyerOrdersAndFillableAmounts to SignedOrder for consistency --- packages/asset-buyer/src/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index 0da30f48d5..75773f0e81 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -65,8 +65,8 @@ export enum AssetBuyerError { } export interface AssetBuyerOrdersAndFillableAmounts { - orders: SignedOrderWithRemainingFillableMakerAssetAmount[]; - feeOrders: SignedOrderWithRemainingFillableMakerAssetAmount[]; + orders: SignedOrder[]; + feeOrders: SignedOrder[]; remainingFillableMakerAssetAmounts: BigNumber[]; remainingFillableFeeAmounts: BigNumber[]; } From 0003666050c4991623cb36ec994d536e4703b4fe Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 18 Sep 2018 16:42:52 +0200 Subject: [PATCH 22/47] make the slippage percentage customizable by integrator --- packages/asset-buyer/src/asset_buyer.ts | 10 +++++----- packages/asset-buyer/src/constants.ts | 11 +++++++++-- packages/asset-buyer/src/types.ts | 6 ++++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 76b96427e9..a2c0f37c9a 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -13,6 +13,7 @@ import { AssetBuyerError, AssetBuyerOrdersAndFillableAmounts, BuyQuote, + BuyQuoteRequestOpts, OrderFetcher, OrderFetcherResponse, } from './types'; @@ -160,12 +161,13 @@ export class AssetBuyer { */ public async getBuyQuoteAsync( assetBuyAmount: BigNumber, - feePercentage: number = constants.DEFAULT_FEE_PERCENTAGE, - forceOrderRefresh: boolean = false, + options: Partial, ): Promise { + const { feePercentage, forceOrderRefresh, slippagePercentage } = { ...options, ...constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS }; assert.isBigNumber('assetBuyAmount', assetBuyAmount); assert.isNumber('feePercentage', feePercentage); assert.isBoolean('forceOrderRefresh', forceOrderRefresh); + assert.isNumber('feePercentage', slippagePercentage); // we should refresh if: // we do not have any orders OR // we are forced to OR @@ -185,13 +187,11 @@ export class AssetBuyer { ordersAndFillableAmounts = this ._currentOrdersAndFillableAmountsIfExists as AssetBuyerOrdersAndFillableAmounts; } - // TODO: optimization - // make the slippage percentage customizable by integrator const buyQuote = buyQuoteCalculator.calculate( ordersAndFillableAmounts, assetBuyAmount, feePercentage, - constants.DEFAULT_SLIPPAGE_PERCENTAGE, + slippagePercentage, ); return buyQuote; } diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts index e20bcc6ed9..53fe891603 100644 --- a/packages/asset-buyer/src/constants.ts +++ b/packages/asset-buyer/src/constants.ts @@ -1,11 +1,18 @@ import { BigNumber } from '@0xproject/utils'; +import { BuyQuoteRequestOpts } from './types'; + +const DEFAULT_BUY_QUOTE_REQUEST_OPTS: BuyQuoteRequestOpts = { + feePercentage: 0, + forceOrderRefresh: false, + slippagePercentage: 0.2, // 20% slippage protection +}; + export const constants = { ZERO_AMOUNT: new BigNumber(0), NULL_ADDRESS: '0x0000000000000000000000000000000000000000', MAINNET_NETWORK_ID: 1, - DEFAULT_SLIPPAGE_PERCENTAGE: 0.2, // 20% slippage protection DEFAULT_ORDER_REFRESH_INTERVAL_MS: 10000, // 10 seconds - DEFAULT_FEE_PERCENTAGE: 0, ETHER_TOKEN_DECIMALS: 18, + DEFAULT_BUY_QUOTE_REQUEST_OPTS, }; diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index 75773f0e81..fb4aecd772 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -52,6 +52,12 @@ export interface BuyQuote { feePercentage?: number; } +export interface BuyQuoteRequestOpts { + feePercentage: number; + forceOrderRefresh: boolean; + slippagePercentage: number; +} + /** * Possible errors thrown by an AssetBuyer instance or associated static methods. */ From fd4b4f8f8289d1e5a873ff5aa0aa9f7ac6a2ba08 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 18 Sep 2018 23:56:07 +0200 Subject: [PATCH 23/47] Make ForwaderWrapper take in a number for feePercentage instead of BigNumber --- .../src/contract_wrappers/forwarder_wrapper.ts | 17 +++++++++++------ .../contract-wrappers/src/utils/constants.ts | 2 ++ packages/contract-wrappers/src/utils/utils.ts | 6 ++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts index 13ef0fe01f..9062227317 100644 --- a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts @@ -12,6 +12,7 @@ import { TransactionOpts } from '../types'; import { assert } from '../utils/assert'; import { calldataOptimizationUtils } from '../utils/calldata_optimization_utils'; import { constants } from '../utils/constants'; +import { utils } from '../utils/utils'; import { ContractWrapper } from './contract_wrapper'; import { ForwarderContract } from './generated/forwarder'; @@ -57,7 +58,7 @@ export class ForwarderWrapper extends ContractWrapper { takerAddress: string, ethAmount: BigNumber, signedFeeOrders: SignedOrder[] = [], - feePercentage: BigNumber = constants.ZERO_AMOUNT, + feePercentage: number = 0, feeRecipientAddress: string = constants.NULL_ADDRESS, txOpts: TransactionOpts = {}, ): Promise { @@ -66,7 +67,7 @@ export class ForwarderWrapper extends ContractWrapper { await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); assert.isBigNumber('ethAmount', ethAmount); assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); - assert.isBigNumber('feePercentage', feePercentage); + assert.isNumber('feePercentage', feePercentage); assert.isETHAddressHex('feeRecipientAddress', feeRecipientAddress); assert.doesConformToSchema('txOpts', txOpts, txOptsSchema); // other assertions @@ -76,6 +77,8 @@ export class ForwarderWrapper extends ContractWrapper { this.getZRXTokenAddress(), this.getEtherTokenAddress(), ); + // format feePercentage + const formattedFeePercentage = utils.numberPercentageToEtherTokenAmountPercentage(feePercentage); // lowercase input addresses const normalizedTakerAddress = takerAddress.toLowerCase(); const normalizedFeeRecipientAddress = feeRecipientAddress.toLowerCase(); @@ -89,7 +92,7 @@ export class ForwarderWrapper extends ContractWrapper { _.map(optimizedMarketOrders, order => order.signature), optimizedFeeOrders, _.map(optimizedFeeOrders, order => order.signature), - feePercentage, + formattedFeePercentage, feeRecipientAddress, { value: ethAmount, @@ -124,7 +127,7 @@ export class ForwarderWrapper extends ContractWrapper { takerAddress: string, ethAmount: BigNumber, signedFeeOrders: SignedOrder[] = [], - feePercentage: BigNumber = constants.ZERO_AMOUNT, + feePercentage: number = 0, feeRecipientAddress: string = constants.NULL_ADDRESS, txOpts: TransactionOpts = {}, ): Promise { @@ -134,7 +137,7 @@ export class ForwarderWrapper extends ContractWrapper { await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); assert.isBigNumber('ethAmount', ethAmount); assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); - assert.isBigNumber('feePercentage', feePercentage); + assert.isNumber('feePercentage', feePercentage); assert.isETHAddressHex('feeRecipientAddress', feeRecipientAddress); assert.doesConformToSchema('txOpts', txOpts, txOptsSchema); // other assertions @@ -144,6 +147,8 @@ export class ForwarderWrapper extends ContractWrapper { this.getZRXTokenAddress(), this.getEtherTokenAddress(), ); + // format feePercentage + const formattedFeePercentage = utils.numberPercentageToEtherTokenAmountPercentage(feePercentage); // lowercase input addresses const normalizedTakerAddress = takerAddress.toLowerCase(); const normalizedFeeRecipientAddress = feeRecipientAddress.toLowerCase(); @@ -158,7 +163,7 @@ export class ForwarderWrapper extends ContractWrapper { _.map(optimizedMarketOrders, order => order.signature), optimizedFeeOrders, _.map(optimizedFeeOrders, order => order.signature), - feePercentage, + formattedFeePercentage, feeRecipientAddress, { value: ethAmount, diff --git a/packages/contract-wrappers/src/utils/constants.ts b/packages/contract-wrappers/src/utils/constants.ts index 2df11538cc..78441decf7 100644 --- a/packages/contract-wrappers/src/utils/constants.ts +++ b/packages/contract-wrappers/src/utils/constants.ts @@ -12,4 +12,6 @@ export const constants = { UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1), DEFAULT_BLOCK_POLLING_INTERVAL: 1000, ZERO_AMOUNT: new BigNumber(0), + ONE_AMOUNT: new BigNumber(1), + ETHER_TOKEN_DECIMALS: 18, }; diff --git a/packages/contract-wrappers/src/utils/utils.ts b/packages/contract-wrappers/src/utils/utils.ts index 689a7ee0a4..f7949ec34d 100644 --- a/packages/contract-wrappers/src/utils/utils.ts +++ b/packages/contract-wrappers/src/utils/utils.ts @@ -1,4 +1,7 @@ import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; + +import { constants } from './constants'; export const utils = { getCurrentUnixTimestampSec(): BigNumber { @@ -8,4 +11,7 @@ export const utils = { getCurrentUnixTimestampMs(): BigNumber { return new BigNumber(Date.now()); }, + numberPercentageToEtherTokenAmountPercentage(percentage: number): BigNumber { + return Web3Wrapper.toBaseUnitAmount(constants.ONE_AMOUNT, constants.ETHER_TOKEN_DECIMALS).mul(percentage); + }, }; From 93f7e33f6a1d0a056198a9f22bcb10ef3e4f4f25 Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 19 Sep 2018 00:57:05 +0200 Subject: [PATCH 24/47] Add isValidPercentage assert --- packages/asset-buyer/src/asset_buyer.ts | 20 +++++++------------- packages/asset-buyer/src/utils/assert.ts | 7 +++++++ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index a2c0f37c9a..7becb82859 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -159,15 +159,14 @@ export class AssetBuyer { * the next orderRefreshIntervalMs. Defaults to false. * @return An object that conforms to BuyQuote that satisfies the request. See type definition for more information. */ - public async getBuyQuoteAsync( - assetBuyAmount: BigNumber, - options: Partial, - ): Promise { - const { feePercentage, forceOrderRefresh, slippagePercentage } = { ...options, ...constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS }; + public async getBuyQuoteAsync(assetBuyAmount: BigNumber, options: Partial): Promise { + const { feePercentage, forceOrderRefresh, slippagePercentage } = { + ...options, + ...constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS, + }; assert.isBigNumber('assetBuyAmount', assetBuyAmount); - assert.isNumber('feePercentage', feePercentage); + assert.isValidPercentage('feePercentage', feePercentage); assert.isBoolean('forceOrderRefresh', forceOrderRefresh); - assert.isNumber('feePercentage', slippagePercentage); // we should refresh if: // we do not have any orders OR // we are forced to OR @@ -236,18 +235,13 @@ export class AssetBuyer { const desiredRate = rate || maxRate; // calculate how much eth is required to buy assetBuyAmount at the desired rate const ethAmount = assetBuyAmount.dividedToIntegerBy(desiredRate); - // TODO: critical - // update the forwarder wrapper to take in feePercentage as a number instead of a BigNumber, verify with Amir that this is being done correctly - const feePercentageBigNumber = !_.isUndefined(feePercentage) - ? Web3Wrapper.toBaseUnitAmount(new BigNumber(1), constants.ETHER_TOKEN_DECIMALS).mul(feePercentage) - : constants.ZERO_AMOUNT; const txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync( orders, assetBuyAmount, finalTakerAddress, ethAmount, feeOrders, - feePercentageBigNumber, + feePercentage, feeRecipient, ); return txHash; diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts index edc90608ce..745ba726f6 100644 --- a/packages/asset-buyer/src/utils/assert.ts +++ b/packages/asset-buyer/src/utils/assert.ts @@ -41,4 +41,11 @@ export const assert = { `Expected all orders in ${variableName} to have the same makerAssetData and takerAssetData.`, ); }, + isValidPercentage(variableName: string, percentage: number): void { + assert.isNumber(variableName, percentage); + assert.assert( + percentage >= 0 && percentage <= 1, + `Expected ${variableName} to be between 0 and 1, but is ${percentage}`, + ); + }, }; From 60e2dfdbda1e089ee4d4419243167eaeb769ff6a Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 19 Sep 2018 15:58:30 +0200 Subject: [PATCH 25/47] Calculate min and max rates in buy quote --- .../src/utils/buy_quote_calculator.ts | 62 +++++++++++++------ packages/order-utils/src/market_utils.ts | 39 +++++++----- packages/order-utils/src/types.ts | 2 + 3 files changed, 70 insertions(+), 33 deletions(-) diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index e05ab1e550..52cecf8adb 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -1,5 +1,6 @@ import { marketUtils } from '@0xproject/order-utils'; import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; import { constants } from '../constants'; import { AssetBuyerError, AssetBuyerOrdersAndFillableAmounts, BuyQuote } from '../types'; @@ -18,41 +19,64 @@ export const buyQuoteCalculator = { remainingFillableFeeAmounts, } = ordersAndFillableAmounts; const slippageBufferAmount = assetBuyAmount.mul(slippagePercentage).round(); - const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( - orders, - assetBuyAmount, - { - remainingFillableMakerAssetAmounts, - slippageBufferAmount, - }, - ); + const { + resultOrders, + remainingFillAmount, + ordersRemainingFillableMakerAssetAmounts, + } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, assetBuyAmount, { + remainingFillableMakerAssetAmounts, + slippageBufferAmount, + }); if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { throw new Error(AssetBuyerError.InsufficientAssetLiquidity); } // TODO: optimization // update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to // finding order that cover all fees, this will help with estimating ETH and minimizing gas usage - const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( - resultOrders, - feeOrders, - { - remainingFillableMakerAssetAmounts, - remainingFillableFeeAmounts, - }, - ); + const { + resultFeeOrders, + remainingFeeAmount, + feeOrdersRemainingFillableMakerAssetAmounts, + } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(resultOrders, feeOrders, { + remainingFillableMakerAssetAmounts, + remainingFillableFeeAmounts, + }); if (remainingFeeAmount.gt(constants.ZERO_AMOUNT)) { throw new Error(AssetBuyerError.InsufficientZrxLiquidity); } const assetData = orders[0].makerAssetData; - // TODO: critical + // calculate minRate and maxRate by calculating min and max eth usage and then dividing into // assetBuyAmount to get assetData / WETH, needs to take into account feePercentage as well + // minEthAmount = (sum(takerAssetAmount[i]) until sum(makerAssetAmount[i]) >= assetBuyAmount ) * (1 + feePercentage) + // maxEthAmount = (sum(takerAssetAmount[i]) until i == orders.length) * (1 + feePercentage) + const allOrders = _.concat(resultOrders, resultFeeOrders); + const allRemainingAmounts = _.concat( + ordersRemainingFillableMakerAssetAmounts, + feeOrdersRemainingFillableMakerAssetAmounts, + ); + let minEthAmount = constants.ZERO_AMOUNT; + let maxEthAmount = constants.ZERO_AMOUNT; + let cumulativeMakerAmount = constants.ZERO_AMOUNT; + _.forEach(allOrders, (order, index) => { + const remainingFillableMakerAssetAmount = allRemainingAmounts[index]; + const orderRate = order.takerAssetAmount.div(order.makerAssetAmount); + const claimableTakerAssetAmount = orderRate.mul(remainingFillableMakerAssetAmount); + // taker asset is always assumed to be WETH + maxEthAmount = maxEthAmount.plus(claimableTakerAssetAmount); + if (cumulativeMakerAmount.lessThan(assetBuyAmount)) { + minEthAmount = minEthAmount.plus(claimableTakerAssetAmount); + } + cumulativeMakerAmount = cumulativeMakerAmount.plus(remainingFillableMakerAssetAmount); + }); + const feeAdjustedMinRate = minEthAmount.mul(feePercentage + 1).div(assetBuyAmount); + const feeAdjustedMaxRate = minEthAmount.mul(feePercentage + 1).div(assetBuyAmount); return { assetData, orders: resultOrders, feeOrders: resultFeeOrders, - minRate: constants.ZERO_AMOUNT, - maxRate: constants.ZERO_AMOUNT, + minRate: feeAdjustedMinRate, + maxRate: feeAdjustedMaxRate, assetBuyAmount, feePercentage, }; diff --git a/packages/order-utils/src/market_utils.ts b/packages/order-utils/src/market_utils.ts index 4a664cb14b..ed6af7d857 100644 --- a/packages/order-utils/src/market_utils.ts +++ b/packages/order-utils/src/market_utils.ts @@ -51,17 +51,23 @@ export const marketUtils = { // iterate through the orders input from left to right until we have enough makerAsset to fill totalFillAmount const result = _.reduce( orders, - ({ resultOrders, remainingFillAmount }, order, index) => { + ({ resultOrders, remainingFillAmount, ordersRemainingFillableMakerAssetAmounts }, order, index) => { if (remainingFillAmount.lessThanOrEqualTo(constants.ZERO_AMOUNT)) { - return { resultOrders, remainingFillAmount: constants.ZERO_AMOUNT }; + return { + resultOrders, + remainingFillAmount: constants.ZERO_AMOUNT, + ordersRemainingFillableMakerAssetAmounts, + }; } else { const makerAssetAmountAvailable = remainingFillableMakerAssetAmounts[index]; + const shouldIncludeOrder = makerAssetAmountAvailable.gt(constants.ZERO_AMOUNT); // if there is no makerAssetAmountAvailable do not append order to resultOrders // if we have exceeded the total amount we want to fill set remainingFillAmount to 0 return { - resultOrders: makerAssetAmountAvailable.gt(constants.ZERO_AMOUNT) - ? _.concat(resultOrders, order) - : resultOrders, + resultOrders: shouldIncludeOrder ? _.concat(resultOrders, order) : resultOrders, + ordersRemainingFillableMakerAssetAmounts: shouldIncludeOrder + ? _.concat(ordersRemainingFillableMakerAssetAmounts, makerAssetAmountAvailable) + : ordersRemainingFillableMakerAssetAmounts, remainingFillAmount: BigNumber.max( constants.ZERO_AMOUNT, remainingFillAmount.minus(makerAssetAmountAvailable), @@ -69,7 +75,11 @@ export const marketUtils = { }; } }, - { resultOrders: [] as T[], remainingFillAmount: totalFillAmount }, + { + resultOrders: [] as T[], + remainingFillAmount: totalFillAmount, + ordersRemainingFillableMakerAssetAmounts: [] as BigNumber[], + }, ); return result; }, @@ -133,17 +143,18 @@ export const marketUtils = { }, constants.ZERO_AMOUNT, ); - const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( - feeOrders, - totalFeeAmount, - { - remainingFillableMakerAssetAmounts: remainingFillableFeeAmounts, - slippageBufferAmount, - }, - ); + const { + resultOrders, + remainingFillAmount, + ordersRemainingFillableMakerAssetAmounts, + } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(feeOrders, totalFeeAmount, { + remainingFillableMakerAssetAmounts: remainingFillableFeeAmounts, + slippageBufferAmount, + }); return { resultFeeOrders: resultOrders, remainingFeeAmount: remainingFillAmount, + feeOrdersRemainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts, }; // TODO: add more orders here to cover rounding // https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarding-contract-specification.md#over-buying-zrx diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts index 09292e557f..a843efaa47 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -72,10 +72,12 @@ export interface FindFeeOrdersThatCoverFeesForTargetOrdersOpts { export interface FeeOrdersAndRemainingFeeAmount { resultFeeOrders: T[]; + feeOrdersRemainingFillableMakerAssetAmounts: BigNumber[]; remainingFeeAmount: BigNumber; } export interface OrdersAndRemainingFillAmount { resultOrders: T[]; + ordersRemainingFillableMakerAssetAmounts: BigNumber[]; remainingFillAmount: BigNumber; } From 14345ab24e0cd8db7bd2804b169acfaf18040ea4 Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 19 Sep 2018 16:02:52 +0200 Subject: [PATCH 26/47] Rename forceOrderRefresh to shouldForceOrderRefresh --- packages/asset-buyer/src/asset_buyer.ts | 6 +++--- packages/asset-buyer/src/constants.ts | 2 +- packages/asset-buyer/src/types.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 7becb82859..81b46d89ab 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -160,20 +160,20 @@ export class AssetBuyer { * @return An object that conforms to BuyQuote that satisfies the request. See type definition for more information. */ public async getBuyQuoteAsync(assetBuyAmount: BigNumber, options: Partial): Promise { - const { feePercentage, forceOrderRefresh, slippagePercentage } = { + const { feePercentage, shouldForceOrderRefresh, slippagePercentage } = { ...options, ...constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS, }; assert.isBigNumber('assetBuyAmount', assetBuyAmount); assert.isValidPercentage('feePercentage', feePercentage); - assert.isBoolean('forceOrderRefresh', forceOrderRefresh); + assert.isBoolean('shouldForceOrderRefresh', shouldForceOrderRefresh); // we should refresh if: // we do not have any orders OR // we are forced to OR // we have some last refresh time AND that time was sufficiently long ago const shouldRefresh = _.isUndefined(this._currentOrdersAndFillableAmountsIfExists) || - forceOrderRefresh || + shouldForceOrderRefresh || (!_.isUndefined(this._lastRefreshTimeIfExists) && this._lastRefreshTimeIfExists + this.orderRefreshIntervalMs < Date.now()); let ordersAndFillableAmounts: AssetBuyerOrdersAndFillableAmounts; diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts index 53fe891603..701832fd44 100644 --- a/packages/asset-buyer/src/constants.ts +++ b/packages/asset-buyer/src/constants.ts @@ -4,7 +4,7 @@ import { BuyQuoteRequestOpts } from './types'; const DEFAULT_BUY_QUOTE_REQUEST_OPTS: BuyQuoteRequestOpts = { feePercentage: 0, - forceOrderRefresh: false, + shouldForceOrderRefresh: false, slippagePercentage: 0.2, // 20% slippage protection }; diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index fb4aecd772..339bce52c9 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -54,7 +54,7 @@ export interface BuyQuote { export interface BuyQuoteRequestOpts { feePercentage: number; - forceOrderRefresh: boolean; + shouldForceOrderRefresh: boolean; slippagePercentage: number; } From a03b1d4d6cc5548f4c2f07a933a4c2cc6be3b053 Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 19 Sep 2018 16:48:57 +0200 Subject: [PATCH 27/47] Move ObjectMap to shared types --- packages/types/CHANGELOG.json | 9 +++++++++ packages/types/src/index.ts | 4 ++++ packages/website/ts/types.ts | 6 +----- packages/website/ts/utils/analytics.ts | 3 ++- packages/website/ts/utils/token_address_overrides.ts | 2 +- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index 008ff8ff4e..8448f4ae62 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.1.0", + "changes": [ + { + "note": "Add ObjectMap type", + "pr": 1037 + } + ] + }, { "version": "1.0.1", "changes": [ diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 48a9e23d11..25803e8c89 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -239,3 +239,7 @@ export enum StatusCodes { MethodNotAllowed = 405, GatewayTimeout = 504, } + +export interface ObjectMap { + [key: string]: T; +} diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 5fe9eef991..3c6d3df4da 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -1,4 +1,4 @@ -import { SignedOrder } from '@0xproject/types'; +import { ObjectMap, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Provider } from 'ethereum-types'; import * as React from 'react'; @@ -519,10 +519,6 @@ export interface OutdatedWrappedEtherByNetworkId { export type ItemByAddress = ObjectMap; -export interface ObjectMap { - [key: string]: T; -} - export type TokenStateByAddress = ItemByAddress; export interface TokenState { diff --git a/packages/website/ts/utils/analytics.ts b/packages/website/ts/utils/analytics.ts index 62dbff2b95..3aae317b03 100644 --- a/packages/website/ts/utils/analytics.ts +++ b/packages/website/ts/utils/analytics.ts @@ -1,6 +1,7 @@ import { assetDataUtils } from '@0xproject/order-utils'; +import { ObjectMap } from '@0xproject/types'; import * as _ from 'lodash'; -import { ObjectMap, PortalOrder } from 'ts/types'; +import { PortalOrder } from 'ts/types'; import { utils } from 'ts/utils/utils'; export interface HeapAnalytics { diff --git a/packages/website/ts/utils/token_address_overrides.ts b/packages/website/ts/utils/token_address_overrides.ts index 8353358bc3..e7e9162737 100644 --- a/packages/website/ts/utils/token_address_overrides.ts +++ b/packages/website/ts/utils/token_address_overrides.ts @@ -1,4 +1,4 @@ -import { ObjectMap } from 'ts/types'; +import { ObjectMap } from '@0xproject/types'; import { constants } from 'ts/utils/constants'; // Map of networkId -> tokenSymbol -> tokenAddress From a44f77a83811146ad68ee6f56bcda94e56d6a634 Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 19 Sep 2018 18:38:50 +0200 Subject: [PATCH 28/47] Implement StandardRelayerAPIAssetBuyerManager --- packages/asset-buyer/src/asset_buyer.ts | 18 +-- packages/asset-buyer/src/constants.ts | 1 + packages/asset-buyer/src/index.ts | 2 + ...tandard_relayer_api_asset_buyer_manager.ts | 125 ++++++++++++++++++ packages/asset-buyer/src/types.ts | 7 + .../asset-buyer/src/utils/asset_data_utils.ts | 26 ++++ .../src/utils/buy_quote_calculator.ts | 1 + 7 files changed, 166 insertions(+), 14 deletions(-) create mode 100644 packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts create mode 100644 packages/asset-buyer/src/utils/asset_data_utils.ts diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 81b46d89ab..5f57e3d7fc 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -1,6 +1,6 @@ import { ContractWrappers } from '@0xproject/contract-wrappers'; import { schemas } from '@0xproject/json-schemas'; -import { assetDataUtils, SignedOrder } from '@0xproject/order-utils'; +import { SignedOrder } from '@0xproject/order-utils'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Provider } from 'ethereum-types'; @@ -19,6 +19,7 @@ import { } from './types'; import { assert } from './utils/assert'; +import { assetDataUtils } from './utils/asset_data_utils'; import { buyQuoteCalculator } from './utils/buy_quote_calculator'; import { orderFetcherResponseProcessor } from './utils/order_fetcher_response_processor'; @@ -284,24 +285,13 @@ export class AssetBuyer { * Will throw if WETH does not exist for the current network. */ private _getEtherTokenAssetData(): string { - const etherTokenAddressIfExists = this._contractWrappers.etherToken.getContractAddressIfExists(); - if (_.isUndefined(etherTokenAddressIfExists)) { - throw new Error(AssetBuyerError.NoEtherTokenContractFound); - } - const etherTokenAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists); - return etherTokenAssetData; + return assetDataUtils.getEtherTokenAssetData(this._contractWrappers); } /** * Get the assetData that represents the ZRX token. * Will throw if ZRX does not exist for the current network. */ private _getZrxTokenAssetData(): string { - let zrxTokenAssetData: string; - try { - zrxTokenAssetData = this._contractWrappers.exchange.getZRXAssetData(); - } catch (err) { - throw new Error(AssetBuyerError.NoZrxTokenContractFound); - } - return zrxTokenAssetData; + return assetDataUtils.getZrxTokenAssetData(this._contractWrappers); } } diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts index 701832fd44..0ebe0f8e2f 100644 --- a/packages/asset-buyer/src/constants.ts +++ b/packages/asset-buyer/src/constants.ts @@ -15,4 +15,5 @@ export const constants = { DEFAULT_ORDER_REFRESH_INTERVAL_MS: 10000, // 10 seconds ETHER_TOKEN_DECIMALS: 18, DEFAULT_BUY_QUOTE_REQUEST_OPTS, + MAX_PER_PAGE: 10000, }; diff --git a/packages/asset-buyer/src/index.ts b/packages/asset-buyer/src/index.ts index efd3523fd9..2156b7e969 100644 --- a/packages/asset-buyer/src/index.ts +++ b/packages/asset-buyer/src/index.ts @@ -5,6 +5,7 @@ export { BigNumber } from '@0xproject/utils'; export { AssetBuyer } from './asset_buyer'; export { ProvidedOrderFetcher } from './order_fetchers/provided_order_fetcher'; export { StandardRelayerAPIOrderFetcher } from './order_fetchers/standard_relayer_api_order_fetcher'; +export { StandardRelayerAPIAssetBuyerManager } from './standard_relayer_api_asset_buyer_manager'; export { AssetBuyerError, BuyQuote, @@ -12,4 +13,5 @@ export { OrderFetcherRequest, OrderFetcherResponse, SignedOrderWithRemainingFillableMakerAssetAmount, + StandardRelayerApiAssetBuyerManagerError, } from './types'; diff --git a/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts b/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts new file mode 100644 index 0000000000..6cd96fab2f --- /dev/null +++ b/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts @@ -0,0 +1,125 @@ +import { HttpClient } from '@0xproject/connect'; +import { ContractWrappers } from '@0xproject/contract-wrappers'; +import { ObjectMap } from '@0xproject/types'; +import { Provider } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { AssetBuyer } from './asset_buyer'; +import { constants } from './constants'; +import { assert } from './utils/assert'; +import { assetDataUtils } from './utils/asset_data_utils'; + +import { OrderFetcher, StandardRelayerApiAssetBuyerManagerError } from './types'; + +export class StandardRelayerAPIAssetBuyerManager { + // Map of assetData to AssetBuyer for that assetData + public readonly assetBuyerMap: ObjectMap; + /** + * Returns an array of all assetDatas available at the provided sraApiUrl + * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from. + * @param pairedWithAssetData Optional filter argument to return assetDatas that only pair with this assetData value. + * + * @return An array of all assetDatas available at the provider sraApiUrl + */ + public static async getAllAvailableAssetDatasAsync( + sraApiUrl: string, + pairedWithAssetData?: string, + ): Promise { + const client = new HttpClient(sraApiUrl); + const params = { + assetDataA: pairedWithAssetData, + perPage: constants.MAX_PER_PAGE, + }; + const assetPairsResponse = await client.getAssetPairsAsync(params); + return _.uniq(_.map(assetPairsResponse.records, pairsItem => pairsItem.assetDataB.assetData)); + } + /** + * Instantiates a new StandardRelayerAPIAssetBuyerManager instance with all available assetDatas at the provided sraApiUrl + * @param provider The Provider instance you would like to use for interacting with the Ethereum network. + * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from. + * @param orderFetcher An object that conforms to OrderFetcher, see type for definition. + * @param networkId The ethereum network id. Defaults to 1 (mainnet). + * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. + * Defaults to 10000ms (10s). + * @return An promise of an instance of StandardRelayerAPIAssetBuyerManager + */ + public static async getAssetBuyerManagerWithAllAvailableAssetDatasAsync( + provider: Provider, + sraApiUrl: string, + orderFetcher: OrderFetcher, + networkId: number = constants.MAINNET_NETWORK_ID, + orderRefreshIntervalMs?: number, + ): Promise { + const contractWrappers = new ContractWrappers(provider, { networkId }); + const etherTokenAssetData = assetDataUtils.getEtherTokenAssetData(contractWrappers); + const assetDatas = await StandardRelayerAPIAssetBuyerManager.getAllAvailableAssetDatasAsync( + sraApiUrl, + etherTokenAssetData, + ); + return new StandardRelayerAPIAssetBuyerManager( + provider, + assetDatas, + orderFetcher, + networkId, + orderRefreshIntervalMs, + ); + } + /** + * Instantiates a new StandardRelayerAPIAssetBuyerManager instance + * @param provider The Provider instance you would like to use for interacting with the Ethereum network. + * @param assetDatas The assetDatas of the desired assets to buy (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). + * @param orderFetcher An object that conforms to OrderFetcher, see type for definition. + * @param networkId The ethereum network id. Defaults to 1 (mainnet). + * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. + * Defaults to 10000ms (10s). + * @return An instance of StandardRelayerAPIAssetBuyerManager + */ + constructor( + provider: Provider, + assetDatas: string[], + orderFetcher: OrderFetcher, + networkId?: number, + orderRefreshIntervalMs?: number, + ) { + assert.assert(assetDatas.length > 0, `Expected 'assetDatas' to be a non-empty array.`); + this.assetBuyerMap = _.reduce( + assetDatas, + (accAssetBuyerMap: ObjectMap, assetData: string) => { + accAssetBuyerMap[assetData] = new AssetBuyer( + provider, + assetData, + orderFetcher, + networkId, + orderRefreshIntervalMs, + ); + return accAssetBuyerMap; + }, + {}, + ); + } + /** + * Get a AssetBuyer for the provided assetData + * @param assetData The desired assetData. + * + * @return An instance of AssetBuyer + */ + public getAssetBuyerFromAssetData(assetData: string): AssetBuyer { + const assetBuyer = this.assetBuyerMap[assetData]; + if (_.isUndefined(assetBuyer)) { + throw new Error( + `${StandardRelayerApiAssetBuyerManagerError.AssetBuyerNotFound}: For assetData ${assetData}`, + ); + } + return assetBuyer; + } + /** + * Get a AssetBuyer for the provided ERC20 tokenAddress + * @param tokenAddress The desired tokenAddress. + * + * @return An instance of AssetBuyer + */ + public getAssetBuyerFromERC20TokenAddress(tokenAddress: string): AssetBuyer { + const assetData = assetDataUtils.encodeERC20AssetData(tokenAddress); + return this.getAssetBuyerFromAssetData(assetData); + } +} diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index 339bce52c9..c30bdf4bb8 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -70,6 +70,13 @@ export enum AssetBuyerError { NoAddressAvailable = 'NO_ADDRESS_AVAILABLE', } +/** + * Possible errors thrown by an StandardRelayerApiAssetBuyerManager instance or associated static methods. + */ +export enum StandardRelayerApiAssetBuyerManagerError { + AssetBuyerNotFound = 'ASSET_BUYER_NOT_FOUND', +} + export interface AssetBuyerOrdersAndFillableAmounts { orders: SignedOrder[]; feeOrders: SignedOrder[]; diff --git a/packages/asset-buyer/src/utils/asset_data_utils.ts b/packages/asset-buyer/src/utils/asset_data_utils.ts new file mode 100644 index 0000000000..10ff310577 --- /dev/null +++ b/packages/asset-buyer/src/utils/asset_data_utils.ts @@ -0,0 +1,26 @@ +import { ContractWrappers } from '@0xproject/contract-wrappers'; +import { assetDataUtils as sharedAssetDataUtils } from '@0xproject/order-utils'; +import * as _ from 'lodash'; + +import { AssetBuyerError } from '../types'; + +export const assetDataUtils = { + ...sharedAssetDataUtils, + getEtherTokenAssetData(contractWrappers: ContractWrappers): string { + const etherTokenAddressIfExists = contractWrappers.etherToken.getContractAddressIfExists(); + if (_.isUndefined(etherTokenAddressIfExists)) { + throw new Error(AssetBuyerError.NoEtherTokenContractFound); + } + const etherTokenAssetData = sharedAssetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists); + return etherTokenAssetData; + }, + getZrxTokenAssetData(contractWrappers: ContractWrappers): string { + let zrxTokenAssetData: string; + try { + zrxTokenAssetData = contractWrappers.exchange.getZRXAssetData(); + } catch (err) { + throw new Error(AssetBuyerError.NoZrxTokenContractFound); + } + return zrxTokenAssetData; + }, +}; diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index 52cecf8adb..31823cc028 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -59,6 +59,7 @@ export const buyQuoteCalculator = { let maxEthAmount = constants.ZERO_AMOUNT; let cumulativeMakerAmount = constants.ZERO_AMOUNT; _.forEach(allOrders, (order, index) => { + // TODO: Move this logic to order_utils const remainingFillableMakerAssetAmount = allRemainingAmounts[index]; const orderRate = order.takerAssetAmount.div(order.makerAssetAmount); const claimableTakerAssetAmount = orderRate.mul(remainingFillableMakerAssetAmount); From 3238925aa4e13750e5a8e45b3130ce9e148efae8 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 20 Sep 2018 14:09:11 +0200 Subject: [PATCH 29/47] Add better description to README --- packages/asset-buyer/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/asset-buyer/README.md b/packages/asset-buyer/README.md index 717849bf2e..f615b93e1b 100644 --- a/packages/asset-buyer/README.md +++ b/packages/asset-buyer/README.md @@ -1,6 +1,8 @@ ## @0xproject/asset-buyer -Convenience package for buying assets +Convenience package for buying assets represented on the Ethereum blockchain using 0x. In its simplest form, the package helps in the usage of the [0x forwarder contract](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md), which allows users to execute Wrapped Ether based 0x orders without having to set allowances, wrap Ether or buy ZRX, meaning they can buy tokens with Ether alone. Given some liquidity (0x signed orders), it helps estimate the Ether cost of buying a certain asset (giving a range) and then buying that asset. + +In its more advanced and useful form, it integrates with the [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) and takes care of sourcing liquidity for you given an SRA compliant endpoint. The final result is a library that tells you what assets are available, provides an Ether based quote for any asset desired, and allows you to buy that asset using Ether alone. ### Read the [Documentation](https://0xproject.com/docs/asset-buyer). From d23487bda9fa918d31bb940fffb74f8be0859be5 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 20 Sep 2018 14:27:20 +0200 Subject: [PATCH 30/47] Update descriptions for methods on AssetBuyer --- packages/asset-buyer/src/asset_buyer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 5f57e3d7fc..2b25c681c3 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -33,7 +33,7 @@ export class AssetBuyer { private _lastRefreshTimeIfExists?: number; private _currentOrdersAndFillableAmountsIfExists?: AssetBuyerOrdersAndFillableAmounts; /** - * Instantiates a new AssetBuyer instance + * Instantiates a new AssetBuyer instance given existing liquidity in the form of orders and feeOrders. * @param provider The Provider instance you would like to use for interacting with the Ethereum network. * @param orders A non-empty array of objects that conform to SignedOrder. All orders must have the same makerAssetData and takerAssetData (WETH). * @param feeOrders A array of objects that conform to SignedOrder. All orders must have the same makerAssetData (ZRX) and takerAssetData (WETH). Defaults to an empty array. @@ -63,7 +63,7 @@ export class AssetBuyer { return assetBuyer; } /** - * Instantiates a new AssetBuyer instance + * Instantiates a new AssetBuyer instance given the desired assetData and a [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) endpoint * @param provider The Provider instance you would like to use for interacting with the Ethereum network. * @param assetData The assetData that identifies the desired asset to buy. * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from. @@ -89,7 +89,7 @@ export class AssetBuyer { return assetBuyer; } /** - * Instantiates a new AssetBuyer instance + * Instantiates a new AssetBuyer instance given the desired ERC20 token address and a [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) endpoint * @param provider The Provider instance you would like to use for interacting with the Ethereum network. * @param tokenAddress The ERC20 token address that identifies the desired asset to buy. * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from. From 03e18ff7c60749fd33228b25077891da26e81d41 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 20 Sep 2018 15:10:15 +0200 Subject: [PATCH 31/47] Rename OrderFetcher to OrderProvider and other small improvements --- packages/asset-buyer/src/asset_buyer.ts | 28 +++++++++---------- packages/asset-buyer/src/index.ts | 6 ++-- .../order_fetchers/provided_order_fetcher.ts | 6 ++-- .../standard_relayer_api_order_fetcher.ts | 10 +++---- ...tandard_relayer_api_asset_buyer_manager.ts | 21 +++++++------- packages/asset-buyer/src/types.ts | 8 +++--- packages/asset-buyer/src/utils/assert.ts | 8 +++--- .../asset-buyer/src/utils/asset_data_utils.ts | 4 +-- .../utils/order_fetcher_response_processor.ts | 6 ++-- 9 files changed, 48 insertions(+), 49 deletions(-) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 2b25c681c3..e74b8eac1c 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -14,8 +14,8 @@ import { AssetBuyerOrdersAndFillableAmounts, BuyQuote, BuyQuoteRequestOpts, - OrderFetcher, - OrderFetcherResponse, + OrderProvider, + OrderProviderResponse, } from './types'; import { assert } from './utils/assert'; @@ -26,7 +26,7 @@ import { orderFetcherResponseProcessor } from './utils/order_fetcher_response_pr export class AssetBuyer { public readonly provider: Provider; public readonly assetData: string; - public readonly orderFetcher: OrderFetcher; + public readonly orderFetcher: OrderProvider; public readonly networkId: number; public readonly orderRefreshIntervalMs: number; private readonly _contractWrappers: ContractWrappers; @@ -133,7 +133,7 @@ export class AssetBuyer { constructor( provider: Provider, assetData: string, - orderFetcher: OrderFetcher, + orderFetcher: OrderProvider, networkId: number = constants.MAINNET_NETWORK_ID, orderRefreshIntervalMs: number = constants.DEFAULT_ORDER_REFRESH_INTERVAL_MS, ) { @@ -152,8 +152,8 @@ export class AssetBuyer { }); } /** - * Get a BuyQuote containing all information relevant to fulfilling a buy. - * Pass the BuyQuote to executeBuyQuoteAsync to execute the buy. + * Get a `BuyQuote` containing all information relevant to fulfilling a buy. + * You can then pass the `BuyQuote` to `executeBuyQuoteAsync` to execute the buy. * @param assetBuyAmount The amount of asset to buy. * @param feePercentage The affiliate fee percentage. Defaults to 0. * @param forceOrderRefresh If set to true, new orders and state will be fetched instead of waiting for @@ -251,10 +251,8 @@ export class AssetBuyer { * Ask the order fetcher for orders and process them. */ private async _getLatestOrdersAndFillableAmountsAsync(): Promise { - // find ether token asset data - const etherTokenAssetData = this._getEtherTokenAssetData(); - // find zrx token asset data - const zrxTokenAssetData = this._getZrxTokenAssetData(); + const etherTokenAssetData = this._getEtherTokenAssetDataOrThrow(); + const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow(); // construct order fetcher requests const targetOrderFetcherRequest = { makerAssetData: this.assetData, @@ -269,7 +267,7 @@ export class AssetBuyer { const requests = [targetOrderFetcherRequest, feeOrderFetcherRequest]; // fetch orders and possible fillable amounts const [targetOrderFetcherResponse, feeOrderFetcherResponse] = await Promise.all( - _.map(requests, async request => this.orderFetcher.fetchOrdersAsync(request)), + _.map(requests, async request => this.orderFetcher.getOrdersAsync(request)), ); // process the responses into one object const ordersAndFillableAmounts = await orderFetcherResponseProcessor.processAsync( @@ -284,14 +282,14 @@ export class AssetBuyer { * Get the assetData that represents the WETH token. * Will throw if WETH does not exist for the current network. */ - private _getEtherTokenAssetData(): string { - return assetDataUtils.getEtherTokenAssetData(this._contractWrappers); + private _getEtherTokenAssetDataOrThrow(): string { + return assetDataUtils.getEtherTokenAssetDataOrThrow(this._contractWrappers); } /** * Get the assetData that represents the ZRX token. * Will throw if ZRX does not exist for the current network. */ - private _getZrxTokenAssetData(): string { - return assetDataUtils.getZrxTokenAssetData(this._contractWrappers); + private _getZrxTokenAssetDataOrThrow(): string { + return assetDataUtils.getZrxTokenAssetDataOrThrow(this._contractWrappers); } } diff --git a/packages/asset-buyer/src/index.ts b/packages/asset-buyer/src/index.ts index 2156b7e969..aa10c3af23 100644 --- a/packages/asset-buyer/src/index.ts +++ b/packages/asset-buyer/src/index.ts @@ -9,9 +9,9 @@ export { StandardRelayerAPIAssetBuyerManager } from './standard_relayer_api_asse export { AssetBuyerError, BuyQuote, - OrderFetcher, - OrderFetcherRequest, - OrderFetcherResponse, + OrderProvider, + OrderProviderRequest, + OrderProviderResponse, SignedOrderWithRemainingFillableMakerAssetAmount, StandardRelayerApiAssetBuyerManagerError, } from './types'; diff --git a/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts b/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts index 7a7ffcdfe6..397f296d7b 100644 --- a/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts +++ b/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts @@ -2,10 +2,10 @@ import { schemas } from '@0xproject/json-schemas'; import { SignedOrder } from '@0xproject/types'; import * as _ from 'lodash'; -import { OrderFetcher, OrderFetcherRequest, OrderFetcherResponse } from '../types'; +import { OrderProvider, OrderProviderRequest, OrderProviderResponse } from '../types'; import { assert } from '../utils/assert'; -export class ProvidedOrderFetcher implements OrderFetcher { +export class ProvidedOrderFetcher implements OrderProvider { public readonly providedOrders: SignedOrder[]; /** * Instantiates a new ProvidedOrderFetcher instance @@ -21,7 +21,7 @@ export class ProvidedOrderFetcher implements OrderFetcher { * @param orderFetchRequest An instance of OrderFetcherRequest. See type for more information. * @return An instance of OrderFetcherResponse. See type for more information. */ - public async fetchOrdersAsync(orderFetchRequest: OrderFetcherRequest): Promise { + public async getOrdersAsync(orderFetchRequest: OrderProviderRequest): Promise { assert.isValidOrderFetcherRequest('orderFetchRequest', orderFetchRequest); const { makerAssetData, takerAssetData } = orderFetchRequest; const orders = _.filter(this.providedOrders, order => { diff --git a/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts b/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts index 3b082b68d6..730eaf4e0b 100644 --- a/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts +++ b/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts @@ -3,15 +3,15 @@ import * as _ from 'lodash'; import { AssetBuyerError, - OrderFetcher, - OrderFetcherRequest, - OrderFetcherResponse, + OrderProvider, + OrderProviderRequest, + OrderProviderResponse, SignedOrderWithRemainingFillableMakerAssetAmount, } from '../types'; import { assert } from '../utils/assert'; import { orderUtils } from '../utils/order_utils'; -export class StandardRelayerAPIOrderFetcher implements OrderFetcher { +export class StandardRelayerAPIOrderFetcher implements OrderProvider { public readonly apiUrl: string; private readonly _sraClient: HttpClient; /** @@ -56,7 +56,7 @@ export class StandardRelayerAPIOrderFetcher implements OrderFetcher { * @param orderFetchRequest An instance of OrderFetcherRequest. See type for more information. * @return An instance of OrderFetcherResponse. See type for more information. */ - public async fetchOrdersAsync(orderFetchRequest: OrderFetcherRequest): Promise { + public async getOrdersAsync(orderFetchRequest: OrderProviderRequest): Promise { assert.isValidOrderFetcherRequest('orderFetchRequest', orderFetchRequest); const { makerAssetData, takerAssetData, networkId } = orderFetchRequest; const orderbookRequest = { baseAssetData: makerAssetData, quoteAssetData: takerAssetData }; diff --git a/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts b/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts index 6cd96fab2f..866bee46e7 100644 --- a/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts +++ b/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts @@ -9,8 +9,9 @@ import { constants } from './constants'; import { assert } from './utils/assert'; import { assetDataUtils } from './utils/asset_data_utils'; -import { OrderFetcher, StandardRelayerApiAssetBuyerManagerError } from './types'; +import { OrderProvider, StandardRelayerApiAssetBuyerManagerError } from './types'; +// TODO: Read-only list of available assetDatas export class StandardRelayerAPIAssetBuyerManager { // Map of assetData to AssetBuyer for that assetData public readonly assetBuyerMap: ObjectMap; @@ -37,7 +38,7 @@ export class StandardRelayerAPIAssetBuyerManager { * Instantiates a new StandardRelayerAPIAssetBuyerManager instance with all available assetDatas at the provided sraApiUrl * @param provider The Provider instance you would like to use for interacting with the Ethereum network. * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from. - * @param orderFetcher An object that conforms to OrderFetcher, see type for definition. + * @param orderProvider An object that conforms to OrderProvider, see type for definition. * @param networkId The ethereum network id. Defaults to 1 (mainnet). * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. * Defaults to 10000ms (10s). @@ -46,12 +47,12 @@ export class StandardRelayerAPIAssetBuyerManager { public static async getAssetBuyerManagerWithAllAvailableAssetDatasAsync( provider: Provider, sraApiUrl: string, - orderFetcher: OrderFetcher, + orderProvider: OrderProvider, networkId: number = constants.MAINNET_NETWORK_ID, orderRefreshIntervalMs?: number, ): Promise { const contractWrappers = new ContractWrappers(provider, { networkId }); - const etherTokenAssetData = assetDataUtils.getEtherTokenAssetData(contractWrappers); + const etherTokenAssetData = assetDataUtils.getEtherTokenAssetDataOrThrow(contractWrappers); const assetDatas = await StandardRelayerAPIAssetBuyerManager.getAllAvailableAssetDatasAsync( sraApiUrl, etherTokenAssetData, @@ -59,7 +60,7 @@ export class StandardRelayerAPIAssetBuyerManager { return new StandardRelayerAPIAssetBuyerManager( provider, assetDatas, - orderFetcher, + orderProvider, networkId, orderRefreshIntervalMs, ); @@ -68,7 +69,7 @@ export class StandardRelayerAPIAssetBuyerManager { * Instantiates a new StandardRelayerAPIAssetBuyerManager instance * @param provider The Provider instance you would like to use for interacting with the Ethereum network. * @param assetDatas The assetDatas of the desired assets to buy (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). - * @param orderFetcher An object that conforms to OrderFetcher, see type for definition. + * @param orderProvider An object that conforms to OrderProvider, see type for definition. * @param networkId The ethereum network id. Defaults to 1 (mainnet). * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. * Defaults to 10000ms (10s). @@ -77,7 +78,7 @@ export class StandardRelayerAPIAssetBuyerManager { constructor( provider: Provider, assetDatas: string[], - orderFetcher: OrderFetcher, + orderProvider: OrderProvider, networkId?: number, orderRefreshIntervalMs?: number, ) { @@ -88,7 +89,7 @@ export class StandardRelayerAPIAssetBuyerManager { accAssetBuyerMap[assetData] = new AssetBuyer( provider, assetData, - orderFetcher, + orderProvider, networkId, orderRefreshIntervalMs, ); @@ -98,7 +99,7 @@ export class StandardRelayerAPIAssetBuyerManager { ); } /** - * Get a AssetBuyer for the provided assetData + * Get an AssetBuyer for the provided assetData * @param assetData The desired assetData. * * @return An instance of AssetBuyer @@ -113,7 +114,7 @@ export class StandardRelayerAPIAssetBuyerManager { return assetBuyer; } /** - * Get a AssetBuyer for the provided ERC20 tokenAddress + * Get an AssetBuyer for the provided ERC20 tokenAddress * @param tokenAddress The desired tokenAddress. * * @return An instance of AssetBuyer diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index c30bdf4bb8..09319fca48 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -6,7 +6,7 @@ import { BigNumber } from '@0xproject/utils'; * takerAssetData: The assetData representing the desired takerAsset. * networkId: The networkId that the desired orders should be for. */ -export interface OrderFetcherRequest { +export interface OrderProviderRequest { makerAssetData: string; takerAssetData: string; networkId: number; @@ -15,7 +15,7 @@ export interface OrderFetcherRequest { /** * orders: An array of orders with optional remaining fillable makerAsset amounts. See type for more info. */ -export interface OrderFetcherResponse { +export interface OrderProviderResponse { orders: SignedOrderWithRemainingFillableMakerAssetAmount[]; } @@ -29,8 +29,8 @@ export interface SignedOrderWithRemainingFillableMakerAssetAmount extends Signed /** * Given an OrderFetchRequest, get an OrderFetchResponse. */ -export interface OrderFetcher { - fetchOrdersAsync: (orderFetchRequest: OrderFetcherRequest) => Promise; +export interface OrderProvider { + getOrdersAsync: (orderProviderRequest: OrderProviderRequest) => Promise; } /** diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts index 745ba726f6..1d57ae9b80 100644 --- a/packages/asset-buyer/src/utils/assert.ts +++ b/packages/asset-buyer/src/utils/assert.ts @@ -3,7 +3,7 @@ import { schemas } from '@0xproject/json-schemas'; import { SignedOrder } from '@0xproject/types'; import * as _ from 'lodash'; -import { BuyQuote, OrderFetcher, OrderFetcherRequest } from '../types'; +import { BuyQuote, OrderProvider, OrderProviderRequest } from '../types'; export const assert = { ...sharedAssert, @@ -18,10 +18,10 @@ export const assert = { sharedAssert.isNumber(`${variableName}.feePercentage`, buyQuote.feePercentage); } }, - isValidOrderFetcher(variableName: string, orderFetcher: OrderFetcher): void { - sharedAssert.isFunction(`${variableName}.fetchOrdersAsync`, orderFetcher.fetchOrdersAsync); + isValidOrderFetcher(variableName: string, orderFetcher: OrderProvider): void { + sharedAssert.isFunction(`${variableName}.fetchOrdersAsync`, orderFetcher.getOrdersAsync); }, - isValidOrderFetcherRequest(variableName: string, orderFetcherRequest: OrderFetcherRequest): void { + isValidOrderFetcherRequest(variableName: string, orderFetcherRequest: OrderProviderRequest): void { sharedAssert.isHexString(`${variableName}.makerAssetData`, orderFetcherRequest.makerAssetData); sharedAssert.isHexString(`${variableName}.takerAssetData`, orderFetcherRequest.takerAssetData); sharedAssert.isNumber(`${variableName}.networkId`, orderFetcherRequest.networkId); diff --git a/packages/asset-buyer/src/utils/asset_data_utils.ts b/packages/asset-buyer/src/utils/asset_data_utils.ts index 10ff310577..d05ff25048 100644 --- a/packages/asset-buyer/src/utils/asset_data_utils.ts +++ b/packages/asset-buyer/src/utils/asset_data_utils.ts @@ -6,7 +6,7 @@ import { AssetBuyerError } from '../types'; export const assetDataUtils = { ...sharedAssetDataUtils, - getEtherTokenAssetData(contractWrappers: ContractWrappers): string { + getEtherTokenAssetDataOrThrow(contractWrappers: ContractWrappers): string { const etherTokenAddressIfExists = contractWrappers.etherToken.getContractAddressIfExists(); if (_.isUndefined(etherTokenAddressIfExists)) { throw new Error(AssetBuyerError.NoEtherTokenContractFound); @@ -14,7 +14,7 @@ export const assetDataUtils = { const etherTokenAssetData = sharedAssetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists); return etherTokenAssetData; }, - getZrxTokenAssetData(contractWrappers: ContractWrappers): string { + getZrxTokenAssetDataOrThrow(contractWrappers: ContractWrappers): string { let zrxTokenAssetData: string; try { zrxTokenAssetData = contractWrappers.exchange.getZRXAssetData(); diff --git a/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts b/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts index f700cadf9b..98ab6b9d1c 100644 --- a/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts +++ b/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts @@ -8,7 +8,7 @@ import * as _ from 'lodash'; import { constants } from '../constants'; import { AssetBuyerOrdersAndFillableAmounts, - OrderFetcherResponse, + OrderProviderResponse, SignedOrderWithRemainingFillableMakerAssetAmount, } from '../types'; @@ -28,8 +28,8 @@ export const orderFetcherResponseProcessor = { * - Sort by rate */ async processAsync( - targetOrderFetcherResponse: OrderFetcherResponse, - feeOrderFetcherResponse: OrderFetcherResponse, + targetOrderFetcherResponse: OrderProviderResponse, + feeOrderFetcherResponse: OrderProviderResponse, zrxTokenAssetData: string, orderValidator?: OrderValidatorWrapper, ): Promise { From 35c324f67c8022f3a71bc723bf69763bf651cb05 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 20 Sep 2018 19:00:23 +0200 Subject: [PATCH 32/47] Add utility to get assetDatas --- .../src/standard_relayer_api_asset_buyer_manager.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts b/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts index 866bee46e7..207713110a 100644 --- a/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts +++ b/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts @@ -11,7 +11,6 @@ import { assetDataUtils } from './utils/asset_data_utils'; import { OrderProvider, StandardRelayerApiAssetBuyerManagerError } from './types'; -// TODO: Read-only list of available assetDatas export class StandardRelayerAPIAssetBuyerManager { // Map of assetData to AssetBuyer for that assetData public readonly assetBuyerMap: ObjectMap; @@ -123,4 +122,12 @@ export class StandardRelayerAPIAssetBuyerManager { const assetData = assetDataUtils.encodeERC20AssetData(tokenAddress); return this.getAssetBuyerFromAssetData(assetData); } + /** + * Get a list of all the assetDatas that the instance supports + * + * @return An array of assetData strings + */ + public getAssetDatas(): string[] { + return _.keys(this.assetBuyerMap); + } } From fcca63a2dc17f7745304d02dfaffe60f0d6785c4 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 20 Sep 2018 23:39:55 +0200 Subject: [PATCH 33/47] Move some logic to order utils --- .../asset-buyer/src/utils/buy_quote_calculator.ts | 10 ++++++---- packages/asset-buyer/src/utils/order_utils.ts | 14 ++++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index 31823cc028..aa7159e6c8 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -3,6 +3,7 @@ import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import { constants } from '../constants'; +import { orderUtils } from './order_utils'; import { AssetBuyerError, AssetBuyerOrdersAndFillableAmounts, BuyQuote } from '../types'; export const buyQuoteCalculator = { @@ -30,7 +31,7 @@ export const buyQuoteCalculator = { if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { throw new Error(AssetBuyerError.InsufficientAssetLiquidity); } - // TODO: optimization + // TODO(bmillman): optimization // update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to // finding order that cover all fees, this will help with estimating ETH and minimizing gas usage const { @@ -59,10 +60,11 @@ export const buyQuoteCalculator = { let maxEthAmount = constants.ZERO_AMOUNT; let cumulativeMakerAmount = constants.ZERO_AMOUNT; _.forEach(allOrders, (order, index) => { - // TODO: Move this logic to order_utils const remainingFillableMakerAssetAmount = allRemainingAmounts[index]; - const orderRate = order.takerAssetAmount.div(order.makerAssetAmount); - const claimableTakerAssetAmount = orderRate.mul(remainingFillableMakerAssetAmount); + const claimableTakerAssetAmount = orderUtils.calculateRemainingTakerAssetAmount( + order, + remainingFillableMakerAssetAmount, + ); // taker asset is always assumed to be WETH maxEthAmount = maxEthAmount.plus(claimableTakerAssetAmount); if (cumulativeMakerAmount.lessThan(assetBuyAmount)) { diff --git a/packages/asset-buyer/src/utils/order_utils.ts b/packages/asset-buyer/src/utils/order_utils.ts index bb0bdb80f7..27db732578 100644 --- a/packages/asset-buyer/src/utils/order_utils.ts +++ b/packages/asset-buyer/src/utils/order_utils.ts @@ -10,10 +10,16 @@ export const orderUtils = { return order.expirationTimeSeconds.lessThan(currentUnixTimestampSec); }, calculateRemainingMakerAssetAmount(order: SignedOrder, remainingTakerAssetAmount: BigNumber): BigNumber { - const result = remainingTakerAssetAmount.eq(0) - ? constants.ZERO_AMOUNT - : remainingTakerAssetAmount.times(order.makerAssetAmount).dividedToIntegerBy(order.takerAssetAmount); - return result; + if (remainingTakerAssetAmount.eq(0)) { + return constants.ZERO_AMOUNT; + } + return remainingTakerAssetAmount.times(order.makerAssetAmount).dividedToIntegerBy(order.takerAssetAmount); + }, + calculateRemainingTakerAssetAmount(order: SignedOrder, remainingMakerAssetAmount: BigNumber): BigNumber { + if (remainingMakerAssetAmount.eq(0)) { + return constants.ZERO_AMOUNT; + } + return remainingMakerAssetAmount.times(order.takerAssetAmount).dividedToIntegerBy(order.makerAssetAmount); }, isOpenOrder(order: SignedOrder): boolean { return order.takerAddress === constants.NULL_ADDRESS; From 97150cf55f472ca5c77b28d064976d84df9f2436 Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 21 Sep 2018 14:56:54 +0200 Subject: [PATCH 34/47] Add labels to TODOs --- packages/asset-buyer/src/utils/buy_quote_calculator.ts | 3 ++- .../src/utils/order_fetcher_response_processor.ts | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index aa7159e6c8..3d90a6ead9 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -3,9 +3,10 @@ import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import { constants } from '../constants'; -import { orderUtils } from './order_utils'; import { AssetBuyerError, AssetBuyerOrdersAndFillableAmounts, BuyQuote } from '../types'; +import { orderUtils } from './order_utils'; + export const buyQuoteCalculator = { calculate( ordersAndFillableAmounts: AssetBuyerOrdersAndFillableAmounts, diff --git a/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts b/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts index 98ab6b9d1c..ec4a6ce7e1 100644 --- a/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts +++ b/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts @@ -41,9 +41,9 @@ export const orderFetcherResponseProcessor = { let unsortedFeeOrders = filteredFeeOrders; // if an orderValidator is provided, use on chain information to calculate remaining fillable makerAsset amounts if (!_.isUndefined(orderValidator)) { - // TODO: critical + // TODO(bmillman): improvement // try catch these requests and throw a more domain specific error - // TODO: optimization + // TODO(bmillman): optimization // reduce this to once RPC call buy combining orders into one array and then splitting up the response const [targetOrdersAndTradersInfo, feeOrdersAndTradersInfo] = await Promise.all( _.map([filteredTargetOrders, filteredFeeOrders], ordersToBeValidated => { @@ -65,7 +65,7 @@ export const orderFetcherResponseProcessor = { ); } // sort orders by rate - // TODO: optimization + // TODO(bmillman): optimization // provide a feeRate to the sorting function to more accurately sort based on the current market for ZRX tokens const sortedTargetOrders = sortingUtils.sortOrdersByFeeAdjustedRate(unsortedTargetOrders); const sortedFeeOrders = sortingUtils.sortFeeOrdersByFeeAdjustedRate(unsortedFeeOrders); From 8dea48ebeff775f7437c45f5a6d39dfc7dbf704e Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 21 Sep 2018 15:21:44 +0200 Subject: [PATCH 35/47] Renamed orderFetcherResponseProcessor to orderProviderResponseProcessoer --- packages/asset-buyer/src/asset_buyer.ts | 4 ++-- ...ocessor.ts => order_provider_response_processor.ts} | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) rename packages/asset-buyer/src/utils/{order_fetcher_response_processor.ts => order_provider_response_processor.ts} (97%) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index e74b8eac1c..74ced0e51c 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -21,7 +21,7 @@ import { import { assert } from './utils/assert'; import { assetDataUtils } from './utils/asset_data_utils'; import { buyQuoteCalculator } from './utils/buy_quote_calculator'; -import { orderFetcherResponseProcessor } from './utils/order_fetcher_response_processor'; +import { orderProviderResponseProcessor } from './utils/order_provider_response_processor'; export class AssetBuyer { public readonly provider: Provider; @@ -270,7 +270,7 @@ export class AssetBuyer { _.map(requests, async request => this.orderFetcher.getOrdersAsync(request)), ); // process the responses into one object - const ordersAndFillableAmounts = await orderFetcherResponseProcessor.processAsync( + const ordersAndFillableAmounts = await orderProviderResponseProcessor.processAsync( targetOrderFetcherResponse, feeOrderFetcherResponse, zrxTokenAssetData, diff --git a/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts b/packages/asset-buyer/src/utils/order_provider_response_processor.ts similarity index 97% rename from packages/asset-buyer/src/utils/order_fetcher_response_processor.ts rename to packages/asset-buyer/src/utils/order_provider_response_processor.ts index ec4a6ce7e1..79e15c3d88 100644 --- a/packages/asset-buyer/src/utils/order_fetcher_response_processor.ts +++ b/packages/asset-buyer/src/utils/order_provider_response_processor.ts @@ -19,7 +19,7 @@ interface OrdersAndRemainingFillableMakerAssetAmounts { remainingFillableMakerAssetAmounts: BigNumber[]; } -export const orderFetcherResponseProcessor = { +export const orderProviderResponseProcessor = { /** * Take the responses for the target orders to buy and fee orders and process them. * Processing includes: @@ -28,14 +28,14 @@ export const orderFetcherResponseProcessor = { * - Sort by rate */ async processAsync( - targetOrderFetcherResponse: OrderProviderResponse, - feeOrderFetcherResponse: OrderProviderResponse, + targetOrderProviderResponse: OrderProviderResponse, + feeOrderProviderResponse: OrderProviderResponse, zrxTokenAssetData: string, orderValidator?: OrderValidatorWrapper, ): Promise { // drop orders that are expired or not open - const filteredTargetOrders = filterOutExpiredAndNonOpenOrders(targetOrderFetcherResponse.orders); - const filteredFeeOrders = filterOutExpiredAndNonOpenOrders(feeOrderFetcherResponse.orders); + const filteredTargetOrders = filterOutExpiredAndNonOpenOrders(targetOrderProviderResponse.orders); + const filteredFeeOrders = filterOutExpiredAndNonOpenOrders(feeOrderProviderResponse.orders); // set the orders to be sorted equal to the filtered orders let unsortedTargetOrders = filteredTargetOrders; let unsortedFeeOrders = filteredFeeOrders; From c64dcec772d8f5bfa7a32c5e60bfe808229d4d5e Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 21 Sep 2018 15:58:04 +0200 Subject: [PATCH 36/47] More renaming --- packages/asset-buyer/src/asset_buyer.ts | 40 +++++++++---------- packages/asset-buyer/src/index.ts | 4 +- .../order_fetchers/provided_order_fetcher.ts | 32 --------------- .../order_providers/basic_order_provider.ts | 32 +++++++++++++++ .../standard_relayer_api_order_provider.ts} | 20 +++++----- packages/asset-buyer/src/types.ts | 2 +- packages/asset-buyer/src/utils/assert.ts | 4 +- .../src/utils/buy_quote_calculator.ts | 1 + 8 files changed, 68 insertions(+), 67 deletions(-) delete mode 100644 packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts create mode 100644 packages/asset-buyer/src/order_providers/basic_order_provider.ts rename packages/asset-buyer/src/{order_fetchers/standard_relayer_api_order_fetcher.ts => order_providers/standard_relayer_api_order_provider.ts} (74%) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 74ced0e51c..9774a8d39d 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -7,8 +7,8 @@ import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; import { constants } from './constants'; -import { ProvidedOrderFetcher } from './order_fetchers/provided_order_fetcher'; -import { StandardRelayerAPIOrderFetcher } from './order_fetchers/standard_relayer_api_order_fetcher'; +import { BasicOrderProvider } from './order_providers/basic_order_provider'; +import { StandardRelayerAPIOrderProvider } from './order_providers/standard_relayer_api_order_provider'; import { AssetBuyerError, AssetBuyerOrdersAndFillableAmounts, @@ -26,7 +26,7 @@ import { orderProviderResponseProcessor } from './utils/order_provider_response_ export class AssetBuyer { public readonly provider: Provider; public readonly assetData: string; - public readonly orderFetcher: OrderProvider; + public readonly orderProvider: OrderProvider; public readonly networkId: number; public readonly orderRefreshIntervalMs: number; private readonly _contractWrappers: ContractWrappers; @@ -58,8 +58,8 @@ export class AssetBuyer { assert.areValidProvidedOrders('feeOrders', feeOrders); assert.assert(orders.length !== 0, `Expected orders to contain at least one order`); const assetData = orders[0].makerAssetData; - const orderFetcher = new ProvidedOrderFetcher(_.concat(orders, feeOrders)); - const assetBuyer = new AssetBuyer(provider, assetData, orderFetcher, networkId, orderRefreshIntervalMs); + const orderProvider = new BasicOrderProvider(_.concat(orders, feeOrders)); + const assetBuyer = new AssetBuyer(provider, assetData, orderProvider, networkId, orderRefreshIntervalMs); return assetBuyer; } /** @@ -84,8 +84,8 @@ export class AssetBuyer { assert.isWebUri('sraApiUrl', sraApiUrl); assert.isNumber('networkId', networkId); assert.isNumber('orderRefreshIntervalMs', orderRefreshIntervalMs); - const orderFetcher = new StandardRelayerAPIOrderFetcher(sraApiUrl); - const assetBuyer = new AssetBuyer(provider, assetData, orderFetcher, networkId, orderRefreshIntervalMs); + const orderProvider = new StandardRelayerAPIOrderProvider(sraApiUrl); + const assetBuyer = new AssetBuyer(provider, assetData, orderProvider, networkId, orderRefreshIntervalMs); return assetBuyer; } /** @@ -124,7 +124,7 @@ export class AssetBuyer { * Instantiates a new AssetBuyer instance * @param provider The Provider instance you would like to use for interacting with the Ethereum network. * @param assetData The assetData of the desired asset to buy (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). - * @param orderFetcher An object that conforms to OrderFetcher, see type for definition. + * @param orderProvider An object that conforms to OrderProvider, see type for definition. * @param networkId The ethereum network id. Defaults to 1 (mainnet). * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. * Defaults to 10000ms (10s). @@ -133,18 +133,18 @@ export class AssetBuyer { constructor( provider: Provider, assetData: string, - orderFetcher: OrderProvider, + orderProvider: OrderProvider, networkId: number = constants.MAINNET_NETWORK_ID, orderRefreshIntervalMs: number = constants.DEFAULT_ORDER_REFRESH_INTERVAL_MS, ) { assert.isWeb3Provider('provider', provider); assert.isString('assetData', assetData); - assert.isValidOrderFetcher('orderFetcher', orderFetcher); + assert.isValidOrderProvider('orderProvider', orderProvider); assert.isNumber('networkId', networkId); assert.isNumber('orderRefreshIntervalMs', orderRefreshIntervalMs); this.provider = provider; this.assetData = assetData; - this.orderFetcher = orderFetcher; + this.orderProvider = orderProvider; this.networkId = networkId; this.orderRefreshIntervalMs = orderRefreshIntervalMs; this._contractWrappers = new ContractWrappers(this.provider, { @@ -248,31 +248,31 @@ export class AssetBuyer { return txHash; } /** - * Ask the order fetcher for orders and process them. + * Ask the order Provider for orders and process them. */ private async _getLatestOrdersAndFillableAmountsAsync(): Promise { const etherTokenAssetData = this._getEtherTokenAssetDataOrThrow(); const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow(); - // construct order fetcher requests - const targetOrderFetcherRequest = { + // construct order Provider requests + const targetOrderProviderRequest = { makerAssetData: this.assetData, takerAssetData: etherTokenAssetData, networkId: this.networkId, }; - const feeOrderFetcherRequest = { + const feeOrderProviderRequest = { makerAssetData: zrxTokenAssetData, takerAssetData: etherTokenAssetData, networkId: this.networkId, }; - const requests = [targetOrderFetcherRequest, feeOrderFetcherRequest]; + const requests = [targetOrderProviderRequest, feeOrderProviderRequest]; // fetch orders and possible fillable amounts - const [targetOrderFetcherResponse, feeOrderFetcherResponse] = await Promise.all( - _.map(requests, async request => this.orderFetcher.getOrdersAsync(request)), + const [targetOrderProviderResponse, feeOrderProviderResponse] = await Promise.all( + _.map(requests, async request => this.orderProvider.getOrdersAsync(request)), ); // process the responses into one object const ordersAndFillableAmounts = await orderProviderResponseProcessor.processAsync( - targetOrderFetcherResponse, - feeOrderFetcherResponse, + targetOrderProviderResponse, + feeOrderProviderResponse, zrxTokenAssetData, this._contractWrappers.orderValidator, ); diff --git a/packages/asset-buyer/src/index.ts b/packages/asset-buyer/src/index.ts index aa10c3af23..8ef529ac0e 100644 --- a/packages/asset-buyer/src/index.ts +++ b/packages/asset-buyer/src/index.ts @@ -3,8 +3,8 @@ export { SignedOrder } from '@0xproject/types'; export { BigNumber } from '@0xproject/utils'; export { AssetBuyer } from './asset_buyer'; -export { ProvidedOrderFetcher } from './order_fetchers/provided_order_fetcher'; -export { StandardRelayerAPIOrderFetcher } from './order_fetchers/standard_relayer_api_order_fetcher'; +export { BasicOrderProvider } from './order_providers/basic_order_provider'; +export { StandardRelayerAPIOrderProvider } from './order_providers/standard_relayer_api_order_provider'; export { StandardRelayerAPIAssetBuyerManager } from './standard_relayer_api_asset_buyer_manager'; export { AssetBuyerError, diff --git a/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts b/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts deleted file mode 100644 index 397f296d7b..0000000000 --- a/packages/asset-buyer/src/order_fetchers/provided_order_fetcher.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { schemas } from '@0xproject/json-schemas'; -import { SignedOrder } from '@0xproject/types'; -import * as _ from 'lodash'; - -import { OrderProvider, OrderProviderRequest, OrderProviderResponse } from '../types'; -import { assert } from '../utils/assert'; - -export class ProvidedOrderFetcher implements OrderProvider { - public readonly providedOrders: SignedOrder[]; - /** - * Instantiates a new ProvidedOrderFetcher instance - * @param providedOrders An array of objects that conform to SignedOrder to fetch from. - * @return An instance of ProvidedOrderFetcher - */ - constructor(providedOrders: SignedOrder[]) { - assert.doesConformToSchema('providedOrders', providedOrders, schemas.signedOrdersSchema); - this.providedOrders = providedOrders; - } - /** - * Given an object that conforms to OrderFetcherRequest, return the corresponding OrderFetcherResponse that satisfies the request. - * @param orderFetchRequest An instance of OrderFetcherRequest. See type for more information. - * @return An instance of OrderFetcherResponse. See type for more information. - */ - public async getOrdersAsync(orderFetchRequest: OrderProviderRequest): Promise { - assert.isValidOrderFetcherRequest('orderFetchRequest', orderFetchRequest); - const { makerAssetData, takerAssetData } = orderFetchRequest; - const orders = _.filter(this.providedOrders, order => { - return order.makerAssetData === makerAssetData && order.takerAssetData === takerAssetData; - }); - return { orders }; - } -} diff --git a/packages/asset-buyer/src/order_providers/basic_order_provider.ts b/packages/asset-buyer/src/order_providers/basic_order_provider.ts new file mode 100644 index 0000000000..9bb2d90acd --- /dev/null +++ b/packages/asset-buyer/src/order_providers/basic_order_provider.ts @@ -0,0 +1,32 @@ +import { schemas } from '@0xproject/json-schemas'; +import { SignedOrder } from '@0xproject/types'; +import * as _ from 'lodash'; + +import { OrderProvider, OrderProviderRequest, OrderProviderResponse } from '../types'; +import { assert } from '../utils/assert'; + +export class BasicOrderProvider implements OrderProvider { + public readonly orders: SignedOrder[]; + /** + * Instantiates a new BasicOrderProvider instance + * @param orders An array of objects that conform to SignedOrder to fetch from. + * @return An instance of BasicOrderProvider + */ + constructor(orders: SignedOrder[]) { + assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); + this.orders = orders; + } + /** + * Given an object that conforms to OrderFetcherRequest, return the corresponding OrderProviderResponse that satisfies the request. + * @param orderProviderRequest An instance of OrderFetcherRequest. See type for more information. + * @return An instance of OrderProviderResponse. See type for more information. + */ + public async getOrdersAsync(orderProviderRequest: OrderProviderRequest): Promise { + assert.isValidOrderProviderRequest('orderProviderRequest', orderProviderRequest); + const { makerAssetData, takerAssetData } = orderProviderRequest; + const orders = _.filter(this.orders, order => { + return order.makerAssetData === makerAssetData && order.takerAssetData === takerAssetData; + }); + return { orders }; + } +} diff --git a/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts similarity index 74% rename from packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts rename to packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts index 730eaf4e0b..be3e9da50d 100644 --- a/packages/asset-buyer/src/order_fetchers/standard_relayer_api_order_fetcher.ts +++ b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts @@ -11,7 +11,7 @@ import { import { assert } from '../utils/assert'; import { orderUtils } from '../utils/order_utils'; -export class StandardRelayerAPIOrderFetcher implements OrderProvider { +export class StandardRelayerAPIOrderProvider implements OrderProvider { public readonly apiUrl: string; private readonly _sraClient: HttpClient; /** @@ -42,9 +42,9 @@ export class StandardRelayerAPIOrderFetcher implements OrderProvider { return result; } /** - * Instantiates a new StandardRelayerAPIOrderFetcher instance + * Instantiates a new StandardRelayerAPIOrderProvider instance * @param apiUrl The standard relayer API base HTTP url you would like to source orders from. - * @return An instance of StandardRelayerAPIOrderFetcher + * @return An instance of StandardRelayerAPIOrderProvider */ constructor(apiUrl: string) { assert.isWebUri('apiUrl', apiUrl); @@ -52,13 +52,13 @@ export class StandardRelayerAPIOrderFetcher implements OrderProvider { this._sraClient = new HttpClient(apiUrl); } /** - * Given an object that conforms to OrderFetcherRequest, return the corresponding OrderFetcherResponse that satisfies the request. - * @param orderFetchRequest An instance of OrderFetcherRequest. See type for more information. - * @return An instance of OrderFetcherResponse. See type for more information. + * Given an object that conforms to OrderProviderRequest, return the corresponding OrderProviderResponse that satisfies the request. + * @param orderProviderRequest An instance of OrderProviderRequest. See type for more information. + * @return An instance of OrderProviderResponse. See type for more information. */ - public async getOrdersAsync(orderFetchRequest: OrderProviderRequest): Promise { - assert.isValidOrderFetcherRequest('orderFetchRequest', orderFetchRequest); - const { makerAssetData, takerAssetData, networkId } = orderFetchRequest; + public async getOrdersAsync(orderProviderRequest: OrderProviderRequest): Promise { + assert.isValidOrderProviderRequest('orderProviderRequest', orderProviderRequest); + const { makerAssetData, takerAssetData, networkId } = orderProviderRequest; const orderbookRequest = { baseAssetData: makerAssetData, quoteAssetData: takerAssetData }; const requestOpts = { networkId }; let orderbook: OrderbookResponse; @@ -68,7 +68,7 @@ export class StandardRelayerAPIOrderFetcher implements OrderProvider { throw new Error(AssetBuyerError.StandardRelayerApiError); } const apiOrders = orderbook.asks.records; - const orders = StandardRelayerAPIOrderFetcher._getSignedOrderWithRemainingFillableMakerAssetAmountFromApi( + const orders = StandardRelayerAPIOrderProvider._getSignedOrderWithRemainingFillableMakerAssetAmountFromApi( apiOrders, ); return { diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index 09319fca48..5df8e632a5 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -27,7 +27,7 @@ export interface SignedOrderWithRemainingFillableMakerAssetAmount extends Signed remainingFillableMakerAssetAmount?: BigNumber; } /** - * Given an OrderFetchRequest, get an OrderFetchResponse. + * Given an OrderProviderRequest, get an OrderProviderResponse. */ export interface OrderProvider { getOrdersAsync: (orderProviderRequest: OrderProviderRequest) => Promise; diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts index 1d57ae9b80..2c34268949 100644 --- a/packages/asset-buyer/src/utils/assert.ts +++ b/packages/asset-buyer/src/utils/assert.ts @@ -18,10 +18,10 @@ export const assert = { sharedAssert.isNumber(`${variableName}.feePercentage`, buyQuote.feePercentage); } }, - isValidOrderFetcher(variableName: string, orderFetcher: OrderProvider): void { + isValidOrderProvider(variableName: string, orderFetcher: OrderProvider): void { sharedAssert.isFunction(`${variableName}.fetchOrdersAsync`, orderFetcher.getOrdersAsync); }, - isValidOrderFetcherRequest(variableName: string, orderFetcherRequest: OrderProviderRequest): void { + isValidOrderProviderRequest(variableName: string, orderFetcherRequest: OrderProviderRequest): void { sharedAssert.isHexString(`${variableName}.makerAssetData`, orderFetcherRequest.makerAssetData); sharedAssert.isHexString(`${variableName}.takerAssetData`, orderFetcherRequest.takerAssetData); sharedAssert.isNumber(`${variableName}.networkId`, orderFetcherRequest.networkId); diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index 3d90a6ead9..9946924ef8 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -7,6 +7,7 @@ import { AssetBuyerError, AssetBuyerOrdersAndFillableAmounts, BuyQuote } from '. import { orderUtils } from './order_utils'; +// Calculates a buy quote for orders that have WETH as the takerAsset export const buyQuoteCalculator = { calculate( ordersAndFillableAmounts: AssetBuyerOrdersAndFillableAmounts, From c48cf3ab3b74a9082eb30540115534c7432667ac Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 21 Sep 2018 16:03:47 +0200 Subject: [PATCH 37/47] Make asset buyer map private --- .../src/standard_relayer_api_asset_buyer_manager.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts b/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts index 207713110a..947c738a19 100644 --- a/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts +++ b/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts @@ -13,7 +13,7 @@ import { OrderProvider, StandardRelayerApiAssetBuyerManagerError } from './types export class StandardRelayerAPIAssetBuyerManager { // Map of assetData to AssetBuyer for that assetData - public readonly assetBuyerMap: ObjectMap; + private readonly _assetBuyerMap: ObjectMap; /** * Returns an array of all assetDatas available at the provided sraApiUrl * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from. @@ -82,7 +82,7 @@ export class StandardRelayerAPIAssetBuyerManager { orderRefreshIntervalMs?: number, ) { assert.assert(assetDatas.length > 0, `Expected 'assetDatas' to be a non-empty array.`); - this.assetBuyerMap = _.reduce( + this._assetBuyerMap = _.reduce( assetDatas, (accAssetBuyerMap: ObjectMap, assetData: string) => { accAssetBuyerMap[assetData] = new AssetBuyer( @@ -104,7 +104,7 @@ export class StandardRelayerAPIAssetBuyerManager { * @return An instance of AssetBuyer */ public getAssetBuyerFromAssetData(assetData: string): AssetBuyer { - const assetBuyer = this.assetBuyerMap[assetData]; + const assetBuyer = this._assetBuyerMap[assetData]; if (_.isUndefined(assetBuyer)) { throw new Error( `${StandardRelayerApiAssetBuyerManagerError.AssetBuyerNotFound}: For assetData ${assetData}`, @@ -128,6 +128,6 @@ export class StandardRelayerAPIAssetBuyerManager { * @return An array of assetData strings */ public getAssetDatas(): string[] { - return _.keys(this.assetBuyerMap); + return _.keys(this._assetBuyerMap); } } From 1bfaefb240d08eb0a8a5d6743d529604383f6e43 Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 21 Sep 2018 16:24:45 +0200 Subject: [PATCH 38/47] Add order provider response validation --- packages/asset-buyer/src/asset_buyer.ts | 4 ++++ packages/asset-buyer/src/types.ts | 1 + .../src/utils/order_provider_response_processor.ts | 10 ++++++++++ 3 files changed, 15 insertions(+) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 9774a8d39d..03f9b5a2b4 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -269,6 +269,10 @@ export class AssetBuyer { const [targetOrderProviderResponse, feeOrderProviderResponse] = await Promise.all( _.map(requests, async request => this.orderProvider.getOrdersAsync(request)), ); + // since the order provider is an injected dependency, validate that it respects the API + // ie. it should only return maker/taker assetDatas that are specified + orderProviderResponseProcessor.throwIfInvalidResponse(targetOrderProviderResponse, targetOrderProviderRequest); + orderProviderResponseProcessor.throwIfInvalidResponse(feeOrderProviderResponse, feeOrderProviderRequest); // process the responses into one object const ordersAndFillableAmounts = await orderProviderResponseProcessor.processAsync( targetOrderProviderResponse, diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index 5df8e632a5..ee6858525d 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -68,6 +68,7 @@ export enum AssetBuyerError { InsufficientAssetLiquidity = 'INSUFFICIENT_ASSET_LIQUIDITY', InsufficientZrxLiquidity = 'INSUFFICIENT_ZRX_LIQUIDITY', NoAddressAvailable = 'NO_ADDRESS_AVAILABLE', + InvalidOrderProviderResponse = 'INVALID_ORDER_PROVIDER_RESPONSE', } /** diff --git a/packages/asset-buyer/src/utils/order_provider_response_processor.ts b/packages/asset-buyer/src/utils/order_provider_response_processor.ts index 79e15c3d88..2f3208a551 100644 --- a/packages/asset-buyer/src/utils/order_provider_response_processor.ts +++ b/packages/asset-buyer/src/utils/order_provider_response_processor.ts @@ -7,7 +7,9 @@ import * as _ from 'lodash'; import { constants } from '../constants'; import { + AssetBuyerError, AssetBuyerOrdersAndFillableAmounts, + OrderProviderRequest, OrderProviderResponse, SignedOrderWithRemainingFillableMakerAssetAmount, } from '../types'; @@ -20,6 +22,14 @@ interface OrdersAndRemainingFillableMakerAssetAmounts { } export const orderProviderResponseProcessor = { + throwIfInvalidResponse(response: OrderProviderResponse, request: OrderProviderRequest): void { + const { makerAssetData, takerAssetData } = request; + _.forEach(response.orders, order => { + if (order.makerAssetData !== makerAssetData || order.takerAssetData !== takerAssetData) { + throw new Error(AssetBuyerError.InvalidOrderProviderResponse); + } + }); + }, /** * Take the responses for the target orders to buy and fee orders and process them. * Processing includes: From 5e84e9689f0899a39dbfaffe13b80fa07fec5ba8 Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 21 Sep 2018 16:53:18 +0200 Subject: [PATCH 39/47] Small typos --- packages/asset-buyer/src/utils/assert.ts | 2 +- .../asset-buyer/src/utils/order_provider_response_processor.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts index 2c34268949..04f4252376 100644 --- a/packages/asset-buyer/src/utils/assert.ts +++ b/packages/asset-buyer/src/utils/assert.ts @@ -19,7 +19,7 @@ export const assert = { } }, isValidOrderProvider(variableName: string, orderFetcher: OrderProvider): void { - sharedAssert.isFunction(`${variableName}.fetchOrdersAsync`, orderFetcher.getOrdersAsync); + sharedAssert.isFunction(`${variableName}.getOrdersAsync`, orderFetcher.getOrdersAsync); }, isValidOrderProviderRequest(variableName: string, orderFetcherRequest: OrderProviderRequest): void { sharedAssert.isHexString(`${variableName}.makerAssetData`, orderFetcherRequest.makerAssetData); diff --git a/packages/asset-buyer/src/utils/order_provider_response_processor.ts b/packages/asset-buyer/src/utils/order_provider_response_processor.ts index 2f3208a551..9e54300b3f 100644 --- a/packages/asset-buyer/src/utils/order_provider_response_processor.ts +++ b/packages/asset-buyer/src/utils/order_provider_response_processor.ts @@ -52,7 +52,7 @@ export const orderProviderResponseProcessor = { // if an orderValidator is provided, use on chain information to calculate remaining fillable makerAsset amounts if (!_.isUndefined(orderValidator)) { // TODO(bmillman): improvement - // try catch these requests and throw a more domain specific error + // try/catch these requests and throw a more domain specific error // TODO(bmillman): optimization // reduce this to once RPC call buy combining orders into one array and then splitting up the response const [targetOrdersAndTradersInfo, feeOrdersAndTradersInfo] = await Promise.all( From e4b664bafa24c2ee788a343edecd0124f784780d Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 24 Sep 2018 13:17:38 +0200 Subject: [PATCH 40/47] Add expiry buffer --- packages/asset-buyer/src/asset_buyer.ts | 45 ++++++++++++++----- packages/asset-buyer/src/constants.ts | 1 + .../order_provider_response_processor.ts | 14 ++++-- packages/asset-buyer/src/utils/order_utils.ts | 5 ++- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 03f9b5a2b4..409e34e74b 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -29,6 +29,7 @@ export class AssetBuyer { public readonly orderProvider: OrderProvider; public readonly networkId: number; public readonly orderRefreshIntervalMs: number; + public readonly expiryBufferSeconds: number; private readonly _contractWrappers: ContractWrappers; private _lastRefreshTimeIfExists?: number; private _currentOrdersAndFillableAmountsIfExists?: AssetBuyerOrdersAndFillableAmounts; @@ -38,8 +39,9 @@ export class AssetBuyer { * @param orders A non-empty array of objects that conform to SignedOrder. All orders must have the same makerAssetData and takerAssetData (WETH). * @param feeOrders A array of objects that conform to SignedOrder. All orders must have the same makerAssetData (ZRX) and takerAssetData (WETH). Defaults to an empty array. * @param networkId The ethereum network id. Defaults to 1 (mainnet). - * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. - * Defaults to 10000ms (10s). + * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s). + * @param expiryBufferSeconds The number of seconds to add when calculating whether an order is expired or not. Defaults to 15s. + * * @return An instance of AssetBuyer */ public static getAssetBuyerForProvidedOrders( @@ -48,6 +50,7 @@ export class AssetBuyer { feeOrders: SignedOrder[] = [], networkId: number = constants.MAINNET_NETWORK_ID, orderRefreshIntervalMs: number = constants.DEFAULT_ORDER_REFRESH_INTERVAL_MS, + expiryBufferSeconds: number = constants.DEFAULT_EXPIRY_BUFFER_SECONDS, ): AssetBuyer { assert.isWeb3Provider('provider', provider); assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); @@ -59,7 +62,14 @@ export class AssetBuyer { assert.assert(orders.length !== 0, `Expected orders to contain at least one order`); const assetData = orders[0].makerAssetData; const orderProvider = new BasicOrderProvider(_.concat(orders, feeOrders)); - const assetBuyer = new AssetBuyer(provider, assetData, orderProvider, networkId, orderRefreshIntervalMs); + const assetBuyer = new AssetBuyer( + provider, + assetData, + orderProvider, + networkId, + orderRefreshIntervalMs, + expiryBufferSeconds, + ); return assetBuyer; } /** @@ -68,8 +78,9 @@ export class AssetBuyer { * @param assetData The assetData that identifies the desired asset to buy. * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from. * @param networkId The ethereum network id. Defaults to 1 (mainnet). - * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. - * Defaults to 10000ms (10s). + * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s). + * @param expiryBufferSeconds The number of seconds to add when calculating whether an order is expired or not. Defaults to 15s. + * * @return An instance of AssetBuyer */ public static getAssetBuyerForAssetData( @@ -78,6 +89,7 @@ export class AssetBuyer { sraApiUrl: string, networkId: number = constants.MAINNET_NETWORK_ID, orderRefreshIntervalMs: number = constants.DEFAULT_ORDER_REFRESH_INTERVAL_MS, + expiryBufferSeconds: number = constants.DEFAULT_EXPIRY_BUFFER_SECONDS, ): AssetBuyer { assert.isWeb3Provider('provider', provider); assert.isHexString('assetData', assetData); @@ -85,7 +97,14 @@ export class AssetBuyer { assert.isNumber('networkId', networkId); assert.isNumber('orderRefreshIntervalMs', orderRefreshIntervalMs); const orderProvider = new StandardRelayerAPIOrderProvider(sraApiUrl); - const assetBuyer = new AssetBuyer(provider, assetData, orderProvider, networkId, orderRefreshIntervalMs); + const assetBuyer = new AssetBuyer( + provider, + assetData, + orderProvider, + networkId, + orderRefreshIntervalMs, + expiryBufferSeconds, + ); return assetBuyer; } /** @@ -94,8 +113,8 @@ export class AssetBuyer { * @param tokenAddress The ERC20 token address that identifies the desired asset to buy. * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from. * @param networkId The ethereum network id. Defaults to 1 (mainnet). - * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. - * Defaults to 10000ms (10s). + * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s). + * @param expiryBufferSeconds The number of seconds to add when calculating whether an order is expired or not. Defaults to 15s. * @return An instance of AssetBuyer */ public static getAssetBuyerForERC20TokenAddress( @@ -104,6 +123,7 @@ export class AssetBuyer { sraApiUrl: string, networkId: number = constants.MAINNET_NETWORK_ID, orderRefreshIntervalMs: number = constants.DEFAULT_ORDER_REFRESH_INTERVAL_MS, + expiryBufferSeconds: number = constants.DEFAULT_EXPIRY_BUFFER_SECONDS, ): AssetBuyer { assert.isWeb3Provider('provider', provider); assert.isETHAddressHex('tokenAddress', tokenAddress); @@ -117,6 +137,7 @@ export class AssetBuyer { sraApiUrl, networkId, orderRefreshIntervalMs, + expiryBufferSeconds, ); return assetBuyer; } @@ -126,8 +147,9 @@ export class AssetBuyer { * @param assetData The assetData of the desired asset to buy (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * @param orderProvider An object that conforms to OrderProvider, see type for definition. * @param networkId The ethereum network id. Defaults to 1 (mainnet). - * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. - * Defaults to 10000ms (10s). + * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s). + * @param expiryBufferSeconds The number of seconds to add when calculating whether an order is expired or not. Defaults to 15s. + * * @return An instance of AssetBuyer */ constructor( @@ -136,6 +158,7 @@ export class AssetBuyer { orderProvider: OrderProvider, networkId: number = constants.MAINNET_NETWORK_ID, orderRefreshIntervalMs: number = constants.DEFAULT_ORDER_REFRESH_INTERVAL_MS, + expiryBufferSeconds: number = constants.DEFAULT_EXPIRY_BUFFER_SECONDS, ) { assert.isWeb3Provider('provider', provider); assert.isString('assetData', assetData); @@ -146,6 +169,7 @@ export class AssetBuyer { this.assetData = assetData; this.orderProvider = orderProvider; this.networkId = networkId; + this.expiryBufferSeconds = expiryBufferSeconds; this.orderRefreshIntervalMs = orderRefreshIntervalMs; this._contractWrappers = new ContractWrappers(this.provider, { networkId, @@ -278,6 +302,7 @@ export class AssetBuyer { targetOrderProviderResponse, feeOrderProviderResponse, zrxTokenAssetData, + this.expiryBufferSeconds, this._contractWrappers.orderValidator, ); return ordersAndFillableAmounts; diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts index 0ebe0f8e2f..79b5d90525 100644 --- a/packages/asset-buyer/src/constants.ts +++ b/packages/asset-buyer/src/constants.ts @@ -16,4 +16,5 @@ export const constants = { ETHER_TOKEN_DECIMALS: 18, DEFAULT_BUY_QUOTE_REQUEST_OPTS, MAX_PER_PAGE: 10000, + DEFAULT_EXPIRY_BUFFER_SECONDS: 15, }; diff --git a/packages/asset-buyer/src/utils/order_provider_response_processor.ts b/packages/asset-buyer/src/utils/order_provider_response_processor.ts index 9e54300b3f..31fdcc182e 100644 --- a/packages/asset-buyer/src/utils/order_provider_response_processor.ts +++ b/packages/asset-buyer/src/utils/order_provider_response_processor.ts @@ -41,11 +41,18 @@ export const orderProviderResponseProcessor = { targetOrderProviderResponse: OrderProviderResponse, feeOrderProviderResponse: OrderProviderResponse, zrxTokenAssetData: string, + expiryBufferSeconds: number, orderValidator?: OrderValidatorWrapper, ): Promise { // drop orders that are expired or not open - const filteredTargetOrders = filterOutExpiredAndNonOpenOrders(targetOrderProviderResponse.orders); - const filteredFeeOrders = filterOutExpiredAndNonOpenOrders(feeOrderProviderResponse.orders); + const filteredTargetOrders = filterOutExpiredAndNonOpenOrders( + targetOrderProviderResponse.orders, + expiryBufferSeconds, + ); + const filteredFeeOrders = filterOutExpiredAndNonOpenOrders( + feeOrderProviderResponse.orders, + expiryBufferSeconds, + ); // set the orders to be sorted equal to the filtered orders let unsortedTargetOrders = filteredTargetOrders; let unsortedFeeOrders = filteredFeeOrders; @@ -98,9 +105,10 @@ export const orderProviderResponseProcessor = { */ function filterOutExpiredAndNonOpenOrders( orders: SignedOrderWithRemainingFillableMakerAssetAmount[], + expiryBufferSeconds: number, ): SignedOrderWithRemainingFillableMakerAssetAmount[] { const result = _.filter(orders, order => { - return orderUtils.isOpenOrder(order) && !orderUtils.isOrderExpired(order); + return orderUtils.isOpenOrder(order) && !orderUtils.willOrderExpire(order, expiryBufferSeconds); }); return result; } diff --git a/packages/asset-buyer/src/utils/order_utils.ts b/packages/asset-buyer/src/utils/order_utils.ts index 27db732578..62166eb76c 100644 --- a/packages/asset-buyer/src/utils/order_utils.ts +++ b/packages/asset-buyer/src/utils/order_utils.ts @@ -5,9 +5,12 @@ import { constants } from '../constants'; export const orderUtils = { isOrderExpired(order: SignedOrder): boolean { + return orderUtils.willOrderExpire(order, 0); + }, + willOrderExpire(order: SignedOrder, secondsFromNow: number): boolean { const millisecondsInSecond = 1000; const currentUnixTimestampSec = new BigNumber(Date.now() / millisecondsInSecond).round(); - return order.expirationTimeSeconds.lessThan(currentUnixTimestampSec); + return order.expirationTimeSeconds.lessThan(currentUnixTimestampSec.minus(secondsFromNow)); }, calculateRemainingMakerAssetAmount(order: SignedOrder, remainingTakerAssetAmount: BigNumber): BigNumber { if (remainingTakerAssetAmount.eq(0)) { From 7a43e1911615d4f59a2cecdb2a98cac95f205f86 Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 24 Sep 2018 15:43:02 +0200 Subject: [PATCH 41/47] Improve README --- packages/asset-buyer/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/asset-buyer/README.md b/packages/asset-buyer/README.md index f615b93e1b..5f7f26f307 100644 --- a/packages/asset-buyer/README.md +++ b/packages/asset-buyer/README.md @@ -1,11 +1,9 @@ ## @0xproject/asset-buyer -Convenience package for buying assets represented on the Ethereum blockchain using 0x. In its simplest form, the package helps in the usage of the [0x forwarder contract](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md), which allows users to execute Wrapped Ether based 0x orders without having to set allowances, wrap Ether or buy ZRX, meaning they can buy tokens with Ether alone. Given some liquidity (0x signed orders), it helps estimate the Ether cost of buying a certain asset (giving a range) and then buying that asset. +Convenience package for buying assets represented on the Ethereum blockchain using 0x. In its simplest form, the package helps in the usage of the [0x forwarder contract](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md), which allows users to execute [Wrapped Ether](https://weth.io/) based 0x orders without having to set allowances, wrap Ether or buy ZRX, meaning they can buy tokens with Ether alone. Given some liquidity (0x signed orders), it helps estimate the Ether cost of buying a certain asset (giving a range) and then buying that asset. In its more advanced and useful form, it integrates with the [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) and takes care of sourcing liquidity for you given an SRA compliant endpoint. The final result is a library that tells you what assets are available, provides an Ether based quote for any asset desired, and allows you to buy that asset using Ether alone. -### Read the [Documentation](https://0xproject.com/docs/asset-buyer). - ## Installation ```bash From d8d1c98a40f5cc895329f73af4d8d000c6a5195b Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 24 Sep 2018 15:53:34 +0200 Subject: [PATCH 42/47] Upgrade all deps --- packages/asset-buyer/package.json | 16 ++++++++-------- yarn.lock | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json index f80f8db7e6..2b5136e310 100644 --- a/packages/asset-buyer/package.json +++ b/packages/asset-buyer/package.json @@ -36,16 +36,16 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/asset-buyer/README.md", "dependencies": { - "@0xproject/assert": "^1.0.8", - "@0xproject/connect": "^2.0.0", - "@0xproject/contract-wrappers": "^1.0.1", - "@0xproject/json-schemas": "^1.0.1", + "@0xproject/assert": "^1.0.9", + "@0xproject/connect": "^2.0.2", + "@0xproject/contract-wrappers": "^1.0.4", + "@0xproject/json-schemas": "^1.0.2", "@0xproject/order-utils": "^1.0.1", "@0xproject/subproviders": "^2.0.2", - "@0xproject/types": "^1.0.1", - "@0xproject/typescript-typings": "^2.0.0", - "@0xproject/utils": "^1.0.8", - "@0xproject/web3-wrapper": "^2.0.2", + "@0xproject/types": "^1.0.2", + "@0xproject/typescript-typings": "^2.0.1", + "@0xproject/utils": "^1.0.9", + "@0xproject/web3-wrapper": "^2.0.3", "ethereum-types": "^1.0.6", "lodash": "^4.17.10" }, diff --git a/yarn.lock b/yarn.lock index aa30946a86..7464a9c5d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5296,9 +5296,9 @@ ethereumjs-wallet@~0.6.0: utf8 "^3.0.0" uuid "^3.3.2" -ethers@0xproject/ethers.js#eip-838-reasons, ethers@3.0.22: - version "3.0.18" - resolved "https://codeload.github.com/0xproject/ethers.js/tar.gz/b91342bd200d142af0165d6befddf783c8ae8447" +ethers@3.0.22: + version "3.0.22" + resolved "https://registry.npmjs.org/ethers/-/ethers-3.0.22.tgz#7fab1ea16521705837aa43c15831877b2716b436" dependencies: aes-js "3.0.0" bn.js "^4.4.0" From 6f2ab23bd028ea460540e1519376bb2dd4e23c72 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Mon, 24 Sep 2018 13:54:25 -0700 Subject: [PATCH 43/47] Feature/custom verdaccio dockerfile (#1083) Create custom verdaccio docker image --- .circleci/config.yml | 2 +- packages/migrations/.gitignore | 1 + packages/verdaccio/Dockerfile | 3 ++ packages/verdaccio/README.md | 18 +++++++++ packages/verdaccio/conf.yaml | 71 ++++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 packages/migrations/.gitignore create mode 100644 packages/verdaccio/Dockerfile create mode 100644 packages/verdaccio/README.md create mode 100644 packages/verdaccio/conf.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index d5ae2c2fcc..446ef499de 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -64,7 +64,7 @@ jobs: resource_class: medium+ docker: - image: circleci/node:9 - - image: verdaccio/verdaccio + - image: albrow/0x-verdaccio working_directory: ~/repo steps: - restore_cache: diff --git a/packages/migrations/.gitignore b/packages/migrations/.gitignore new file mode 100644 index 0000000000..dc4af7cedb --- /dev/null +++ b/packages/migrations/.gitignore @@ -0,0 +1 @@ +artifacts/development diff --git a/packages/verdaccio/Dockerfile b/packages/verdaccio/Dockerfile new file mode 100644 index 0000000000..6a6a64abf5 --- /dev/null +++ b/packages/verdaccio/Dockerfile @@ -0,0 +1,3 @@ +FROM verdaccio/verdaccio + +ADD conf.yaml /verdaccio/conf/config.yaml diff --git a/packages/verdaccio/README.md b/packages/verdaccio/README.md new file mode 100644 index 0000000000..340cd55124 --- /dev/null +++ b/packages/verdaccio/README.md @@ -0,0 +1,18 @@ +## 0x Verdaccio + +This package contains a Dockerfile and conf.yaml file for configuring our own +Docker image for Verdaccio. + +See https://verdaccio.org/docs/en/configuration for more information. + +## Build + +In the root directory for _this package_, run: + +`sudo docker build . -t 0x-verdaccio` + +## Run + +To start Verdaccio run: + +`sudo docker run --rm -i -p 4873:4873 0x-verdaccio` diff --git a/packages/verdaccio/conf.yaml b/packages/verdaccio/conf.yaml new file mode 100644 index 0000000000..24e0369328 --- /dev/null +++ b/packages/verdaccio/conf.yaml @@ -0,0 +1,71 @@ +# +# Copied from https://github.com/verdaccio/verdaccio/blob/90fc216649ecd8df31d3706b5c17555fa0a35e4a/conf/docker.yaml +# Only the uplinks section has been modified. +# +# This is the config file used for the docker images. +# It allows all users to do anything, so don't use it on production systems. +# +# Do not configure host and port under `listen` in this file +# as it will be ignored when using docker. +# see https://github.com/verdaccio/verdaccio/blob/master/wiki/docker.md#docker-and-custom-port-configuration +# +# Look here for more config file examples: +# https://github.com/verdaccio/verdaccio/tree/master/conf +# + +# path to a directory with all packages +storage: /verdaccio/storage +# path to a directory with plugins to include +plugins: /verdaccio/plugins + +web: + # WebUI is enabled as default, if you want disable it, just uncomment this line + enable: false + +auth: + htpasswd: + file: /verdaccio/conf/htpasswd + # Maximum amount of users allowed to register, defaults to "+infinity". + # You can set this to -1 to disable registration. + #max_users: 1000 + +# a list of other known repositories we can talk to +uplinks: + npmjs: + url: https://registry.npmjs.org/ + cache: true + max_fails: 5 + +packages: + '@*/*': + # scoped packages + access: $all + publish: $authenticated + proxy: npmjs + + '**': + # allow all users (including non-authenticated users) to read and + # publish all packages + # + # you can specify usernames/groupnames (depending on your auth plugin) + # and three keywords: "$all", "$anonymous", "$authenticated" + access: $all + + # allow all known users to publish packages + # (anyone can register by default, remember?) + publish: $authenticated + + # if package is not available locally, proxy requests to 'npmjs' registry + proxy: npmjs + +# To use `npm audit` uncomment the following section +# middlewares: +# audit: +# enabled: true + +# log settings +logs: + - {type: stdout, format: pretty, level: http} + #- {type: file, path: verdaccio.log, level: info} + +max_body_size: 100mb From 408f5731880d61877964275301ad10d522aad6a7 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Mon, 24 Sep 2018 14:31:26 -0700 Subject: [PATCH 44/47] Use the 0xorg DockerHub account instead of albrow (#1084) --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 446ef499de..537b193489 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -51,7 +51,7 @@ jobs: test-contracts-geth: docker: - image: circleci/node:9 - - image: albrow/0x-devnet + - image: 0xorg/devnet working_directory: ~/repo steps: - restore_cache: @@ -64,7 +64,7 @@ jobs: resource_class: medium+ docker: - image: circleci/node:9 - - image: albrow/0x-verdaccio + - image: 0xorg/verdaccio working_directory: ~/repo steps: - restore_cache: From b85db17e75e818b5b554cc2b8068aba980d95804 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 25 Sep 2018 12:47:31 +0200 Subject: [PATCH 45/47] Move SRA types from @0xproject/connect to @0xproject/sra-types --- .../standard_relayer_api_order_provider.ts | 3 +- packages/connect/CHANGELOG.json | 9 ++ packages/connect/src/http_client.ts | 18 +-- packages/connect/src/index.ts | 25 ++- packages/connect/src/types.ts | 145 ++---------------- .../utils/orders_channel_message_parser.ts | 2 +- .../utils/relayer_response_json_parsers.ts | 2 +- packages/connect/src/utils/type_converters.ts | 2 +- packages/connect/src/ws_orders_channel.ts | 3 +- .../standard_relayer_api/asset_pairs.ts | 2 +- .../standard_relayer_api/fee_recipients.ts | 2 +- .../standard_relayer_api/order_config.ts | 2 +- .../standard_relayer_api/orderbook.ts | 2 +- .../fixtures/standard_relayer_api/orders.ts | 2 +- packages/types/CHANGELOG.json | 4 + packages/types/src/index.ts | 128 ++++++++++++++++ yarn.lock | 6 +- 17 files changed, 190 insertions(+), 167 deletions(-) diff --git a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts index be3e9da50d..31942c25ba 100644 --- a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts +++ b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts @@ -1,4 +1,5 @@ -import { APIOrder, HttpClient, OrderbookResponse } from '@0xproject/connect'; +import { HttpClient } from '@0xproject/connect'; +import { APIOrder, OrderbookResponse } from '@0xproject/types'; import * as _ from 'lodash'; import { diff --git a/packages/connect/CHANGELOG.json b/packages/connect/CHANGELOG.json index f5ca802791..b39c8d272a 100644 --- a/packages/connect/CHANGELOG.json +++ b/packages/connect/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "2.0.3", + "changes": [ + { + "note": "Import SRA-related types from @0xproject/types", + "pr": 1085 + } + ] + }, { "timestamp": 1537541580, "version": "2.0.2", diff --git a/packages/connect/src/http_client.ts b/packages/connect/src/http_client.ts index bdcfdd8d23..8a68d6c233 100644 --- a/packages/connect/src/http_client.ts +++ b/packages/connect/src/http_client.ts @@ -1,19 +1,10 @@ import { assert } from '@0xproject/assert'; import { schemas } from '@0xproject/json-schemas'; -import { SignedOrder } from '@0xproject/types'; -import { fetchAsync } from '@0xproject/utils'; -import * as _ from 'lodash'; -import * as queryString from 'query-string'; - -import { schemas as clientSchemas } from './schemas/schemas'; import { APIOrder, AssetPairsRequestOpts, AssetPairsResponse, - Client, FeeRecipientsResponse, - HttpRequestOptions, - HttpRequestType, OrderbookRequest, OrderbookResponse, OrderConfigRequest, @@ -22,7 +13,14 @@ import { OrdersResponse, PagedRequestOpts, RequestOpts, -} from './types'; + SignedOrder, +} from '@0xproject/types'; +import { fetchAsync } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as queryString from 'query-string'; + +import { schemas as clientSchemas } from './schemas/schemas'; +import { Client, HttpRequestOptions, HttpRequestType } from './types'; import { relayerResponseJsonParsers } from './utils/relayer_response_json_parsers'; const TRAILING_SLASHES_REGEX = /\/+$/; diff --git a/packages/connect/src/index.ts b/packages/connect/src/index.ts index 0b9cad038c..0ec5a0f681 100644 --- a/packages/connect/src/index.ts +++ b/packages/connect/src/index.ts @@ -1,23 +1,20 @@ export { HttpClient } from './http_client'; export { ordersChannelFactory } from './orders_channel_factory'; +export { Client, OrdersChannel, OrdersChannelHandler } from './types'; export { - Client, + APIOrder, + AssetPairsRequestOpts, + AssetPairsResponse, + FeeRecipientsResponse, + OrderbookRequest, + OrderbookResponse, OrderConfigRequest, OrderConfigResponse, - OrdersChannel, - OrdersChannelHandler, OrdersChannelSubscriptionOpts, - OrderbookRequest, - OrderbookResponse, OrdersRequestOpts, - PagedRequestOpts, - AssetPairsRequestOpts, - RequestOpts, - AssetPairsResponse, - FeeRecipientsResponse, - APIOrder, OrdersResponse, + PagedRequestOpts, PaginatedCollection, -} from './types'; - -export { SignedOrder } from '@0xproject/types'; + RequestOpts, + SignedOrder, +} from '@0xproject/types'; diff --git a/packages/connect/src/types.ts b/packages/connect/src/types.ts index b76af081ce..4bb0ae534a 100644 --- a/packages/connect/src/types.ts +++ b/packages/connect/src/types.ts @@ -1,5 +1,18 @@ -import { SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; +import { + APIOrder, + AssetPairsItem, + AssetPairsRequestOpts, + FeeRecipientsResponse, + OrderbookRequest, + OrderbookResponse, + OrderConfigRequest, + OrderConfigResponse, + OrdersChannelSubscriptionOpts, + OrdersRequestOpts, + PagedRequestOpts, + PaginatedCollection, + SignedOrder, +} from '@0xproject/types'; export interface Client { getAssetPairsAsync: ( @@ -18,140 +31,12 @@ export interface OrdersChannel { close: () => void; } -/** - * baseAssetData: The address of assetData designated as the baseToken in the currency pair calculation of price - * quoteAssetData: The address of assetData designated as the quoteToken in the currency pair calculation of price - * limit: Maximum number of bids and asks in orderbook snapshot - */ -export interface OrdersChannelSubscriptionOpts { - baseAssetData: string; - quoteAssetData: string; - limit: number; -} - export interface OrdersChannelHandler { onUpdate: (channel: OrdersChannel, subscriptionOpts: OrdersChannelSubscriptionOpts, orders: APIOrder[]) => void; onError: (channel: OrdersChannel, err: Error, subscriptionOpts?: OrdersChannelSubscriptionOpts) => void; onClose: (channel: OrdersChannel) => void; } -export type OrdersChannelMessage = UpdateOrdersChannelMessage | UnknownOrdersChannelMessage; - -export enum OrdersChannelMessageTypes { - Update = 'update', - Unknown = 'unknown', -} - -export interface UpdateOrdersChannelMessage { - type: OrdersChannelMessageTypes.Update; - requestId: string; - payload: APIOrder[]; -} - -export interface UnknownOrdersChannelMessage { - type: OrdersChannelMessageTypes.Unknown; - requestId: string; - payload: undefined; -} - -export enum WebsocketConnectionEventType { - Close = 'close', - Error = 'error', - Message = 'message', -} - -export enum WebsocketClientEventType { - Connect = 'connect', - ConnectFailed = 'connectFailed', -} - -export type OrdersResponse = PaginatedCollection; - -export interface APIOrder { - order: SignedOrder; - metaData: object; -} - -export interface AssetPairsRequestOpts { - assetDataA?: string; - assetDataB?: string; -} - -export type AssetPairsResponse = PaginatedCollection; - -export interface AssetPairsItem { - assetDataA: Asset; - assetDataB: Asset; -} - -export interface Asset { - assetData: string; - minAmount: BigNumber; - maxAmount: BigNumber; - precision: number; -} - -export interface OrdersRequestOpts { - makerAssetProxyId?: string; - takerAssetProxyId?: string; - makerAssetAddress?: string; - takerAssetAddress?: string; - exchangeAddress?: string; - senderAddress?: string; - makerAssetData?: string; - takerAssetData?: string; - makerAddress?: string; - takerAddress?: string; - traderAddress?: string; - feeRecipientAddress?: string; -} - -export interface OrderbookRequest { - baseAssetData: string; - quoteAssetData: string; -} - -export interface OrderbookResponse { - bids: PaginatedCollection; - asks: PaginatedCollection; -} - -export interface PaginatedCollection { - total: number; - page: number; - perPage: number; - records: T[]; -} - -export interface OrderConfigRequest { - makerAddress: string; - takerAddress: string; - makerAssetAmount: BigNumber; - takerAssetAmount: BigNumber; - makerAssetData: string; - takerAssetData: string; - exchangeAddress: string; - expirationTimeSeconds: BigNumber; -} - -export interface OrderConfigResponse { - makerFee: BigNumber; - takerFee: BigNumber; - feeRecipientAddress: string; - senderAddress: string; -} - -export type FeeRecipientsResponse = PaginatedCollection; - -export interface RequestOpts { - networkId?: number; -} - -export interface PagedRequestOpts { - page?: number; - perPage?: number; -} - export interface HttpRequestOptions { params?: object; payload?: object; diff --git a/packages/connect/src/utils/orders_channel_message_parser.ts b/packages/connect/src/utils/orders_channel_message_parser.ts index 1b6cda17b9..943d7802e4 100644 --- a/packages/connect/src/utils/orders_channel_message_parser.ts +++ b/packages/connect/src/utils/orders_channel_message_parser.ts @@ -2,7 +2,7 @@ import { assert } from '@0xproject/assert'; import { schemas } from '@0xproject/json-schemas'; import * as _ from 'lodash'; -import { OrdersChannelMessage, OrdersChannelMessageTypes } from '../types'; +import { OrdersChannelMessage, OrdersChannelMessageTypes } from '@0xproject/types'; import { relayerResponseJsonParsers } from './relayer_response_json_parsers'; diff --git a/packages/connect/src/utils/relayer_response_json_parsers.ts b/packages/connect/src/utils/relayer_response_json_parsers.ts index bc31f231de..356e2dde6a 100644 --- a/packages/connect/src/utils/relayer_response_json_parsers.ts +++ b/packages/connect/src/utils/relayer_response_json_parsers.ts @@ -9,7 +9,7 @@ import { OrderbookResponse, OrderConfigResponse, OrdersResponse, -} from '../types'; +} from '@0xproject/types'; import { typeConverters } from './type_converters'; diff --git a/packages/connect/src/utils/type_converters.ts b/packages/connect/src/utils/type_converters.ts index d57ea00e52..99760cf744 100644 --- a/packages/connect/src/utils/type_converters.ts +++ b/packages/connect/src/utils/type_converters.ts @@ -1,7 +1,7 @@ import { orderParsingUtils } from '@0xproject/order-utils'; import * as _ from 'lodash'; -import { APIOrder } from '../types'; +import { APIOrder } from '@0xproject/types'; export const typeConverters = { convertOrderbookStringFieldsToBigNumber(orderbook: any): any { diff --git a/packages/connect/src/ws_orders_channel.ts b/packages/connect/src/ws_orders_channel.ts index cde4acbc36..bf5e8508d8 100644 --- a/packages/connect/src/ws_orders_channel.ts +++ b/packages/connect/src/ws_orders_channel.ts @@ -1,8 +1,9 @@ +import { OrdersChannelMessageTypes, OrdersChannelSubscriptionOpts } from '@0xproject/types'; import * as _ from 'lodash'; import { v4 as uuid } from 'uuid'; import * as WebSocket from 'websocket'; -import { OrdersChannel, OrdersChannelHandler, OrdersChannelMessageTypes, OrdersChannelSubscriptionOpts } from './types'; +import { OrdersChannel, OrdersChannelHandler } from './types'; import { assert } from './utils/assert'; import { ordersChannelMessageParser } from './utils/orders_channel_message_parser'; diff --git a/packages/connect/test/fixtures/standard_relayer_api/asset_pairs.ts b/packages/connect/test/fixtures/standard_relayer_api/asset_pairs.ts index 5ce703317c..27216e4214 100644 --- a/packages/connect/test/fixtures/standard_relayer_api/asset_pairs.ts +++ b/packages/connect/test/fixtures/standard_relayer_api/asset_pairs.ts @@ -1,6 +1,6 @@ import { BigNumber } from '@0xproject/utils'; -import { AssetPairsResponse } from '../../../src/types'; +import { AssetPairsResponse } from '@0xproject/types'; export const assetDataPairsResponse: AssetPairsResponse = { total: 43, diff --git a/packages/connect/test/fixtures/standard_relayer_api/fee_recipients.ts b/packages/connect/test/fixtures/standard_relayer_api/fee_recipients.ts index e17ffe7a85..6f544e9d36 100644 --- a/packages/connect/test/fixtures/standard_relayer_api/fee_recipients.ts +++ b/packages/connect/test/fixtures/standard_relayer_api/fee_recipients.ts @@ -1,4 +1,4 @@ -import { FeeRecipientsResponse } from '../../../src/types'; +import { FeeRecipientsResponse } from '@0xproject/types'; export const feeRecipientsResponse: FeeRecipientsResponse = { total: 3, diff --git a/packages/connect/test/fixtures/standard_relayer_api/order_config.ts b/packages/connect/test/fixtures/standard_relayer_api/order_config.ts index 36f01a0094..56a4ac5500 100644 --- a/packages/connect/test/fixtures/standard_relayer_api/order_config.ts +++ b/packages/connect/test/fixtures/standard_relayer_api/order_config.ts @@ -1,6 +1,6 @@ import { BigNumber } from '@0xproject/utils'; -import { OrderConfigResponse } from '../../../src/types'; +import { OrderConfigResponse } from '@0xproject/types'; export const orderConfigResponse: OrderConfigResponse = { senderAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32', diff --git a/packages/connect/test/fixtures/standard_relayer_api/orderbook.ts b/packages/connect/test/fixtures/standard_relayer_api/orderbook.ts index d5f39a51fc..21d51da742 100644 --- a/packages/connect/test/fixtures/standard_relayer_api/orderbook.ts +++ b/packages/connect/test/fixtures/standard_relayer_api/orderbook.ts @@ -1,6 +1,6 @@ import { BigNumber } from '@0xproject/utils'; -import { OrderbookResponse } from '../../../src/types'; +import { OrderbookResponse } from '@0xproject/types'; export const orderbookResponse: OrderbookResponse = { bids: { diff --git a/packages/connect/test/fixtures/standard_relayer_api/orders.ts b/packages/connect/test/fixtures/standard_relayer_api/orders.ts index 01f8974b81..fb85990d07 100644 --- a/packages/connect/test/fixtures/standard_relayer_api/orders.ts +++ b/packages/connect/test/fixtures/standard_relayer_api/orders.ts @@ -1,6 +1,6 @@ import { BigNumber } from '@0xproject/utils'; -import { OrdersResponse } from '../../../src/types'; +import { OrdersResponse } from '@0xproject/types'; export const ordersResponse: OrdersResponse = { total: 984, diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index 8b13463477..9cdb3bc1f7 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -5,6 +5,10 @@ { "note": "Add ObjectMap type", "pr": 1037 + }, + { + "note": "Add SRA types from connect", + "pr": 1085 } ] }, diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 25803e8c89..d27060ee55 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -243,3 +243,131 @@ export enum StatusCodes { export interface ObjectMap { [key: string]: T; } + +/** + * baseAssetData: The address of assetData designated as the baseToken in the currency pair calculation of price + * quoteAssetData: The address of assetData designated as the quoteToken in the currency pair calculation of price + * limit: Maximum number of bids and asks in orderbook snapshot + */ +export interface OrdersChannelSubscriptionOpts { + baseAssetData: string; + quoteAssetData: string; + limit: number; +} + +export type OrdersChannelMessage = UpdateOrdersChannelMessage | UnknownOrdersChannelMessage; + +export enum OrdersChannelMessageTypes { + Update = 'update', + Unknown = 'unknown', +} + +export interface UpdateOrdersChannelMessage { + type: OrdersChannelMessageTypes.Update; + requestId: string; + payload: APIOrder[]; +} + +export interface UnknownOrdersChannelMessage { + type: OrdersChannelMessageTypes.Unknown; + requestId: string; + payload: undefined; +} + +export enum WebsocketConnectionEventType { + Close = 'close', + Error = 'error', + Message = 'message', +} + +export enum WebsocketClientEventType { + Connect = 'connect', + ConnectFailed = 'connectFailed', +} + +export type OrdersResponse = PaginatedCollection; + +export interface APIOrder { + order: SignedOrder; + metaData: object; +} + +export interface AssetPairsRequestOpts { + assetDataA?: string; + assetDataB?: string; +} + +export type AssetPairsResponse = PaginatedCollection; + +export interface AssetPairsItem { + assetDataA: Asset; + assetDataB: Asset; +} + +export interface Asset { + assetData: string; + minAmount: BigNumber; + maxAmount: BigNumber; + precision: number; +} + +export interface OrdersRequestOpts { + makerAssetProxyId?: string; + takerAssetProxyId?: string; + makerAssetAddress?: string; + takerAssetAddress?: string; + exchangeAddress?: string; + senderAddress?: string; + makerAssetData?: string; + takerAssetData?: string; + makerAddress?: string; + takerAddress?: string; + traderAddress?: string; + feeRecipientAddress?: string; +} + +export interface OrderbookRequest { + baseAssetData: string; + quoteAssetData: string; +} + +export interface OrderbookResponse { + bids: PaginatedCollection; + asks: PaginatedCollection; +} + +export interface PaginatedCollection { + total: number; + page: number; + perPage: number; + records: T[]; +} + +export interface OrderConfigRequest { + makerAddress: string; + takerAddress: string; + makerAssetAmount: BigNumber; + takerAssetAmount: BigNumber; + makerAssetData: string; + takerAssetData: string; + exchangeAddress: string; + expirationTimeSeconds: BigNumber; +} + +export interface OrderConfigResponse { + makerFee: BigNumber; + takerFee: BigNumber; + feeRecipientAddress: string; + senderAddress: string; +} + +export type FeeRecipientsResponse = PaginatedCollection; + +export interface RequestOpts { + networkId?: number; +} + +export interface PagedRequestOpts { + page?: number; + perPage?: number; +} diff --git a/yarn.lock b/yarn.lock index 7464a9c5d7..aa30946a86 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5296,9 +5296,9 @@ ethereumjs-wallet@~0.6.0: utf8 "^3.0.0" uuid "^3.3.2" -ethers@3.0.22: - version "3.0.22" - resolved "https://registry.npmjs.org/ethers/-/ethers-3.0.22.tgz#7fab1ea16521705837aa43c15831877b2716b436" +ethers@0xproject/ethers.js#eip-838-reasons, ethers@3.0.22: + version "3.0.18" + resolved "https://codeload.github.com/0xproject/ethers.js/tar.gz/b91342bd200d142af0165d6befddf783c8ae8447" dependencies: aes-js "3.0.0" bn.js "^4.4.0" From 411813d8d9f23d535f10dd3fdd9e824b244bf187 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 25 Sep 2018 13:48:44 +0200 Subject: [PATCH 46/47] Updated CHANGELOGS --- packages/0x.js/CHANGELOG.json | 9 +++++++++ packages/0x.js/CHANGELOG.md | 4 ++++ packages/abi-gen/CHANGELOG.json | 9 +++++++++ packages/abi-gen/CHANGELOG.md | 4 ++++ packages/assert/CHANGELOG.json | 9 +++++++++ packages/assert/CHANGELOG.md | 4 ++++ packages/asset-buyer/CHANGELOG.json | 9 +++++++++ packages/asset-buyer/CHANGELOG.md | 13 +++++++++++++ packages/base-contract/CHANGELOG.json | 9 +++++++++ packages/base-contract/CHANGELOG.md | 4 ++++ packages/connect/CHANGELOG.json | 3 ++- packages/connect/CHANGELOG.md | 4 ++++ packages/contract-wrappers/CHANGELOG.json | 9 +++++++++ packages/contract-wrappers/CHANGELOG.md | 4 ++++ packages/dev-utils/CHANGELOG.json | 9 +++++++++ packages/dev-utils/CHANGELOG.md | 4 ++++ packages/fill-scenarios/CHANGELOG.json | 9 +++++++++ packages/fill-scenarios/CHANGELOG.md | 4 ++++ packages/json-schemas/CHANGELOG.json | 9 +++++++++ packages/json-schemas/CHANGELOG.md | 4 ++++ packages/migrations/CHANGELOG.json | 9 +++++++++ packages/migrations/CHANGELOG.md | 4 ++++ packages/order-utils/CHANGELOG.json | 9 +++++++++ packages/order-utils/CHANGELOG.md | 4 ++++ packages/order-watcher/CHANGELOG.json | 9 +++++++++ packages/order-watcher/CHANGELOG.md | 4 ++++ packages/react-docs/CHANGELOG.json | 9 +++++++++ packages/react-docs/CHANGELOG.md | 4 ++++ packages/react-shared/CHANGELOG.json | 9 +++++++++ packages/react-shared/CHANGELOG.md | 4 ++++ packages/sol-compiler/CHANGELOG.json | 9 +++++++++ packages/sol-compiler/CHANGELOG.md | 4 ++++ packages/sol-cov/CHANGELOG.json | 9 +++++++++ packages/sol-cov/CHANGELOG.md | 4 ++++ packages/sol-resolver/CHANGELOG.json | 9 +++++++++ packages/sol-resolver/CHANGELOG.md | 4 ++++ packages/sra-report/CHANGELOG.json | 9 +++++++++ packages/sra-report/CHANGELOG.md | 4 ++++ packages/sra-spec/CHANGELOG.json | 9 +++++++++ packages/sra-spec/CHANGELOG.md | 4 ++++ packages/subproviders/CHANGELOG.json | 9 +++++++++ packages/subproviders/CHANGELOG.md | 4 ++++ packages/types/CHANGELOG.json | 3 ++- packages/types/CHANGELOG.md | 5 +++++ packages/utils/CHANGELOG.json | 9 +++++++++ packages/utils/CHANGELOG.md | 4 ++++ packages/web3-wrapper/CHANGELOG.json | 3 ++- packages/web3-wrapper/CHANGELOG.md | 4 ++++ 48 files changed, 301 insertions(+), 3 deletions(-) diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index 391c5fa17d..38966a0b73 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.5", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.4", diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md index 7031425ab2..d881cd825c 100644 --- a/packages/0x.js/CHANGELOG.md +++ b/packages/0x.js/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.5 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.4 - _September 21, 2018_ * Dependencies updated diff --git a/packages/abi-gen/CHANGELOG.json b/packages/abi-gen/CHANGELOG.json index 2ecacb05d6..18aed8716a 100644 --- a/packages/abi-gen/CHANGELOG.json +++ b/packages/abi-gen/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.10", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.9", diff --git a/packages/abi-gen/CHANGELOG.md b/packages/abi-gen/CHANGELOG.md index 88ab428641..99fc44f0cb 100644 --- a/packages/abi-gen/CHANGELOG.md +++ b/packages/abi-gen/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.10 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.9 - _September 21, 2018_ * Dependencies updated diff --git a/packages/assert/CHANGELOG.json b/packages/assert/CHANGELOG.json index 8a9b6afd65..851ba6c045 100644 --- a/packages/assert/CHANGELOG.json +++ b/packages/assert/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.10", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.9", diff --git a/packages/assert/CHANGELOG.md b/packages/assert/CHANGELOG.md index d394413387..26760306e0 100644 --- a/packages/assert/CHANGELOG.md +++ b/packages/assert/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.10 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.9 - _September 21, 2018_ * Dependencies updated diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index 2e4db1f6e8..50b965f2de 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.0", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "version": "1.0.0-rc.1", "changes": [ diff --git a/packages/asset-buyer/CHANGELOG.md b/packages/asset-buyer/CHANGELOG.md index 8b13789179..b27795d187 100644 --- a/packages/asset-buyer/CHANGELOG.md +++ b/packages/asset-buyer/CHANGELOG.md @@ -1 +1,14 @@ + +CHANGELOG + +## v1.0.0 - _September 25, 2018_ + + * Dependencies updated + +## v1.0.0-rc.1 - _Invalid date_ + + * Init diff --git a/packages/base-contract/CHANGELOG.json b/packages/base-contract/CHANGELOG.json index 723963f87d..5f94afa43e 100644 --- a/packages/base-contract/CHANGELOG.json +++ b/packages/base-contract/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "2.0.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "2.0.3", diff --git a/packages/base-contract/CHANGELOG.md b/packages/base-contract/CHANGELOG.md index c4ba59e26b..b3f3b5619a 100644 --- a/packages/base-contract/CHANGELOG.md +++ b/packages/base-contract/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.4 - _September 25, 2018_ + + * Dependencies updated + ## v2.0.3 - _September 21, 2018_ * Dependencies updated diff --git a/packages/connect/CHANGELOG.json b/packages/connect/CHANGELOG.json index b39c8d272a..d4c501eb19 100644 --- a/packages/connect/CHANGELOG.json +++ b/packages/connect/CHANGELOG.json @@ -6,7 +6,8 @@ "note": "Import SRA-related types from @0xproject/types", "pr": 1085 } - ] + ], + "timestamp": 1537875740 }, { "timestamp": 1537541580, diff --git a/packages/connect/CHANGELOG.md b/packages/connect/CHANGELOG.md index 1f8116f887..2c2483cb66 100644 --- a/packages/connect/CHANGELOG.md +++ b/packages/connect/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.3 - _September 25, 2018_ + + * Import SRA-related types from @0xproject/types (#1085) + ## v2.0.2 - _September 21, 2018_ * Dependencies updated diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 2f8a02a1a2..65aaf15c7a 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.5", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.4", diff --git a/packages/contract-wrappers/CHANGELOG.md b/packages/contract-wrappers/CHANGELOG.md index 3757bd6ba5..98de9c7f2c 100644 --- a/packages/contract-wrappers/CHANGELOG.md +++ b/packages/contract-wrappers/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.5 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.4 - _September 21, 2018_ * Dependencies updated diff --git a/packages/dev-utils/CHANGELOG.json b/packages/dev-utils/CHANGELOG.json index e834eb1a37..bd4f280ba5 100644 --- a/packages/dev-utils/CHANGELOG.json +++ b/packages/dev-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.9", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.8", diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 34f29418bc..a362330c25 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.9 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.8 - _September 21, 2018_ * Dependencies updated diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json index e378c7cf06..3023a43c5b 100644 --- a/packages/fill-scenarios/CHANGELOG.json +++ b/packages/fill-scenarios/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.3", diff --git a/packages/fill-scenarios/CHANGELOG.md b/packages/fill-scenarios/CHANGELOG.md index 16fbb4f345..4fb3f3c541 100644 --- a/packages/fill-scenarios/CHANGELOG.md +++ b/packages/fill-scenarios/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.4 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.3 - _September 21, 2018_ * Dependencies updated diff --git a/packages/json-schemas/CHANGELOG.json b/packages/json-schemas/CHANGELOG.json index e3308f98b4..afe8c6f55f 100644 --- a/packages/json-schemas/CHANGELOG.json +++ b/packages/json-schemas/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.3", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.2", diff --git a/packages/json-schemas/CHANGELOG.md b/packages/json-schemas/CHANGELOG.md index 0817874ba3..dc9a69a3cf 100644 --- a/packages/json-schemas/CHANGELOG.md +++ b/packages/json-schemas/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.3 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.2 - _September 21, 2018_ * Dependencies updated diff --git a/packages/migrations/CHANGELOG.json b/packages/migrations/CHANGELOG.json index a25de1a77f..1c8418818b 100644 --- a/packages/migrations/CHANGELOG.json +++ b/packages/migrations/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.11", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.10", diff --git a/packages/migrations/CHANGELOG.md b/packages/migrations/CHANGELOG.md index 2a6aeb96d4..80038bebb3 100644 --- a/packages/migrations/CHANGELOG.md +++ b/packages/migrations/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.11 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.10 - _September 21, 2018_ * Dependencies updated diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index 5bf538c1f4..d6742df2e6 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.3", diff --git a/packages/order-utils/CHANGELOG.md b/packages/order-utils/CHANGELOG.md index e35867fa6f..67e5a54a6c 100644 --- a/packages/order-utils/CHANGELOG.md +++ b/packages/order-utils/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.4 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.3 - _September 21, 2018_ * Dependencies updated diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json index dec154f0f2..d4b0e66076 100644 --- a/packages/order-watcher/CHANGELOG.json +++ b/packages/order-watcher/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.5", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.4", diff --git a/packages/order-watcher/CHANGELOG.md b/packages/order-watcher/CHANGELOG.md index a4e1cc79fb..d179b2e1a3 100644 --- a/packages/order-watcher/CHANGELOG.md +++ b/packages/order-watcher/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.5 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.4 - _September 21, 2018_ * Dependencies updated diff --git a/packages/react-docs/CHANGELOG.json b/packages/react-docs/CHANGELOG.json index c8a44aa9e8..4e3dfed999 100644 --- a/packages/react-docs/CHANGELOG.json +++ b/packages/react-docs/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.10", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.9", diff --git a/packages/react-docs/CHANGELOG.md b/packages/react-docs/CHANGELOG.md index 595b0cf987..ca96d479d8 100644 --- a/packages/react-docs/CHANGELOG.md +++ b/packages/react-docs/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.10 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.9 - _September 21, 2018_ * Dependencies updated diff --git a/packages/react-shared/CHANGELOG.json b/packages/react-shared/CHANGELOG.json index 898ab26aa6..96e2a92f97 100644 --- a/packages/react-shared/CHANGELOG.json +++ b/packages/react-shared/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.11", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.10", diff --git a/packages/react-shared/CHANGELOG.md b/packages/react-shared/CHANGELOG.md index f6878e43fc..09f6066cdd 100644 --- a/packages/react-shared/CHANGELOG.md +++ b/packages/react-shared/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.11 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.10 - _September 21, 2018_ * Dependencies updated diff --git a/packages/sol-compiler/CHANGELOG.json b/packages/sol-compiler/CHANGELOG.json index 9a1961b799..af6897b402 100644 --- a/packages/sol-compiler/CHANGELOG.json +++ b/packages/sol-compiler/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.1.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.1.3", diff --git a/packages/sol-compiler/CHANGELOG.md b/packages/sol-compiler/CHANGELOG.md index a5d1332f8f..02058b378c 100644 --- a/packages/sol-compiler/CHANGELOG.md +++ b/packages/sol-compiler/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.1.4 - _September 25, 2018_ + + * Dependencies updated + ## v1.1.3 - _September 21, 2018_ * Dependencies updated diff --git a/packages/sol-cov/CHANGELOG.json b/packages/sol-cov/CHANGELOG.json index 5f49e04430..ef009b586b 100644 --- a/packages/sol-cov/CHANGELOG.json +++ b/packages/sol-cov/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "2.1.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "2.1.3", diff --git a/packages/sol-cov/CHANGELOG.md b/packages/sol-cov/CHANGELOG.md index e1b898b194..5dddb1c4dd 100644 --- a/packages/sol-cov/CHANGELOG.md +++ b/packages/sol-cov/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.4 - _September 25, 2018_ + + * Dependencies updated + ## v2.1.3 - _September 21, 2018_ * Dependencies updated diff --git a/packages/sol-resolver/CHANGELOG.json b/packages/sol-resolver/CHANGELOG.json index bfc7cb93b3..ee439c2d01 100644 --- a/packages/sol-resolver/CHANGELOG.json +++ b/packages/sol-resolver/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.10", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.9", diff --git a/packages/sol-resolver/CHANGELOG.md b/packages/sol-resolver/CHANGELOG.md index 5bed9f43cb..53354f4eff 100644 --- a/packages/sol-resolver/CHANGELOG.md +++ b/packages/sol-resolver/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.10 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.9 - _September 21, 2018_ * Dependencies updated diff --git a/packages/sra-report/CHANGELOG.json b/packages/sra-report/CHANGELOG.json index ee12caf34e..006965b015 100644 --- a/packages/sra-report/CHANGELOG.json +++ b/packages/sra-report/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.10", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.9", diff --git a/packages/sra-report/CHANGELOG.md b/packages/sra-report/CHANGELOG.md index 5e75625df7..298ea66ec9 100644 --- a/packages/sra-report/CHANGELOG.md +++ b/packages/sra-report/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.10 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.9 - _September 21, 2018_ * Dependencies updated diff --git a/packages/sra-spec/CHANGELOG.json b/packages/sra-spec/CHANGELOG.json index e0b3731917..a4a1fe8ff1 100644 --- a/packages/sra-spec/CHANGELOG.json +++ b/packages/sra-spec/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.3", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.2", diff --git a/packages/sra-spec/CHANGELOG.md b/packages/sra-spec/CHANGELOG.md index 59f397bc62..e279d517fc 100644 --- a/packages/sra-spec/CHANGELOG.md +++ b/packages/sra-spec/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.3 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.2 - _September 21, 2018_ * Dependencies updated diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json index 2d3c376152..8b6b87d2ab 100644 --- a/packages/subproviders/CHANGELOG.json +++ b/packages/subproviders/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "2.0.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "2.0.3", diff --git a/packages/subproviders/CHANGELOG.md b/packages/subproviders/CHANGELOG.md index 6e268d7ecd..708a94b21d 100644 --- a/packages/subproviders/CHANGELOG.md +++ b/packages/subproviders/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.4 - _September 25, 2018_ + + * Dependencies updated + ## v2.0.3 - _September 21, 2018_ * Dependencies updated diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index 9cdb3bc1f7..f9479f789e 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -10,7 +10,8 @@ "note": "Add SRA types from connect", "pr": 1085 } - ] + ], + "timestamp": 1537875740 }, { "timestamp": 1537541580, diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index d89416fd62..9841aa301a 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.1.0 - _September 25, 2018_ + + * Add ObjectMap type (#1037) + * Add SRA types from connect (#1085) + ## v1.0.2 - _September 21, 2018_ * Dependencies updated diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index 7449355ceb..1536c1960a 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1537875740, + "version": "1.0.10", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1537541580, "version": "1.0.9", diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index aa2c089406..ff26a09006 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.10 - _September 25, 2018_ + + * Dependencies updated + ## v1.0.9 - _September 21, 2018_ * Dependencies updated diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json index ce3f5e3bea..b1ea2fe2e3 100644 --- a/packages/web3-wrapper/CHANGELOG.json +++ b/packages/web3-wrapper/CHANGELOG.json @@ -7,7 +7,8 @@ "Rename `getBlockAsync` to `getBlockIfExistsAsync` and rather then throw if the requested block wasn't found, return undefined.", "pr": 1082 } - ] + ], + "timestamp": 1537875740 }, { "version": "2.0.3", diff --git a/packages/web3-wrapper/CHANGELOG.md b/packages/web3-wrapper/CHANGELOG.md index 72aff4a95a..cafbe13403 100644 --- a/packages/web3-wrapper/CHANGELOG.md +++ b/packages/web3-wrapper/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.0 - _September 25, 2018_ + + * Rename `getBlockAsync` to `getBlockIfExistsAsync` and rather then throw if the requested block wasn't found, return undefined. (#1082) + ## v2.0.3 - _September 21, 2018_ * Fixes issue #1076 where Parity now returns a placeholder transactionReceipt before the transaction is mined. (#1079) From 78ef98c27ce954f7e46b261b0809ff9d8d70519b Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 25 Sep 2018 13:48:55 +0200 Subject: [PATCH 47/47] Publish - 0x.js@1.0.5 - @0xproject/abi-gen@1.0.10 - @0xproject/assert@1.0.10 - @0xproject/asset-buyer@1.0.0 - @0xproject/base-contract@2.0.4 - @0xproject/connect@2.0.3 - @0xproject/contract-wrappers@1.0.5 - contracts@2.1.46 - @0xproject/dev-utils@1.0.9 - @0xproject/fill-scenarios@1.0.4 - @0xproject/json-schemas@1.0.3 - @0xproject/metacoin@0.0.20 - @0xproject/migrations@1.0.11 - @0xproject/order-utils@1.0.4 - @0xproject/order-watcher@1.0.5 - @0xproject/react-docs@1.0.10 - @0xproject/react-shared@1.0.11 - @0xproject/sol-compiler@1.1.4 - @0xproject/sol-cov@2.1.4 - @0xproject/sol-resolver@1.0.10 - @0xproject/sra-report@1.0.10 - @0xproject/sra-spec@1.0.3 - @0xproject/subproviders@2.0.4 - @0xproject/testnet-faucets@1.0.48 - @0xproject/types@1.1.0 - @0xproject/utils@1.0.10 - @0xproject/web3-wrapper@3.0.0 - @0xproject/website@0.0.51 --- packages/0x.js/package.json | 26 ++++++++++++------------- packages/abi-gen/package.json | 4 ++-- packages/assert/package.json | 6 +++--- packages/asset-buyer/package.json | 20 +++++++++---------- packages/base-contract/package.json | 6 +++--- packages/connect/package.json | 12 ++++++------ packages/contract-wrappers/package.json | 26 ++++++++++++------------- packages/contracts/package.json | 22 ++++++++++----------- packages/dev-utils/package.json | 10 +++++----- packages/fill-scenarios/package.json | 14 ++++++------- packages/json-schemas/package.json | 4 ++-- packages/metacoin/package.json | 20 +++++++++---------- packages/migrations/package.json | 20 +++++++++---------- packages/order-utils/package.json | 16 +++++++-------- packages/order-watcher/package.json | 26 ++++++++++++------------- packages/react-docs/package.json | 8 ++++---- packages/react-shared/package.json | 4 ++-- packages/sol-compiler/package.json | 16 +++++++-------- packages/sol-cov/package.json | 12 ++++++------ packages/sol-resolver/package.json | 4 ++-- packages/sra-report/package.json | 6 +++--- packages/sra-spec/package.json | 4 ++-- packages/subproviders/package.json | 10 +++++----- packages/testnet-faucets/package.json | 10 +++++----- packages/types/package.json | 2 +- packages/utils/package.json | 4 ++-- packages/web3-wrapper/package.json | 8 ++++---- packages/website/package.json | 20 +++++++++---------- 28 files changed, 170 insertions(+), 170 deletions(-) diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index c92e24c520..d53571dbfe 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -1,6 +1,6 @@ { "name": "0x.js", - "version": "1.0.4", + "version": "1.0.5", "engines": { "node": ">=6.12" }, @@ -41,9 +41,9 @@ }, "license": "Apache-2.0", "devDependencies": { - "@0xproject/abi-gen": "^1.0.9", - "@0xproject/dev-utils": "^1.0.8", - "@0xproject/migrations": "^1.0.10", + "@0xproject/abi-gen": "^1.0.10", + "@0xproject/dev-utils": "^1.0.9", + "@0xproject/migrations": "^1.0.11", "@0xproject/monorepo-scripts": "^1.0.9", "@0xproject/tslint-config": "^1.0.7", "@types/lodash": "4.14.104", @@ -73,16 +73,16 @@ "webpack": "^3.1.0" }, "dependencies": { - "@0xproject/assert": "^1.0.9", - "@0xproject/base-contract": "^2.0.3", - "@0xproject/contract-wrappers": "^1.0.4", - "@0xproject/order-utils": "^1.0.3", - "@0xproject/order-watcher": "^1.0.4", - "@0xproject/subproviders": "^2.0.3", - "@0xproject/types": "^1.0.2", + "@0xproject/assert": "^1.0.10", + "@0xproject/base-contract": "^2.0.4", + "@0xproject/contract-wrappers": "^1.0.5", + "@0xproject/order-utils": "^1.0.4", + "@0xproject/order-watcher": "^1.0.5", + "@0xproject/subproviders": "^2.0.4", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "ethereum-types": "^1.0.7", "ethers": "3.0.22", "lodash": "^4.17.5", diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json index 6b8581ef3e..1fb697299f 100644 --- a/packages/abi-gen/package.json +++ b/packages/abi-gen/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/abi-gen", - "version": "1.0.9", + "version": "1.0.10", "engines": { "node": ">=6.12" }, @@ -31,7 +31,7 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen/README.md", "dependencies": { "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", + "@0xproject/utils": "^1.0.10", "chalk": "^2.3.0", "ethereum-types": "^1.0.7", "glob": "^7.1.2", diff --git a/packages/assert/package.json b/packages/assert/package.json index 629cb23e55..ee510d14f5 100644 --- a/packages/assert/package.json +++ b/packages/assert/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/assert", - "version": "1.0.9", + "version": "1.0.10", "engines": { "node": ">=6.12" }, @@ -44,9 +44,9 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/json-schemas": "^1.0.2", + "@0xproject/json-schemas": "^1.0.3", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", + "@0xproject/utils": "^1.0.10", "lodash": "^4.17.5", "valid-url": "^1.0.9" }, diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json index 2b5136e310..d15213e0b4 100644 --- a/packages/asset-buyer/package.json +++ b/packages/asset-buyer/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/asset-buyer", - "version": "1.0.0-rc.1", + "version": "1.0.0", "engines": { "node": ">=6.12" }, @@ -36,16 +36,16 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/asset-buyer/README.md", "dependencies": { - "@0xproject/assert": "^1.0.9", - "@0xproject/connect": "^2.0.2", - "@0xproject/contract-wrappers": "^1.0.4", - "@0xproject/json-schemas": "^1.0.2", - "@0xproject/order-utils": "^1.0.1", - "@0xproject/subproviders": "^2.0.2", - "@0xproject/types": "^1.0.2", + "@0xproject/assert": "^1.0.10", + "@0xproject/connect": "^2.0.3", + "@0xproject/contract-wrappers": "^1.0.5", + "@0xproject/json-schemas": "^1.0.3", + "@0xproject/order-utils": "^1.0.4", + "@0xproject/subproviders": "^2.0.4", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "ethereum-types": "^1.0.6", "lodash": "^4.17.10" }, diff --git a/packages/base-contract/package.json b/packages/base-contract/package.json index 44b0807ba8..cd7f291140 100644 --- a/packages/base-contract/package.json +++ b/packages/base-contract/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/base-contract", - "version": "2.0.3", + "version": "2.0.4", "engines": { "node": ">=6.12" }, @@ -41,8 +41,8 @@ }, "dependencies": { "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "ethereum-types": "^1.0.7", "ethers": "3.0.22", "lodash": "^4.17.5" diff --git a/packages/connect/package.json b/packages/connect/package.json index 1575afe01e..075089fd2a 100644 --- a/packages/connect/package.json +++ b/packages/connect/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/connect", - "version": "2.0.2", + "version": "2.0.3", "engines": { "node": ">=6.12" }, @@ -43,12 +43,12 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/connect/README.md", "dependencies": { - "@0xproject/assert": "^1.0.9", - "@0xproject/json-schemas": "^1.0.2", - "@0xproject/order-utils": "^1.0.3", - "@0xproject/types": "^1.0.2", + "@0xproject/assert": "^1.0.10", + "@0xproject/json-schemas": "^1.0.3", + "@0xproject/order-utils": "^1.0.4", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", + "@0xproject/utils": "^1.0.10", "lodash": "^4.17.5", "query-string": "^5.0.1", "sinon": "^4.0.0", diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index 331ff17017..544d667c6e 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/contract-wrappers", - "version": "1.0.4", + "version": "1.0.5", "description": "Smart TS wrappers for 0x smart contracts", "keywords": [ "0xproject", @@ -41,10 +41,10 @@ "node": ">=6.0.0" }, "devDependencies": { - "@0xproject/abi-gen": "^1.0.9", - "@0xproject/dev-utils": "^1.0.8", - "@0xproject/migrations": "^1.0.10", - "@0xproject/subproviders": "^2.0.3", + "@0xproject/abi-gen": "^1.0.10", + "@0xproject/dev-utils": "^1.0.9", + "@0xproject/migrations": "^1.0.11", + "@0xproject/subproviders": "^2.0.4", "@0xproject/tslint-config": "^1.0.7", "@types/lodash": "4.14.104", "@types/mocha": "^2.2.42", @@ -72,15 +72,15 @@ "web3-provider-engine": "14.0.6" }, "dependencies": { - "@0xproject/assert": "^1.0.9", - "@0xproject/base-contract": "^2.0.3", - "@0xproject/fill-scenarios": "^1.0.3", - "@0xproject/json-schemas": "^1.0.2", - "@0xproject/order-utils": "^1.0.3", - "@0xproject/types": "^1.0.2", + "@0xproject/assert": "^1.0.10", + "@0xproject/base-contract": "^2.0.4", + "@0xproject/fill-scenarios": "^1.0.4", + "@0xproject/json-schemas": "^1.0.3", + "@0xproject/order-utils": "^1.0.4", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "ethereum-types": "^1.0.7", "ethereumjs-blockstream": "5.0.0", "ethereumjs-util": "^5.1.1", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 63de510b10..6a7a7c208c 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "contracts", - "version": "2.1.45", + "version": "2.1.46", "engines": { "node": ">=6.12" }, @@ -45,11 +45,11 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts/README.md", "devDependencies": { - "@0xproject/abi-gen": "^1.0.9", - "@0xproject/dev-utils": "^1.0.8", - "@0xproject/sol-compiler": "^1.1.3", - "@0xproject/sol-cov": "^2.1.3", - "@0xproject/subproviders": "^2.0.3", + "@0xproject/abi-gen": "^1.0.10", + "@0xproject/dev-utils": "^1.0.9", + "@0xproject/sol-compiler": "^1.1.4", + "@0xproject/sol-cov": "^2.1.4", + "@0xproject/subproviders": "^2.0.4", "@0xproject/tslint-config": "^1.0.7", "@types/bn.js": "^4.11.0", "@types/ethereumjs-abi": "^0.6.0", @@ -72,12 +72,12 @@ "yargs": "^10.0.3" }, "dependencies": { - "@0xproject/base-contract": "^2.0.3", - "@0xproject/order-utils": "^1.0.3", - "@0xproject/types": "^1.0.2", + "@0xproject/base-contract": "^2.0.4", + "@0xproject/order-utils": "^1.0.4", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "@types/js-combinatorics": "^0.5.29", "bn.js": "^4.11.8", "ethereum-types": "^1.0.7", diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 3ded87c059..c45581e45e 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/dev-utils", - "version": "1.0.8", + "version": "1.0.9", "engines": { "node": ">=6.12" }, @@ -42,11 +42,11 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/subproviders": "^2.0.3", - "@0xproject/types": "^1.0.2", + "@0xproject/subproviders": "^2.0.4", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "ethereum-types": "^1.0.7", "lodash": "^4.17.5" }, diff --git a/packages/fill-scenarios/package.json b/packages/fill-scenarios/package.json index 474618a14a..857ec496fd 100644 --- a/packages/fill-scenarios/package.json +++ b/packages/fill-scenarios/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/fill-scenarios", - "version": "1.0.3", + "version": "1.0.4", "description": "0x order fill scenario generator", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -26,7 +26,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/fill-scenarios/README.md", "devDependencies": { - "@0xproject/abi-gen": "^1.0.9", + "@0xproject/abi-gen": "^1.0.10", "@0xproject/tslint-config": "^1.0.7", "@types/lodash": "4.14.104", "copyfiles": "^2.0.0", @@ -37,12 +37,12 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/base-contract": "^2.0.3", - "@0xproject/order-utils": "^1.0.3", - "@0xproject/types": "^1.0.2", + "@0xproject/base-contract": "^2.0.4", + "@0xproject/order-utils": "^1.0.4", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "ethereum-types": "^1.0.7", "ethers": "3.0.22", "lodash": "^4.17.5" diff --git a/packages/json-schemas/package.json b/packages/json-schemas/package.json index 3d5399d2ad..7dfdb10661 100644 --- a/packages/json-schemas/package.json +++ b/packages/json-schemas/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/json-schemas", - "version": "1.0.2", + "version": "1.0.3", "engines": { "node": ">=6.12" }, @@ -45,7 +45,7 @@ }, "devDependencies": { "@0xproject/tslint-config": "^1.0.7", - "@0xproject/utils": "^1.0.9", + "@0xproject/utils": "^1.0.10", "@types/lodash.foreach": "^4.5.3", "@types/lodash.values": "^4.3.3", "@types/mocha": "^2.2.42", diff --git a/packages/metacoin/package.json b/packages/metacoin/package.json index 9824da332f..924408d530 100644 --- a/packages/metacoin/package.json +++ b/packages/metacoin/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/metacoin", - "version": "0.0.19", + "version": "0.0.20", "engines": { "node": ">=6.12" }, @@ -28,15 +28,15 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@0xproject/abi-gen": "^1.0.9", - "@0xproject/base-contract": "^2.0.3", - "@0xproject/sol-cov": "^2.1.3", - "@0xproject/subproviders": "^2.0.3", + "@0xproject/abi-gen": "^1.0.10", + "@0xproject/base-contract": "^2.0.4", + "@0xproject/sol-cov": "^2.1.4", + "@0xproject/subproviders": "^2.0.4", "@0xproject/tslint-config": "^1.0.7", - "@0xproject/types": "^1.0.2", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "@types/mocha": "^5.2.2", "copyfiles": "^2.0.0", "ethereum-types": "^1.0.7", @@ -45,8 +45,8 @@ "run-s": "^0.0.0" }, "devDependencies": { - "@0xproject/dev-utils": "^1.0.8", - "@0xproject/sol-compiler": "^1.1.3", + "@0xproject/dev-utils": "^1.0.9", + "@0xproject/sol-compiler": "^1.1.4", "chai": "^4.0.1", "chai-as-promised": "^7.1.0", "chai-bignumber": "^2.0.1", diff --git a/packages/migrations/package.json b/packages/migrations/package.json index 1229c78dc1..f44a254ed9 100644 --- a/packages/migrations/package.json +++ b/packages/migrations/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/migrations", - "version": "1.0.10", + "version": "1.0.11", "engines": { "node": ">=6.12" }, @@ -30,10 +30,10 @@ }, "license": "Apache-2.0", "devDependencies": { - "@0xproject/abi-gen": "^1.0.9", - "@0xproject/dev-utils": "^1.0.8", + "@0xproject/abi-gen": "^1.0.10", + "@0xproject/dev-utils": "^1.0.9", "@0xproject/tslint-config": "^1.0.7", - "@0xproject/types": "^1.0.2", + "@0xproject/types": "^1.1.0", "@types/yargs": "^10.0.0", "copyfiles": "^2.0.0", "make-promises-safe": "^1.1.0", @@ -44,13 +44,13 @@ "yargs": "^10.0.3" }, "dependencies": { - "@0xproject/base-contract": "^2.0.3", - "@0xproject/order-utils": "^1.0.3", - "@0xproject/sol-compiler": "^1.1.3", - "@0xproject/subproviders": "^2.0.3", + "@0xproject/base-contract": "^2.0.4", + "@0xproject/order-utils": "^1.0.4", + "@0xproject/sol-compiler": "^1.1.4", + "@0xproject/subproviders": "^2.0.4", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "@ledgerhq/hw-app-eth": "^4.3.0", "ethereum-types": "^1.0.7", "ethers": "3.0.22", diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index c9620bffac..167d32608c 100644 --- a/packages/order-utils/package.json +++ b/packages/order-utils/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/order-utils", - "version": "1.0.3", + "version": "1.0.4", "engines": { "node": ">=6.12" }, @@ -38,7 +38,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/order-utils/README.md", "devDependencies": { - "@0xproject/dev-utils": "^1.0.8", + "@0xproject/dev-utils": "^1.0.9", "@0xproject/tslint-config": "^1.0.7", "@types/bn.js": "^4.11.0", "@types/lodash": "4.14.104", @@ -57,13 +57,13 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/assert": "^1.0.9", - "@0xproject/base-contract": "^2.0.3", - "@0xproject/json-schemas": "^1.0.2", - "@0xproject/types": "^1.0.2", + "@0xproject/assert": "^1.0.10", + "@0xproject/base-contract": "^2.0.4", + "@0xproject/json-schemas": "^1.0.3", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "@types/node": "*", "bn.js": "^4.11.8", "ethereum-types": "^1.0.7", diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json index 8ce57364ff..d383211a87 100644 --- a/packages/order-watcher/package.json +++ b/packages/order-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/order-watcher", - "version": "1.0.4", + "version": "1.0.5", "description": "An order watcher daemon that watches for order validity", "keywords": [ "0x", @@ -42,9 +42,9 @@ "node": ">=6.0.0" }, "devDependencies": { - "@0xproject/abi-gen": "^1.0.9", - "@0xproject/dev-utils": "^1.0.8", - "@0xproject/migrations": "^1.0.10", + "@0xproject/abi-gen": "^1.0.10", + "@0xproject/dev-utils": "^1.0.9", + "@0xproject/migrations": "^1.0.11", "@0xproject/tslint-config": "^1.0.7", "@types/bintrees": "^1.0.2", "@types/lodash": "4.14.104", @@ -70,16 +70,16 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/assert": "^1.0.9", - "@0xproject/base-contract": "^2.0.3", - "@0xproject/contract-wrappers": "^1.0.4", - "@0xproject/fill-scenarios": "^1.0.3", - "@0xproject/json-schemas": "^1.0.2", - "@0xproject/order-utils": "^1.0.3", - "@0xproject/types": "^1.0.2", + "@0xproject/assert": "^1.0.10", + "@0xproject/base-contract": "^2.0.4", + "@0xproject/contract-wrappers": "^1.0.5", + "@0xproject/fill-scenarios": "^1.0.4", + "@0xproject/json-schemas": "^1.0.3", + "@0xproject/order-utils": "^1.0.4", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "bintrees": "^1.0.2", "ethereum-types": "^1.0.7", "ethereumjs-blockstream": "5.0.0", diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index 0efe7dbc99..d933489b7a 100644 --- a/packages/react-docs/package.json +++ b/packages/react-docs/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/react-docs", - "version": "1.0.9", + "version": "1.0.10", "engines": { "node": ">=6.12" }, @@ -23,7 +23,7 @@ "url": "https://github.com/0xProject/0x-monorepo.git" }, "devDependencies": { - "@0xproject/dev-utils": "^1.0.8", + "@0xproject/dev-utils": "^1.0.9", "@0xproject/tslint-config": "^1.0.7", "@types/compare-versions": "^3.0.0", "copyfiles": "^2.0.0", @@ -33,8 +33,8 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/react-shared": "^1.0.10", - "@0xproject/utils": "^1.0.9", + "@0xproject/react-shared": "^1.0.11", + "@0xproject/utils": "^1.0.10", "@types/lodash": "4.14.104", "@types/material-ui": "^0.20.0", "@types/node": "*", diff --git a/packages/react-shared/package.json b/packages/react-shared/package.json index 458968c399..9154c2bb69 100644 --- a/packages/react-shared/package.json +++ b/packages/react-shared/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/react-shared", - "version": "1.0.10", + "version": "1.0.11", "engines": { "node": ">=6.12" }, @@ -24,7 +24,7 @@ "url": "https://github.com/0xProject/0x-monorepo.git" }, "devDependencies": { - "@0xproject/dev-utils": "^1.0.8", + "@0xproject/dev-utils": "^1.0.9", "@0xproject/tslint-config": "^1.0.7", "copyfiles": "^2.0.0", "make-promises-safe": "^1.1.0", diff --git a/packages/sol-compiler/package.json b/packages/sol-compiler/package.json index 2ad0f9f090..841e2960c4 100644 --- a/packages/sol-compiler/package.json +++ b/packages/sol-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/sol-compiler", - "version": "1.1.3", + "version": "1.1.4", "engines": { "node": ">=6.12" }, @@ -41,7 +41,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/sol-compiler/README.md", "devDependencies": { - "@0xproject/dev-utils": "^1.0.8", + "@0xproject/dev-utils": "^1.0.9", "@0xproject/tslint-config": "^1.0.7", "@types/mkdirp": "^0.5.2", "@types/require-from-string": "^1.2.0", @@ -64,13 +64,13 @@ "zeppelin-solidity": "1.8.0" }, "dependencies": { - "@0xproject/assert": "^1.0.9", - "@0xproject/json-schemas": "^1.0.2", - "@0xproject/sol-resolver": "^1.0.9", - "@0xproject/types": "^1.0.2", + "@0xproject/assert": "^1.0.10", + "@0xproject/json-schemas": "^1.0.3", + "@0xproject/sol-resolver": "^1.0.10", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "@types/yargs": "^11.0.0", "chalk": "^2.3.0", "ethereum-types": "^1.0.7", diff --git a/packages/sol-cov/package.json b/packages/sol-cov/package.json index f80ae78e05..63b8f06d50 100644 --- a/packages/sol-cov/package.json +++ b/packages/sol-cov/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/sol-cov", - "version": "2.1.3", + "version": "2.1.4", "engines": { "node": ">=6.12" }, @@ -41,12 +41,12 @@ }, "homepage": "https://github.com/0xProject/0x.js/packages/sol-cov/README.md", "dependencies": { - "@0xproject/dev-utils": "^1.0.8", - "@0xproject/sol-compiler": "^1.1.3", - "@0xproject/subproviders": "^2.0.3", + "@0xproject/dev-utils": "^1.0.9", + "@0xproject/sol-compiler": "^1.1.4", + "@0xproject/subproviders": "^2.0.4", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "ethereum-types": "^1.0.7", "ethereumjs-util": "^5.1.1", "glob": "^7.1.2", diff --git a/packages/sol-resolver/package.json b/packages/sol-resolver/package.json index da3f187887..caf7df3aaa 100644 --- a/packages/sol-resolver/package.json +++ b/packages/sol-resolver/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/sol-resolver", - "version": "1.0.9", + "version": "1.0.10", "engines": { "node": ">=6.12" }, @@ -30,7 +30,7 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/types": "^1.0.2", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", "lodash": "^4.17.5" }, diff --git a/packages/sra-report/package.json b/packages/sra-report/package.json index eebf0cc0b9..c916765e2b 100644 --- a/packages/sra-report/package.json +++ b/packages/sra-report/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/sra-report", - "version": "1.0.9", + "version": "1.0.10", "engines": { "node": ">=6.12" }, @@ -33,13 +33,13 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/sra-report/README.md", "dependencies": { - "@0xproject/assert": "^1.0.9", + "@0xproject/assert": "^1.0.10", "@0xproject/connect": "1.0.4", "@0xproject/json-schemas": "^0.8.3", "@0xproject/order-utils": "^0.0.9", "@0xproject/types": "^0.8.2", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", + "@0xproject/utils": "^1.0.10", "chalk": "^2.3.0", "lodash": "^4.17.5", "newman": "^3.9.3", diff --git a/packages/sra-spec/package.json b/packages/sra-spec/package.json index 3da886e34e..0c51318486 100644 --- a/packages/sra-spec/package.json +++ b/packages/sra-spec/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/sra-spec", - "version": "1.0.2", + "version": "1.0.3", "engines": { "node": ">=6.12" }, @@ -34,7 +34,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/sra-spec/README.md", "dependencies": { - "@0xproject/json-schemas": "^1.0.2" + "@0xproject/json-schemas": "^1.0.3" }, "devDependencies": { "@0xproject/tslint-config": "^1.0.7", diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index 347c717269..6740a0743f 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/subproviders", - "version": "2.0.3", + "version": "2.0.4", "engines": { "node": ">=6.12" }, @@ -28,11 +28,11 @@ } }, "dependencies": { - "@0xproject/assert": "^1.0.9", - "@0xproject/types": "^1.0.2", + "@0xproject/assert": "^1.0.10", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "@ledgerhq/hw-app-eth": "^4.3.0", "@ledgerhq/hw-transport-u2f": "^4.3.0", "@types/eth-lightwallet": "^3.0.0", diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json index 448186b340..cfac1b0187 100644 --- a/packages/testnet-faucets/package.json +++ b/packages/testnet-faucets/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@0xproject/testnet-faucets", - "version": "1.0.47", + "version": "1.0.48", "engines": { "node": ">=6.12" }, @@ -17,11 +17,11 @@ "author": "Fabio Berger", "license": "Apache-2.0", "dependencies": { - "0x.js": "^1.0.4", - "@0xproject/subproviders": "^2.0.3", + "0x.js": "^1.0.5", + "@0xproject/subproviders": "^2.0.4", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "body-parser": "^1.17.1", "ethereum-types": "^1.0.7", "ethereumjs-tx": "^1.3.5", diff --git a/packages/types/package.json b/packages/types/package.json index f1c04b39ba..f783681980 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/types", - "version": "1.0.2", + "version": "1.1.0", "engines": { "node": ">=6.12" }, diff --git a/packages/utils/package.json b/packages/utils/package.json index a3977cef68..77d11c41c1 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/utils", - "version": "1.0.9", + "version": "1.0.10", "engines": { "node": ">=6.12" }, @@ -41,7 +41,7 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/types": "^1.0.2", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", "@types/node": "*", "abortcontroller-polyfill": "^1.1.9", diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index bcd640839f..f895296e97 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/web3-wrapper", - "version": "2.0.3", + "version": "3.0.0", "engines": { "node": ">=6.12" }, @@ -53,10 +53,10 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/assert": "^1.0.9", - "@0xproject/json-schemas": "^1.0.2", + "@0xproject/assert": "^1.0.10", + "@0xproject/json-schemas": "^1.0.3", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", + "@0xproject/utils": "^1.0.10", "ethereum-types": "^1.0.7", "ethereumjs-util": "^5.1.1", "ethers": "3.0.22", diff --git a/packages/website/package.json b/packages/website/package.json index 65a0d4c10c..67b2fb4ec4 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/website", - "version": "0.0.50", + "version": "0.0.51", "engines": { "node": ">=6.12" }, @@ -19,16 +19,16 @@ "license": "Apache-2.0", "dependencies": { "0x.js": "^0.38.6", - "@0xproject/contract-wrappers": "^1.0.4", - "@0xproject/json-schemas": "^1.0.1", - "@0xproject/order-utils": "^1.0.3", - "@0xproject/react-docs": "^1.0.9", - "@0xproject/react-shared": "^1.0.10", - "@0xproject/subproviders": "^2.0.3", - "@0xproject/types": "^1.0.2", + "@0xproject/contract-wrappers": "^1.0.5", + "@0xproject/json-schemas": "^1.0.3", + "@0xproject/order-utils": "^1.0.4", + "@0xproject/react-docs": "^1.0.10", + "@0xproject/react-shared": "^1.0.11", + "@0xproject/subproviders": "^2.0.4", + "@0xproject/types": "^1.1.0", "@0xproject/typescript-typings": "^2.0.1", - "@0xproject/utils": "^1.0.9", - "@0xproject/web3-wrapper": "^2.0.3", + "@0xproject/utils": "^1.0.10", + "@0xproject/web3-wrapper": "^3.0.0", "accounting": "^0.4.1", "basscss": "^8.0.3", "blockies": "^0.0.2",