diff options
Diffstat (limited to 'packages/asset-buyer')
-rw-r--r-- | packages/asset-buyer/CHANGELOG.json | 33 | ||||
-rw-r--r-- | packages/asset-buyer/CHANGELOG.md | 10 | ||||
-rw-r--r-- | packages/asset-buyer/package.json | 28 | ||||
-rw-r--r-- | packages/asset-buyer/src/asset_buyer.ts | 51 | ||||
-rw-r--r-- | packages/asset-buyer/src/constants.ts | 3 | ||||
-rw-r--r-- | packages/asset-buyer/src/order_providers/basic_order_provider.ts | 9 | ||||
-rw-r--r-- | packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts | 36 | ||||
-rw-r--r-- | packages/asset-buyer/src/types.ts | 5 | ||||
-rw-r--r-- | packages/asset-buyer/src/utils/assert.ts | 17 | ||||
-rw-r--r-- | packages/asset-buyer/src/utils/buy_quote_calculator.ts | 2 |
10 files changed, 135 insertions, 59 deletions
diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index 7ebcd8c2f..df4531063 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -1,5 +1,38 @@ [ { + "version": "2.2.0", + "changes": [ + { + "note": "`getAssetBuyerForProvidedOrders` factory function now takes 3 args instead of 4", + "pr": 1187 + }, + { + "note": + "the `OrderProvider` now requires a new method `getAvailableMakerAssetDatasAsync` and the `StandardRelayerAPIOrderProvider` requires the network id at init.", + "pr": 1203 + }, + { + "note": "No longer require that provided orders all have the same maker and taker asset data", + "pr": 1197 + }, + { + "note": + "Fix bug where `BuyQuoteInfo` objects could return `totalEthAmount` and `feeEthAmount` that were not whole numbers", + "pr": 1207 + }, + { + "note": + "Fix bug where default values for `AssetBuyer` public facing methods could get overriden by `undefined` values", + "pr": 1207 + }, + { + "note": "Lower default expiry buffer from 5 minutes to 2 minutes", + "pr": 1217 + } + ], + "timestamp": 1541740904 + }, + { "version": "2.1.0", "changes": [ { diff --git a/packages/asset-buyer/CHANGELOG.md b/packages/asset-buyer/CHANGELOG.md index 8845e7041..d6220767e 100644 --- a/packages/asset-buyer/CHANGELOG.md +++ b/packages/asset-buyer/CHANGELOG.md @@ -5,6 +5,15 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.2.0 - _November 9, 2018_ + + * `getAssetBuyerForProvidedOrders` factory function now takes 3 args instead of 4 (#1187) + * the `OrderProvider` now requires a new method `getAvailableMakerAssetDatasAsync` and the `StandardRelayerAPIOrderProvider` requires the network id at init. (#1203) + * No longer require that provided orders all have the same maker and taker asset data (#1197) + * Fix bug where `BuyQuoteInfo` objects could return `totalEthAmount` and `feeEthAmount` that were not whole numbers (#1207) + * Fix bug where default values for `AssetBuyer` public facing methods could get overriden by `undefined` values (#1207) + * Lower default expiry buffer from 5 minutes to 2 minutes (#1217) + ## v2.1.0 - _October 18, 2018_ * Add `gasLimit` and `gasPrice` as optional properties on `BuyQuoteExecutionOpts` @@ -13,6 +22,7 @@ CHANGELOG * Add `gasLimit` and `gasPrice` as optional properties on `BuyQuoteExecutionOpts` (#1116) * Add `docs:json` command to package.json (#1139) * Add missing types to public interface (#1139) + * Throw `SignatureRequestDenied` and `TransactionValueTooLow` errors when executing buy (#1147) ## v2.0.0 - _October 4, 2018_ diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json index e4b4d19c0..fc2414952 100644 --- a/packages/asset-buyer/package.json +++ b/packages/asset-buyer/package.json @@ -1,6 +1,6 @@ { "name": "@0x/asset-buyer", - "version": "2.1.0", + "version": "2.2.0", "engines": { "node": ">=6.12" }, @@ -10,7 +10,7 @@ "scripts": { "build": "yarn tsc -b", "build:ci": "yarn build", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "test": "yarn run_mocha", "rebuild_and_test": "run-s clean build test", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", @@ -36,21 +36,21 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/asset-buyer/README.md", "dependencies": { - "@0x/assert": "^1.0.14", - "@0x/connect": "^3.0.2", - "@0x/contract-wrappers": "^3.0.0", - "@0x/json-schemas": "^2.0.0", - "@0x/order-utils": "^2.0.0", - "@0x/subproviders": "^2.1.0", - "@0x/types": "^1.2.0", - "@0x/typescript-typings": "^3.0.3", - "@0x/utils": "^2.0.3", - "@0x/web3-wrapper": "^3.1.0", - "ethereum-types": "^1.1.1", + "@0x/assert": "^1.0.15", + "@0x/connect": "^3.0.3", + "@0x/contract-wrappers": "^3.0.1", + "@0x/json-schemas": "^2.0.1", + "@0x/order-utils": "^2.0.1", + "@0x/subproviders": "^2.1.1", + "@0x/types": "^1.2.1", + "@0x/typescript-typings": "^3.0.4", + "@0x/utils": "^2.0.4", + "@0x/web3-wrapper": "^3.1.1", + "ethereum-types": "^1.1.2", "lodash": "^4.17.10" }, "devDependencies": { - "@0x/tslint-config": "^1.0.9", + "@0x/tslint-config": "^1.0.10", "@types/lodash": "^4.14.116", "@types/mocha": "^2.2.42", "@types/node": "*", diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 74f3cb471..934410c55 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -52,16 +52,12 @@ export class AssetBuyer { public static getAssetBuyerForProvidedOrders( provider: Provider, orders: SignedOrder[], - feeOrders: SignedOrder[] = [], options: Partial<AssetBuyerOpts> = {}, ): AssetBuyer { assert.isWeb3Provider('provider', provider); assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); - assert.doesConformToSchema('feeOrders', feeOrders, schemas.signedOrdersSchema); - assert.areValidProvidedOrders('orders', orders); - assert.areValidProvidedOrders('feeOrders', feeOrders); assert.assert(orders.length !== 0, `Expected orders to contain at least one order`); - const orderProvider = new BasicOrderProvider(_.concat(orders, feeOrders)); + const orderProvider = new BasicOrderProvider(orders); const assetBuyer = new AssetBuyer(provider, orderProvider, options); return assetBuyer; } @@ -80,7 +76,8 @@ export class AssetBuyer { ): AssetBuyer { assert.isWeb3Provider('provider', provider); assert.isWebUri('sraApiUrl', sraApiUrl); - const orderProvider = new StandardRelayerAPIOrderProvider(sraApiUrl); + const networkId = options.networkId || constants.DEFAULT_ASSET_BUYER_OPTS.networkId; + const orderProvider = new StandardRelayerAPIOrderProvider(sraApiUrl, networkId); const assetBuyer = new AssetBuyer(provider, orderProvider, options); return assetBuyer; } @@ -93,10 +90,11 @@ export class AssetBuyer { * @return An instance of AssetBuyer */ constructor(provider: Provider, orderProvider: OrderProvider, options: Partial<AssetBuyerOpts> = {}) { - const { networkId, orderRefreshIntervalMs, expiryBufferSeconds } = { - ...constants.DEFAULT_ASSET_BUYER_OPTS, - ...options, - }; + const { networkId, orderRefreshIntervalMs, expiryBufferSeconds } = _.merge( + {}, + constants.DEFAULT_ASSET_BUYER_OPTS, + options, + ); assert.isWeb3Provider('provider', provider); assert.isValidOrderProvider('orderProvider', orderProvider); assert.isNumber('networkId', networkId); @@ -125,10 +123,11 @@ export class AssetBuyer { assetBuyAmount: BigNumber, options: Partial<BuyQuoteRequestOpts> = {}, ): Promise<BuyQuote> { - const { feePercentage, shouldForceOrderRefresh, slippagePercentage } = { - ...constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS, - ...options, - }; + const { feePercentage, shouldForceOrderRefresh, slippagePercentage } = _.merge( + {}, + constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS, + options, + ); assert.isString('assetData', assetData); assert.isBigNumber('assetBuyAmount', assetBuyAmount); assert.isValidPercentage('feePercentage', feePercentage); @@ -189,10 +188,11 @@ export class AssetBuyer { buyQuote: BuyQuote, options: Partial<BuyQuoteExecutionOpts> = {}, ): Promise<string> { - const { ethAmount, takerAddress, feeRecipient, gasLimit, gasPrice } = { - ...constants.DEFAULT_BUY_QUOTE_EXECUTION_OPTS, - ...options, - }; + const { ethAmount, takerAddress, feeRecipient, gasLimit, gasPrice } = _.merge( + {}, + constants.DEFAULT_BUY_QUOTE_EXECUTION_OPTS, + options, + ); assert.isValidBuyQuote('buyQuote', buyQuote); if (!_.isUndefined(ethAmount)) { assert.isBigNumber('ethAmount', ethAmount); @@ -201,6 +201,12 @@ export class AssetBuyer { assert.isETHAddressHex('takerAddress', takerAddress); } assert.isETHAddressHex('feeRecipient', feeRecipient); + if (!_.isUndefined(gasLimit)) { + assert.isNumber('gasLimit', gasLimit); + } + if (!_.isUndefined(gasPrice)) { + assert.isBigNumber('gasPrice', gasPrice); + } const { orders, feeOrders, feePercentage, assetBuyAmount, worstCaseQuoteInfo } = buyQuote; // if no takerAddress is provided, try to get one from the provider let finalTakerAddress; @@ -244,6 +250,15 @@ export class AssetBuyer { } } /** + * Get the asset data of all assets that are purchaseable with ether token (wETH) in the order provider passed in at init. + * + * @return An array of asset data strings that can be purchased using wETH. + */ + public async getAvailableAssetDatasAsync(): Promise<string[]> { + const etherTokenAssetData = this._getEtherTokenAssetDataOrThrow(); + return this.orderProvider.getAvailableMakerAssetDatasAsync(etherTokenAssetData); + } + /** * Grab orders from the map, if there is a miss or it is time to refresh, fetch and process the orders */ private async _getOrdersAndFillableAmountsAsync( diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts index c912253bd..c0e1bf27d 100644 --- a/packages/asset-buyer/src/constants.ts +++ b/packages/asset-buyer/src/constants.ts @@ -9,7 +9,7 @@ const MAINNET_NETWORK_ID = 1; const DEFAULT_ASSET_BUYER_OPTS: AssetBuyerOpts = { networkId: MAINNET_NETWORK_ID, orderRefreshIntervalMs: 10000, // 10 seconds - expiryBufferSeconds: 300, // 5 minutes + expiryBufferSeconds: 120, // 2 minutes }; const DEFAULT_BUY_QUOTE_REQUEST_OPTS: BuyQuoteRequestOpts = { @@ -36,6 +36,5 @@ export const constants = { DEFAULT_ASSET_BUYER_OPTS, DEFAULT_BUY_QUOTE_EXECUTION_OPTS, DEFAULT_BUY_QUOTE_REQUEST_OPTS, - MAX_PER_PAGE: 10000, EMPTY_ORDERS_AND_FILLABLE_AMOUNTS, }; diff --git a/packages/asset-buyer/src/order_providers/basic_order_provider.ts b/packages/asset-buyer/src/order_providers/basic_order_provider.ts index 68406f19b..76685f27a 100644 --- a/packages/asset-buyer/src/order_providers/basic_order_provider.ts +++ b/packages/asset-buyer/src/order_providers/basic_order_provider.ts @@ -29,4 +29,13 @@ export class BasicOrderProvider implements OrderProvider { }); return { orders }; } + /** + * Given a taker asset data string, return all availabled paired maker asset data strings. + * @param takerAssetData A string representing the taker asset data. + * @return An array of asset data strings that can be purchased using takerAssetData. + */ + public async getAvailableMakerAssetDatasAsync(takerAssetData: string): Promise<string[]> { + const ordersWithTakerAssetData = _.filter(this.orders, { takerAssetData }); + return _.map(ordersWithTakerAssetData, order => order.makerAssetData); + } } 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 41fd1fb30..be1fc55d6 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,5 +1,5 @@ import { HttpClient } from '@0x/connect'; -import { APIOrder, OrderbookResponse } from '@0x/types'; +import { APIOrder, AssetPairsResponse, OrderbookResponse } from '@0x/types'; import * as _ from 'lodash'; import { @@ -14,6 +14,7 @@ import { orderUtils } from '../utils/order_utils'; export class StandardRelayerAPIOrderProvider implements OrderProvider { public readonly apiUrl: string; + public readonly networkId: number; private readonly _sraClient: HttpClient; /** * Given an array of APIOrder objects from a standard relayer api, return an array @@ -44,12 +45,15 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider { } /** * Instantiates a new StandardRelayerAPIOrderProvider instance - * @param apiUrl The standard relayer API base HTTP url you would like to source orders from. + * @param apiUrl The standard relayer API base HTTP url you would like to source orders from. + * @param networkId The ethereum network id. * @return An instance of StandardRelayerAPIOrderProvider */ - constructor(apiUrl: string) { + constructor(apiUrl: string, networkId: number) { assert.isWebUri('apiUrl', apiUrl); + assert.isNumber('networkId', networkId); this.apiUrl = apiUrl; + this.networkId = networkId; this._sraClient = new HttpClient(apiUrl); } /** @@ -59,9 +63,9 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider { */ public async getOrdersAsync(orderProviderRequest: OrderProviderRequest): Promise<OrderProviderResponse> { assert.isValidOrderProviderRequest('orderProviderRequest', orderProviderRequest); - const { makerAssetData, takerAssetData, networkId } = orderProviderRequest; + const { makerAssetData, takerAssetData } = orderProviderRequest; const orderbookRequest = { baseAssetData: makerAssetData, quoteAssetData: takerAssetData }; - const requestOpts = { networkId }; + const requestOpts = { networkId: this.networkId }; let orderbook: OrderbookResponse; try { orderbook = await this._sraClient.getOrderbookAsync(orderbookRequest, requestOpts); @@ -76,4 +80,26 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider { orders, }; } + /** + * Given a taker asset data string, return all availabled paired maker asset data strings. + * @param takerAssetData A string representing the taker asset data. + * @return An array of asset data strings that can be purchased using takerAssetData. + */ + public async getAvailableMakerAssetDatasAsync(takerAssetData: string): Promise<string[]> { + // Return a maximum of 1000 asset datas + const maxPerPage = 1000; + const requestOpts = { networkId: this.networkId, perPage: maxPerPage }; + const assetPairsRequest = { assetDataA: takerAssetData }; + const fullRequest = { + ...requestOpts, + ...assetPairsRequest, + }; + let response: AssetPairsResponse; + try { + response = await this._sraClient.getAssetPairsAsync(fullRequest); + } catch (err) { + throw new Error(AssetBuyerError.StandardRelayerApiError); + } + return _.map(response.records, item => item.assetDataB.assetData); + } } diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index f15e7e934..3f1e6ff21 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -9,7 +9,6 @@ import { BigNumber } from '@0x/utils'; export interface OrderProviderRequest { makerAssetData: string; takerAssetData: string; - networkId: number; } /** @@ -27,10 +26,12 @@ export interface SignedOrderWithRemainingFillableMakerAssetAmount extends Signed remainingFillableMakerAssetAmount?: BigNumber; } /** - * Given an OrderProviderRequest, get an OrderProviderResponse. + * gerOrdersAsync: Given an OrderProviderRequest, get an OrderProviderResponse. + * getAvailableMakerAssetDatasAsync: Given a taker asset data string, return all availabled paired maker asset data strings. */ export interface OrderProvider { getOrdersAsync: (orderProviderRequest: OrderProviderRequest) => Promise<OrderProviderResponse>; + getAvailableMakerAssetDatasAsync: (takerAssetData: string) => Promise<string[]>; } /** diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts index e8cb7f763..2466f53a4 100644 --- a/packages/asset-buyer/src/utils/assert.ts +++ b/packages/asset-buyer/src/utils/assert.ts @@ -1,6 +1,5 @@ import { assert as sharedAssert } from '@0x/assert'; import { schemas } from '@0x/json-schemas'; -import { SignedOrder } from '@0x/types'; import * as _ from 'lodash'; import { BuyQuote, BuyQuoteInfo, OrderProvider, OrderProviderRequest } from '../types'; @@ -29,22 +28,6 @@ export const assert = { isValidOrderProviderRequest(variableName: string, orderFetcherRequest: OrderProviderRequest): void { sharedAssert.isHexString(`${variableName}.makerAssetData`, orderFetcherRequest.makerAssetData); 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.`, - ); }, isValidPercentage(variableName: string, percentage: number): void { assert.isNumber(variableName, percentage); diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index f94ab3fa4..6a67ed1ed 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -119,7 +119,7 @@ function calculateQuoteInfo( ethAmountToBuyZrx = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset); } /// find the eth amount needed to buy the affiliate fee - const ethAmountToBuyAffiliateFee = ethAmountToBuyAsset.mul(feePercentage); + const ethAmountToBuyAffiliateFee = ethAmountToBuyAsset.mul(feePercentage).ceil(); const totalEthAmountWithoutAffiliateFee = ethAmountToBuyAsset.plus(ethAmountToBuyZrx); const ethAmountTotal = totalEthAmountWithoutAffiliateFee.plus(ethAmountToBuyAffiliateFee); // divide into the assetBuyAmount in order to find rate of makerAsset / WETH |