removed asset-buyer

This commit is contained in:
David Sun 2019-11-12 16:31:08 -05:00 committed by Jacob Evans
parent 64d25e6522
commit dd00f2016f
No known key found for this signature in database
GPG Key ID: 2036DA2ADDFB0842
27 changed files with 4 additions and 2814 deletions

View File

@ -1,504 +0,0 @@
[
{
"version": "6.2.0-beta.3",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1574238768
},
{
"version": "6.2.0-beta.2",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1574030254
},
{
"version": "6.2.0-beta.1",
"changes": [
{
"note": "All references to network ID have been removed, and references to chain ID have been introduced instead",
"pr": 2313
}
],
"timestamp": 1573159180
},
{
"version": "6.2.0-beta.0",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,
"version": "6.1.14",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1567521715,
"version": "6.1.13",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1566446343,
"version": "6.1.12",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1565296576,
"version": "6.1.11",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1564604963,
"version": "6.1.10",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1563957393,
"version": "6.1.9",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1563193019,
"version": "6.1.8",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1563047529,
"version": "6.1.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1563006338,
"version": "6.1.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1558712885,
"version": "6.1.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1557961111,
"version": "6.1.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "6.1.3",
"changes": [
{
"note": "Convert `metaData.remainingTakerAssetAmount` to BigNumber if present in APIOrder",
"pr": 1810
}
],
"timestamp": 1557799313
},
{
"timestamp": 1557507213,
"version": "6.1.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "6.1.0",
"changes": [
{
"note": "Moved order_utils into `@0x/order-utils` package as `orderCalculationUtils`",
"pr": 1714
}
],
"timestamp": 1554997931
},
{
"timestamp": 1553183790,
"version": "6.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1553091633,
"version": "6.0.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1551479279,
"version": "6.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1551299797,
"version": "6.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1551220833,
"version": "6.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "6.0.0",
"changes": [
{
"note": "Add support for EIP1193 providers & Web3.js providers >= 1.0-beta.38",
"pr": 1627
},
{
"note": "Update provider params to type SupportedProvider which outlines all supported providers",
"pr": 1627
}
],
"timestamp": 1551130135
},
{
"timestamp": 1549733923,
"version": "5.0.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "5.0.3",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1549547375
},
{
"timestamp": 1549504360,
"version": "5.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1549452781,
"version": "5.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "5.0.0",
"changes": [
{
"note": "Upgrade the bignumber.js to v8.0.2",
"pr": 1517
}
],
"timestamp": 1549373905
},
{
"version": "4.1.0",
"changes": [
{
"note": "Adds new public method getLiquidityForAssetDataAsync, and exposes getOrdersAndFillableAmountsAsync as public method",
"pr": 1512
}
]
},
{
"timestamp": 1547747677,
"version": "4.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1547561734,
"version": "4.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "4.0.0",
"changes": [
{
"note": "Raise custom InsufficientAssetLiquidityError error with amountAvailableToFill attribute",
"pr": 1437
}
],
"timestamp": 1547225310
},
{
"timestamp": 1547040760,
"version": "3.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.4",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1544739608
},
{
"version": "3.0.3",
"changes": [
{
"note": "Update SRA order provider to include Dai"
}
],
"timestamp": 1544570656
},
{
"timestamp": 1543401373,
"version": "3.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated",
"pr": 1276
}
],
"timestamp": 1542821676
},
{
"version": "3.0.0",
"changes": [
{
"note": "update `getBuyQuoteAsync` to return eth spent on assets instead of per unit amount",
"pr": 1252
}
],
"timestamp": 1542208198
},
{
"timestamp": 1542134075,
"version": "2.2.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1542028948,
"version": "2.2.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"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",
"changes": [
{
"note": "Expand AssetBuyer to work with multiple assets at once",
"pr": 1086
},
{
"note": "Fix minRate and maxRate calculation",
"pr": 1113
}
],
"timestamp": 1538693146
},
{
"timestamp": 1538475601,
"version": "1.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1538157789,
"version": "1.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1537907159,
"version": "1.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1537875740,
"version": "1.0.0",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.0.0-rc.1",
"changes": [
{
"note": "Init"
}
]
}
]

View File

