aboutsummaryrefslogtreecommitdiffstats
path: root/packages/asset-buyer
diff options
context:
space:
mode:
Diffstat (limited to 'packages/asset-buyer')
-rw-r--r--packages/asset-buyer/CHANGELOG.json61
-rw-r--r--packages/asset-buyer/CHANGELOG.md19
-rw-r--r--packages/asset-buyer/README.md14
-rw-r--r--packages/asset-buyer/package.json42
-rw-r--r--packages/asset-buyer/src/asset_buyer.ts116
-rw-r--r--packages/asset-buyer/src/constants.ts14
-rw-r--r--packages/asset-buyer/src/index.ts13
-rw-r--r--packages/asset-buyer/src/order_providers/basic_order_provider.ts13
-rw-r--r--packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts40
-rw-r--r--packages/asset-buyer/src/types.ts11
-rw-r--r--packages/asset-buyer/src/utils/assert.ts21
-rw-r--r--packages/asset-buyer/src/utils/asset_data_utils.ts24
-rw-r--r--packages/asset-buyer/src/utils/buy_quote_calculator.ts122
-rw-r--r--packages/asset-buyer/src/utils/order_provider_response_processor.ts15
-rw-r--r--packages/asset-buyer/src/utils/order_utils.ts72
-rw-r--r--packages/asset-buyer/test/buy_quote_calculator_test.ts16
-rw-r--r--packages/asset-buyer/tslint.json2
17 files changed, 408 insertions, 207 deletions
diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json
index dbb801b69..df4531063 100644
--- a/packages/asset-buyer/CHANGELOG.json
+++ b/packages/asset-buyer/CHANGELOG.json
@@ -1,11 +1,70 @@
[
{
+ "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": [
{
"note": "Add `gasLimit` and `gasPrice` as optional properties on `BuyQuoteExecutionOpts`"
+ },
+ {
+ "note": "Export `BuyQuoteInfo` type",
+ "pr": 1131
+ },
+ {
+ "note":
+ "Updated to use new modularized artifacts and the latest version of @0xproject/contract-wrappers",
+ "pr": 1105
+ },
+ {
+ "note": "Add `gasLimit` and `gasPrice` as optional properties on `BuyQuoteExecutionOpts`",
+ "pr": 1116
+ },
+ {
+ "note": "Add `docs:json` command to package.json",
+ "pr": 1139
+ },
+ {
+ "note": "Add missing types to public interface",
+ "pr": 1139
+ },
+ {
+ "note": "Throw `SignatureRequestDenied` and `TransactionValueTooLow` errors when executing buy",
+ "pr": 1147
}
- ]
+ ],
+ "timestamp": 1539871071
},
{
"version": "2.0.0",
diff --git a/packages/asset-buyer/CHANGELOG.md b/packages/asset-buyer/CHANGELOG.md
index 65d190a2d..d6220767e 100644
--- a/packages/asset-buyer/CHANGELOG.md
+++ b/packages/asset-buyer/CHANGELOG.md
@@ -5,6 +5,25 @@ 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`
+ * Export `BuyQuoteInfo` type (#1131)
+ * Updated to use new modularized artifacts and the latest version of @0xproject/contract-wrappers (#1105)
+ * 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_
* Expand AssetBuyer to work with multiple assets at once (#1086)
diff --git a/packages/asset-buyer/README.md b/packages/asset-buyer/README.md
index 81c7e1e85..383a3836a 100644
--- a/packages/asset-buyer/README.md
+++ b/packages/asset-buyer/README.md
@@ -1,4 +1,4 @@
-## @0xproject/asset-buyer
+## @0x/asset-buyer
**Warning: In Beta, has not been extensively tested.**
@@ -9,26 +9,26 @@ In its more advanced and useful form, it integrates with the [Standard Relayer A
## Installation
```bash
-yarn add @0xproject/asset-buyer
+yarn add @0x/asset-buyer
```
**Import**
```typescript
-import { AssetBuyer } from '@0xproject/asset-buyer';
+import { AssetBuyer } from '@0x/asset-buyer';
```
or
```javascript
-var AssetBuyer = require('@0xproject/asset-buyer').AssetBuyer;
+var AssetBuyer = require('@0x/asset-buyer').AssetBuyer;
```
If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`:
```json
"compilerOptions": {
- "typeRoots": ["node_modules/@0xproject/typescript-typings/types", "node_modules/@types"],
+ "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"],
}
```
@@ -57,13 +57,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/asset-buyer yarn build
+PKG=@0x/asset-buyer yarn build
```
Or continuously rebuild on change:
```bash
-PKG=@0xproject/asset-buyer yarn watch
+PKG=@0x/asset-buyer yarn watch
```
### Clean
diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json
index 291342169..fc2414952 100644
--- a/packages/asset-buyer/package.json
+++ b/packages/asset-buyer/package.json
@@ -1,6 +1,6 @@
{
- "name": "@0xproject/asset-buyer",
- "version": "2.0.0",
+ "name": "@0x/asset-buyer",
+ "version": "2.2.0",
"engines": {
"node": ">=6.12"
},
@@ -8,18 +8,17 @@
"main": "lib/src/index.js",
"types": "lib/src/index.d.ts",
"scripts": {
- "watch_without_deps": "tsc -w",
- "lint": "tslint --project .",
+ "build": "yarn tsc -b",
+ "build:ci": "yarn build",
+ "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",
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
"test:circleci": "yarn test:coverage",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --exit",
- "clean": "shx rm -rf lib test_temp scripts",
- "build": "tsc && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts",
- "build:ci": "yarn build",
- "manual:postpublish": "yarn build; node ./scripts/postpublish.js"
+ "clean": "shx rm -rf lib test_temp",
+ "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
},
"config": {
"postpublish": {
@@ -37,28 +36,27 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/packages/asset-buyer/README.md",
"dependencies": {
- "@0xproject/assert": "^1.0.13",
- "@0xproject/connect": "^3.0.1",
- "@0xproject/contract-wrappers": "^2.0.2",
- "@0xproject/json-schemas": "^1.0.7",
- "@0xproject/order-utils": "^1.0.7",
- "@0xproject/subproviders": "^2.0.7",
- "@0xproject/types": "^1.1.4",
- "@0xproject/typescript-typings": "^3.0.2",
- "@0xproject/utils": "^2.0.2",
- "@0xproject/web3-wrapper": "^3.0.3",
- "ethereum-types": "^1.0.11",
+ "@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": {
- "@0xproject/tslint-config": "^1.0.8",
+ "@0x/tslint-config": "^1.0.10",
"@types/lodash": "^4.14.116",
"@types/mocha": "^2.2.42",
"@types/node": "*",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^2.0.1",
- "copyfiles": "^1.2.0",
"dirty-chai": "^2.0.1",
"make-promises-safe": "^1.1.0",
"mocha": "^4.1.0",
@@ -66,7 +64,7 @@
"nyc": "^11.0.1",
"shx": "^0.2.2",
"tslint": "5.11.0",
- "typedoc": "0.12.0",
+ "typedoc": "0.13.0",
"typescript": "3.0.1"
},
"publishConfig": {
diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts
index 50343efde..934410c55 100644
--- a/packages/asset-buyer/src/asset_buyer.ts
+++ b/packages/asset-buyer/src/asset_buyer.ts
@@ -1,9 +1,9 @@
-import { ContractWrappers } from '@0xproject/contract-wrappers';
-import { schemas } from '@0xproject/json-schemas';
-import { SignedOrder } from '@0xproject/order-utils';
-import { ObjectMap } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { ContractWrappers, ContractWrappersError, ForwarderWrapperError } from '@0x/contract-wrappers';
+import { schemas } from '@0x/json-schemas';
+import { SignedOrder } from '@0x/order-utils';
+import { ObjectMap } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
@@ -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,19 +123,25 @@ 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);
assert.isBoolean('shouldForceOrderRefresh', shouldForceOrderRefresh);
assert.isNumber('slippagePercentage', slippagePercentage);
const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow();
+ const isMakerAssetZrxToken = assetData === zrxTokenAssetData;
+ // get the relevant orders for the makerAsset and fees
+ // if the requested assetData is ZRX, don't get the fee info
const [ordersAndFillableAmounts, feeOrdersAndFillableAmounts] = await Promise.all([
this._getOrdersAndFillableAmountsAsync(assetData, shouldForceOrderRefresh),
- this._getOrdersAndFillableAmountsAsync(zrxTokenAssetData, shouldForceOrderRefresh),
+ isMakerAssetZrxToken
+ ? Promise.resolve(constants.EMPTY_ORDERS_AND_FILLABLE_AMOUNTS)
+ : this._getOrdersAndFillableAmountsAsync(zrxTokenAssetData, shouldForceOrderRefresh),
shouldForceOrderRefresh,
]);
if (ordersAndFillableAmounts.orders.length === 0) {
@@ -149,6 +153,7 @@ export class AssetBuyer {
assetBuyAmount,
feePercentage,
slippagePercentage,
+ isMakerAssetZrxToken,
);
return buyQuote;
}
@@ -183,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);
@@ -195,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;
@@ -210,21 +222,41 @@ export class AssetBuyer {
throw new Error(AssetBuyerError.NoAddressAvailable);
}
}
- // if no ethAmount is provided, default to the worst ethAmount from buyQuote
- const txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
- orders,
- assetBuyAmount,
- finalTakerAddress,
- ethAmount || worstCaseQuoteInfo.totalEthAmount,
- feeOrders,
- feePercentage,
- feeRecipient,
- {
- gasLimit,
- gasPrice,
- },
- );
- return txHash;
+ try {
+ // if no ethAmount is provided, default to the worst ethAmount from buyQuote
+ const txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
+ orders,
+ assetBuyAmount,
+ finalTakerAddress,
+ ethAmount || worstCaseQuoteInfo.totalEthAmount,
+ feeOrders,
+ feePercentage,
+ feeRecipient,
+ {
+ gasLimit,
+ gasPrice,
+ shouldValidate: true,
+ },
+ );
+ return txHash;
+ } catch (err) {
+ if (_.includes(err.message, ContractWrappersError.SignatureRequestDenied)) {
+ throw new Error(AssetBuyerError.SignatureRequestDenied);
+ } else if (_.includes(err.message, ForwarderWrapperError.CompleteFillFailed)) {
+ throw new Error(AssetBuyerError.TransactionValueTooLow);
+ } else {
+ throw err;
+ }
+ }
+ }
+ /**
+ * 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
@@ -283,13 +315,13 @@ export class AssetBuyer {
* Will throw if WETH does not exist for the current network.
*/
private _getEtherTokenAssetDataOrThrow(): string {
- return assetDataUtils.getEtherTokenAssetDataOrThrow(this._contractWrappers);
+ 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 _getZrxTokenAssetDataOrThrow(): string {
- return assetDataUtils.getZrxTokenAssetDataOrThrow(this._contractWrappers);
+ return this._contractWrappers.exchange.getZRXAssetData();
}
}
diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts
index e095dee06..c0e1bf27d 100644
--- a/packages/asset-buyer/src/constants.ts
+++ b/packages/asset-buyer/src/constants.ts
@@ -1,6 +1,7 @@
-import { BigNumber } from '@0xproject/utils';
+import { SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
-import { AssetBuyerOpts, BuyQuoteExecutionOpts, BuyQuoteRequestOpts } from './types';
+import { AssetBuyerOpts, BuyQuoteExecutionOpts, BuyQuoteRequestOpts, OrdersAndFillableAmounts } from './types';
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
const MAINNET_NETWORK_ID = 1;
@@ -8,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 = {
@@ -22,6 +23,11 @@ const DEFAULT_BUY_QUOTE_EXECUTION_OPTS: BuyQuoteExecutionOpts = {
feeRecipient: NULL_ADDRESS,
};
+const EMPTY_ORDERS_AND_FILLABLE_AMOUNTS: OrdersAndFillableAmounts = {
+ orders: [] as SignedOrder[],
+ remainingFillableMakerAssetAmounts: [] as BigNumber[],
+};
+
export const constants = {
ZERO_AMOUNT: new BigNumber(0),
NULL_ADDRESS,
@@ -30,5 +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/index.ts b/packages/asset-buyer/src/index.ts
index 2da2724d7..8418edb42 100644
--- a/packages/asset-buyer/src/index.ts
+++ b/packages/asset-buyer/src/index.ts
@@ -1,6 +1,12 @@
-export { Provider } from 'ethereum-types';
-export { SignedOrder } from '@0xproject/types';
-export { BigNumber } from '@0xproject/utils';
+export {
+ JSONRPCRequestPayload,
+ JSONRPCResponsePayload,
+ JSONRPCResponseError,
+ JSONRPCErrorCallback,
+ Provider,
+} from 'ethereum-types';
+export { SignedOrder } from '@0x/types';
+export { BigNumber } from '@0x/utils';
export { AssetBuyer } from './asset_buyer';
export { BasicOrderProvider } from './order_providers/basic_order_provider';
@@ -10,6 +16,7 @@ export {
AssetBuyerOpts,
BuyQuote,
BuyQuoteExecutionOpts,
+ BuyQuoteInfo,
BuyQuoteRequestOpts,
OrderProvider,
OrderProviderRequest,
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 9bb2d90ac..76685f27a 100644
--- a/packages/asset-buyer/src/order_providers/basic_order_provider.ts
+++ b/packages/asset-buyer/src/order_providers/basic_order_provider.ts
@@ -1,5 +1,5 @@
-import { schemas } from '@0xproject/json-schemas';
-import { SignedOrder } from '@0xproject/types';
+import { schemas } from '@0x/json-schemas';
+import { SignedOrder } from '@0x/types';
import * as _ from 'lodash';
import { OrderProvider, OrderProviderRequest, OrderProviderResponse } from '../types';
@@ -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 31942c25b..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 '@0xproject/connect';
-import { APIOrder, OrderbookResponse } from '@0xproject/types';
+import { HttpClient } from '@0x/connect';
+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
@@ -30,7 +31,7 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider {
'remainingTakerAssetAmount',
order.takerAssetAmount,
);
- const remainingFillableMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount(
+ const remainingFillableMakerAssetAmount = orderUtils.getRemainingMakerAmount(
order,
remainingFillableTakerAssetAmount,
);
@@ -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 6218f4ba4..3f1e6ff21 100644
--- a/packages/asset-buyer/src/types.ts
+++ b/packages/asset-buyer/src/types.ts
@@ -1,5 +1,5 @@
-import { SignedOrder } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
/**
* makerAssetData: The assetData representing the desired makerAsset.
@@ -9,7 +9,6 @@ import { BigNumber } from '@0xproject/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[]>;
}
/**
@@ -112,6 +113,8 @@ export enum AssetBuyerError {
NoAddressAvailable = 'NO_ADDRESS_AVAILABLE',
InvalidOrderProviderResponse = 'INVALID_ORDER_PROVIDER_RESPONSE',
AssetUnavailable = 'ASSET_UNAVAILABLE',
+ SignatureRequestDenied = 'SIGNATURE_REQUEST_DENIED',
+ TransactionValueTooLow = 'TRANSACTION_VALUE_TOO_LOW',
}
export interface OrdersAndFillableAmounts {
diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts
index d43b71fee..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 '@0xproject/assert';
-import { schemas } from '@0xproject/json-schemas';
-import { SignedOrder } from '@0xproject/types';
+import { assert as sharedAssert } from '@0x/assert';
+import { schemas } from '@0x/json-schemas';
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/asset_data_utils.ts b/packages/asset-buyer/src/utils/asset_data_utils.ts
index d05ff2504..70f646902 100644
--- a/packages/asset-buyer/src/utils/asset_data_utils.ts
+++ b/packages/asset-buyer/src/utils/asset_data_utils.ts
@@ -1,26 +1,12 @@
-import { ContractWrappers } from '@0xproject/contract-wrappers';
-import { assetDataUtils as sharedAssetDataUtils } from '@0xproject/order-utils';
+import { ContractWrappers } from '@0x/contract-wrappers';
+import { assetDataUtils as sharedAssetDataUtils } from '@0x/order-utils';
import * as _ from 'lodash';
-import { AssetBuyerError } from '../types';
-
export const assetDataUtils = {
...sharedAssetDataUtils,
- getEtherTokenAssetDataOrThrow(contractWrappers: ContractWrappers): string {
- const etherTokenAddressIfExists = contractWrappers.etherToken.getContractAddressIfExists();
- if (_.isUndefined(etherTokenAddressIfExists)) {
- throw new Error(AssetBuyerError.NoEtherTokenContractFound);
- }
- const etherTokenAssetData = sharedAssetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists);
+ getEtherTokenAssetData(contractWrappers: ContractWrappers): string {
+ const etherTokenAddress = contractWrappers.forwarder.etherTokenAddress;
+ const etherTokenAssetData = sharedAssetDataUtils.encodeERC20AssetData(etherTokenAddress);
return etherTokenAssetData;
},
- getZrxTokenAssetDataOrThrow(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 a1d334eef..6a67ed1ed 100644
--- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts
+++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts
@@ -1,10 +1,12 @@
-import { marketUtils, rateUtils } from '@0xproject/order-utils';
-import { BigNumber } from '@0xproject/utils';
+import { marketUtils, SignedOrder } from '@0x/order-utils';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { constants } from '../constants';
import { AssetBuyerError, BuyQuote, BuyQuoteInfo, OrdersAndFillableAmounts } from '../types';
+import { orderUtils } from './order_utils';
+
// Calculates a buy quote for orders that have WETH as the takerAsset
export const buyQuoteCalculator = {
calculate(
@@ -13,6 +15,7 @@ export const buyQuoteCalculator = {
assetBuyAmount: BigNumber,
feePercentage: number,
slippagePercentage: number,
+ isMakerAssetZrxToken: boolean,
): BuyQuote {
const orders = ordersAndFillableAmounts.orders;
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
@@ -32,22 +35,31 @@ export const buyQuoteCalculator = {
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
throw new Error(AssetBuyerError.InsufficientAssetLiquidity);
}
+ // if we are not buying ZRX:
// given the orders calculated above, find the fee-orders that cover the desired assetBuyAmount (with slippage)
// 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 {
- resultFeeOrders,
- remainingFeeAmount,
- feeOrdersRemainingFillableMakerAssetAmounts,
- } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(resultOrders, feeOrders, {
- remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
- remainingFillableFeeAmounts,
- });
- // if we do not have enough feeOrders to cover the fees, throw
- if (remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
- throw new Error(AssetBuyerError.InsufficientZrxLiquidity);
+ let resultFeeOrders = [] as SignedOrder[];
+ let feeOrdersRemainingFillableMakerAssetAmounts = [] as BigNumber[];
+ if (!isMakerAssetZrxToken) {
+ const feeOrdersAndRemainingFeeAmount = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
+ resultOrders,
+ feeOrders,
+ {
+ remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
+ remainingFillableFeeAmounts,
+ },
+ );
+ // if we do not have enough feeOrders to cover the fees, throw
+ if (feeOrdersAndRemainingFeeAmount.remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
+ throw new Error(AssetBuyerError.InsufficientZrxLiquidity);
+ }
+ resultFeeOrders = feeOrdersAndRemainingFeeAmount.resultFeeOrders;
+ feeOrdersRemainingFillableMakerAssetAmounts =
+ feeOrdersAndRemainingFeeAmount.feeOrdersRemainingFillableMakerAssetAmounts;
}
+
// assetData information for the result
const assetData = orders[0].makerAssetData;
// compile the resulting trimmed set of orders for makerAsset and feeOrders that are needed for assetBuyAmount
@@ -64,6 +76,7 @@ export const buyQuoteCalculator = {
trimmedFeeOrdersAndFillableAmounts,
assetBuyAmount,
feePercentage,
+ isMakerAssetZrxToken,
);
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
const worstCaseQuoteInfo = calculateQuoteInfo(
@@ -71,6 +84,7 @@ export const buyQuoteCalculator = {
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
assetBuyAmount,
feePercentage,
+ isMakerAssetZrxToken,
);
return {
assetData,
@@ -89,22 +103,30 @@ function calculateQuoteInfo(
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
assetBuyAmount: BigNumber,
feePercentage: number,
+ isMakerAssetZrxToken: boolean,
): BuyQuoteInfo {
// find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right
- const [ethAmountToBuyAsset, zrxAmountToBuyAsset] = findEthAndZrxAmountNeededToBuyAsset(
- ordersAndFillableAmounts,
- assetBuyAmount,
- );
- // find the total eth needed to buy fees
- const ethAmountToBuyFees = findEthAmountNeededToBuyFees(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
- const affiliateFeeEthAmount = ethAmountToBuyAsset.mul(feePercentage);
- const totalEthAmountWithoutAffiliateFee = ethAmountToBuyAsset.plus(ethAmountToBuyFees);
- const totalEthAmount = totalEthAmountWithoutAffiliateFee.plus(affiliateFeeEthAmount);
+ let ethAmountToBuyAsset = constants.ZERO_AMOUNT;
+ let ethAmountToBuyZrx = constants.ZERO_AMOUNT;
+ if (isMakerAssetZrxToken) {
+ ethAmountToBuyAsset = findEthAmountNeededToBuyZrx(ordersAndFillableAmounts, assetBuyAmount);
+ } else {
+ // find eth and zrx amounts needed to buy
+ const ethAndZrxAmountToBuyAsset = findEthAndZrxAmountNeededToBuyAsset(ordersAndFillableAmounts, assetBuyAmount);
+ ethAmountToBuyAsset = ethAndZrxAmountToBuyAsset[0];
+ const zrxAmountToBuyAsset = ethAndZrxAmountToBuyAsset[1];
+ // find eth amount needed to buy zrx
+ ethAmountToBuyZrx = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
+ }
+ /// find the eth amount needed to buy the affiliate fee
+ 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
const ethPerAssetPrice = totalEthAmountWithoutAffiliateFee.div(assetBuyAmount);
return {
- totalEthAmount,
- feeEthAmount: affiliateFeeEthAmount,
+ totalEthAmount: ethAmountTotal,
+ feeEthAmount: ethAmountToBuyAffiliateFee,
ethPerAssetPrice,
};
}
@@ -119,29 +141,38 @@ function reverseOrdersAndFillableAmounts(ordersAndFillableAmounts: OrdersAndFill
};
}
-function findEthAmountNeededToBuyFees(
+function findEthAmountNeededToBuyZrx(
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
- feeAmount: BigNumber,
+ zrxBuyAmount: BigNumber,
): BigNumber {
const { orders, remainingFillableMakerAssetAmounts } = feeOrdersAndFillableAmounts;
const result = _.reduce(
orders,
(acc, order, index) => {
+ const { totalEthAmount, remainingZrxBuyAmount } = acc;
const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
- const amountToFill = BigNumber.min(acc.remainingFeeAmount, remainingFillableMakerAssetAmount);
- const feeAdjustedRate = rateUtils.getFeeAdjustedRateOfFeeOrder(order);
- const ethAmountForThisOrder = feeAdjustedRate.mul(amountToFill);
+ const makerFillAmount = BigNumber.min(remainingZrxBuyAmount, remainingFillableMakerAssetAmount);
+ const [takerFillAmount, adjustedMakerFillAmount] = orderUtils.getTakerFillAmountForFeeOrder(
+ order,
+ makerFillAmount,
+ );
+ const extraFeeAmount = remainingFillableMakerAssetAmount.greaterThanOrEqualTo(adjustedMakerFillAmount)
+ ? constants.ZERO_AMOUNT
+ : adjustedMakerFillAmount.sub(makerFillAmount);
return {
- ethAmount: acc.ethAmount.plus(ethAmountForThisOrder),
- remainingFeeAmount: BigNumber.max(constants.ZERO_AMOUNT, acc.remainingFeeAmount.minus(amountToFill)),
+ totalEthAmount: totalEthAmount.plus(takerFillAmount),
+ remainingZrxBuyAmount: BigNumber.max(
+ constants.ZERO_AMOUNT,
+ remainingZrxBuyAmount.minus(makerFillAmount).plus(extraFeeAmount),
+ ),
};
},
{
- ethAmount: constants.ZERO_AMOUNT,
- remainingFeeAmount: feeAmount,
+ totalEthAmount: constants.ZERO_AMOUNT,
+ remainingZrxBuyAmount: zrxBuyAmount,
},
);
- return result.ethAmount;
+ return result.totalEthAmount;
}
function findEthAndZrxAmountNeededToBuyAsset(
@@ -152,28 +183,25 @@ function findEthAndZrxAmountNeededToBuyAsset(
const result = _.reduce(
orders,
(acc, order, index) => {
+ const { totalEthAmount, totalZrxAmount, remainingAssetBuyAmount } = acc;
const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
- const amountToFill = BigNumber.min(acc.remainingAssetBuyAmount, remainingFillableMakerAssetAmount);
- // find the amount of eth required to fill amountToFill (amountToFill / makerAssetAmount) * takerAssetAmount
- const ethAmountForThisOrder = amountToFill
- .mul(order.takerAssetAmount)
- .dividedToIntegerBy(order.makerAssetAmount);
- // find the amount of zrx required to fill fees for amountToFill (amountToFill / makerAssetAmount) * takerFee
- const zrxAmountForThisOrder = amountToFill.mul(order.takerFee).dividedToIntegerBy(order.makerAssetAmount);
+ const makerFillAmount = BigNumber.min(acc.remainingAssetBuyAmount, remainingFillableMakerAssetAmount);
+ const takerFillAmount = orderUtils.getTakerFillAmount(order, makerFillAmount);
+ const takerFeeAmount = orderUtils.getTakerFeeAmount(order, takerFillAmount);
return {
- ethAmount: acc.ethAmount.plus(ethAmountForThisOrder),
- zrxAmount: acc.zrxAmount.plus(zrxAmountForThisOrder),
+ totalEthAmount: totalEthAmount.plus(takerFillAmount),
+ totalZrxAmount: totalZrxAmount.plus(takerFeeAmount),
remainingAssetBuyAmount: BigNumber.max(
constants.ZERO_AMOUNT,
- acc.remainingAssetBuyAmount.minus(amountToFill),
+ remainingAssetBuyAmount.minus(makerFillAmount),
),
};
},
{
- ethAmount: constants.ZERO_AMOUNT,
- zrxAmount: constants.ZERO_AMOUNT,
+ totalEthAmount: constants.ZERO_AMOUNT,
+ totalZrxAmount: constants.ZERO_AMOUNT,
remainingAssetBuyAmount: assetBuyAmount,
},
);
- return [result.ethAmount, result.zrxAmount];
+ return [result.totalEthAmount, result.totalZrxAmount];
}
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 74eec162d..28f684f3c 100644
--- a/packages/asset-buyer/src/utils/order_provider_response_processor.ts
+++ b/packages/asset-buyer/src/utils/order_provider_response_processor.ts
@@ -1,8 +1,8 @@
-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 { OrderAndTraderInfo, OrderStatus, OrderValidatorWrapper } from '@0x/contract-wrappers';
+import { sortingUtils } from '@0x/order-utils';
+import { RemainingFillableCalculator } from '@0x/order-utils/lib/src/remaining_fillable_calculator';
+import { SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { constants } from '../constants';
@@ -110,10 +110,7 @@ function getValidOrdersWithRemainingFillableMakerAssetAmountsFromOnChain(
traderInfo.makerZrxBalance,
]);
const remainingTakerAssetAmount = order.takerAssetAmount.minus(orderInfo.orderTakerAssetFilledAmount);
- const remainingMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount(
- order,
- remainingTakerAssetAmount,
- );
+ const remainingMakerAssetAmount = orderUtils.getRemainingMakerAmount(order, remainingTakerAssetAmount);
const remainingFillableCalculator = new RemainingFillableCalculator(
order.makerFee,
order.makerAssetAmount,
diff --git a/packages/asset-buyer/src/utils/order_utils.ts b/packages/asset-buyer/src/utils/order_utils.ts
index cfc13a8a1..1cc2cf95f 100644
--- a/packages/asset-buyer/src/utils/order_utils.ts
+++ b/packages/asset-buyer/src/utils/order_utils.ts
@@ -1,5 +1,5 @@
-import { SignedOrder } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import { constants } from '../constants';
@@ -12,19 +12,63 @@ export const orderUtils = {
const currentUnixTimestampSec = new BigNumber(Date.now() / millisecondsInSecond).round();
return order.expirationTimeSeconds.lessThan(currentUnixTimestampSec.plus(secondsFromNow));
},
- calculateRemainingMakerAssetAmount(order: SignedOrder, remainingTakerAssetAmount: BigNumber): BigNumber {
- 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;
},
+ // given a remaining amount of takerAsset, calculate how much makerAsset is available
+ getRemainingMakerAmount(order: SignedOrder, remainingTakerAmount: BigNumber): BigNumber {
+ const remainingMakerAmount = remainingTakerAmount
+ .times(order.makerAssetAmount)
+ .div(order.takerAssetAmount)
+ .floor();
+ return remainingMakerAmount;
+ },
+ // given a desired amount of makerAsset, calculate how much takerAsset is required to fill that amount
+ getTakerFillAmount(order: SignedOrder, makerFillAmount: BigNumber): BigNumber {
+ // Round up because exchange rate favors Maker
+ const takerFillAmount = makerFillAmount
+ .mul(order.takerAssetAmount)
+ .div(order.makerAssetAmount)
+ .ceil();
+ return takerFillAmount;
+ },
+ // given a desired amount of takerAsset to fill, calculate how much fee is required by the taker to fill that amount
+ getTakerFeeAmount(order: SignedOrder, takerFillAmount: BigNumber): BigNumber {
+ // Round down because Taker fee rate favors Taker
+ const takerFeeAmount = takerFillAmount
+ .mul(order.takerFee)
+ .div(order.takerAssetAmount)
+ .floor();
+ return takerFeeAmount;
+ },
+ // given a desired amount of takerAsset to fill, calculate how much makerAsset will be filled
+ getMakerFillAmount(order: SignedOrder, takerFillAmount: BigNumber): BigNumber {
+ // Round down because exchange rate favors Maker
+ const makerFillAmount = takerFillAmount
+ .mul(order.makerAssetAmount)
+ .div(order.takerAssetAmount)
+ .floor();
+ return makerFillAmount;
+ },
+ // given a desired amount of makerAsset, calculate how much fee is required by the maker to fill that amount
+ getMakerFeeAmount(order: SignedOrder, makerFillAmount: BigNumber): BigNumber {
+ // Round down because Maker fee rate favors Maker
+ const makerFeeAmount = makerFillAmount
+ .mul(order.makerFee)
+ .div(order.makerAssetAmount)
+ .floor();
+ return makerFeeAmount;
+ },
+ // given a desired amount of ZRX from a fee order, calculate how much takerAsset is required to fill that amount
+ // also calculate how much ZRX needs to be bought in order fill the desired amount + takerFee
+ getTakerFillAmountForFeeOrder(order: SignedOrder, makerFillAmount: BigNumber): [BigNumber, BigNumber] {
+ // For each unit of TakerAsset we buy (MakerAsset - TakerFee)
+ const adjustedTakerFillAmount = makerFillAmount
+ .mul(order.takerAssetAmount)
+ .div(order.makerAssetAmount.sub(order.takerFee))
+ .ceil();
+ // The amount that we buy will be greater than makerFillAmount, since we buy some amount for fees.
+ const adjustedMakerFillAmount = orderUtils.getMakerFillAmount(order, adjustedTakerFillAmount);
+ return [adjustedTakerFillAmount, adjustedMakerFillAmount];
+ },
};
diff --git a/packages/asset-buyer/test/buy_quote_calculator_test.ts b/packages/asset-buyer/test/buy_quote_calculator_test.ts
index fda6958cd..0ea371982 100644
--- a/packages/asset-buyer/test/buy_quote_calculator_test.ts
+++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts
@@ -1,5 +1,5 @@
-import { orderFactory } from '@0xproject/order-utils/lib/src/order_factory';
-import { BigNumber } from '@0xproject/utils';
+import { orderFactory } from '@0x/order-utils/lib/src/order_factory';
+import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import 'mocha';
@@ -49,9 +49,9 @@ describe('buyQuoteCalculator', () => {
remainingFillableMakerAssetAmounts: [smallFeeOrder.makerAssetAmount],
};
const largeFeeOrder = orderFactory.createSignedOrderFromPartial({
- makerAssetAmount: new BigNumber(110),
+ makerAssetAmount: new BigNumber(113),
takerAssetAmount: new BigNumber(200),
- takerFee: new BigNumber(10),
+ takerFee: new BigNumber(11),
});
allFeeOrdersAndFillableAmounts = {
orders: [smallFeeOrder, largeFeeOrder],
@@ -70,6 +70,7 @@ describe('buyQuoteCalculator', () => {
new BigNumber(500),
0,
0,
+ false,
),
).to.throw(AssetBuyerError.InsufficientAssetLiquidity);
});
@@ -82,6 +83,7 @@ describe('buyQuoteCalculator', () => {
new BigNumber(300),
0,
0,
+ false,
),
).to.throw(AssetBuyerError.InsufficientZrxLiquidity);
});
@@ -97,6 +99,7 @@ describe('buyQuoteCalculator', () => {
assetBuyAmount,
feePercentage,
slippagePercentage,
+ false,
);
// test if orders are correct
expect(buyQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
@@ -134,6 +137,7 @@ describe('buyQuoteCalculator', () => {
assetBuyAmount,
feePercentage,
slippagePercentage,
+ false,
);
// test if orders are correct
expect(buyQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
@@ -149,9 +153,9 @@ describe('buyQuoteCalculator', () => {
expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
expect(buyQuote.bestCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice);
- // 100 eth to fill the first order + 200 eth for fees
+ // 100 eth to fill the first order + 208 eth for fees
const expectedWorstEthAmountForAsset = new BigNumber(100);
- const expectedWorstEthAmountForZrxFees = new BigNumber(200);
+ const expectedWorstEthAmountForZrxFees = new BigNumber(208);
const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset.plus(expectedWorstEthAmountForZrxFees);
const expectedWorstFeeEthAmount = expectedWorstEthAmountForAsset.mul(feePercentage);
const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.plus(expectedWorstFeeEthAmount);
diff --git a/packages/asset-buyer/tslint.json b/packages/asset-buyer/tslint.json
index ffaefe83a..dd9053357 100644
--- a/packages/asset-buyer/tslint.json
+++ b/packages/asset-buyer/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0x/tslint-config"]
}