@ -1,215 +0,0 @@
<!--
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
Edit the package's CHANGELOG.json file only.
-->
CHANGELOG
## v6.2.0-beta.3 - _November 20, 2019_
* Dependencies updated
## v6.2.0-beta.2 - _November 17, 2019_
* Dependencies updated
## v6.2.0-beta.1 - _November 7, 2019_
* All references to network ID have been removed, and references to chain ID have been introduced instead (#2313)
## v6.2.0-beta.0 - _October 3, 2019_
* Dependencies updated
## v6.1.14 - _September 17, 2019_
* Dependencies updated
## v6.1.13 - _September 3, 2019_
* Dependencies updated
## v6.1.12 - _August 22, 2019_
* Dependencies updated
## v6.1.11 - _August 8, 2019_
* Dependencies updated
## v6.1.10 - _July 31, 2019_
* Dependencies updated
## v6.1.9 - _July 24, 2019_
* Dependencies updated
## v6.1.8 - _July 15, 2019_
* Dependencies updated
## v6.1.7 - _July 13, 2019_
* Dependencies updated
## v6.1.6 - _July 13, 2019_
* Dependencies updated
## v6.1.5 - _May 24, 2019_
* Dependencies updated
## v6.1.4 - _May 15, 2019_
* Dependencies updated
## v6.1.3 - _May 14, 2019_
* Convert `metaData.remainingTakerAssetAmount` to BigNumber if present in APIOrder (#1810)
## v6.1.1 - _May 10, 2019_
* Dependencies updated
## v6.1.0 - _April 11, 2019_
* Moved order_utils into `@0x/order-utils` package as `orderCalculationUtils` (#1714)
## v6.0.5 - _March 21, 2019_
* Dependencies updated
## v6.0.4 - _March 20, 2019_
* Dependencies updated
## v6.0.3 - _March 1, 2019_
* Dependencies updated
## v6.0.2 - _February 27, 2019_
* Dependencies updated
## v6.0.1 - _February 26, 2019_
* Dependencies updated
## v6.0.0 - _February 25, 2019_
* Add support for EIP1193 providers & Web3.js providers >= 1.0-beta.38 (#1627)
* Update provider params to type SupportedProvider which outlines all supported providers (#1627)
## v5.0.4 - _February 9, 2019_
* Dependencies updated
## v5.0.3 - _February 7, 2019_
* Dependencies updated
## v5.0.2 - _February 7, 2019_
* Dependencies updated
## v5.0.1 - _February 6, 2019_
* Dependencies updated
## v5.0.0 - _February 5, 2019_
* Upgrade the bignumber.js to v8.0.2 (#1517)
## v4.1.0 - _Invalid date_
* Adds new public method getLiquidityForAssetDataAsync, and exposes getOrdersAndFillableAmountsAsync as public method (#1512)
## v4.0.2 - _January 17, 2019_
* Dependencies updated
## v4.0.1 - _January 15, 2019_
* Dependencies updated
## v4.0.0 - _January 11, 2019_
* Raise custom InsufficientAssetLiquidityError error with amountAvailableToFill attribute (#1437)
## v3.0.5 - _January 9, 2019_
* Dependencies updated
## v3.0.4 - _December 13, 2018_
* Dependencies updated
## v3.0.3 - _December 11, 2018_
* Update SRA order provider to include Dai
## v3.0.2 - _November 28, 2018_
* Dependencies updated
## v3.0.1 - _November 21, 2018_
* Dependencies updated (#1276)
## v3.0.0 - _November 14, 2018_
* update `getBuyQuoteAsync` to return eth spent on assets instead of per unit amount (#1252)
## v2.2.2 - _November 13, 2018_
* Dependencies updated
## v2.2.1 - _November 12, 2018_
* Dependencies updated
## 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)
* Fix minRate and maxRate calculation (#1113)
## v1.0.3 - _October 2, 2018_
* Dependencies updated
## v1.0.2 - _September 28, 2018_
* Dependencies updated
## v1.0.1 - _September 25, 2018_
* Dependencies updated
## v1.0.0 - _September 25, 2018_
* Dependencies updated
## v1.0.0-rc.1 - _Invalid date_
* Init

View File

@ -1,83 +0,0 @@
## @0x/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](https://weth.io/) based 0x orders without having to set allowances, wrap Ether or own 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.
## Installation
```bash
yarn add @0x/asset-buyer
```
**Import**
```typescript
import { AssetBuyer } from '@0x/asset-buyer';
```
or
```javascript
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/@0x/typescript-typings/types", "node_modules/@types"],
}
```
## Contributing
We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository.
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
### Install dependencies
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
```bash
yarn config set workspaces-experimental true
```
Then install dependencies
```bash
yarn install
```
### Build
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
```bash
PKG=@0x/asset-buyer yarn build
```
Or continuously rebuild on change:
```bash
PKG=@0x/asset-buyer yarn watch
```
### Clean
```bash
yarn clean
```
### Lint
```bash
yarn lint
```
### Run Tests
```bash
yarn test
```

View File

@ -1,75 +0,0 @@
{
"name": "@0x/asset-buyer",
"version": "6.2.0-beta.3",
"engines": {
"node": ">=6.12"
},
"description": "Convenience package for discovering and buying assets with Ether.",
"main": "lib/src/index.js",
"types": "lib/src/index.d.ts",
"scripts": {
"build": "yarn tsc -b",
"build:ci": "yarn build",
"lint": "tslint --format stylish --project .",
"fix": "tslint --fix --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"
},
"config": {
"postpublish": {
"assets": []
}
},
"repository": {
"type": "git",
"url": "https://github.com/0xProject/0x-monorepo.git"
},
"author": "",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/0xProject/0x-monorepo/issues"
},
"homepage": "https://github.com/0xProject/0x-monorepo/packages/asset-buyer/README.md",
"dependencies": {
"@0x/assert": "^2.2.0-beta.2",
"@0x/connect": "^5.1.0-beta.2",
"@0x/contract-wrappers": "^12.2.0-beta.3",
"@0x/json-schemas": "^4.1.0-beta.2",
"@0x/order-utils": "^8.5.0-beta.3",
"@0x/subproviders": "^5.1.0-beta.2",
"@0x/types": "^2.5.0-beta.2",
"@0x/typescript-typings": "^4.4.0-beta.2",
"@0x/utils": "^4.6.0-beta.2",
"@0x/web3-wrapper": "^6.1.0-beta.2",
"ethereum-types": "^2.2.0-beta.2",
"lodash": "^4.17.11"
},
"devDependencies": {
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^3.1.0-beta.2",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.2",
"nyc": "^11.0.1",
"shx": "^0.2.2",
"tslint": "5.11.0",
"typedoc": "^0.15.0",
"typemoq": "^2.1.0",
"typescript": "3.0.1"
},
"publishConfig": {
"access": "public"
}
}

View File

@ -1,377 +0,0 @@
import { ContractError, ContractWrappers, ForwarderError } from '@0x/contract-wrappers';
import { schemas } from '@0x/json-schemas';
import { assetDataUtils, SignedOrder } from '@0x/order-utils';
import { ObjectMap } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { SupportedProvider, ZeroExProvider } from 'ethereum-types';
import * as _ from 'lodash';
import { constants } from './constants';
import { BasicOrderProvider } from './order_providers/basic_order_provider';
import { StandardRelayerAPIOrderProvider } from './order_providers/standard_relayer_api_order_provider';
import {
AssetBuyerError,
AssetBuyerOpts,
BuyQuote,
BuyQuoteExecutionOpts,
BuyQuoteRequestOpts,
LiquidityForAssetData,
LiquidityRequestOpts,
OrderProvider,
OrdersAndFillableAmounts,
} from './types';
import { assert } from './utils/assert';
import { buyQuoteCalculator } from './utils/buy_quote_calculator';
import { calculateLiquidity } from './utils/calculate_liquidity';
import { numberPercentageToEtherTokenAmountPercentage } from './utils/number_percentage_to_ethertoken_amount_percentage';
import { orderProviderResponseProcessor } from './utils/order_provider_response_processor';
interface OrdersEntry {
ordersAndFillableAmounts: OrdersAndFillableAmounts;
lastRefreshTime: number;
}
export class AssetBuyer {
public readonly provider: ZeroExProvider;
public readonly orderProvider: OrderProvider;
public readonly chainId: number;
public readonly orderRefreshIntervalMs: number;
public readonly expiryBufferSeconds: number;
private readonly _contractWrappers: ContractWrappers;
// cache of orders along with the time last updated keyed by assetData
private readonly _ordersEntryMap: ObjectMap<OrdersEntry> = {};
/**
* Instantiates a new AssetBuyer instance given existing liquidity in the form of orders and feeOrders.
* @param supportedProvider 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 options Initialization options for the AssetBuyer. See type definition for details.
*
* @return An instance of AssetBuyer
*/
public static getAssetBuyerForProvidedOrders(
supportedProvider: SupportedProvider,
orders: SignedOrder[],
options: Partial<AssetBuyerOpts> = {},
): AssetBuyer {
assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
assert.assert(orders.length !== 0, `Expected orders to contain at least one order`);
const orderProvider = new BasicOrderProvider(orders);
const assetBuyer = new AssetBuyer(supportedProvider, orderProvider, options);
return assetBuyer;
}
/**
* Instantiates a new AssetBuyer instance given a [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) endpoint
* @param supportedProvider 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 options Initialization options for the AssetBuyer. See type definition for details.
*
* @return An instance of AssetBuyer
*/
public static getAssetBuyerForStandardRelayerAPIUrl(
supportedProvider: SupportedProvider,
sraApiUrl: string,
options: Partial<AssetBuyerOpts> = {},
): AssetBuyer {
const provider = providerUtils.standardizeOrThrow(supportedProvider);
assert.isWebUri('sraApiUrl', sraApiUrl);
// HACK: asset-buy will be deleted, but do not pass in chainId to allow everything to compile.
const orderProvider = new StandardRelayerAPIOrderProvider(sraApiUrl);
const assetBuyer = new AssetBuyer(provider, orderProvider, options);
return assetBuyer;
}
/**
* Instantiates a new AssetBuyer instance
* @param supportedProvider The Provider instance you would like to use for interacting with the Ethereum network.
* @param orderProvider An object that conforms to OrderProvider, see type for definition.
* @param options Initialization options for the AssetBuyer. See type definition for details.
*
* @return An instance of AssetBuyer
*/
constructor(
supportedProvider: SupportedProvider,
orderProvider: OrderProvider,
options: Partial<AssetBuyerOpts> = {},
) {
const { chainId, orderRefreshIntervalMs, expiryBufferSeconds } = _.merge(
{},
constants.DEFAULT_ASSET_BUYER_OPTS,
options,
);
const provider = providerUtils.standardizeOrThrow(supportedProvider);
assert.isValidOrderProvider('orderProvider', orderProvider);
assert.isNumber('chainId', chainId);
assert.isNumber('orderRefreshIntervalMs', orderRefreshIntervalMs);
assert.isNumber('expiryBufferSeconds', expiryBufferSeconds);
this.provider = provider;
this.orderProvider = orderProvider;
this.chainId = chainId;
this.orderRefreshIntervalMs = orderRefreshIntervalMs;
this.expiryBufferSeconds = expiryBufferSeconds;
this._contractWrappers = new ContractWrappers(this.provider, {
chainId,
});
}
/**
* Get a `BuyQuote` containing all information relevant to fulfilling a buy given a desired assetData.
* You can then pass the `BuyQuote` to `executeBuyQuoteAsync` to execute the buy.
* @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 assetBuyAmount The amount of asset to buy.
* @param options Options for the request. See type definition for more information.
*
* @return An object that conforms to BuyQuote that satisfies the request. See type definition for more information.
*/
public async getBuyQuoteAsync(
assetData: string,
assetBuyAmount: BigNumber,
options: Partial<BuyQuoteRequestOpts> = {},
): Promise<BuyQuote> {
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 = await this._getZrxTokenAssetDataOrThrowAsync();
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),
isMakerAssetZrxToken
? Promise.resolve(constants.EMPTY_ORDERS_AND_FILLABLE_AMOUNTS)
: this.getOrdersAndFillableAmountsAsync(zrxTokenAssetData, shouldForceOrderRefresh),
shouldForceOrderRefresh,
]);
if (ordersAndFillableAmounts.orders.length === 0) {
throw new Error(`${AssetBuyerError.AssetUnavailable}: For assetData ${assetData}`);
}
const buyQuote = buyQuoteCalculator.calculate(
ordersAndFillableAmounts,
feeOrdersAndFillableAmounts,
assetBuyAmount,
feePercentage,
slippagePercentage,
isMakerAssetZrxToken,
);
return buyQuote;
}
/**
* Get a `BuyQuote` containing all information relevant to fulfilling a buy given a desired ERC20 token address.
* You can then pass the `BuyQuote` to `executeBuyQuoteAsync` to execute the buy.
* @param tokenAddress The ERC20 token address.
* @param assetBuyAmount The amount of asset to buy.
* @param options Options for the request. See type definition for more information.
*
* @return An object that conforms to BuyQuote that satisfies the request. See type definition for more information.
*/
public async getBuyQuoteForERC20TokenAddressAsync(
tokenAddress: string,
assetBuyAmount: BigNumber,
options: Partial<BuyQuoteRequestOpts> = {},
): Promise<BuyQuote> {
assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.isBigNumber('assetBuyAmount', assetBuyAmount);
const assetData = await this._contractWrappers.devUtils.encodeERC20AssetData(tokenAddress).callAsync();
const buyQuote = this.getBuyQuoteAsync(assetData, assetBuyAmount, options);
return buyQuote;
}
/**
* Returns information about available liquidity for an asset
* Does not factor in slippage or fees
* @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 options Options for the request. See type definition for more information.
*
* @return An object that conforms to LiquidityForAssetData that satisfies the request. See type definition for more information.
*/
public async getLiquidityForAssetDataAsync(
assetData: string,
options: Partial<LiquidityRequestOpts> = {},
): Promise<LiquidityForAssetData> {
const shouldForceOrderRefresh =
options.shouldForceOrderRefresh !== undefined ? options.shouldForceOrderRefresh : false;
assert.isString('assetData', assetData);
assetDataUtils.decodeAssetDataOrThrow(assetData);
assert.isBoolean('options.shouldForceOrderRefresh', shouldForceOrderRefresh);
const assetPairs = await this.orderProvider.getAvailableMakerAssetDatasAsync(assetData);
const etherTokenAssetData = await this._getEtherTokenAssetDataOrThrowAsync();
if (!assetPairs.includes(etherTokenAssetData)) {
return {
tokensAvailableInBaseUnits: new BigNumber(0),
ethValueAvailableInWei: new BigNumber(0),
};
}
const ordersAndFillableAmounts = await this.getOrdersAndFillableAmountsAsync(
assetData,
shouldForceOrderRefresh,
);
return calculateLiquidity(ordersAndFillableAmounts);
}
/**
* 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 options Options for the execution of the BuyQuote. See type definition for more information.
*
* @return A promise of the txHash.
*/
public async executeBuyQuoteAsync(
buyQuote: BuyQuote,
options: Partial<BuyQuoteExecutionOpts> = {},
): Promise<string> {
const { ethAmount, takerAddress, feeRecipient, gasLimit, gasPrice } = _.merge(
{},
constants.DEFAULT_BUY_QUOTE_EXECUTION_OPTS,
options,
);
assert.isValidBuyQuote('buyQuote', buyQuote);
if (ethAmount !== undefined) {
assert.isBigNumber('ethAmount', ethAmount);
}
if (takerAddress !== undefined) {
assert.isETHAddressHex('takerAddress', takerAddress);
}
assert.isETHAddressHex('feeRecipient', feeRecipient);
if (gasLimit !== undefined) {
assert.isNumber('gasLimit', gasLimit);
}
if (gasPrice !== undefined) {
assert.isBigNumber('gasPrice', gasPrice);
}
const { orders, feeOrders, feePercentage, assetBuyAmount, worstCaseQuoteInfo } = buyQuote; // tslint:disable-line:no-unused-variable
// if no takerAddress is provided, try to get one from the provider
let finalTakerAddress;
if (takerAddress !== undefined) {
finalTakerAddress = takerAddress;
} else {
const web3Wrapper = new Web3Wrapper(this.provider);
const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
const firstAvailableAddress = _.head(availableAddresses);
if (firstAvailableAddress !== undefined) {
finalTakerAddress = firstAvailableAddress;
} else {
throw new Error(AssetBuyerError.NoAddressAvailable);
}
}
try {
// format fee percentage
const formattedFeePercentage = numberPercentageToEtherTokenAmountPercentage(feePercentage || 0);
// if no ethAmount is provided, default to the worst ethAmount from buyQuote
const value = ethAmount || worstCaseQuoteInfo.totalEthAmount;
const txHash = await this._contractWrappers.forwarder
.marketBuyOrdersWithEth(
orders,
assetBuyAmount,
orders.map(o => o.signature),
formattedFeePercentage,
feeRecipient,
)
.sendTransactionAsync({
value,
from: finalTakerAddress.toLowerCase(),
gas: gasLimit,
gasPrice,
});
return txHash;
} catch (err) {
if (_.includes(err.message, ContractError.SignatureRequestDenied)) {
throw new Error(AssetBuyerError.SignatureRequestDenied);
} else if (_.includes(err.message, ForwarderError.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 = await this._getEtherTokenAssetDataOrThrowAsync();
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
* @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 shouldForceOrderRefresh If set to true, new orders and state will be fetched instead of waiting for the next orderRefreshIntervalMs.
*/
public async getOrdersAndFillableAmountsAsync(
assetData: string,
shouldForceOrderRefresh: boolean,
): Promise<OrdersAndFillableAmounts> {
// try to get ordersEntry from the map
const ordersEntryIfExists = this._ordersEntryMap[assetData];
// 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 =
ordersEntryIfExists === undefined ||
shouldForceOrderRefresh ||
// tslint:disable:restrict-plus-operands
ordersEntryIfExists.lastRefreshTime + this.orderRefreshIntervalMs < Date.now();
if (!shouldRefresh) {
const result = ordersEntryIfExists.ordersAndFillableAmounts;
return result;
}
const etherTokenAssetData = await this._getEtherTokenAssetDataOrThrowAsync();
const zrxTokenAssetData = await this._getZrxTokenAssetDataOrThrowAsync();
// construct orderProvider request
const orderProviderRequest = {
makerAssetData: assetData,
takerAssetData: etherTokenAssetData,
chainId: this.chainId,
};
const request = orderProviderRequest;
// get provider response
const response = await 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(response, request);
// process the responses into one object
const isMakerAssetZrxToken = assetData === zrxTokenAssetData;
const ordersAndFillableAmounts = await orderProviderResponseProcessor.processAsync(
response,
isMakerAssetZrxToken,
this.expiryBufferSeconds,
this._contractWrappers.orderValidator,
);
const lastRefreshTime = Date.now();
const updatedOrdersEntry = {
ordersAndFillableAmounts,
lastRefreshTime,
};
this._ordersEntryMap[assetData] = updatedOrdersEntry;
return ordersAndFillableAmounts;
}
/**
* Get the assetData that represents the WETH token.
* Will throw if WETH does not exist for the current chain.
*/
private async _getEtherTokenAssetDataOrThrowAsync(): Promise<string> {
return this._contractWrappers.devUtils
.encodeERC20AssetData(this._contractWrappers.contractAddresses.etherToken)
.callAsync();
}
/**
* Get the assetData that represents the ZRX token.
* Will throw if ZRX does not exist for the current chain.
*/
private async _getZrxTokenAssetDataOrThrowAsync(): Promise<string> {
return this._contractWrappers.devUtils
.encodeERC20AssetData(this._contractWrappers.contractAddresses.zrxToken)
.callAsync();
}
}

View File

@ -1,40 +0,0 @@
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { AssetBuyerOpts, BuyQuoteExecutionOpts, BuyQuoteRequestOpts, OrdersAndFillableAmounts } from './types';
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
const MAINNET_CHAIN_ID = 1;
const DEFAULT_ASSET_BUYER_OPTS: AssetBuyerOpts = {
chainId: MAINNET_CHAIN_ID,
orderRefreshIntervalMs: 10000, // 10 seconds
expiryBufferSeconds: 120, // 2 minutes
};
const DEFAULT_BUY_QUOTE_REQUEST_OPTS: BuyQuoteRequestOpts = {
feePercentage: 0,
shouldForceOrderRefresh: false,
slippagePercentage: 0.2, // 20% slippage protection
};
// Other default values are dynamically determined
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,
MAINNET_CHAIN_ID,
ETHER_TOKEN_DECIMALS: 18,
DEFAULT_ASSET_BUYER_OPTS,
DEFAULT_BUY_QUOTE_EXECUTION_OPTS,
DEFAULT_BUY_QUOTE_REQUEST_OPTS,
EMPTY_ORDERS_AND_FILLABLE_AMOUNTS,
};

View File

@ -1,22 +0,0 @@
import { BigNumber } from '@0x/utils';
import { AssetBuyerError } from './types';
/**
* Error class representing insufficient asset liquidity
*/
export class InsufficientAssetLiquidityError extends Error {
/**
* The amount availabe to fill (in base units) factoring in slippage.
*/
public amountAvailableToFill: BigNumber;
/**
* @param amountAvailableToFill The amount availabe to fill (in base units) factoring in slippage
*/
constructor(amountAvailableToFill: BigNumber) {
super(AssetBuyerError.InsufficientAssetLiquidity);
this.amountAvailableToFill = amountAvailableToFill;
// Setting prototype so instanceof works. See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
Object.setPrototypeOf(this, InsufficientAssetLiquidityError.prototype);
}
}

View File

@ -1,6 +0,0 @@
declare module '*.json' {
const json: any;
/* tslint:disable */
export default json;
/* tslint:enable */
}

View File

@ -1,37 +0,0 @@
export {
JSONRPCRequestPayload,
JSONRPCResponsePayload,
JSONRPCResponseError,
JSONRPCErrorCallback,
SupportedProvider,
Web3JsProvider,
GanacheProvider,
EIP1193Provider,
ZeroExProvider,
EIP1193Event,
Web3JsV1Provider,
Web3JsV2Provider,
Web3JsV3Provider,
} from 'ethereum-types';
export { SignedOrder } from '@0x/types';
export { BigNumber } from '@0x/utils';
export { AssetBuyer } from './asset_buyer';
export { InsufficientAssetLiquidityError } from './errors';
export { BasicOrderProvider } from './order_providers/basic_order_provider';
export { StandardRelayerAPIOrderProvider } from './order_providers/standard_relayer_api_order_provider';
export {
AssetBuyerError,
AssetBuyerOpts,
BuyQuote,
BuyQuoteExecutionOpts,
BuyQuoteInfo,
BuyQuoteRequestOpts,
LiquidityForAssetData,
LiquidityRequestOpts,
OrdersAndFillableAmounts,
OrderProvider,
OrderProviderRequest,
OrderProviderResponse,
SignedOrderWithRemainingFillableMakerAssetAmount,
} from './types';

View File

@ -1,41 +0,0 @@
import { schemas } from '@0x/json-schemas';
import { SignedOrder } from '@0x/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<OrderProviderResponse> {
assert.isValidOrderProviderRequest('orderProviderRequest', orderProviderRequest);
const { makerAssetData, takerAssetData } = orderProviderRequest;
const orders = _.filter(this.orders, order => {
return order.makerAssetData === makerAssetData && order.takerAssetData === takerAssetData;
});
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);
}
}

View File

@ -1,109 +0,0 @@
import { HttpClient } from '@0x/connect';
import { orderCalculationUtils } from '@0x/order-utils';
import { APIOrder, AssetPairsResponse, OrderbookResponse } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import {
AssetBuyerError,
OrderProvider,
OrderProviderRequest,
OrderProviderResponse,
SignedOrderWithRemainingFillableMakerAssetAmount,
} from '../types';
import { assert } from '../utils/assert';
export class StandardRelayerAPIOrderProvider implements OrderProvider {
public readonly apiUrl: string;
private readonly _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;
// The contents of metaData is not explicity defined in the spec
// We check for remainingTakerAssetAmount as a string and use this value if populated
const metaDataRemainingTakerAssetAmount = _.get(metaData, 'remainingTakerAssetAmount') as
| string
| undefined;
const remainingFillableTakerAssetAmount = metaDataRemainingTakerAssetAmount
? new BigNumber(metaDataRemainingTakerAssetAmount)
: order.takerAssetAmount;
const remainingFillableMakerAssetAmount = orderCalculationUtils.getMakerFillAmount(
order,
remainingFillableTakerAssetAmount,
);
const newOrder = {
...order,
remainingFillableMakerAssetAmount,
};
return newOrder;
});
return result;
}
/**
* 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 StandardRelayerAPIOrderProvider
*/
constructor(apiUrl: string) {
assert.isWebUri('apiUrl', apiUrl);
this.apiUrl = apiUrl;
this._sraClient = new HttpClient(apiUrl);
}
/**
* 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(orderProviderRequest: OrderProviderRequest): Promise<OrderProviderResponse> {
assert.isValidOrderProviderRequest('orderProviderRequest', orderProviderRequest);
const { makerAssetData, takerAssetData } = orderProviderRequest;
const orderbookRequest = { baseAssetData: makerAssetData, quoteAssetData: takerAssetData };
let orderbook: OrderbookResponse;
try {
orderbook = await this._sraClient.getOrderbookAsync(orderbookRequest);
} catch (err) {
throw new Error(AssetBuyerError.StandardRelayerApiError);
}
const apiOrders = orderbook.asks.records;
const orders = StandardRelayerAPIOrderProvider._getSignedOrderWithRemainingFillableMakerAssetAmountFromApi(
apiOrders,
);
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[]> {
// Return a maximum of 1000 asset datas
const maxPerPage = 1000;
const requestOpts = { 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 => {
if (item.assetDataA.assetData === takerAssetData) {
return item.assetDataB.assetData;
} else {
return item.assetDataA.assetData;
}
});
}
}

View File

@ -1,142 +0,0 @@
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
/**
* makerAssetData: The assetData representing the desired makerAsset.
* takerAssetData: The assetData representing the desired takerAsset.
* chainId: The chainId that the desired orders should be for.
*/
export interface OrderProviderRequest {
makerAssetData: string;
takerAssetData: string;
}
/**
* orders: An array of orders with optional remaining fillable makerAsset amounts. See type for more info.
*/
export interface OrderProviderResponse {
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;
}
/**
* 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[]>;
}
/**
* assetData: String that represents a specific asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
* assetBuyAmount: The amount of asset to buy.
* 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.
* feePercentage: Optional affiliate fee percentage used to calculate the eth amounts above.
* bestCaseQuoteInfo: Info about the best case price for the asset.
* worstCaseQuoteInfo: Info about the worst case price for the asset.
*/
export interface BuyQuote {
assetData: string;
assetBuyAmount: BigNumber;
orders: SignedOrder[];
feeOrders: SignedOrder[];
feePercentage?: number;
bestCaseQuoteInfo: BuyQuoteInfo;
worstCaseQuoteInfo: BuyQuoteInfo;
}
/**
* assetEthAmount: The amount of eth required to pay for the requested asset.
* feeEthAmount: The amount of eth required to pay the affiliate fee.
* totalEthAmount: The total amount of eth required to complete the buy (filling orders, feeOrders, and paying affiliate fee).
*/
export interface BuyQuoteInfo {
assetEthAmount: BigNumber;
feeEthAmount: BigNumber;
totalEthAmount: BigNumber;
}
/**
* feePercentage: The affiliate fee percentage. Defaults to 0.
* shouldForceOrderRefresh: If set to true, new orders and state will be fetched instead of waiting for the next orderRefreshIntervalMs. Defaults to false.
* slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.2 (20%).
*/
export interface BuyQuoteRequestOpts {
feePercentage: number;
shouldForceOrderRefresh: boolean;
slippagePercentage: number;
}
/*
* Options for checking liquidity
*
* shouldForceOrderRefresh: If set to true, new orders and state will be fetched instead of waiting for the next orderRefreshIntervalMs. Defaults to false.
*/
export type LiquidityRequestOpts = Pick<BuyQuoteRequestOpts, 'shouldForceOrderRefresh'>;
/**
* ethAmount: The desired amount of eth to spend. Defaults to buyQuote.worstCaseQuoteInfo.totalEthAmount.
* takerAddress: The address to perform the buy. Defaults to the first available address from the provider.
* gasLimit: The amount of gas to send with a transaction (in Gwei). Defaults to an eth_estimateGas rpc call.
* gasPrice: Gas price in Wei to use for a transaction
* feeRecipient: The address where affiliate fees are sent. Defaults to null address (0x000...000).
*/
export interface BuyQuoteExecutionOpts {
ethAmount?: BigNumber;
takerAddress?: string;
gasLimit?: number;
gasPrice?: BigNumber;
feeRecipient: string;
}
/**
* chainId: The ethereum chain id. Defaults to 1 (mainnet).
* orderRefreshIntervalMs: The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s).
* expiryBufferSeconds: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m).
*/
export interface AssetBuyerOpts {
chainId: number;
orderRefreshIntervalMs: number;
expiryBufferSeconds: number;
}
/**
* Possible error messages 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',
InvalidOrderProviderResponse = 'INVALID_ORDER_PROVIDER_RESPONSE',
AssetUnavailable = 'ASSET_UNAVAILABLE',
SignatureRequestDenied = 'SIGNATURE_REQUEST_DENIED',
TransactionValueTooLow = 'TRANSACTION_VALUE_TOO_LOW',
}
/**
* orders: An array of signed orders
* remainingFillableMakerAssetAmounts: A list of fillable amounts for the signed orders. The index of an item in the array associates the amount with the corresponding order.
*/
export interface OrdersAndFillableAmounts {
orders: SignedOrder[];
remainingFillableMakerAssetAmounts: BigNumber[];
}
/**
* Represents available liquidity for a given assetData
*/
export interface LiquidityForAssetData {
tokensAvailableInBaseUnits: BigNumber;
ethValueAvailableInWei: BigNumber;
}

View File

@ -1,38 +0,0 @@
import { assert as sharedAssert } from '@0x/assert';
import { schemas } from '@0x/json-schemas';
import { BuyQuote, BuyQuoteInfo, OrderProvider, OrderProviderRequest } 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);
assert.isValidBuyQuoteInfo(`${variableName}.bestCaseQuoteInfo`, buyQuote.bestCaseQuoteInfo);
assert.isValidBuyQuoteInfo(`${variableName}.worstCaseQuoteInfo`, buyQuote.worstCaseQuoteInfo);
sharedAssert.isBigNumber(`${variableName}.assetBuyAmount`, buyQuote.assetBuyAmount);
if (buyQuote.feePercentage !== undefined) {
sharedAssert.isNumber(`${variableName}.feePercentage`, buyQuote.feePercentage);
}
},
isValidBuyQuoteInfo(variableName: string, buyQuoteInfo: BuyQuoteInfo): void {
sharedAssert.isBigNumber(`${variableName}.assetEthAmount`, buyQuoteInfo.assetEthAmount);
sharedAssert.isBigNumber(`${variableName}.feeEthAmount`, buyQuoteInfo.feeEthAmount);
sharedAssert.isBigNumber(`${variableName}.totalEthAmount`, buyQuoteInfo.totalEthAmount);
},
isValidOrderProvider(variableName: string, orderFetcher: OrderProvider): void {
sharedAssert.isFunction(`${variableName}.getOrdersAsync`, orderFetcher.getOrdersAsync);
},
isValidOrderProviderRequest(variableName: string, orderFetcherRequest: OrderProviderRequest): void {
sharedAssert.isHexString(`${variableName}.makerAssetData`, orderFetcherRequest.makerAssetData);
sharedAssert.isHexString(`${variableName}.takerAssetData`, orderFetcherRequest.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}`,
);
},
};

View File

@ -1,219 +0,0 @@
import { marketUtils, orderCalculationUtils, SignedOrder } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { constants } from '../constants';
import { InsufficientAssetLiquidityError } from '../errors';
import { AssetBuyerError, BuyQuote, BuyQuoteInfo, OrdersAndFillableAmounts } from '../types';
// Calculates a buy quote for orders that have WETH as the takerAsset
export const buyQuoteCalculator = {
calculate(
ordersAndFillableAmounts: OrdersAndFillableAmounts,
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
assetBuyAmount: BigNumber,
feePercentage: number,
slippagePercentage: number,
isMakerAssetZrxToken: boolean,
): BuyQuote {
const orders = ordersAndFillableAmounts.orders;
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
const feeOrders = feeOrdersAndFillableAmounts.orders;
const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts;
const slippageBufferAmount = assetBuyAmount.multipliedBy(slippagePercentage).integerValue();
// find the orders that cover the desired assetBuyAmount (with slippage)
const {
resultOrders,
remainingFillAmount,
ordersRemainingFillableMakerAssetAmounts,
} = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, assetBuyAmount, {
remainingFillableMakerAssetAmounts,
slippageBufferAmount,
});
// if we do not have enough orders to cover the desired assetBuyAmount, throw
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
// We needed the amount they requested to buy, plus the amount for slippage
const totalAmountRequested = assetBuyAmount.plus(slippageBufferAmount);
const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount);
// multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by
// in order to get the total amount needed considering slippage
// i.e. if slippagePercent was 0.2 (20%), multiplierNeededWithSlippage would be 1.2
const multiplierNeededWithSlippage = new BigNumber(1).plus(slippagePercentage);
// Given amountAvailableToFillConsideringSlippage * multiplierNeededWithSlippage = amountAbleToFill
// We divide amountUnableToFill by multiplierNeededWithSlippage to determine amountAvailableToFillConsideringSlippage
const amountAvailableToFillConsideringSlippage = amountAbleToFill
.div(multiplierNeededWithSlippage)
.integerValue(BigNumber.ROUND_FLOOR);
throw new InsufficientAssetLiquidityError(amountAvailableToFillConsideringSlippage);
}
// 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
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
const trimmedOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
orders: resultOrders,
remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
};
const trimmedFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
orders: resultFeeOrders,
remainingFillableMakerAssetAmounts: feeOrdersRemainingFillableMakerAssetAmounts,
};
const bestCaseQuoteInfo = calculateQuoteInfo(
trimmedOrdersAndFillableAmounts,
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(
reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
assetBuyAmount,
feePercentage,
isMakerAssetZrxToken,
);
return {
assetData,
orders: resultOrders,
feeOrders: resultFeeOrders,
bestCaseQuoteInfo,
worstCaseQuoteInfo,
assetBuyAmount,
feePercentage,
};
},
};
function calculateQuoteInfo(
ordersAndFillableAmounts: OrdersAndFillableAmounts,
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
let assetEthAmount = constants.ZERO_AMOUNT;
let zrxEthAmount = constants.ZERO_AMOUNT;
if (isMakerAssetZrxToken) {
assetEthAmount = findEthAmountNeededToBuyZrx(ordersAndFillableAmounts, assetBuyAmount);
} else {
// find eth and zrx amounts needed to buy
const ethAndZrxAmountToBuyAsset = findEthAndZrxAmountNeededToBuyAsset(ordersAndFillableAmounts, assetBuyAmount);
assetEthAmount = ethAndZrxAmountToBuyAsset[0];
const zrxAmountToBuyAsset = ethAndZrxAmountToBuyAsset[1];
// find eth amount needed to buy zrx
zrxEthAmount = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
}
// eth amount needed to buy the affiliate fee
const affiliateFeeEthAmount = assetEthAmount.multipliedBy(feePercentage).integerValue(BigNumber.ROUND_CEIL);
// eth amount needed for fees is the sum of affiliate fee and zrx fee
const feeEthAmount = affiliateFeeEthAmount.plus(zrxEthAmount);
// eth amount needed in total is the sum of the amount needed for the asset and the amount needed for fees
const totalEthAmount = assetEthAmount.plus(feeEthAmount);
return {
assetEthAmount,
feeEthAmount,
totalEthAmount,
};
}
// given an OrdersAndFillableAmounts, reverse the orders and remainingFillableMakerAssetAmounts properties
function reverseOrdersAndFillableAmounts(ordersAndFillableAmounts: OrdersAndFillableAmounts): OrdersAndFillableAmounts {
const ordersCopy = _.clone(ordersAndFillableAmounts.orders);
const remainingFillableMakerAssetAmountsCopy = _.clone(ordersAndFillableAmounts.remainingFillableMakerAssetAmounts);
return {
orders: ordersCopy.reverse(),
remainingFillableMakerAssetAmounts: remainingFillableMakerAssetAmountsCopy.reverse(),
};
}
function findEthAmountNeededToBuyZrx(
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
zrxBuyAmount: BigNumber,
): BigNumber {
const { orders, remainingFillableMakerAssetAmounts } = feeOrdersAndFillableAmounts;
const result = _.reduce(
orders,
(acc, order, index) => {
const { totalEthAmount, remainingZrxBuyAmount } = acc;
const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
const makerFillAmount = BigNumber.min(remainingZrxBuyAmount, remainingFillableMakerAssetAmount);
const [takerFillAmount, adjustedMakerFillAmount] = orderCalculationUtils.getTakerFillAmountForFeeOrder(
order,
makerFillAmount,
);
const extraFeeAmount = remainingFillableMakerAssetAmount.isGreaterThanOrEqualTo(adjustedMakerFillAmount)
? constants.ZERO_AMOUNT
: adjustedMakerFillAmount.minus(makerFillAmount);
return {
totalEthAmount: totalEthAmount.plus(takerFillAmount),
remainingZrxBuyAmount: BigNumber.max(
constants.ZERO_AMOUNT,
remainingZrxBuyAmount.minus(makerFillAmount).plus(extraFeeAmount),
),
};
},
{
totalEthAmount: constants.ZERO_AMOUNT,
remainingZrxBuyAmount: zrxBuyAmount,
},
);
return result.totalEthAmount;
}
function findEthAndZrxAmountNeededToBuyAsset(
ordersAndFillableAmounts: OrdersAndFillableAmounts,
assetBuyAmount: BigNumber,
): [BigNumber, BigNumber] {
const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts;
const result = _.reduce(
orders,
(acc, order, index) => {
const { totalEthAmount, totalZrxAmount, remainingAssetBuyAmount } = acc;
const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
const makerFillAmount = BigNumber.min(acc.remainingAssetBuyAmount, remainingFillableMakerAssetAmount);
const takerFillAmount = orderCalculationUtils.getTakerFillAmount(order, makerFillAmount);
const takerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount);
return {
totalEthAmount: totalEthAmount.plus(takerFillAmount),
totalZrxAmount: totalZrxAmount.plus(takerFeeAmount),
remainingAssetBuyAmount: BigNumber.max(
constants.ZERO_AMOUNT,
remainingAssetBuyAmount.minus(makerFillAmount),
),
};
},
{
totalEthAmount: constants.ZERO_AMOUNT,
totalZrxAmount: constants.ZERO_AMOUNT,
remainingAssetBuyAmount: assetBuyAmount,
},
);
return [result.totalEthAmount, result.totalZrxAmount];
}

View File

@ -1,36 +0,0 @@
import { orderCalculationUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { LiquidityForAssetData, OrdersAndFillableAmounts } from '../types';
export const calculateLiquidity = (ordersAndFillableAmounts: OrdersAndFillableAmounts): LiquidityForAssetData => {
const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts;
const liquidityInBigNumbers = orders.reduce(
(acc, order, curIndex) => {
const availableMakerAssetAmount = remainingFillableMakerAssetAmounts[curIndex];
if (availableMakerAssetAmount === undefined) {
throw new Error(`No corresponding fillableMakerAssetAmounts at index ${curIndex}`);
}
const tokensAvailableForCurrentOrder = availableMakerAssetAmount;
const ethValueAvailableForCurrentOrder = orderCalculationUtils.getTakerFillAmount(
order,
availableMakerAssetAmount,
);
return {
tokensAvailableInBaseUnits: acc.tokensAvailableInBaseUnits.plus(tokensAvailableForCurrentOrder),
ethValueAvailableInWei: acc.ethValueAvailableInWei.plus(ethValueAvailableForCurrentOrder),
};
},
{
tokensAvailableInBaseUnits: new BigNumber(0),
ethValueAvailableInWei: new BigNumber(0),
},
);
// Turn into regular numbers
return {
tokensAvailableInBaseUnits: liquidityInBigNumbers.tokensAvailableInBaseUnits,
ethValueAvailableInWei: liquidityInBigNumbers.ethValueAvailableInWei,
};
};

View File

@ -1,9 +0,0 @@
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
// HACK (xianny): copied from asset-swapper, which will replace this package
const ONE_AMOUNT = new BigNumber(1);
const ETHER_TOKEN_DECIMALS = 18;
export const numberPercentageToEtherTokenAmountPercentage = (percentage: number): BigNumber => {
return Web3Wrapper.toBaseUnitAmount(ONE_AMOUNT, ETHER_TOKEN_DECIMALS).multipliedBy(percentage);
};

View File

@ -1,180 +0,0 @@
import { OrderStatus, OrderValidatorContract } from '@0x/contract-wrappers';
import { orderCalculationUtils, 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';
import {
AssetBuyerError,
OrderProviderRequest,
OrderProviderResponse,
OrdersAndFillableAmounts,
SignedOrderWithRemainingFillableMakerAssetAmount,
} from '../types';
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:
* - 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(
orderProviderResponse: OrderProviderResponse,
isMakerAssetZrxToken: boolean,
expiryBufferSeconds: number,
orderValidator?: OrderValidatorContract,
): Promise<OrdersAndFillableAmounts> {
// drop orders that are expired or not open
const filteredOrders = filterOutExpiredAndNonOpenOrders(orderProviderResponse.orders, expiryBufferSeconds);
// set the orders to be sorted equal to the filtered orders
let unsortedOrders = filteredOrders;
// if an orderValidator is provided, use on chain information to calculate remaining fillable makerAsset amounts
if (orderValidator !== undefined) {
const takerAddresses = _.map(filteredOrders, () => constants.NULL_ADDRESS);
try {
const [ordersInfo, tradersInfo] = await orderValidator
.getOrdersAndTradersInfo(filteredOrders, takerAddresses)
.callAsync();
const ordersAndTradersInfo = ordersInfo.map((orderInfo, index) => {
return {
orderInfo,
traderInfo: tradersInfo[index],
};
});
// take orders + on chain information and find the valid orders and remaining fillable maker asset amounts
unsortedOrders = getValidOrdersWithRemainingFillableMakerAssetAmountsFromOnChain(
filteredOrders,
ordersAndTradersInfo,
isMakerAssetZrxToken,
);
} catch (err) {
// Sometimes we observe this call to orderValidator fail with response `0x`
// Because of differences in Parity / Geth implementations, its very hard to tell if this response is a "system error"
// or a revert. In this case we just swallow these errors and fallback to partial fill information from the SRA.
// TODO(bmillman): report these errors so we have an idea of how often we're getting these failures.
}
}
// sort orders by rate
// TODO(bmillman): optimization
// provide a feeRate to the sorting function to more accurately sort based on the current market for ZRX tokens
const sortedOrders = isMakerAssetZrxToken
? sortingUtils.sortFeeOrdersByFeeAdjustedRate(unsortedOrders)
: sortingUtils.sortOrdersByFeeAdjustedRate(unsortedOrders);
// unbundle orders and fillable amounts and compile final result
const result = unbundleOrdersWithAmounts(sortedOrders);
return result;
},
};
/**
* Given an array of orders, return a new array with expired and non open orders filtered out.
*/
function filterOutExpiredAndNonOpenOrders(
orders: SignedOrderWithRemainingFillableMakerAssetAmount[],
expiryBufferSeconds: number,
): SignedOrderWithRemainingFillableMakerAssetAmount[] {
const result = _.filter(orders, order => {
return (
orderCalculationUtils.isOpenOrder(order) &&
!orderCalculationUtils.willOrderExpire(order, expiryBufferSeconds)
);
});
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: any[],
isMakerAssetZrxToken: boolean,
): 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 = orderCalculationUtils.getMakerFillAmount(
order,
remainingTakerAssetAmount,
);
const remainingFillableCalculator = new RemainingFillableCalculator(
order.makerFee,
order.makerAssetAmount,
isMakerAssetZrxToken,
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[],
): OrdersAndFillableAmounts {
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;
// 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),
};
return newAcc;
},
{
orders: [] as SignedOrder[],
remainingFillableMakerAssetAmounts: [] as BigNumber[],
},
);
return result;
}

View File

@ -1,216 +0,0 @@
import { orderFactory } from '@0x/order-utils/lib/src/order_factory';
import { Web3ProviderEngine } from '@0x/subproviders';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai';
import 'mocha';
import * as TypeMoq from 'typemoq';
import { AssetBuyer } from '../src';
import { constants } from '../src/constants';
import { LiquidityForAssetData, OrderProvider, OrdersAndFillableAmounts } from '../src/types';
import { chaiSetup } from './utils/chai_setup';
import {
mockAvailableAssetDatas,
mockedAssetBuyerWithOrdersAndFillableAmounts,
orderProviderMock,
} from './utils/mocks';
chaiSetup.configure();
const expect = chai.expect;
const FAKE_SRA_URL = 'https://fakeurl.com';
const FAKE_ASSET_DATA = '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48';
const TOKEN_DECIMALS = 18;
const DAI_ASSET_DATA = '0xf47261b000000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359"';
const WETH_ASSET_DATA = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
const WETH_DECIMALS = constants.ETHER_TOKEN_DECIMALS;
const baseUnitAmount = (unitAmount: number, decimals = TOKEN_DECIMALS): BigNumber => {
return Web3Wrapper.toBaseUnitAmount(new BigNumber(unitAmount), decimals);
};
const expectLiquidityResult = async (
web3Provider: Web3ProviderEngine,
orderProvider: OrderProvider,
ordersAndFillableAmounts: OrdersAndFillableAmounts,
expectedLiquidityResult: LiquidityForAssetData,
) => {
const mockedAssetBuyer = mockedAssetBuyerWithOrdersAndFillableAmounts(
web3Provider,
orderProvider,
FAKE_ASSET_DATA,
ordersAndFillableAmounts,
);
const liquidityResult = await mockedAssetBuyer.object.getLiquidityForAssetDataAsync(FAKE_ASSET_DATA);
expect(liquidityResult).to.deep.equal(expectedLiquidityResult);
};
// tslint:disable:custom-no-magic-numbers
describe('AssetBuyer', () => {
describe('getLiquidityForAssetDataAsync', () => {
const mockWeb3Provider = TypeMoq.Mock.ofType(Web3ProviderEngine);
const mockOrderProvider = orderProviderMock();
beforeEach(() => {
mockWeb3Provider.reset();
mockOrderProvider.reset();
});
afterEach(() => {
mockWeb3Provider.verifyAll();
mockOrderProvider.verifyAll();
});
describe('validation', () => {
it('should ensure assetData is a string', async () => {
const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(
mockWeb3Provider.object,
FAKE_SRA_URL,
);
expect(assetBuyer.getLiquidityForAssetDataAsync(false as any)).to.be.rejectedWith(
'Expected assetData to be of type string, encountered: false',
);
});
});
describe('asset pair not supported', () => {
it('should return 0s when no asset pair not supported', async () => {
mockAvailableAssetDatas(mockOrderProvider, FAKE_ASSET_DATA, []);
const assetBuyer = new AssetBuyer(mockWeb3Provider.object, mockOrderProvider.object);
const liquidityResult = await assetBuyer.getLiquidityForAssetDataAsync(FAKE_ASSET_DATA);
expect(liquidityResult).to.deep.equal({
tokensAvailableInBaseUnits: new BigNumber(0),
ethValueAvailableInWei: new BigNumber(0),
});
});
it('should return 0s when only other asset pair supported', async () => {
mockAvailableAssetDatas(mockOrderProvider, FAKE_ASSET_DATA, [DAI_ASSET_DATA]);
const assetBuyer = new AssetBuyer(mockWeb3Provider.object, mockOrderProvider.object);
const liquidityResult = await assetBuyer.getLiquidityForAssetDataAsync(FAKE_ASSET_DATA);
expect(liquidityResult).to.deep.equal({
tokensAvailableInBaseUnits: new BigNumber(0),
ethValueAvailableInWei: new BigNumber(0),
});
});
});
// TODO (xianny): needs to be updated to new SignedOrder interface
describe('assetData is supported', () => {
const chainId = 1;
// orders
const sellTwoTokensFor1Weth: SignedOrder = orderFactory.createSignedOrderFromPartial({
makerAssetAmount: baseUnitAmount(2),
takerAssetAmount: baseUnitAmount(1, WETH_DECIMALS),
chainId,
});
const sellTenTokensFor10Weth: SignedOrder = orderFactory.createSignedOrderFromPartial({
makerAssetAmount: baseUnitAmount(10),
takerAssetAmount: baseUnitAmount(10, WETH_DECIMALS),
chainId,
});
beforeEach(() => {
mockAvailableAssetDatas(mockOrderProvider, FAKE_ASSET_DATA, [WETH_ASSET_DATA]);
});
it('should return 0s when no orders available', async () => {
const ordersAndFillableAmounts: OrdersAndFillableAmounts = {
orders: [],
remainingFillableMakerAssetAmounts: [],
};
const expectedResult = {
tokensAvailableInBaseUnits: new BigNumber(0),
ethValueAvailableInWei: new BigNumber(0),
};
await expectLiquidityResult(
mockWeb3Provider.object,
mockOrderProvider.object,
ordersAndFillableAmounts,
expectedResult,
);
});
it('should return correct computed value when orders provided with full fillableAmounts', async () => {
const orders: SignedOrder[] = [sellTwoTokensFor1Weth, sellTenTokensFor10Weth];
const ordersAndFillableAmounts = {
orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth],
remainingFillableMakerAssetAmounts: orders.map(o => o.makerAssetAmount),
};
const expectedTokensAvailable = orders[0].makerAssetAmount.plus(orders[1].makerAssetAmount);
const expectedEthValueAvailable = orders[0].takerAssetAmount.plus(orders[1].takerAssetAmount);
const expectedResult = {
tokensAvailableInBaseUnits: expectedTokensAvailable,
ethValueAvailableInWei: expectedEthValueAvailable,
};
await expectLiquidityResult(
mockWeb3Provider.object,
mockOrderProvider.object,
ordersAndFillableAmounts,
expectedResult,
);
});
it('should return correct computed value with one partial fillableAmounts', async () => {
const ordersAndFillableAmounts = {
orders: [sellTwoTokensFor1Weth],
remainingFillableMakerAssetAmounts: [baseUnitAmount(1)],
};
const expectedResult = {
tokensAvailableInBaseUnits: baseUnitAmount(1),
ethValueAvailableInWei: baseUnitAmount(0.5, WETH_DECIMALS),
};
await expectLiquidityResult(
mockWeb3Provider.object,
mockOrderProvider.object,
ordersAndFillableAmounts,
expectedResult,
);
});
it('should return correct computed value with multiple orders and fillable amounts', async () => {
const ordersAndFillableAmounts = {
orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth],
remainingFillableMakerAssetAmounts: [baseUnitAmount(1), baseUnitAmount(3)],
};
const expectedResult = {
tokensAvailableInBaseUnits: baseUnitAmount(4),
ethValueAvailableInWei: baseUnitAmount(3.5, WETH_DECIMALS),
};
await expectLiquidityResult(
mockWeb3Provider.object,
mockOrderProvider.object,
ordersAndFillableAmounts,
expectedResult,
);
});
it('should return 0s when no amounts fillable', async () => {
const ordersAndFillableAmounts = {
orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth],
remainingFillableMakerAssetAmounts: [baseUnitAmount(0), baseUnitAmount(0)],
};
const expectedResult = {
tokensAvailableInBaseUnits: baseUnitAmount(0),
ethValueAvailableInWei: baseUnitAmount(0, WETH_DECIMALS),
};
await expectLiquidityResult(
mockWeb3Provider.object,
mockOrderProvider.object,
ordersAndFillableAmounts,
expectedResult,
);
});
});
});
});

View File

@ -1,303 +0,0 @@
import { orderFactory } from '@0x/order-utils/lib/src/order_factory';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import 'mocha';
import { AssetBuyerError, OrdersAndFillableAmounts } from '../src/types';
import { buyQuoteCalculator } from '../src/utils/buy_quote_calculator';
import { chaiSetup } from './utils/chai_setup';
import { testHelpers } from './utils/test_helpers';
chaiSetup.configure();
const expect = chai.expect;
// tslint:disable:custom-no-magic-numbers
describe('buyQuoteCalculator', () => {
describe('#calculate', () => {
let firstOrder: SignedOrder;
let firstRemainingFillAmount: BigNumber;
let secondOrder: SignedOrder;
let secondRemainingFillAmount: BigNumber;
let ordersAndFillableAmounts: OrdersAndFillableAmounts;
let smallFeeOrderAndFillableAmount: OrdersAndFillableAmounts;
let allFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts;
const chainId = 1;
beforeEach(() => {
// generate two orders for our desired maker asset
// the first order has a rate of 4 makerAsset / WETH with a takerFee of 200 ZRX and has only 200 / 400 makerAsset units left to fill (half fillable)
// the second order has a rate of 2 makerAsset / WETH with a takerFee of 100 ZRX and has 200 / 200 makerAsset units left to fill (completely fillable)
// generate one order for fees
// the fee order has a rate of 1 ZRX / WETH with no taker fee and has 100 ZRX left to fill (completely fillable)
firstOrder = orderFactory.createSignedOrderFromPartial({
makerAssetAmount: new BigNumber(400),
takerAssetAmount: new BigNumber(100),
takerFee: new BigNumber(200),
chainId,
});
firstRemainingFillAmount = new BigNumber(200);
secondOrder = orderFactory.createSignedOrderFromPartial({
makerAssetAmount: new BigNumber(200),
takerAssetAmount: new BigNumber(100),
takerFee: new BigNumber(100),
chainId,
});
secondRemainingFillAmount = secondOrder.makerAssetAmount;
ordersAndFillableAmounts = {
orders: [firstOrder, secondOrder],
remainingFillableMakerAssetAmounts: [firstRemainingFillAmount, secondRemainingFillAmount],
};
const smallFeeOrder = orderFactory.createSignedOrderFromPartial({
makerAssetAmount: new BigNumber(100),
takerAssetAmount: new BigNumber(100),
chainId,
});
smallFeeOrderAndFillableAmount = {
orders: [smallFeeOrder],
remainingFillableMakerAssetAmounts: [smallFeeOrder.makerAssetAmount],
};
const largeFeeOrder = orderFactory.createSignedOrderFromPartial({
makerAssetAmount: new BigNumber(113),
takerAssetAmount: new BigNumber(200),
takerFee: new BigNumber(11),
chainId,
});
allFeeOrdersAndFillableAmounts = {
orders: [smallFeeOrder, largeFeeOrder],
remainingFillableMakerAssetAmounts: [
smallFeeOrder.makerAssetAmount,
largeFeeOrder.makerAssetAmount.minus(largeFeeOrder.takerFee),
],
};
});
describe('InsufficientLiquidityError', () => {
it('should throw if not enough maker asset liquidity (multiple orders)', () => {
// we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
const errorFunction = () => {
buyQuoteCalculator.calculate(
ordersAndFillableAmounts,
smallFeeOrderAndFillableAmount,
new BigNumber(500),
0,
0,
false,
);
};
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(400));
});
it('should throw if not enough maker asset liquidity (multiple orders with 20% slippage)', () => {
// we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
const errorFunction = () => {
buyQuoteCalculator.calculate(
ordersAndFillableAmounts,
smallFeeOrderAndFillableAmount,
new BigNumber(500),
0,
0.2,
false,
);
};
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(333));
});
it('should throw if not enough maker asset liquidity (multiple orders with 5% slippage)', () => {
// we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
const errorFunction = () => {
buyQuoteCalculator.calculate(
ordersAndFillableAmounts,
smallFeeOrderAndFillableAmount,
new BigNumber(600),
0,
0.05,
false,
);
};
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(380));
});
it('should throw if not enough maker asset liquidity (partially filled order)', () => {
const firstOrderAndFillableAmount: OrdersAndFillableAmounts = {
orders: [firstOrder],
remainingFillableMakerAssetAmounts: [firstRemainingFillAmount],
};
const errorFunction = () => {
buyQuoteCalculator.calculate(
firstOrderAndFillableAmount,
smallFeeOrderAndFillableAmount,
new BigNumber(201),
0,
0,
false,
);
};
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(200));
});
it('should throw if not enough maker asset liquidity (completely fillable order)', () => {
const completelyFillableOrder = orderFactory.createSignedOrderFromPartial({
makerAssetAmount: new BigNumber(123),
takerAssetAmount: new BigNumber(100),
takerFee: new BigNumber(200),
chainId,
});
const completelyFillableOrdersAndFillableAmount: OrdersAndFillableAmounts = {
orders: [completelyFillableOrder],
remainingFillableMakerAssetAmounts: [completelyFillableOrder.makerAssetAmount],
};
const errorFunction = () => {
buyQuoteCalculator.calculate(
completelyFillableOrdersAndFillableAmount,
smallFeeOrderAndFillableAmount,
new BigNumber(124),
0,
0,
false,
);
};
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(123));
});
it('should throw with 1 amount available if no slippage', () => {
const smallOrder = orderFactory.createSignedOrderFromPartial({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(1),
takerFee: new BigNumber(0),
chainId,
});
const errorFunction = () => {
buyQuoteCalculator.calculate(
{ orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] },
smallFeeOrderAndFillableAmount,
new BigNumber(600),
0,
0,
false,
);
};
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(1));
});
it('should throw with 0 available to fill if amount rounds to 0', () => {
const smallOrder = orderFactory.createSignedOrderFromPartial({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(1),
takerFee: new BigNumber(0),
chainId,
});
const errorFunction = () => {
buyQuoteCalculator.calculate(
{ orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] },
smallFeeOrderAndFillableAmount,
new BigNumber(600),
0,
0.2,
false,
);
};
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(0));
});
});
it('should not throw if order is fillable', () => {
expect(() =>
buyQuoteCalculator.calculate(
ordersAndFillableAmounts,
allFeeOrdersAndFillableAmounts,
new BigNumber(300),
0,
0,
false,
),
).to.not.throw();
});
it('should throw if not enough ZRX liquidity', () => {
// we request 300 makerAsset units but the ZRX order is only enough to fill the first order, which only has 200 makerAssetUnits available
expect(() =>
buyQuoteCalculator.calculate(
ordersAndFillableAmounts,
smallFeeOrderAndFillableAmount,
new BigNumber(300),
0,
0,
false,
),
).to.throw(AssetBuyerError.InsufficientZrxLiquidity);
});
it('calculates a correct buyQuote with no slippage', () => {
// we request 200 makerAsset units which can be filled using the first order
// the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder
const assetBuyAmount = new BigNumber(200);
const feePercentage = 0.02;
const slippagePercentage = 0;
const buyQuote = buyQuoteCalculator.calculate(
ordersAndFillableAmounts,
smallFeeOrderAndFillableAmount,
assetBuyAmount,
feePercentage,
slippagePercentage,
false,
);
// test if orders are correct
expect(buyQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
expect(buyQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]);
// test if rates are correct
// 50 eth to fill the first order + 100 eth for fees
const expectedEthAmountForAsset = new BigNumber(50);
const expectedEthAmountForZrxFees = new BigNumber(100);
const expectedFillEthAmount = expectedEthAmountForAsset;
const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.multipliedBy(feePercentage);
const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees);
const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount);
expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount);
expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
// because we have no slippage protection, minRate is equal to maxRate
expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount);
expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
// test if feePercentage gets passed through
expect(buyQuote.feePercentage).to.equal(feePercentage);
});
it('calculates a correct buyQuote with with slippage', () => {
// we request 200 makerAsset units which can be filled using the first order
// however with 50% slippage we are protecting the buy with 100 extra makerAssetUnits
// so we need enough orders to fill 300 makerAssetUnits
// 300 makerAssetUnits can only be filled using both orders
// the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder
const assetBuyAmount = new BigNumber(200);
const feePercentage = 0.02;
const slippagePercentage = 0.5;
const buyQuote = buyQuoteCalculator.calculate(
ordersAndFillableAmounts,
allFeeOrdersAndFillableAmounts,
assetBuyAmount,
feePercentage,
slippagePercentage,
false,
);
// test if orders are correct
expect(buyQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
expect(buyQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders);
// test if rates are correct
// 50 eth to fill the first order + 100 eth for fees
const expectedEthAmountForAsset = new BigNumber(50);
const expectedEthAmountForZrxFees = new BigNumber(100);
const expectedFillEthAmount = expectedEthAmountForAsset;
const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.multipliedBy(feePercentage);
const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees);
const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount);
expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount);
expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
// 100 eth to fill the first order + 208 eth for fees
const expectedWorstEthAmountForAsset = new BigNumber(100);
const expectedWorstEthAmountForZrxFees = new BigNumber(208);
const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset;
const expectedWorstAffiliateFeeEthAmount = expectedWorstEthAmountForAsset.multipliedBy(feePercentage);
const expectedWorstFeeEthAmount = expectedWorstAffiliateFeeEthAmount.plus(expectedWorstEthAmountForZrxFees);
const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.plus(expectedWorstFeeEthAmount);
expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedWorstFillEthAmount);
expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedWorstFeeEthAmount);
expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedWorstTotalEthAmount);
// test if feePercentage gets passed through
expect(buyQuote.feePercentage).to.equal(feePercentage);
});
});
});

View File

@ -1,13 +0,0 @@
import * as chai from 'chai';
import chaiAsPromised = require('chai-as-promised');
import ChaiBigNumber = require('chai-bignumber');
import * as dirtyChai from 'dirty-chai';
export const chaiSetup = {
configure(): void {
chai.config.includeStack = true;
chai.use(ChaiBigNumber());
chai.use(dirtyChai);
chai.use(chaiAsPromised);
},
};

View File

@ -1,68 +0,0 @@
import { Web3ProviderEngine } from '@0x/subproviders';
import * as TypeMoq from 'typemoq';
import { AssetBuyer } from '../../src/asset_buyer';
import { OrderProvider, OrderProviderResponse, OrdersAndFillableAmounts } from '../../src/types';
// tslint:disable:promise-function-async
// Implementing dummy class for using in mocks, see https://github.com/florinn/typemoq/issues/3
class OrderProviderClass implements OrderProvider {
// tslint:disable-next-line:prefer-function-over-method
public async getOrdersAsync(): Promise<OrderProviderResponse> {
return Promise.resolve({ orders: [] });
}
// tslint:disable-next-line:prefer-function-over-method
public async getAvailableMakerAssetDatasAsync(takerAssetData: string): Promise<string[]> {
return Promise.resolve([]);
}
}
export const orderProviderMock = () => {
return TypeMoq.Mock.ofType(OrderProviderClass, TypeMoq.MockBehavior.Strict);
};
export const mockAvailableAssetDatas = (
mockOrderProvider: TypeMoq.IMock<OrderProviderClass>,
assetData: string,
availableAssetDatas: string[],
) => {
mockOrderProvider
.setup(op => op.getAvailableMakerAssetDatasAsync(TypeMoq.It.isValue(assetData)))
.returns(() => {
return Promise.resolve(availableAssetDatas);
})
.verifiable(TypeMoq.Times.once());
};
const partiallyMockedAssetBuyer = (
provider: Web3ProviderEngine,
orderProvider: OrderProvider,
): TypeMoq.IMock<AssetBuyer> => {
const rawAssetBuyer = new AssetBuyer(provider, orderProvider);
const mockedAssetBuyer = TypeMoq.Mock.ofInstance(rawAssetBuyer, TypeMoq.MockBehavior.Loose, false);
mockedAssetBuyer.callBase = true;
return mockedAssetBuyer;
};
const mockGetOrdersAndAvailableAmounts = (
mockedAssetBuyer: TypeMoq.IMock<AssetBuyer>,
assetData: string,
ordersAndFillableAmounts: OrdersAndFillableAmounts,
): void => {
mockedAssetBuyer
.setup(a => a.getOrdersAndFillableAmountsAsync(assetData, false))
.returns(() => Promise.resolve(ordersAndFillableAmounts))
.verifiable(TypeMoq.Times.once());
};
export const mockedAssetBuyerWithOrdersAndFillableAmounts = (
provider: Web3ProviderEngine,
orderProvider: OrderProvider,
assetData: string,
ordersAndFillableAmounts: OrdersAndFillableAmounts,
): TypeMoq.IMock<AssetBuyer> => {
const mockedAssetBuyer = partiallyMockedAssetBuyer(provider, orderProvider);
mockGetOrdersAndAvailableAmounts(mockedAssetBuyer, assetData, ordersAndFillableAmounts);
return mockedAssetBuyer;
};

View File

@ -1,26 +0,0 @@
import { BigNumber } from '@0x/utils';
import { InsufficientAssetLiquidityError } from '../../src/errors';
export const testHelpers = {
expectInsufficientLiquidityError: (
expect: Chai.ExpectStatic,
functionWhichTriggersError: () => void,
expectedAmountAvailableToFill: BigNumber,
): void => {
let wasErrorThrown = false;
try {
functionWhichTriggersError();
} catch (e) {
wasErrorThrown = true;
expect(e).to.be.instanceOf(InsufficientAssetLiquidityError);
if (expectedAmountAvailableToFill) {
expect(e.amountAvailableToFill).to.be.bignumber.equal(expectedAmountAvailableToFill);
} else {
expect(e.amountAvailableToFill).to.be.undefined();
}
}
expect(wasErrorThrown).to.be.true();
},
};

View File

@ -1,8 +0,0 @@
{
"extends": "../../tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "."
},
"include": ["./src/**/*", "./test/**/*"]
}

View File

@ -1,3 +0,0 @@
{
"extends": ["@0x/tslint-config"]
}

View File

@ -1,7 +0,0 @@
{
"extends": "../../typedoc-tsconfig",
"compilerOptions": {
"outDir": "lib"
},
"include": ["./src/**/*", "./test/**/*"]
}

View File

@ -661,23 +661,6 @@
lodash "^4.17.11"
valid-url "^1.0.9"
"@0x/asset-buyer@6.1.8":
version "6.1.8"
resolved "https://registry.yarnpkg.com/@0x/asset-buyer/-/asset-buyer-6.1.8.tgz#71f6abb366e89e62457c256644edb37e12113e94"
dependencies:
"@0x/assert" "^2.1.0"
"@0x/connect" "^5.0.13"
"@0x/contract-wrappers" "^9.1.7"
"@0x/json-schemas" "^3.0.11"
"@0x/order-utils" "^8.2.2"
"@0x/subproviders" "^4.1.1"
"@0x/types" "^2.4.0"
"@0x/typescript-typings" "^4.2.3"
"@0x/utils" "^4.4.0"
"@0x/web3-wrapper" "^6.0.7"
ethereum-types "^2.1.3"
lodash "^4.17.11"
"@0x/base-contract@^5.4.0":
version "5.4.0"
resolved "https://registry.npmjs.org/@0x/base-contract/-/base-contract-5.4.0.tgz#466ea98af22d7e629a21a7d16211c825664e2a48"
@ -697,23 +680,7 @@
lodash "^4.17.11"
uuid "^3.3.2"
"@0x/connect@^5.0.13":
version "5.0.19"
resolved "https://registry.npmjs.org/@0x/connect/-/connect-5.0.19.tgz#569679af661ef84a4c34958388e1be7f1c250a04"
dependencies:
"@0x/assert" "^2.1.6"
"@0x/json-schemas" "^4.0.2"
"@0x/order-utils" "^8.4.0"
"@0x/types" "^2.4.3"
"@0x/typescript-typings" "^4.3.0"
"@0x/utils" "^4.5.2"
lodash "^4.17.11"
query-string "^6.0.0"
sinon "^4.0.0"
uuid "^3.3.2"
websocket "^1.0.26"
"@0x/contract-addresses@^3.0.2", "@0x/contract-addresses@^3.2.0":
"@0x/contract-addresses@^3.0.1", "@0x/contract-addresses@^3.0.2", "@0x/contract-addresses@^3.2.0":
version "3.2.0"
resolved "https://registry.npmjs.org/@0x/contract-addresses/-/contract-addresses-3.2.0.tgz#606307696d9622764220a34e9d4638b899093eec"
dependencies:
@ -723,7 +690,7 @@
version "2.2.2"
resolved "https://registry.npmjs.org/@0x/contract-artifacts/-/contract-artifacts-2.2.2.tgz#e6d771afb58d0b59c19c5364af5a42a3dfd17219"
"@0x/contract-wrappers@^9.1.7":
"@0x/contract-wrappers@^9.1.6":
version "9.1.8"
resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-9.1.8.tgz#5923d35af3e4b442a57d02f74e02620b2d5b1356"
dependencies:
@ -807,7 +774,7 @@
uuid "^3.3.2"
websocket "^1.0.29"
"@0x/order-utils@^8.2.2", "@0x/order-utils@^8.2.3", "@0x/order-utils@^8.4.0":
"@0x/order-utils@^8.2.1", "@0x/order-utils@^8.2.3", "@0x/order-utils@^8.2.4", "@0x/order-utils@^8.4.0":
version "8.4.0"
resolved "https://registry.npmjs.org/@0x/order-utils/-/order-utils-8.4.0.tgz#f7fe9c73f9fd82ab05ec3c04951049e904aab46a"
dependencies:
@ -6744,7 +6711,7 @@ ethereum-common@^0.0.18:
version "0.0.18"
resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f"
ethereum-types@^2.1.3, ethereum-types@^2.1.4, ethereum-types@^2.1.6:
ethereum-types@^2.1.4, ethereum-types@^2.1.6:
version "2.1.6"
resolved "https://registry.npmjs.org/ethereum-types/-/ethereum-types-2.1.6.tgz#57d9d515fad86ab987c0f6962c4203be37da8579"
dependencies: