From 439c98a6e597f90c40535b159632e8161787ed92 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Mon, 18 Nov 2019 09:37:02 -0500 Subject: [PATCH] `@0x/asset-swapper`: Rebase against development and pay protocol fees. --- packages/asset-swapper/CHANGELOG.json | 15 +- packages/asset-swapper/CHANGELOG.md | 4 - packages/asset-swapper/docs/reference.mdx | 1417 ++++++------- packages/asset-swapper/package-lock.json | 1839 +++++++++++++++++ packages/asset-swapper/package.json | 36 +- packages/asset-swapper/src/constants.ts | 45 +- packages/asset-swapper/src/index.ts | 15 +- .../exchange_swap_quote_consumer.ts | 110 +- .../forwarder_swap_quote_consumer.ts | 167 +- .../quote_consumers/swap_quote_consumer.ts | 42 +- packages/asset-swapper/src/swap_quoter.ts | 182 +- packages/asset-swapper/src/types.ts | 157 +- .../src/utils/affiliate_fee_utils.ts | 41 +- packages/asset-swapper/src/utils/assert.ts | 32 +- .../src/utils/calculate_liquidity.ts | 45 +- .../asset-swapper/src/utils/market_utils.ts | 92 + .../order_provider_response_processor.ts | 182 -- .../src/utils/order_prune_utils.ts | 96 + .../src/utils/protocol_fee_utils.ts | 31 + .../asset-swapper/src/utils/sorting_utils.ts | 29 + .../src/utils/swap_quote_calculator.ts | 425 ++-- .../src/utils/swap_quote_consumer_utils.ts | 40 +- packages/asset-swapper/src/utils/utils.ts | 39 +- .../test/affiliate_fee_utils_test.ts | 92 - .../test/calculate_liquidity_test.ts | 57 + .../test/exchange_swap_quote_consumer_test.ts | 254 ++- .../forwarder_swap_quote_consumer_test.ts | 472 +++-- .../asset-swapper/test/market_utils_test.ts | 374 ++++ .../test/order_prune_utils_test.ts | 336 +++ .../asset-swapper/test/sorting_utils_test.ts | 134 ++ .../test/swap_quote_calculator_test.ts | 1114 +++++----- .../test/swap_quote_consumer_test.ts | 176 -- .../test/swap_quote_consumer_utils_test.ts | 165 +- .../asset-swapper/test/swap_quoter_test.ts | 160 +- .../test/utils/consumer_utils.ts | 0 packages/asset-swapper/test/utils/mocks.ts | 16 +- .../asset-swapper/test/utils/swap_quote.ts | 122 +- .../test/utils/test_order_factory.ts | 63 + .../asset-swapper/test/utils/test_orders.ts | 146 ++ packages/asset-swapper/test/utils/utils.ts | 9 + packages/asset-swapper/test/utils_test.ts | 82 - packages/migrations/CHANGELOG.json | 2 +- 42 files changed, 5681 insertions(+), 3174 deletions(-) create mode 100644 packages/asset-swapper/package-lock.json create mode 100644 packages/asset-swapper/src/utils/market_utils.ts delete mode 100644 packages/asset-swapper/src/utils/order_provider_response_processor.ts create mode 100644 packages/asset-swapper/src/utils/order_prune_utils.ts create mode 100644 packages/asset-swapper/src/utils/protocol_fee_utils.ts create mode 100644 packages/asset-swapper/src/utils/sorting_utils.ts delete mode 100644 packages/asset-swapper/test/affiliate_fee_utils_test.ts create mode 100644 packages/asset-swapper/test/calculate_liquidity_test.ts create mode 100644 packages/asset-swapper/test/market_utils_test.ts create mode 100644 packages/asset-swapper/test/order_prune_utils_test.ts create mode 100644 packages/asset-swapper/test/sorting_utils_test.ts delete mode 100644 packages/asset-swapper/test/swap_quote_consumer_test.ts create mode 100644 packages/asset-swapper/test/utils/consumer_utils.ts create mode 100644 packages/asset-swapper/test/utils/test_order_factory.ts create mode 100644 packages/asset-swapper/test/utils/test_orders.ts create mode 100644 packages/asset-swapper/test/utils/utils.ts delete mode 100644 packages/asset-swapper/test/utils_test.ts diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index f3f843e37e..0fabf63a12 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -1,4 +1,17 @@ [ + { + "version": "2.1.0-beta.3", + "changes": [ + { + "note": "Refactor of logic for marketBuy/marketSell order pruning and selecting, introduced protocol fees, and refactored types used by the package", + "pr": 2272 + }, + { + "note": "Incorporate paying protocol fees.", + "pr": 2350 + } + ] + }, { "version": "2.1.0-beta.2", "changes": [ @@ -7,7 +20,7 @@ "pr": 2342 } ], - "timestamp": 1574030254 + "timestamp": 1573159180 }, { "version": "2.1.0-beta.1", diff --git a/packages/asset-swapper/CHANGELOG.md b/packages/asset-swapper/CHANGELOG.md index b83b2535ef..a9c2f3a6cf 100644 --- a/packages/asset-swapper/CHANGELOG.md +++ b/packages/asset-swapper/CHANGELOG.md @@ -5,10 +5,6 @@ Edit the package's CHANGELOG.json file only. CHANGELOG -## v2.1.0-beta.2 - _November 17, 2019_ - - * Update BigNumber version to ~9.0.0 (#2342) - ## v2.1.0-beta.1 - _November 7, 2019_ * All references to network ID have been removed, and references to chain ID have been introduced instead (#2313) diff --git a/packages/asset-swapper/docs/reference.mdx b/packages/asset-swapper/docs/reference.mdx index 69482509a5..96af714365 100644 --- a/packages/asset-swapper/docs/reference.mdx +++ b/packages/asset-swapper/docs/reference.mdx @@ -1,45 +1,42 @@ -# Interface: TupleDataItem +# Interface: Web3JsV1Provider + +Web3.js version 1 provider interface +This provider interface was implemented in the pre-1.0Beta releases for Web3.js. +This interface allowed sending synchonous requests, support for which was later dropped. -## Properties +## Methods -### components +### send -• **components**: *[DataItem](#class-dataitem)[]* +▸ **send**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md)): *[JSONRPCResponsePayload](#class-jsonrpcresponsepayload)* -*Overrides [DataItem](_ethereum_types_src_index_.dataitem.md).[components](#optional-components)* +*Defined in [ethereum-types/src/index.ts:45](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L45)* -*Defined in [ethereum-types/src/index.ts:142](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L142)* +**Parameters:** + +Name | Type | +------ | ------ | +`payload` | [JSONRPCRequestPayload](#class-jsonrpcrequestpayload) | + +**Returns:** *[JSONRPCResponsePayload](#class-jsonrpcresponsepayload)* ___ -### `Optional` internalType +### sendAsync -• **internalType**? : *undefined | string* +▸ **sendAsync**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md), `callback`: [JSONRPCErrorCallback](#jsonrpcerrorcallback)): *void* -*Inherited from [DataItem](#interface-dataitem).[internalType](#optional-internaltype)* +*Defined in [ethereum-types/src/index.ts:44](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L44)* -*Defined in [ethereum-types/src/index.ts:137](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L137)* +**Parameters:** -___ +Name | Type | +------ | ------ | +`payload` | [JSONRPCRequestPayload](#class-jsonrpcrequestpayload) | +`callback` | [JSONRPCErrorCallback](#jsonrpcerrorcallback) | -### name - -• **name**: *string* - -*Inherited from [DataItem](#interface-dataitem).[name](#name)* - -*Defined in [ethereum-types/src/index.ts:135](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L135)* - -___ - -### type - -• **type**: *string* - -*Inherited from [DataItem](#interface-dataitem).[type](#type)* - -*Defined in [ethereum-types/src/index.ts:136](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L136)* +**Returns:** *void*
@@ -57,7 +54,7 @@ ___ \+ **new SwapQuoteConsumer**(`supportedProvider`: [SupportedProvider](#supportedprovider), `options`: `Partial`): *[SwapQuoteConsumer](#class-swapquoteconsumer)* -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:31](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L31)* +*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:39](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L39)* **Parameters:** @@ -74,7 +71,7 @@ Name | Type | Default | • **chainId**: *number* -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:27](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L27)* +*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:28](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L28)* ___ @@ -82,26 +79,26 @@ ___ • **provider**: *`ZeroExProvider`* -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:26](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L26)* +*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:27](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L27)* ## Methods ### executeSwapQuoteOrThrowAsync -▸ **executeSwapQuoteOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* +▸ **executeSwapQuoteOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* *Implementation of [SwapQuoteConsumerBase](#interface-swapquoteconsumerbase)* -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:81](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L81)* +*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:89](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L89)* -Given a SwapQuote and desired rate (in takerAsset), attempt to execute the swap. +Given a SwapQuote and desired rate (in takerAsset), attempt to execute the swap with 0x extension or exchange contract. **Parameters:** Name | Type | Default | Description | ------ | ------ | ------ | ------ | `quote` | [SwapQuote](#swapquote) | - | An object that conforms to SwapQuote. See type definition for more information. | -`opts` | `Partial` | {} | Options for getting CalldataInfo. See type definition for more information. | +`opts` | `Partial` | {} | Options for getting CalldataInfo. See type definition for more information. | **Returns:** *`Promise`* @@ -109,20 +106,20 @@ ___ ### getCalldataOrThrowAsync -▸ **getCalldataOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* +▸ **getCalldataOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* *Implementation of [SwapQuoteConsumerBase](#interface-swapquoteconsumerbase)* -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:53](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L53)* +*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:61](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L61)* -Given a SwapQuote, returns 'CalldataInfo' for a 0x exchange call. See type definition of CalldataInfo for more information. +Given a SwapQuote, returns 'CalldataInfo' for a 0x extesion or exchange call. See type definition of CalldataInfo for more information. **Parameters:** Name | Type | Default | Description | ------ | ------ | ------ | ------ | `quote` | [SwapQuote](#swapquote) | - | An object that conforms to SwapQuote. See type definition for more information. | -`opts` | `Partial` | {} | Options for getting SmartContractParams. See type definition for more information. | +`opts` | `Partial` | {} | Options for getting SmartContractParams. See type definition for more information. | **Returns:** *`Promise`* @@ -132,14 +129,16 @@ ___ ▸ **getOptimalExtensionContractTypeAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:90](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L90)* +*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:103](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L103)* + +Given a SwapQuote, returns optimal 0x protocol interface (extension or no extension) to perform the swap. **Parameters:** -Name | Type | Default | ------- | ------ | ------ | -`quote` | [SwapQuote](#swapquote) | - | -`opts` | `Partial` | {} | +Name | Type | Default | Description | +------ | ------ | ------ | ------ | +`quote` | [SwapQuote](#swapquote) | - | An object that conforms to SwapQuote. See type definition for more information. | +`opts` | `Partial` | {} | Options for getting optimal exteion contract to fill quote. See type definition for more information. | **Returns:** *`Promise`* @@ -147,23 +146,40 @@ ___ ### getSmartContractParamsOrThrowAsync -▸ **getSmartContractParamsOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise>`* +▸ **getSmartContractParamsOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise>`* *Implementation of [SwapQuoteConsumerBase](#interface-swapquoteconsumerbase)* -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:67](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L67)* +*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:75](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L75)* -Given a SwapQuote, returns 'SmartContractParamsInfo' for a 0x exchange call. See type definition of SmartContractParamsInfo for more information. +Given a SwapQuote, returns 'SmartContractParamsInfo' for a 0x extension or exchange call. See type definition of SmartContractParamsInfo for more information. **Parameters:** Name | Type | Default | Description | ------ | ------ | ------ | ------ | `quote` | [SwapQuote](#swapquote) | - | An object that conforms to SwapQuote. See type definition for more information. | -`opts` | `Partial` | {} | Options for getting SmartContractParams. See type definition for more information. | +`opts` | `Partial` | {} | Options for getting SmartContractParams. See type definition for more information. | **Returns:** *`Promise>`* +___ + +### `Static` getSwapQuoteConsumer + +▸ **getSwapQuoteConsumer**(`supportedProvider`: [SupportedProvider](#supportedprovider), `options`: `Partial`): *[SwapQuoteConsumer](#class-swapquoteconsumer)* + +*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:34](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L34)* + +**Parameters:** + +Name | Type | Default | +------ | ------ | ------ | +`supportedProvider` | [SupportedProvider](#supportedprovider) | - | +`options` | `Partial` | {} | + +**Returns:** *[SwapQuoteConsumer](#class-swapquoteconsumer)* +
# Class: SwapQuoter @@ -175,7 +191,7 @@ Name | Type | Default | Description | \+ **new SwapQuoter**(`supportedProvider`: [SupportedProvider](#supportedprovider), `orderbook`: `Orderbook`, `options`: `Partial`): *[SwapQuoter](#class-swapquoter)* -*Defined in [asset-swapper/src/swap_quoter.ts:124](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L124)* +*Defined in [asset-swapper/src/swap_quoter.ts:129](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L129)* Instantiates a new SwapQuoter instance @@ -197,7 +213,7 @@ An instance of SwapQuoter • **expiryBufferMs**: *number* -*Defined in [asset-swapper/src/swap_quoter.ts:30](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L30)* +*Defined in [asset-swapper/src/swap_quoter.ts:32](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L32)* ___ @@ -205,7 +221,15 @@ ___ • **orderbook**: *`Orderbook`* -*Defined in [asset-swapper/src/swap_quoter.ts:29](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L29)* +*Defined in [asset-swapper/src/swap_quoter.ts:31](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L31)* + +___ + +### permittedOrderFeeTypes + +• **permittedOrderFeeTypes**: *`Set`* + +*Defined in [asset-swapper/src/swap_quoter.ts:33](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L33)* ___ @@ -213,7 +237,7 @@ ___ • **provider**: *`ZeroExProvider`* -*Defined in [asset-swapper/src/swap_quoter.ts:28](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L28)* +*Defined in [asset-swapper/src/swap_quoter.ts:30](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L30)* ## Methods @@ -221,7 +245,7 @@ ___ ▸ **destroyAsync**(): *`Promise`* -*Defined in [asset-swapper/src/swap_quoter.ts:396](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L396)* +*Defined in [asset-swapper/src/swap_quoter.ts:393](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L393)* Destroys any subscriptions or connections. @@ -233,7 +257,7 @@ ___ ▸ **getAvailableMakerAssetDatasAsync**(`takerAssetData`: string): *`Promise`* -*Defined in [asset-swapper/src/swap_quoter.ts:315](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L315)* +*Defined in [asset-swapper/src/swap_quoter.ts:321](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L321)* Get the asset data of all assets that are purchaseable with takerAssetData in the order provider passed in at init. @@ -253,7 +277,7 @@ ___ ▸ **getAvailableTakerAssetDatasAsync**(`makerAssetData`: string): *`Promise`* -*Defined in [asset-swapper/src/swap_quoter.ts:300](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L300)* +*Defined in [asset-swapper/src/swap_quoter.ts:306](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L306)* Get the asset data of all assets that can be used to purchase makerAssetData in the order provider passed in at init. @@ -269,11 +293,23 @@ An array of asset data strings that can purchase makerAssetData. ___ +### getEtherTokenAssetDataOrThrowAsync + +▸ **getEtherTokenAssetDataOrThrowAsync**(): *`Promise`* + +*Defined in [asset-swapper/src/swap_quoter.ts:400](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L400)* + +Utility function to get assetData for Ether token. + +**Returns:** *`Promise`* + +___ + ### getLiquidityForMakerTakerAssetDataPairAsync -▸ **getLiquidityForMakerTakerAssetDataPairAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* +▸ **getLiquidityForMakerTakerAssetDataPairAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* -*Defined in [asset-swapper/src/swap_quoter.ts:274](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L274)* +*Defined in [asset-swapper/src/swap_quoter.ts:281](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L281)* Returns information about available liquidity for an asset Does not factor in slippage or fees @@ -285,9 +321,9 @@ Name | Type | Description | `makerAssetData` | string | The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | `takerAssetData` | string | The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | -**Returns:** *`Promise`* +**Returns:** *`Promise`* -An object that conforms to LiquidityForAssetData that satisfies the request. See type definition for more information. +An object that conforms to LiquidityForTakerMakerAssetDataPair that satisfies the request. See type definition for more information. ___ @@ -295,7 +331,7 @@ ___ ▸ **getMarketBuySwapQuoteAsync**(`makerTokenAddress`: string, `takerTokenAddress`: string, `makerAssetBuyAmount`: `BigNumber`, `options`: `Partial`): *`Promise`* -*Defined in [asset-swapper/src/swap_quoter.ts:208](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L208)* +*Defined in [asset-swapper/src/swap_quoter.ts:223](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L223)* Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address. You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption. @@ -319,7 +355,7 @@ ___ ▸ **getMarketBuySwapQuoteForAssetDataAsync**(`makerAssetData`: string, `takerAssetData`: string, `makerAssetBuyAmount`: `BigNumber`, `options`: `Partial`): *`Promise`* -*Defined in [asset-swapper/src/swap_quoter.ts:183](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L183)* +*Defined in [asset-swapper/src/swap_quoter.ts:198](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L198)* Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address. You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption. @@ -343,7 +379,7 @@ ___ ▸ **getMarketSellSwapQuoteAsync**(`makerTokenAddress`: string, `takerTokenAddress`: string, `takerAssetSellAmount`: `BigNumber`, `options`: `Partial`): *`Promise`* -*Defined in [asset-swapper/src/swap_quoter.ts:242](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L242)* +*Defined in [asset-swapper/src/swap_quoter.ts:253](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L253)* Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address. You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption. @@ -367,7 +403,7 @@ ___ ▸ **getMarketSellSwapQuoteForAssetDataAsync**(`makerAssetData`: string, `takerAssetData`: string, `takerAssetSellAmount`: `BigNumber`, `options`: `Partial`): *`Promise`* -*Defined in [asset-swapper/src/swap_quoter.ts:157](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L157)* +*Defined in [asset-swapper/src/swap_quoter.ts:172](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L172)* Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address. You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption. @@ -387,13 +423,13 @@ An object that conforms to SwapQuote that satisfies the request. See type defini ___ -### getOrdersAndFillableAmountsAsync +### getPrunedSignedOrdersAsync -▸ **getOrdersAndFillableAmountsAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* +▸ **getPrunedSignedOrdersAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* -*Defined in [asset-swapper/src/swap_quoter.ts:347](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L347)* +*Defined in [asset-swapper/src/swap_quoter.ts:355](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L355)* -Grab orders from the map, if there is a miss or it is time to refresh, fetch and process the orders +Grab orders from the order provider, prunes for valid orders with provided OrderPruner options **Parameters:** @@ -402,15 +438,15 @@ Name | Type | Description | `makerAssetData` | string | The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | `takerAssetData` | string | The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | -**Returns:** *`Promise`* +**Returns:** *`Promise`* ___ -### isTakerAddressAllowanceEnoughForBestAndWorstQuoteInfoAsync +### isSwapQuoteFillableByTakerAddressAsync -▸ **isTakerAddressAllowanceEnoughForBestAndWorstQuoteInfoAsync**(`swapQuote`: [SwapQuote](#swapquote), `takerAddress`: string): *`Promise<[boolean, boolean]>`* +▸ **isSwapQuoteFillableByTakerAddressAsync**(`swapQuote`: [SwapQuote](#swapquote), `takerAddress`: string): *`Promise<[boolean, boolean]>`* -*Defined in [asset-swapper/src/swap_quoter.ts:378](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L378)* +*Defined in [asset-swapper/src/swap_quoter.ts:376](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L376)* Util function to check if takerAddress's allowance is enough for 0x exchange contracts to conduct the swap specified by the swapQuote. @@ -429,16 +465,16 @@ ___ ▸ **isTakerMakerAssetDataPairAvailableAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* -*Defined in [asset-swapper/src/swap_quoter.ts:330](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L330)* +*Defined in [asset-swapper/src/swap_quoter.ts:338](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L338)* Validates the taker + maker asset pair is available from the order provider provided to `SwapQuote`. **Parameters:** -Name | Type | ------- | ------ | -`makerAssetData` | string | -`takerAssetData` | string | +Name | Type | Description | +------ | ------ | ------ | +`makerAssetData` | string | The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | +`takerAssetData` | string | The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | **Returns:** *`Promise`* @@ -450,7 +486,7 @@ ___ ▸ **getSwapQuoterForMeshEndpoint**(`supportedProvider`: [SupportedProvider](#supportedprovider), `meshEndpoint`: string, `options`: `Partial`): *[SwapQuoter](#class-swapquoter)* -*Defined in [asset-swapper/src/swap_quoter.ts:111](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L111)* +*Defined in [asset-swapper/src/swap_quoter.ts:116](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L116)* Instantiates a new SwapQuoter instance given a 0x Mesh endpoint. This pulls all available liquidity stored in Mesh @@ -472,7 +508,7 @@ ___ ▸ **getSwapQuoterForProvidedOrders**(`supportedProvider`: [SupportedProvider](#supportedprovider), `orders`: `SignedOrder`[], `options`: `Partial`): *[SwapQuoter](#class-swapquoter)* -*Defined in [asset-swapper/src/swap_quoter.ts:41](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L41)* +*Defined in [asset-swapper/src/swap_quoter.ts:44](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L44)* Instantiates a new SwapQuoter instance given existing liquidity in the form of orders and feeOrders. @@ -494,7 +530,7 @@ ___ ▸ **getSwapQuoterForStandardRelayerAPIUrl**(`supportedProvider`: [SupportedProvider](#supportedprovider), `sraApiUrl`: string, `options`: `Partial`): *[SwapQuoter](#class-swapquoter)* -*Defined in [asset-swapper/src/swap_quoter.ts:61](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L61)* +*Defined in [asset-swapper/src/swap_quoter.ts:64](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L64)* Instantiates a new SwapQuoter instance given a [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) endpoint @@ -516,7 +552,7 @@ ___ ▸ **getSwapQuoterForStandardRelayerAPIWebsocket**(`supportedProvider`: [SupportedProvider](#supportedprovider), `sraApiUrl`: string, `sraWebsocketAPIUrl`: string, `options`: `Partial`): *[SwapQuoter](#class-swapquoter)* -*Defined in [asset-swapper/src/swap_quoter.ts:87](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/swap_quoter.ts#L87)* +*Defined in [asset-swapper/src/swap_quoter.ts:91](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L91)* Instantiates a new SwapQuoter instance given a [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) endpoint and a websocket endpoint. This is more effecient than `getSwapQuoterForStandardRelayerAPIUrl` when requesting multiple quotes. @@ -545,7 +581,7 @@ An instance of SwapQuoter \+ **new BaseOrderProvider**(`orderStore`: [OrderStore](_orderbook_src_order_store_.orderstore.md)): *[BaseOrderProvider](#class-baseorderprovider)* -*Defined in [orderbook/src/order_provider/base_order_provider.ts:12](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_provider/base_order_provider.ts#L12)* +*Defined in [orderbook/src/order_provider/base_order_provider.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L12)* **Parameters:** @@ -561,7 +597,7 @@ Name | Type | • **_orderStore**: *[OrderStore](#class-orderstore)* -*Defined in [orderbook/src/order_provider/base_order_provider.ts:12](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_provider/base_order_provider.ts#L12)* +*Defined in [orderbook/src/order_provider/base_order_provider.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L12)* ## Methods @@ -569,7 +605,7 @@ Name | Type | ▸ **addOrdersAsync**(`orders`: `SignedOrder`[]): *`Promise`* -*Defined in [orderbook/src/order_provider/base_order_provider.ts:27](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_provider/base_order_provider.ts#L27)* +*Defined in [orderbook/src/order_provider/base_order_provider.ts:27](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L27)* **Parameters:** @@ -585,7 +621,7 @@ ___ ▸ **createSubscriptionForAssetPairAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* -*Defined in [orderbook/src/order_provider/base_order_provider.ts:18](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_provider/base_order_provider.ts#L18)* +*Defined in [orderbook/src/order_provider/base_order_provider.ts:18](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L18)* **Parameters:** @@ -602,7 +638,7 @@ ___ ▸ **destroyAsync**(): *`Promise`* -*Defined in [orderbook/src/order_provider/base_order_provider.ts:25](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_provider/base_order_provider.ts#L25)* +*Defined in [orderbook/src/order_provider/base_order_provider.ts:25](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L25)* **Returns:** *`Promise`* @@ -612,7 +648,7 @@ ___ ▸ **getAvailableAssetDatasAsync**(): *`Promise`* -*Defined in [orderbook/src/order_provider/base_order_provider.ts:23](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_provider/base_order_provider.ts#L23)* +*Defined in [orderbook/src/order_provider/base_order_provider.ts:23](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L23)* **Returns:** *`Promise`* @@ -625,19 +661,25 @@ ___ -\+ **new OrderSet**(): *[OrderSet](#class-orderset)* +\+ **new OrderSet**(`orders`: `APIOrder`[]): *[OrderSet](#class-orderset)* -*Defined in [orderbook/src/order_set.ts:6](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_set.ts#L6)* +*Defined in [orderbook/src/order_set.ts:6](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L6)* + +**Parameters:** + +Name | Type | Default | +------ | ------ | ------ | +`orders` | `APIOrder`[] | [] | **Returns:** *[OrderSet](#class-orderset)* ## Methods -### addAsync +### add -▸ **addAsync**(`item`: `APIOrder`): *`Promise`* +▸ **add**(`item`: `APIOrder`): *void* -*Defined in [orderbook/src/order_set.ts:16](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_set.ts#L16)* +*Defined in [orderbook/src/order_set.ts:19](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L19)* **Parameters:** @@ -645,15 +687,15 @@ Name | Type | ------ | ------ | `item` | `APIOrder` | -**Returns:** *`Promise`* +**Returns:** *void* ___ -### addManyAsync +### addMany -▸ **addManyAsync**(`items`: `APIOrder`[]): *`Promise`* +▸ **addMany**(`items`: `APIOrder`[]): *void* -*Defined in [orderbook/src/order_set.ts:22](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_set.ts#L22)* +*Defined in [orderbook/src/order_set.ts:25](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L25)* **Parameters:** @@ -661,15 +703,15 @@ Name | Type | ------ | ------ | `items` | `APIOrder`[] | -**Returns:** *`Promise`* +**Returns:** *void* ___ -### deleteAsync +### delete -▸ **deleteAsync**(`item`: `APIOrder`): *`Promise`* +▸ **delete**(`item`: `APIOrder`): *boolean* -*Defined in [orderbook/src/order_set.ts:54](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_set.ts#L54)* +*Defined in [orderbook/src/order_set.ts:57](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L57)* **Parameters:** @@ -677,15 +719,15 @@ Name | Type | ------ | ------ | `item` | `APIOrder` | -**Returns:** *`Promise`* +**Returns:** *boolean* ___ -### deleteManyAsync +### deleteMany -▸ **deleteManyAsync**(`items`: `APIOrder`[]): *`Promise`* +▸ **deleteMany**(`items`: `APIOrder`[]): *void* -*Defined in [orderbook/src/order_set.ts:58](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_set.ts#L58)* +*Defined in [orderbook/src/order_set.ts:61](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L61)* **Parameters:** @@ -693,15 +735,15 @@ Name | Type | ------ | ------ | `items` | `APIOrder`[] | -**Returns:** *`Promise`* +**Returns:** *void* ___ -### diffAsync +### diff -▸ **diffAsync**(`other`: [OrderSet](#class-orderset)): *`Promise`* +▸ **diff**(`other`: [OrderSet](#class-orderset)): *object* -*Defined in [orderbook/src/order_set.ts:32](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_set.ts#L32)* +*Defined in [orderbook/src/order_set.ts:35](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L35)* **Parameters:** @@ -709,15 +751,15 @@ Name | Type | ------ | ------ | `other` | [OrderSet](#class-orderset) | -**Returns:** *`Promise`* +**Returns:** *object* ___ -### hasAsync +### has -▸ **hasAsync**(`order`: `APIOrder`): *`Promise`* +▸ **has**(`order`: `APIOrder`): *boolean* -*Defined in [orderbook/src/order_set.ts:28](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_set.ts#L28)* +*Defined in [orderbook/src/order_set.ts:31](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L31)* **Parameters:** @@ -725,7 +767,7 @@ Name | Type | ------ | ------ | `order` | `APIOrder` | -**Returns:** *`Promise`* +**Returns:** *boolean* ___ @@ -733,7 +775,7 @@ ___ ▸ **size**(): *number* -*Defined in [orderbook/src/order_set.ts:12](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_set.ts#L12)* +*Defined in [orderbook/src/order_set.ts:15](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L15)* **Returns:** *number* @@ -743,7 +785,7 @@ ___ ▸ **values**(): *`IterableIterator`* -*Defined in [orderbook/src/order_set.ts:50](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_set.ts#L50)* +*Defined in [orderbook/src/order_set.ts:53](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L53)* **Returns:** *`IterableIterator`* @@ -758,7 +800,7 @@ ___ ▸ **getOrderSetForAssetPair**(`assetPairKey`: string): *[OrderSet](#class-orderset)* -*Defined in [orderbook/src/order_store.ts:19](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_store.ts#L19)* +*Defined in [orderbook/src/order_store.ts:19](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L19)* **Parameters:** @@ -774,7 +816,7 @@ ___ ▸ **getOrderSetForAssets**(`makerAssetData`: string, `takerAssetData`: string): *[OrderSet](#class-orderset)* -*Defined in [orderbook/src/order_store.ts:15](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_store.ts#L15)* +*Defined in [orderbook/src/order_store.ts:15](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L15)* **Parameters:** @@ -791,7 +833,7 @@ ___ ▸ **has**(`assetPairKey`: string): *boolean* -*Defined in [orderbook/src/order_store.ts:34](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_store.ts#L34)* +*Defined in [orderbook/src/order_store.ts:34](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L34)* **Parameters:** @@ -807,17 +849,17 @@ ___ ▸ **keys**(): *`IterableIterator`* -*Defined in [orderbook/src/order_store.ts:40](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_store.ts#L40)* +*Defined in [orderbook/src/order_store.ts:40](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L40)* **Returns:** *`IterableIterator`* ___ -### updateAsync +### update -▸ **updateAsync**(`addedRemoved`: [AddedRemovedOrders](#interface-addedremovedorders)): *`Promise`* +▸ **update**(`addedRemoved`: [AddedRemovedOrders](#interface-addedremovedorders)): *void* -*Defined in [orderbook/src/order_store.ts:28](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_store.ts#L28)* +*Defined in [orderbook/src/order_store.ts:28](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L28)* **Parameters:** @@ -825,7 +867,7 @@ Name | Type | ------ | ------ | `addedRemoved` | [AddedRemovedOrders](#interface-addedremovedorders) | -**Returns:** *`Promise`* +**Returns:** *void* ___ @@ -833,7 +875,7 @@ ___ ▸ **values**(`assetPairKey`: string): *`APIOrder`[]* -*Defined in [orderbook/src/order_store.ts:37](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_store.ts#L37)* +*Defined in [orderbook/src/order_store.ts:37](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L37)* **Parameters:** @@ -849,7 +891,7 @@ ___ ▸ **assetPairKeyToAssets**(`assetPairKey`: string): *string[]* -*Defined in [orderbook/src/order_store.ts:12](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_store.ts#L12)* +*Defined in [orderbook/src/order_store.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L12)* **Parameters:** @@ -865,7 +907,7 @@ ___ ▸ **getKeyForAssetPair**(`makerAssetData`: string, `takerAssetData`: string): *string* -*Defined in [orderbook/src/order_store.ts:9](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_store.ts#L9)* +*Defined in [orderbook/src/order_store.ts:9](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L9)* **Parameters:** @@ -887,7 +929,7 @@ Name | Type | \+ **new Orderbook**(`orderProvider`: [BaseOrderProvider](_orderbook_src_order_provider_base_order_provider_.baseorderprovider.md), `orderStore`: [OrderStore](_orderbook_src_order_store_.orderstore.md)): *[Orderbook](#class-orderbook)* -*Defined in [orderbook/src/orderbook.ts:55](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/orderbook.ts#L55)* +*Defined in [orderbook/src/orderbook.ts:55](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L55)* Creates an Orderbook with the order provider. All order updates are stored in the `OrderStore`. @@ -907,7 +949,7 @@ Name | Type | Description | ▸ **addOrdersAsync**(`orders`: `SignedOrder`[]): *`Promise`* -*Defined in [orderbook/src/orderbook.ts:98](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/orderbook.ts#L98)* +*Defined in [orderbook/src/orderbook.ts:98](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L98)* Adds the orders to the Order Provider. All accepted orders will be returned and rejected orders will be returned with an message indicating a reason for its rejection @@ -926,7 +968,7 @@ ___ ▸ **destroyAsync**(): *`Promise`* -*Defined in [orderbook/src/orderbook.ts:104](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/orderbook.ts#L104)* +*Defined in [orderbook/src/orderbook.ts:104](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L104)* Destroys any subscriptions or connections. @@ -938,7 +980,7 @@ ___ ▸ **getAvailableAssetDatasAsync**(): *`Promise`* -*Defined in [orderbook/src/orderbook.ts:90](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/orderbook.ts#L90)* +*Defined in [orderbook/src/orderbook.ts:90](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L90)* Returns all of the Available Asset Pairs for the provided Order Provider. @@ -950,7 +992,7 @@ ___ ▸ **getOrdersAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* -*Defined in [orderbook/src/orderbook.ts:75](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/orderbook.ts#L75)* +*Defined in [orderbook/src/orderbook.ts:75](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L75)* Returns all orders where the order.makerAssetData == makerAssetData and order.takerAssetData == takerAssetData. This pair is then subscribed to @@ -973,7 +1015,7 @@ ___ ▸ **getOrderbookForMeshProvider**(`opts`: [MeshOrderProviderOpts](#interface-meshorderprovideropts)): *[Orderbook](#class-orderbook)* -*Defined in [orderbook/src/orderbook.ts:52](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/orderbook.ts#L52)* +*Defined in [orderbook/src/orderbook.ts:52](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L52)* Creates an Orderbook with a Mesh Order Provider. This Provider fetches ALL orders and subscribes to updates on ALL orders. @@ -992,7 +1034,7 @@ ___ ▸ **getOrderbookForPollingProvider**(`opts`: [SRAPollingOrderProviderOpts](#interface-srapollingorderprovideropts)): *[Orderbook](#class-orderbook)* -*Defined in [orderbook/src/orderbook.ts:43](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/orderbook.ts#L43)* +*Defined in [orderbook/src/orderbook.ts:43](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L43)* Creates an Orderbook with SRA Polling Provider. This Provider simply polls every interval. @@ -1010,7 +1052,7 @@ ___ ▸ **getOrderbookForProvidedOrders**(`orders`: `SignedOrder`[]): *[Orderbook](#class-orderbook)* -*Defined in [orderbook/src/orderbook.ts:26](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/orderbook.ts#L26)* +*Defined in [orderbook/src/orderbook.ts:26](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L26)* Creates an Orderbook with the provided orders. This provider simply stores the orders and allows querying. No validation or subscriptions occur. @@ -1029,7 +1071,7 @@ ___ ▸ **getOrderbookForWebsocketProvider**(`opts`: [SRAWebsocketOrderProviderOpts](#interface-srawebsocketorderprovideropts)): *[Orderbook](#class-orderbook)* -*Defined in [orderbook/src/orderbook.ts:35](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/orderbook.ts#L35)* +*Defined in [orderbook/src/orderbook.ts:35](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L35)* Creates an Orderbook with the SRA Websocket Provider. This Provider fetches orders via the SRA http endpoint and then subscribes to the asset pair for future updates. @@ -1055,7 +1097,7 @@ Represents the varying smart contracts that can consume a valid swap quote • **Forwarder**: = "FORWARDER" -*Defined in [asset-swapper/src/types.ts:88](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L88)* +*Defined in [asset-swapper/src/types.ts:106](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L106)* ___ @@ -1063,12 +1105,16 @@ ___ • **None**: = "NONE" -*Defined in [asset-swapper/src/types.ts:89](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L89)* +*Defined in [asset-swapper/src/types.ts:107](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L107)*
+ + + + # Enumeration: SwapQuoterError Possible error messages thrown by an SwapQuoter instance or associated static methods. @@ -1080,15 +1126,7 @@ Possible error messages thrown by an SwapQuoter instance or associated static me • **AssetUnavailable**: = "ASSET_UNAVAILABLE" -*Defined in [asset-swapper/src/types.ts:303](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L303)* - -___ - -### FeeAssetUnavailable - -• **FeeAssetUnavailable**: = "FEE_ASSET_UNAVAILABLE" - -*Defined in [asset-swapper/src/types.ts:304](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L304)* +*Defined in [asset-swapper/src/types.ts:298](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L298)* ___ @@ -1096,23 +1134,7 @@ ___ • **InsufficientAssetLiquidity**: = "INSUFFICIENT_ASSET_LIQUIDITY" -*Defined in [asset-swapper/src/types.ts:300](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L300)* - -___ - -### InsufficientZrxLiquidity - -• **InsufficientZrxLiquidity**: = "INSUFFICIENT_ZRX_LIQUIDITY" - -*Defined in [asset-swapper/src/types.ts:301](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L301)* - -___ - -### InvalidOrderProviderResponse - -• **InvalidOrderProviderResponse**: = "INVALID_ORDER_PROVIDER_RESPONSE" - -*Defined in [asset-swapper/src/types.ts:302](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L302)* +*Defined in [asset-swapper/src/types.ts:297](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L297)* ___ @@ -1120,15 +1142,15 @@ ___ • **NoEtherTokenContractFound**: = "NO_ETHER_TOKEN_CONTRACT_FOUND" -*Defined in [asset-swapper/src/types.ts:297](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L297)* +*Defined in [asset-swapper/src/types.ts:295](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L295)* ___ -### NoZrxTokenContractFound +### NoGasPriceProvidedOrEstimated -• **NoZrxTokenContractFound**: = "NO_ZRX_TOKEN_CONTRACT_FOUND" +• **NoGasPriceProvidedOrEstimated**: = "NO_GAS_PRICE_PROVIDED_OR_ESTIMATED" -*Defined in [asset-swapper/src/types.ts:298](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L298)* +*Defined in [asset-swapper/src/types.ts:299](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L299)* ___ @@ -1136,7 +1158,7 @@ ___ • **StandardRelayerApiError**: = "STANDARD_RELAYER_API_ERROR" -*Defined in [asset-swapper/src/types.ts:299](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L299)* +*Defined in [asset-swapper/src/types.ts:296](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L296)*
@@ -1169,8 +1191,6 @@ ___ - - @@ -1182,7 +1202,7 @@ Represents the metadata to call a smart contract with calldata. calldataHexString: The hexstring of the calldata. methodAbi: The ABI of the smart contract method to call. toAddress: The contract address to call. -ethAmount: If provided, the eth amount in wei to send with the smart contract call. +ethAmount: The eth amount in wei to send with the smart contract call. ## Properties @@ -1191,15 +1211,15 @@ ethAmount: If provided, the eth amount in wei to send with the smart contract ca • **calldataHexString**: *string* -*Defined in [asset-swapper/src/types.ts:37](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L37)* +*Defined in [asset-swapper/src/types.ts:55](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L55)* ___ -### `Optional` ethAmount +### ethAmount -• **ethAmount**? : *`BigNumber`* +• **ethAmount**: *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:40](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L40)* +*Defined in [asset-swapper/src/types.ts:58](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L58)* ___ @@ -1207,7 +1227,7 @@ ___ • **methodAbi**: *`MethodAbi`* -*Defined in [asset-swapper/src/types.ts:38](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L38)* +*Defined in [asset-swapper/src/types.ts:56](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L56)* ___ @@ -1215,7 +1235,7 @@ ___ • **toAddress**: *string* -*Defined in [asset-swapper/src/types.ts:39](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L39)* +*Defined in [asset-swapper/src/types.ts:57](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L57)*
@@ -1229,104 +1249,8 @@ ___ -# Interface: ForwarderSwapQuoteExecutionOpts -## Properties - -### `Optional` ethAmount - -• **ethAmount**? : *`BigNumber`* - -*Inherited from [ForwarderSwapQuoteGetOutputOpts](#interface-forwarderswapquotegetoutputopts).[ethAmount](#optional-ethamount)* - -*Defined in [asset-swapper/src/types.ts:179](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L179)* - -___ - -### feePercentage - -• **feePercentage**: *number* - -*Inherited from [ForwarderSwapQuoteGetOutputOpts](#interface-forwarderswapquotegetoutputopts).[feePercentage](#feepercentage)* - -*Defined in [asset-swapper/src/types.ts:177](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L177)* - -___ - -### feeRecipient - -• **feeRecipient**: *string* - -*Inherited from [ForwarderSwapQuoteGetOutputOpts](#interface-forwarderswapquotegetoutputopts).[feeRecipient](#feerecipient)* - -*Defined in [asset-swapper/src/types.ts:178](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L178)* - -___ - -### `Optional` gasLimit - -• **gasLimit**? : *undefined | number* - -*Inherited from [SwapQuoteExecutionOptsBase](#interface-swapquoteexecutionoptsbase).[gasLimit](#optional-gaslimit)* - -*Defined in [asset-swapper/src/types.ts:167](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L167)* - -___ - -### `Optional` gasPrice - -• **gasPrice**? : *`BigNumber`* - -*Inherited from [SwapQuoteExecutionOptsBase](#interface-swapquoteexecutionoptsbase).[gasPrice](#optional-gasprice)* - -*Defined in [asset-swapper/src/types.ts:168](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L168)* - -___ - -### `Optional` takerAddress - -• **takerAddress**? : *undefined | string* - -*Inherited from [SwapQuoteExecutionOptsBase](#interface-swapquoteexecutionoptsbase).[takerAddress](#optional-takeraddress)* - -*Defined in [asset-swapper/src/types.ts:166](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L166)* - -
- -# Interface: ForwarderSwapQuoteGetOutputOpts - -feePercentage: percentage (up to 5%) of the taker asset paid to feeRecipient -feeRecipient: address of the receiver of the feePercentage of taker asset -ethAmount: The amount of eth (in Wei) sent to the forwarder contract. - - -## Properties - -### `Optional` ethAmount - -• **ethAmount**? : *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:179](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L179)* - -___ - -### feePercentage - -• **feePercentage**: *number* - -*Defined in [asset-swapper/src/types.ts:177](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L177)* - -___ - -### feeRecipient - -• **feeRecipient**: *string* - -*Defined in [asset-swapper/src/types.ts:178](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L178)* - -
- # Interface: GetExtensionContractTypeOpts @@ -1336,7 +1260,7 @@ ___ • **ethAmount**? : *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:186](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L186)* +*Defined in [asset-swapper/src/types.ts:209](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L209)* ___ @@ -1344,30 +1268,30 @@ ___ • **takerAddress**? : *undefined | string* -*Defined in [asset-swapper/src/types.ts:185](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L185)* +*Defined in [asset-swapper/src/types.ts:208](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L208)*
-# Interface: LiquidityForAssetData +# Interface: LiquidityForTakerMakerAssetDataPair -Represents available liquidity for a given assetData +Represents available liquidity for a given assetData. ## Properties -### makerTokensAvailableInBaseUnits +### makerAssetAvailableInBaseUnits -• **makerTokensAvailableInBaseUnits**: *`BigNumber`* +• **makerAssetAvailableInBaseUnits**: *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:320](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L320)* +*Defined in [asset-swapper/src/types.ts:306](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L306)* ___ -### takerTokensAvailableInBaseUnits +### takerAssetAvailableInBaseUnits -• **takerTokensAvailableInBaseUnits**: *`BigNumber`* +• **takerAssetAvailableInBaseUnits**: *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:321](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L321)* +*Defined in [asset-swapper/src/types.ts:307](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L307)*
@@ -1385,17 +1309,7 @@ type: Specified MarketOperation the SwapQuote is provided for *Inherited from [SwapQuoteBase](#interface-swapquotebase).[bestCaseQuoteInfo](#bestcasequoteinfo)* -*Defined in [asset-swapper/src/types.ts:217](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L217)* - -___ - -### feeOrders - -• **feeOrders**: *`SignedOrder`[]* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[feeOrders](#feeorders)* - -*Defined in [asset-swapper/src/types.ts:216](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L216)* +*Defined in [asset-swapper/src/types.ts:223](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L223)* ___ @@ -1405,7 +1319,7 @@ ___ *Inherited from [SwapQuoteBase](#interface-swapquotebase).[makerAssetData](#makerassetdata)* -*Defined in [asset-swapper/src/types.ts:214](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L214)* +*Defined in [asset-swapper/src/types.ts:221](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L221)* ___ @@ -1413,7 +1327,7 @@ ___ • **makerAssetFillAmount**: *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:235](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L235)* +*Defined in [asset-swapper/src/types.ts:241](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L241)* ___ @@ -1423,7 +1337,7 @@ ___ *Inherited from [SwapQuoteBase](#interface-swapquotebase).[orders](#orders)* -*Defined in [asset-swapper/src/types.ts:215](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L215)* +*Defined in [asset-swapper/src/types.ts:222](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L222)* ___ @@ -1433,15 +1347,15 @@ ___ *Inherited from [SwapQuoteBase](#interface-swapquotebase).[takerAssetData](#takerassetdata)* -*Defined in [asset-swapper/src/types.ts:213](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L213)* +*Defined in [asset-swapper/src/types.ts:220](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L220)* ___ ### type -• **type**: *`Buy`* +• **type**: *[Buy](#buy)* -*Defined in [asset-swapper/src/types.ts:236](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L236)* +*Defined in [asset-swapper/src/types.ts:242](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L242)* ___ @@ -1451,102 +1365,7 @@ ___ *Inherited from [SwapQuoteBase](#interface-swapquotebase).[worstCaseQuoteInfo](#worstcasequoteinfo)* -*Defined in [asset-swapper/src/types.ts:218](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L218)* - -
- -# Interface: MarketBuySwapQuoteWithAffiliateFee - - -## Properties - -### bestCaseQuoteInfo - -• **bestCaseQuoteInfo**: *[SwapQuoteInfo](#class-swapquoteinfo)* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[bestCaseQuoteInfo](#bestcasequoteinfo)* - -*Defined in [asset-swapper/src/types.ts:217](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L217)* - -___ - -### feeOrders - -• **feeOrders**: *`SignedOrder`[]* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[feeOrders](#feeorders)* - -*Defined in [asset-swapper/src/types.ts:216](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L216)* - -___ - -### feePercentage - -• **feePercentage**: *number* - -*Inherited from [SwapQuoteWithAffiliateFeeBase](#interface-swapquotewithaffiliatefeebase).[feePercentage](#feepercentage)* - -*Defined in [asset-swapper/src/types.ts:240](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L240)* - -___ - -### makerAssetData - -• **makerAssetData**: *string* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[makerAssetData](#makerassetdata)* - -*Defined in [asset-swapper/src/types.ts:214](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L214)* - -___ - -### makerAssetFillAmount - -• **makerAssetFillAmount**: *`BigNumber`* - -*Inherited from [MarketBuySwapQuote](#interface-marketbuyswapquote).[makerAssetFillAmount](#makerassetfillamount)* - -*Defined in [asset-swapper/src/types.ts:235](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L235)* - -___ - -### orders - -• **orders**: *`SignedOrder`[]* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[orders](#orders)* - -*Defined in [asset-swapper/src/types.ts:215](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L215)* - -___ - -### takerAssetData - -• **takerAssetData**: *string* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[takerAssetData](#takerassetdata)* - -*Defined in [asset-swapper/src/types.ts:213](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L213)* - -___ - -### type - -• **type**: *`Buy`* - -*Inherited from [MarketBuySwapQuote](#interface-marketbuyswapquote).[type](#type)* - -*Defined in [asset-swapper/src/types.ts:236](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L236)* - -___ - -### worstCaseQuoteInfo - -• **worstCaseQuoteInfo**: *[SwapQuoteInfo](#class-swapquoteinfo)* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[worstCaseQuoteInfo](#worstcasequoteinfo)* - -*Defined in [asset-swapper/src/types.ts:218](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L218)* +*Defined in [asset-swapper/src/types.ts:224](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L224)*
@@ -1564,17 +1383,7 @@ type: Specified MarketOperation the SwapQuote is provided for *Inherited from [SwapQuoteBase](#interface-swapquotebase).[bestCaseQuoteInfo](#bestcasequoteinfo)* -*Defined in [asset-swapper/src/types.ts:217](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L217)* - -___ - -### feeOrders - -• **feeOrders**: *`SignedOrder`[]* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[feeOrders](#feeorders)* - -*Defined in [asset-swapper/src/types.ts:216](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L216)* +*Defined in [asset-swapper/src/types.ts:223](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L223)* ___ @@ -1584,7 +1393,7 @@ ___ *Inherited from [SwapQuoteBase](#interface-swapquotebase).[makerAssetData](#makerassetdata)* -*Defined in [asset-swapper/src/types.ts:214](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L214)* +*Defined in [asset-swapper/src/types.ts:221](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L221)* ___ @@ -1594,7 +1403,7 @@ ___ *Inherited from [SwapQuoteBase](#interface-swapquotebase).[orders](#orders)* -*Defined in [asset-swapper/src/types.ts:215](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L215)* +*Defined in [asset-swapper/src/types.ts:222](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L222)* ___ @@ -1604,7 +1413,7 @@ ___ *Inherited from [SwapQuoteBase](#interface-swapquotebase).[takerAssetData](#takerassetdata)* -*Defined in [asset-swapper/src/types.ts:213](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L213)* +*Defined in [asset-swapper/src/types.ts:220](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L220)* ___ @@ -1612,15 +1421,15 @@ ___ • **takerAssetFillAmount**: *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:226](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L226)* +*Defined in [asset-swapper/src/types.ts:232](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L232)* ___ ### type -• **type**: *`Sell`* +• **type**: *[Sell](#sell)* -*Defined in [asset-swapper/src/types.ts:227](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L227)* +*Defined in [asset-swapper/src/types.ts:233](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L233)* ___ @@ -1630,42 +1439,106 @@ ___ *Inherited from [SwapQuoteBase](#interface-swapquotebase).[worstCaseQuoteInfo](#worstcasequoteinfo)* -*Defined in [asset-swapper/src/types.ts:218](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L218)* +*Defined in [asset-swapper/src/types.ts:224](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L224)*
-# Interface: MarketSellSwapQuoteWithAffiliateFee + + + + + + +# Interface: PrunedSignedOrder + +fillableMakerAssetAmount: Amount of makerAsset that is fillable +fillableTakerAssetAmount: Amount of takerAsset that is fillable +fillableTakerFeeAmount: Amount of takerFee paid to fill fillableTakerAssetAmount ## Properties -### bestCaseQuoteInfo +### chainId -• **bestCaseQuoteInfo**: *[SwapQuoteInfo](#class-swapquoteinfo)* +• **chainId**: *number* -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[bestCaseQuoteInfo](#bestcasequoteinfo)* -*Defined in [asset-swapper/src/types.ts:217](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L217)* + +Defined in types/lib/index.d.ts:4 ___ -### feeOrders +### exchangeAddress -• **feeOrders**: *`SignedOrder`[]* +• **exchangeAddress**: *string* -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[feeOrders](#feeorders)* -*Defined in [asset-swapper/src/types.ts:216](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L216)* + +Defined in types/lib/index.d.ts:5 ___ -### feePercentage +### expirationTimeSeconds -• **feePercentage**: *number* +• **expirationTimeSeconds**: *`BigNumber`* -*Inherited from [SwapQuoteWithAffiliateFeeBase](#interface-swapquotewithaffiliatefeebase).[feePercentage](#feepercentage)* -*Defined in [asset-swapper/src/types.ts:240](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L240)* + +Defined in types/lib/index.d.ts:14 + +___ + +### feeRecipientAddress + +• **feeRecipientAddress**: *string* + + + +Defined in types/lib/index.d.ts:8 + +___ + +### fillableMakerAssetAmount + +• **fillableMakerAssetAmount**: *`BigNumber`* + +*Defined in [asset-swapper/src/types.ts:42](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L42)* + +___ + +### fillableTakerAssetAmount + +• **fillableTakerAssetAmount**: *`BigNumber`* + +*Defined in [asset-swapper/src/types.ts:43](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L43)* + +___ + +### fillableTakerFeeAmount + +• **fillableTakerFeeAmount**: *`BigNumber`* + +*Defined in [asset-swapper/src/types.ts:44](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L44)* + +___ + +### makerAddress + +• **makerAddress**: *string* + + + +Defined in types/lib/index.d.ts:6 + +___ + +### makerAssetAmount + +• **makerAssetAmount**: *`BigNumber`* + + + +Defined in types/lib/index.d.ts:10 ___ @@ -1673,19 +1546,79 @@ ___ • **makerAssetData**: *string* -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[makerAssetData](#makerassetdata)* -*Defined in [asset-swapper/src/types.ts:214](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L214)* + +Defined in types/lib/index.d.ts:16 ___ -### orders +### makerFee -• **orders**: *`SignedOrder`[]* +• **makerFee**: *`BigNumber`* -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[orders](#orders)* -*Defined in [asset-swapper/src/types.ts:215](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L215)* + +Defined in types/lib/index.d.ts:12 + +___ + +### makerFeeAssetData + +• **makerFeeAssetData**: *string* + + + +Defined in types/lib/index.d.ts:18 + +___ + +### salt + +• **salt**: *`BigNumber`* + + + +Defined in types/lib/index.d.ts:15 + +___ + +### senderAddress + +• **senderAddress**: *string* + + + +Defined in types/lib/index.d.ts:9 + +___ + +### signature + +• **signature**: *string* + + + +Defined in types/lib/index.d.ts:22 + +___ + +### takerAddress + +• **takerAddress**: *string* + + + +Defined in types/lib/index.d.ts:7 + +___ + +### takerAssetAmount + +• **takerAssetAmount**: *`BigNumber`* + + + +Defined in types/lib/index.d.ts:11 ___ @@ -1693,52 +1626,34 @@ ___ • **takerAssetData**: *string* -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[takerAssetData](#takerassetdata)* -*Defined in [asset-swapper/src/types.ts:213](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L213)* + +Defined in types/lib/index.d.ts:17 ___ -### takerAssetFillAmount +### takerFee -• **takerAssetFillAmount**: *`BigNumber`* +• **takerFee**: *`BigNumber`* -*Inherited from [MarketSellSwapQuote](#interface-marketsellswapquote).[takerAssetFillAmount](#takerassetfillamount)* -*Defined in [asset-swapper/src/types.ts:226](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L226)* + +Defined in types/lib/index.d.ts:13 ___ -### type +### takerFeeAssetData -• **type**: *`Sell`* +• **takerFeeAssetData**: *string* -*Inherited from [MarketSellSwapQuote](#interface-marketsellswapquote).[type](#type)* -*Defined in [asset-swapper/src/types.ts:227](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L227)* -___ - -### worstCaseQuoteInfo - -• **worstCaseQuoteInfo**: *[SwapQuoteInfo](#class-swapquoteinfo)* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[worstCaseQuoteInfo](#worstcasequoteinfo)* - -*Defined in [asset-swapper/src/types.ts:218](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L218)* +Defined in types/lib/index.d.ts:19
- - - - - - - - # Interface: SmartContractParamsInfo <**T**> Represents the metadata to call a smart contract with parameters. @@ -1754,11 +1669,11 @@ methodAbi: The ABI of the smart contract method to call with params. ## Properties -### `Optional` ethAmount +### ethAmount -• **ethAmount**? : *`BigNumber`* +• **ethAmount**: *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:53](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L53)* +*Defined in [asset-swapper/src/types.ts:71](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L71)* ___ @@ -1766,7 +1681,7 @@ ___ • **methodAbi**: *`MethodAbi`* -*Defined in [asset-swapper/src/types.ts:54](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L54)* +*Defined in [asset-swapper/src/types.ts:72](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L72)* ___ @@ -1774,7 +1689,7 @@ ___ • **params**: *`T`* -*Defined in [asset-swapper/src/types.ts:51](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L51)* +*Defined in [asset-swapper/src/types.ts:69](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L69)* ___ @@ -1782,7 +1697,7 @@ ___ • **toAddress**: *string* -*Defined in [asset-swapper/src/types.ts:52](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L52)* +*Defined in [asset-swapper/src/types.ts:70](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L70)*
@@ -1809,16 +1724,16 @@ executeSwapQuoteOrThrowAsync: Executes a web3 transaction to swap for tokens wit ### executeSwapQuoteOrThrowAsync -▸ **executeSwapQuoteOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* +▸ **executeSwapQuoteOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* -*Defined in [asset-swapper/src/types.ts:145](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L145)* +*Defined in [asset-swapper/src/types.ts:159](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L159)* **Parameters:** Name | Type | ------ | ------ | `quote` | [SwapQuote](#swapquote) | -`opts` | `Partial` | +`opts` | `Partial` | **Returns:** *`Promise`* @@ -1826,16 +1741,16 @@ ___ ### getCalldataOrThrowAsync -▸ **getCalldataOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* +▸ **getCalldataOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* -*Defined in [asset-swapper/src/types.ts:140](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L140)* +*Defined in [asset-swapper/src/types.ts:154](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L154)* **Parameters:** Name | Type | ------ | ------ | `quote` | [SwapQuote](#swapquote) | -`opts` | `Partial` | +`opts` | `Partial` | **Returns:** *`Promise`* @@ -1843,16 +1758,16 @@ ___ ### getSmartContractParamsOrThrowAsync -▸ **getSmartContractParamsOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise>`* +▸ **getSmartContractParamsOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise>`* -*Defined in [asset-swapper/src/types.ts:141](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L141)* +*Defined in [asset-swapper/src/types.ts:155](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L155)* **Parameters:** Name | Type | ------ | ------ | `quote` | [SwapQuote](#swapquote) | -`opts` | `Partial` | +`opts` | `Partial` | **Returns:** *`Promise>`* @@ -1869,13 +1784,29 @@ chainId: The chainId that the desired orders should be for. • **chainId**: *number* -*Defined in [asset-swapper/src/types.ts:152](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L152)* +*Defined in [asset-swapper/src/types.ts:166](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L166)* + +
+ +# Interface: SwapQuoteConsumingOpts + + +## Properties + +### useExtensionContract + +• **useExtensionContract**: *[ExtensionContractType](#enumeration-extensioncontracttype)* + +*Defined in [asset-swapper/src/types.ts:202](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L202)*
# Interface: SwapQuoteExecutionOpts -Represents the options for executing a swap quote with SwapQuoteConsumer +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 +ethAmount: The amount of eth sent with the execution of a swap ## Properties @@ -1884,35 +1815,7 @@ Represents the options for executing a swap quote with SwapQuoteConsumer • **ethAmount**? : *`BigNumber`* -*Inherited from [ForwarderSwapQuoteGetOutputOpts](#interface-forwarderswapquotegetoutputopts).[ethAmount](#optional-ethamount)* - -*Overrides [ForwarderSwapQuoteGetOutputOpts](_asset_swapper_src_types_.forwarderswapquotegetoutputopts.md).[ethAmount](#optional-ethamount)* - -*Defined in [asset-swapper/src/types.ts:179](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L179)* - -___ - -### feePercentage - -• **feePercentage**: *number* - -*Inherited from [ForwarderSwapQuoteGetOutputOpts](#interface-forwarderswapquotegetoutputopts).[feePercentage](#feepercentage)* - -*Overrides [ForwarderSwapQuoteGetOutputOpts](_asset_swapper_src_types_.forwarderswapquotegetoutputopts.md).[feePercentage](#feepercentage)* - -*Defined in [asset-swapper/src/types.ts:177](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L177)* - -___ - -### feeRecipient - -• **feeRecipient**: *string* - -*Inherited from [ForwarderSwapQuoteGetOutputOpts](#interface-forwarderswapquotegetoutputopts).[feeRecipient](#feerecipient)* - -*Overrides [ForwarderSwapQuoteGetOutputOpts](_asset_swapper_src_types_.forwarderswapquotegetoutputopts.md).[feeRecipient](#feerecipient)* - -*Defined in [asset-swapper/src/types.ts:178](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L178)* +*Defined in [asset-swapper/src/types.ts:184](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L184)* ___ @@ -1920,9 +1823,7 @@ ___ • **gasLimit**? : *undefined | number* -*Inherited from [SwapQuoteExecutionOptsBase](#interface-swapquoteexecutionoptsbase).[gasLimit](#optional-gaslimit)* - -*Defined in [asset-swapper/src/types.ts:167](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L167)* +*Defined in [asset-swapper/src/types.ts:182](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L182)* ___ @@ -1930,9 +1831,7 @@ ___ • **gasPrice**? : *`BigNumber`* -*Inherited from [SwapQuoteExecutionOptsBase](#interface-swapquoteexecutionoptsbase).[gasPrice](#optional-gasprice)* - -*Defined in [asset-swapper/src/types.ts:168](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L168)* +*Defined in [asset-swapper/src/types.ts:183](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L183)* ___ @@ -1940,103 +1839,12 @@ ___ • **takerAddress**? : *undefined | string* -*Inherited from [SwapQuoteExecutionOptsBase](#interface-swapquoteexecutionoptsbase).[takerAddress](#optional-takeraddress)* - -*Defined in [asset-swapper/src/types.ts:166](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L166)* - -___ - -### useExtensionContract - -• **useExtensionContract**: *[ExtensionContractType](#enumeration-extensioncontracttype)* - -*Inherited from [SwapQuoteGetOutputOpts](#interface-swapquotegetoutputopts).[useExtensionContract](#useextensioncontract)* - -*Defined in [asset-swapper/src/types.ts:194](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L194)* - -
- -# Interface: SwapQuoteExecutionOptsBase - -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 - - -## Properties - -### `Optional` gasLimit - -• **gasLimit**? : *undefined | number* - -*Defined in [asset-swapper/src/types.ts:167](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L167)* - -___ - -### `Optional` gasPrice - -• **gasPrice**? : *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:168](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L168)* - -___ - -### `Optional` takerAddress - -• **takerAddress**? : *undefined | string* - -*Defined in [asset-swapper/src/types.ts:166](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L166)* +*Defined in [asset-swapper/src/types.ts:181](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L181)*
# Interface: SwapQuoteGetOutputOpts -takerAddress: The address to perform the buy. Defaults to the first available address from the provider. -useConsumerType: If provided, defaults the SwapQuoteConsumer to create output consumed by ConsumerType. - - -## Properties - -### `Optional` ethAmount - -• **ethAmount**? : *`BigNumber`* - -*Inherited from [ForwarderSwapQuoteGetOutputOpts](#interface-forwarderswapquotegetoutputopts).[ethAmount](#optional-ethamount)* - -*Defined in [asset-swapper/src/types.ts:179](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L179)* - -___ - -### feePercentage - -• **feePercentage**: *number* - -*Inherited from [ForwarderSwapQuoteGetOutputOpts](#interface-forwarderswapquotegetoutputopts).[feePercentage](#feepercentage)* - -*Defined in [asset-swapper/src/types.ts:177](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L177)* - -___ - -### feeRecipient - -• **feeRecipient**: *string* - -*Inherited from [ForwarderSwapQuoteGetOutputOpts](#interface-forwarderswapquotegetoutputopts).[feeRecipient](#feerecipient)* - -*Defined in [asset-swapper/src/types.ts:178](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L178)* - -___ - -### useExtensionContract - -• **useExtensionContract**: *[ExtensionContractType](#enumeration-extensioncontracttype)* - -*Defined in [asset-swapper/src/types.ts:194](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L194)* - -
- -# Interface: SwapQuoteGetOutputOptsBase - Represents the options provided to a generic SwapQuoteConsumer @@ -2047,51 +1855,59 @@ Represents the options provided to a generic SwapQuoteConsumer ## Properties -### feeTakerTokenAmount +### feeTakerAssetAmount -• **feeTakerTokenAmount**: *`BigNumber`* +• **feeTakerAssetAmount**: *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:256](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L256)* +*Defined in [asset-swapper/src/types.ts:253](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L253)* ___ -### makerTokenAmount +### makerAssetAmount -• **makerTokenAmount**: *`BigNumber`* +• **makerAssetAmount**: *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:259](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L259)* +*Defined in [asset-swapper/src/types.ts:256](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L256)* ___ -### takerTokenAmount +### protocolFeeInEthAmount -• **takerTokenAmount**: *`BigNumber`* +• **protocolFeeInEthAmount**: *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:258](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L258)* +*Defined in [asset-swapper/src/types.ts:257](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L257)* ___ -### totalTakerTokenAmount +### takerAssetAmount -• **totalTakerTokenAmount**: *`BigNumber`* +• **takerAssetAmount**: *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:257](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L257)* +*Defined in [asset-swapper/src/types.ts:254](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L254)* + +___ + +### totalTakerAssetAmount + +• **totalTakerAssetAmount**: *`BigNumber`* + +*Defined in [asset-swapper/src/types.ts:255](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L255)*
# Interface: SwapQuoteRequestOpts -shouldDisableRequestingFeeOrders: If set to true, requesting a swapQuote will not perform any computation or requests for fees. slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.2 (20%). +gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount ## Properties -### shouldDisableRequestingFeeOrders +### `Optional` gasPrice -• **shouldDisableRequestingFeeOrders**: *boolean* +• **gasPrice**? : *`BigNumber`* -*Defined in [asset-swapper/src/types.ts:267](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L267)* +*Defined in [asset-swapper/src/types.ts:266](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L266)* ___ @@ -2099,7 +1915,7 @@ ___ • **slippagePercentage**: *number* -*Defined in [asset-swapper/src/types.ts:268](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L268)* +*Defined in [asset-swapper/src/types.ts:265](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L265)*
@@ -2116,7 +1932,7 @@ expiryBufferMs: The number of seconds to add when calculating whether an order i • **chainId**: *number* -*Defined in [asset-swapper/src/types.ts:277](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L277)* +*Defined in [asset-swapper/src/types.ts:275](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L275)* ___ @@ -2124,7 +1940,9 @@ ___ • **expiryBufferMs**: *number* -*Defined in [asset-swapper/src/types.ts:279](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L279)* +*Overrides [OrderPrunerOpts](_asset_swapper_src_types_.orderpruneropts.md).[expiryBufferMs](#expirybufferms)* + +*Defined in [asset-swapper/src/types.ts:277](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L277)* ___ @@ -2132,7 +1950,17 @@ ___ • **orderRefreshIntervalMs**: *number* -*Defined in [asset-swapper/src/types.ts:278](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L278)* +*Defined in [asset-swapper/src/types.ts:276](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L276)* + +___ + +### permittedOrderFeeTypes + +• **permittedOrderFeeTypes**: *`Set`* + +*Inherited from [OrderPrunerOpts](#interface-orderpruneropts).[permittedOrderFeeTypes](#permittedorderfeetypes)* + +*Defined in [asset-swapper/src/types.ts:11](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L11)*
@@ -2161,8 +1989,6 @@ ___ - - @@ -2175,15 +2001,7 @@ ___ • **components**? : *[DataItem](#class-dataitem)[]* -*Defined in [ethereum-types/src/index.ts:138](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L138)* - -___ - -### `Optional` internalType - -• **internalType**? : *undefined | string* - -*Defined in [ethereum-types/src/index.ts:137](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L137)* +*Defined in [ethereum-types/src/index.ts:137](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L137)* ___ @@ -2191,7 +2009,7 @@ ___ • **name**: *string* -*Defined in [ethereum-types/src/index.ts:135](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L135)* +*Defined in [ethereum-types/src/index.ts:135](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L135)* ___ @@ -2199,7 +2017,7 @@ ___ • **type**: *string* -*Defined in [ethereum-types/src/index.ts:136](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L136)* +*Defined in [ethereum-types/src/index.ts:136](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L136)*
@@ -2218,7 +2036,7 @@ ___ • **isEIP1193**: *boolean* -*Defined in [ethereum-types/src/index.ts:73](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L73)* +*Defined in [ethereum-types/src/index.ts:73](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L73)* ## Methods @@ -2226,7 +2044,7 @@ ___ ▸ **on**(`event`: [EIP1193Event](#eip1193event), `listener`: function): *this* -*Defined in [ethereum-types/src/index.ts:75](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L75)* +*Defined in [ethereum-types/src/index.ts:75](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L75)* **Parameters:** @@ -2250,7 +2068,7 @@ ___ ▸ **send**(`method`: string, `params?`: any[]): *`Promise`* -*Defined in [ethereum-types/src/index.ts:74](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L74)* +*Defined in [ethereum-types/src/index.ts:74](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L74)* **Parameters:** @@ -2284,7 +2102,7 @@ Name | Type | ▸ **sendAsync**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md), `callback`: [JSONRPCErrorCallback](#jsonrpcerrorcallback)): *void* -*Defined in [ethereum-types/src/index.ts:14](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L14)* +*Defined in [ethereum-types/src/index.ts:14](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L14)* **Parameters:** @@ -2308,7 +2126,7 @@ Name | Type | • **id**: *number* -*Defined in [ethereum-types/src/index.ts:331](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L331)* +*Defined in [ethereum-types/src/index.ts:330](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L330)* ___ @@ -2316,7 +2134,7 @@ ___ • **jsonrpc**: *string* -*Defined in [ethereum-types/src/index.ts:332](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L332)* +*Defined in [ethereum-types/src/index.ts:331](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L331)* ___ @@ -2324,7 +2142,7 @@ ___ • **method**: *string* -*Defined in [ethereum-types/src/index.ts:330](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L330)* +*Defined in [ethereum-types/src/index.ts:329](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L329)* ___ @@ -2332,7 +2150,7 @@ ___ • **params**: *any[]* -*Defined in [ethereum-types/src/index.ts:329](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L329)* +*Defined in [ethereum-types/src/index.ts:328](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L328)*
@@ -2345,7 +2163,7 @@ ___ • **code**: *number* -*Defined in [ethereum-types/src/index.ts:337](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L337)* +*Defined in [ethereum-types/src/index.ts:336](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L336)* ___ @@ -2353,7 +2171,7 @@ ___ • **message**: *string* -*Defined in [ethereum-types/src/index.ts:336](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L336)* +*Defined in [ethereum-types/src/index.ts:335](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L335)*
@@ -2366,7 +2184,7 @@ ___ • **error**? : *[JSONRPCResponseError](#class-jsonrpcresponseerror)* -*Defined in [ethereum-types/src/index.ts:344](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L344)* +*Defined in [ethereum-types/src/index.ts:343](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L343)* ___ @@ -2374,7 +2192,7 @@ ___ • **id**: *number* -*Defined in [ethereum-types/src/index.ts:342](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L342)* +*Defined in [ethereum-types/src/index.ts:341](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L341)* ___ @@ -2382,7 +2200,7 @@ ___ • **jsonrpc**: *string* -*Defined in [ethereum-types/src/index.ts:343](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L343)* +*Defined in [ethereum-types/src/index.ts:342](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L342)* ___ @@ -2390,7 +2208,7 @@ ___ • **result**: *any* -*Defined in [ethereum-types/src/index.ts:341](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L341)* +*Defined in [ethereum-types/src/index.ts:340](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L340)*
@@ -2409,7 +2227,7 @@ ___ • **constant**: *boolean* -*Defined in [ethereum-types/src/index.ts:94](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L94)* +*Defined in [ethereum-types/src/index.ts:94](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L94)* ___ @@ -2417,7 +2235,7 @@ ___ • **inputs**: *[DataItem](#class-dataitem)[]* -*Defined in [ethereum-types/src/index.ts:92](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L92)* +*Defined in [ethereum-types/src/index.ts:92](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L92)* ___ @@ -2425,7 +2243,7 @@ ___ • **name**: *string* -*Defined in [ethereum-types/src/index.ts:91](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L91)* +*Defined in [ethereum-types/src/index.ts:91](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L91)* ___ @@ -2433,7 +2251,7 @@ ___ • **outputs**: *[DataItem](#class-dataitem)[]* -*Defined in [ethereum-types/src/index.ts:93](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L93)* +*Defined in [ethereum-types/src/index.ts:93](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L93)* ___ @@ -2441,7 +2259,7 @@ ___ • **payable**: *boolean* -*Defined in [ethereum-types/src/index.ts:96](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L96)* +*Defined in [ethereum-types/src/index.ts:96](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L96)* ___ @@ -2449,7 +2267,7 @@ ___ • **stateMutability**: *[StateMutability](#statemutability)* -*Defined in [ethereum-types/src/index.ts:95](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L95)* +*Defined in [ethereum-types/src/index.ts:95](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L95)* ___ @@ -2457,7 +2275,7 @@ ___ • **type**: *string* -*Defined in [ethereum-types/src/index.ts:90](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L90)* +*Defined in [ethereum-types/src/index.ts:90](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L90)*
@@ -2486,6 +2304,45 @@ ___ + + + + + +# Interface: TupleDataItem + + +## Properties + +### components + +• **components**: *[DataItem](#class-dataitem)[]* + +*Overrides [DataItem](_ethereum_types_src_index_.dataitem.md).[components](#optional-components)* + +*Defined in [ethereum-types/src/index.ts:141](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L141)* + +___ + +### name + +• **name**: *string* + +*Inherited from [DataItem](#interface-dataitem).[name](#name)* + +*Defined in [ethereum-types/src/index.ts:135](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L135)* + +___ + +### type + +• **type**: *string* + +*Inherited from [DataItem](#interface-dataitem).[type](#type)* + +*Defined in [ethereum-types/src/index.ts:136](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L136)* + +
@@ -2502,7 +2359,7 @@ Error class representing insufficient asset liquidity \+ **new InsufficientAssetLiquidityError**(`amountAvailableToFill`: `BigNumber`): *[InsufficientAssetLiquidityError](#class-insufficientassetliquidityerror)* -*Defined in [asset-swapper/src/errors.ts:12](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/errors.ts#L12)* +*Defined in [asset-swapper/src/errors.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/errors.ts#L12)* **Parameters:** @@ -2518,7 +2375,7 @@ Name | Type | Description | • **amountAvailableToFill**: *`BigNumber`* -*Defined in [asset-swapper/src/errors.ts:12](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/errors.ts#L12)* +*Defined in [asset-swapper/src/errors.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/errors.ts#L12)* The amount availabe to fill (in base units) factoring in slippage. @@ -2564,52 +2421,6 @@ ___
- - - - -# Interface: Web3JsV1Provider - -Web3.js version 1 provider interface -This provider interface was implemented in the pre-1.0Beta releases for Web3.js. -This interface allowed sending synchonous requests, support for which was later dropped. - - -## Methods - -### send - -▸ **send**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md)): *[JSONRPCResponsePayload](#class-jsonrpcresponsepayload)* - -*Defined in [ethereum-types/src/index.ts:45](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L45)* - -**Parameters:** - -Name | Type | ------- | ------ | -`payload` | [JSONRPCRequestPayload](#class-jsonrpcrequestpayload) | - -**Returns:** *[JSONRPCResponsePayload](#class-jsonrpcresponsepayload)* - -___ - -### sendAsync - -▸ **sendAsync**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md), `callback`: [JSONRPCErrorCallback](#jsonrpcerrorcallback)): *void* - -*Defined in [ethereum-types/src/index.ts:44](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L44)* - -**Parameters:** - -Name | Type | ------- | ------ | -`payload` | [JSONRPCRequestPayload](#class-jsonrpcrequestpayload) | -`callback` | [JSONRPCErrorCallback](#jsonrpcerrorcallback) | - -**Returns:** *void* - -
- # Interface: Web3JsV2Provider Web3.js version 2 provider interface @@ -2623,7 +2434,7 @@ before the first attempts to conform to EIP1193 ▸ **send**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md), `callback`: [JSONRPCErrorCallback](#jsonrpcerrorcallback)): *void* -*Defined in [ethereum-types/src/index.ts:54](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L54)* +*Defined in [ethereum-types/src/index.ts:54](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L54)* **Parameters:** @@ -2649,7 +2460,7 @@ however it does not conform entirely. ▸ **send**(`method`: string, `params?`: any[]): *`Promise`* -*Defined in [ethereum-types/src/index.ts:63](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L63)* +*Defined in [ethereum-types/src/index.ts:63](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L63)* **Parameters:** @@ -2675,7 +2486,7 @@ add here • **isMetaMask**? : *undefined | false | true* -*Defined in [ethereum-types/src/index.ts:31](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L31)* +*Defined in [ethereum-types/src/index.ts:31](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L31)* ___ @@ -2683,7 +2494,7 @@ ___ • **isParity**? : *undefined | false | true* -*Defined in [ethereum-types/src/index.ts:32](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L32)* +*Defined in [ethereum-types/src/index.ts:32](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L32)* ___ @@ -2691,7 +2502,7 @@ ___ • **isZeroExProvider**? : *undefined | false | true* -*Defined in [ethereum-types/src/index.ts:30](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L30)* +*Defined in [ethereum-types/src/index.ts:30](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L30)* ## Methods @@ -2699,7 +2510,7 @@ ___ ▸ **enable**(): *`Promise`* -*Defined in [ethereum-types/src/index.ts:34](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L34)* +*Defined in [ethereum-types/src/index.ts:34](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L34)* **Returns:** *`Promise`* @@ -2709,7 +2520,7 @@ ___ ▸ **sendAsync**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md), `callback`: [JSONRPCErrorCallback](#jsonrpcerrorcallback)): *void* -*Defined in [ethereum-types/src/index.ts:35](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L35)* +*Defined in [ethereum-types/src/index.ts:35](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L35)* **Parameters:** @@ -2726,7 +2537,7 @@ ___ ▸ **stop**(): *void* -*Defined in [ethereum-types/src/index.ts:33](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L33)* +*Defined in [ethereum-types/src/index.ts:33](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L33)* **Returns:** *void* @@ -2741,7 +2552,7 @@ ___ • **accepted**: *`SignedOrder`[]* -*Defined in [orderbook/src/types.ts:15](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L15)* +*Defined in [orderbook/src/types.ts:15](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L15)* ___ @@ -2749,7 +2560,7 @@ ___ • **rejected**: *[RejectedOrder](#class-rejectedorder)[]* -*Defined in [orderbook/src/types.ts:16](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L16)* +*Defined in [orderbook/src/types.ts:16](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L16)*
@@ -2762,7 +2573,7 @@ ___ • **added**: *`APIOrder`[]* -*Defined in [orderbook/src/types.ts:6](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L6)* +*Defined in [orderbook/src/types.ts:6](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L6)* ___ @@ -2770,7 +2581,7 @@ ___ • **assetPairKey**: *string* -*Defined in [orderbook/src/types.ts:5](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L5)* +*Defined in [orderbook/src/types.ts:5](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L5)* ___ @@ -2778,7 +2589,7 @@ ___ • **removed**: *`APIOrder`[]* -*Defined in [orderbook/src/types.ts:7](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L7)* +*Defined in [orderbook/src/types.ts:7](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L7)*
@@ -2793,7 +2604,7 @@ Constructor options for a Mesh Order Provider • **websocketEndpoint**: *string* -*Defined in [orderbook/src/types.ts:50](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L50)* +*Defined in [orderbook/src/types.ts:52](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L52)* ___ @@ -2801,7 +2612,7 @@ ___ • **wsOpts**? : *`WSOpts`* -*Defined in [orderbook/src/types.ts:52](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L52)* +*Defined in [orderbook/src/types.ts:54](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L54)*
@@ -2814,7 +2625,7 @@ ___ • **message**: *string* -*Defined in [orderbook/src/types.ts:11](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L11)* +*Defined in [orderbook/src/types.ts:11](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L11)* ___ @@ -2822,7 +2633,7 @@ ___ • **order**: *`SignedOrder`* -*Defined in [orderbook/src/types.ts:12](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L12)* +*Defined in [orderbook/src/types.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L12)*
@@ -2837,7 +2648,7 @@ Constructor options for a SRA Polling Order Provider • **chainId**? : *undefined | number* -*Defined in [orderbook/src/types.ts:42](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L42)* +*Defined in [orderbook/src/types.ts:44](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L44)* ___ @@ -2845,7 +2656,7 @@ ___ • **httpEndpoint**: *string* -*Defined in [orderbook/src/types.ts:36](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L36)* +*Defined in [orderbook/src/types.ts:38](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L38)* ___ @@ -2853,7 +2664,7 @@ ___ • **perPage**? : *undefined | number* -*Defined in [orderbook/src/types.ts:40](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L40)* +*Defined in [orderbook/src/types.ts:42](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L42)* ___ @@ -2861,7 +2672,7 @@ ___ • **pollingIntervalMs**: *number* -*Defined in [orderbook/src/types.ts:38](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L38)* +*Defined in [orderbook/src/types.ts:40](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L40)*
@@ -2872,11 +2683,19 @@ Constructor options for a SRA Websocket Order Provider ## Properties +### `Optional` chainId + +• **chainId**? : *undefined | number* + +*Defined in [orderbook/src/types.ts:30](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L30)* + +___ + ### httpEndpoint • **httpEndpoint**: *string* -*Defined in [orderbook/src/types.ts:26](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L26)* +*Defined in [orderbook/src/types.ts:26](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L26)* ___ @@ -2884,7 +2703,7 @@ ___ • **websocketEndpoint**: *string* -*Defined in [orderbook/src/types.ts:28](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/types.ts#L28)* +*Defined in [orderbook/src/types.ts:28](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L28)*
@@ -2897,7 +2716,7 @@ ___ • **metaData**: *object* -*Defined in [types/src/index.ts:408](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L408)* +*Defined in [types/src/index.ts:403](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L403)* ___ @@ -2905,7 +2724,7 @@ ___ • **order**: *[SignedOrder](#class-signedorder)* -*Defined in [types/src/index.ts:407](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L407)* +*Defined in [types/src/index.ts:402](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L402)*
@@ -2920,7 +2739,7 @@ ___ • **assetData**: *string* -*Defined in [types/src/index.ts:424](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L424)* +*Defined in [types/src/index.ts:419](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L419)* ___ @@ -2928,7 +2747,7 @@ ___ • **maxAmount**: *`BigNumber`* -*Defined in [types/src/index.ts:426](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L426)* +*Defined in [types/src/index.ts:421](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L421)* ___ @@ -2936,7 +2755,7 @@ ___ • **minAmount**: *`BigNumber`* -*Defined in [types/src/index.ts:425](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L425)* +*Defined in [types/src/index.ts:420](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L420)* ___ @@ -2944,7 +2763,7 @@ ___ • **precision**: *number* -*Defined in [types/src/index.ts:427](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L427)* +*Defined in [types/src/index.ts:422](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L422)*
@@ -2957,7 +2776,7 @@ ___ • **assetDataA**: *[Asset](#class-asset)* -*Defined in [types/src/index.ts:419](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L419)* +*Defined in [types/src/index.ts:414](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L414)* ___ @@ -2965,7 +2784,7 @@ ___ • **assetDataB**: *[Asset](#class-asset)* -*Defined in [types/src/index.ts:420](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L420)* +*Defined in [types/src/index.ts:415](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L415)*
@@ -3011,6 +2830,8 @@ ___ + + @@ -3076,7 +2897,7 @@ ___ *Inherited from [Order](#interface-order).[chainId](#chainid)* -*Defined in [types/src/index.ts:14](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L14)* +*Defined in [types/src/index.ts:14](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L14)* ___ @@ -3086,7 +2907,7 @@ ___ *Inherited from [Order](#interface-order).[exchangeAddress](#exchangeaddress)* -*Defined in [types/src/index.ts:15](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L15)* +*Defined in [types/src/index.ts:15](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L15)* ___ @@ -3096,7 +2917,7 @@ ___ *Inherited from [Order](#interface-order).[expirationTimeSeconds](#expirationtimeseconds)* -*Defined in [types/src/index.ts:24](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L24)* +*Defined in [types/src/index.ts:24](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L24)* ___ @@ -3106,7 +2927,7 @@ ___ *Inherited from [Order](#interface-order).[feeRecipientAddress](#feerecipientaddress)* -*Defined in [types/src/index.ts:18](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L18)* +*Defined in [types/src/index.ts:18](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L18)* ___ @@ -3116,7 +2937,7 @@ ___ *Inherited from [Order](#interface-order).[makerAddress](#makeraddress)* -*Defined in [types/src/index.ts:16](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L16)* +*Defined in [types/src/index.ts:16](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L16)* ___ @@ -3126,7 +2947,7 @@ ___ *Inherited from [Order](#interface-order).[makerAssetAmount](#makerassetamount)* -*Defined in [types/src/index.ts:20](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L20)* +*Defined in [types/src/index.ts:20](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L20)* ___ @@ -3136,7 +2957,7 @@ ___ *Inherited from [Order](#interface-order).[makerAssetData](#makerassetdata)* -*Defined in [types/src/index.ts:26](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L26)* +*Defined in [types/src/index.ts:26](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L26)* ___ @@ -3146,7 +2967,7 @@ ___ *Inherited from [Order](#interface-order).[makerFee](#makerfee)* -*Defined in [types/src/index.ts:22](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L22)* +*Defined in [types/src/index.ts:22](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L22)* ___ @@ -3156,7 +2977,7 @@ ___ *Inherited from [Order](#interface-order).[makerFeeAssetData](#makerfeeassetdata)* -*Defined in [types/src/index.ts:28](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L28)* +*Defined in [types/src/index.ts:28](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L28)* ___ @@ -3166,7 +2987,7 @@ ___ *Inherited from [Order](#interface-order).[salt](#salt)* -*Defined in [types/src/index.ts:25](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L25)* +*Defined in [types/src/index.ts:25](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L25)* ___ @@ -3176,7 +2997,7 @@ ___ *Inherited from [Order](#interface-order).[senderAddress](#senderaddress)* -*Defined in [types/src/index.ts:19](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L19)* +*Defined in [types/src/index.ts:19](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L19)* ___ @@ -3184,7 +3005,7 @@ ___ • **signature**: *string* -*Defined in [types/src/index.ts:33](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L33)* +*Defined in [types/src/index.ts:33](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L33)* ___ @@ -3194,7 +3015,7 @@ ___ *Inherited from [Order](#interface-order).[takerAddress](#takeraddress)* -*Defined in [types/src/index.ts:17](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L17)* +*Defined in [types/src/index.ts:17](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L17)* ___ @@ -3204,7 +3025,7 @@ ___ *Inherited from [Order](#interface-order).[takerAssetAmount](#takerassetamount)* -*Defined in [types/src/index.ts:21](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L21)* +*Defined in [types/src/index.ts:21](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L21)* ___ @@ -3214,7 +3035,7 @@ ___ *Inherited from [Order](#interface-order).[takerAssetData](#takerassetdata)* -*Defined in [types/src/index.ts:27](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L27)* +*Defined in [types/src/index.ts:27](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L27)* ___ @@ -3224,7 +3045,7 @@ ___ *Inherited from [Order](#interface-order).[takerFee](#takerfee)* -*Defined in [types/src/index.ts:23](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L23)* +*Defined in [types/src/index.ts:23](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L23)* ___ @@ -3234,7 +3055,7 @@ ___ *Inherited from [Order](#interface-order).[takerFeeAssetData](#takerfeeassetdata)* -*Defined in [types/src/index.ts:29](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/types/src/index.ts#L29)* +*Defined in [types/src/index.ts:29](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L29)*
@@ -3338,13 +3159,85 @@ ___ Ƭ **SwapQuote**: *[MarketBuySwapQuote](#interface-marketbuyswapquote) | [MarketSellSwapQuote](#interface-marketsellswapquote)* -*Defined in [asset-swapper/src/types.ts:182](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/asset-swapper/src/types.ts#L182)* +*Defined in [asset-swapper/src/types.ts:205](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L205)* -___ +
+## Object literals + +### `Const` affiliateFeeUtils + +#### ▪ **affiliateFeeUtils**: *object* + +*Defined in [asset-swapper/src/utils/affiliate_fee_utils.ts:7](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/utils/affiliate_fee_utils.ts#L7)* + +#### getTotalEthAmountWithAffiliateFee + +▸ **getTotalEthAmountWithAffiliateFee**(`swapQuoteInfo`: [SwapQuoteInfo](#interface-swapquoteinfo), `feePercentage`: number): *`BigNumber`* + +*Defined in [asset-swapper/src/utils/affiliate_fee_utils.ts:13](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/utils/affiliate_fee_utils.ts#L13)* + +Get the amount of eth to send for a forwarder contract call (includes takerAssetAmount, protocol fees, and specified affiliate fee amount) + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`swapQuoteInfo` | [SwapQuoteInfo](#interface-swapquoteinfo) | SwapQuoteInfo to generate total eth amount from | +`feePercentage` | number | Percentage of additive fees to apply to totalTakerAssetAmount + protocol fee. (max 5%) | + +**Returns:** *`BigNumber`* + +
+ + + + +## Object literals + +#### `Const` protocolFeeUtils + +#### ▪ **protocolFeeUtils**: *object* + +*Defined in [asset-swapper/src/utils/protocol_fee_utils.ts:9](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/utils/protocol_fee_utils.ts#L9)* + +#### calculateWorstCaseProtocolFee + +▸ **calculateWorstCaseProtocolFee**<**T**>(`orders`: `T`[], `gasPrice`: `BigNumber`): *`BigNumber`* + +*Defined in [asset-swapper/src/utils/protocol_fee_utils.ts:27](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/utils/protocol_fee_utils.ts#L27)* + +Calculates protocol fee with protofol fee multiplier for each fill. + +**Type parameters:** + +▪ **T**: *`Order`* + +**Parameters:** + +Name | Type | +------ | ------ | +`orders` | `T`[] | +`gasPrice` | `BigNumber` | + +**Returns:** *`BigNumber`* + +#### getGasPriceEstimationOrThrowAsync + +▸ **getGasPriceEstimationOrThrowAsync**(): *`Promise`* + +*Defined in [asset-swapper/src/utils/protocol_fee_utils.ts:13](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/utils/protocol_fee_utils.ts#L13)* + +Gets 'fast' gas price from Eth Gas Station. + +**Returns:** *`Promise`* + +
+ + ## Type aliases @@ -3357,7 +3250,7 @@ ___ Ƭ **ConstructorStateMutability**: *"nonpayable" | "payable"* -*Defined in [ethereum-types/src/index.ts:84](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L84)* +*Defined in [ethereum-types/src/index.ts:84](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L84)* ___ @@ -3371,7 +3264,7 @@ ___ Ƭ **EIP1193Event**: *"accountsChanged" | "networkChanged" | "close" | "connect" | "notification"* -*Defined in [ethereum-types/src/index.ts:70](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L70)* +*Defined in [ethereum-types/src/index.ts:70](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L70)* Interface for providers that conform to EIP 1193 Source: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md @@ -3388,7 +3281,7 @@ ___ Ƭ **JSONRPCErrorCallback**: *function* -*Defined in [ethereum-types/src/index.ts:3](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L3)* +*Defined in [ethereum-types/src/index.ts:3](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L3)* #### Type declaration: @@ -3415,7 +3308,7 @@ ___ Ƭ **StateMutability**: *"pure" | "view" | [ConstructorStateMutability](#constructorstatemutability)* -*Defined in [ethereum-types/src/index.ts:85](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L85)* +*Defined in [ethereum-types/src/index.ts:85](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L85)* ___ @@ -3423,7 +3316,7 @@ ___ Ƭ **SupportedProvider**: *[Web3JsProvider](_ethereum_types_src_index_.md#web3jsprovider) | [GanacheProvider](#interface-ganacheprovider) | [EIP1193Provider](#interface-eip1193provider) | [ZeroExProvider](#interface-zeroexprovider)* -*Defined in [ethereum-types/src/index.ts:9](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L9)* +*Defined in [ethereum-types/src/index.ts:9](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L9)* Do not create your own provider. Use an existing provider from a Web3 or ProviderEngine library Read more about Providers in the guides section of the 0x docs. @@ -3438,7 +3331,7 @@ ___ Ƭ **Web3JsProvider**: *[Web3JsV1Provider](#interface-web3jsv1provider) | [Web3JsV2Provider](#interface-web3jsv2provider) | [Web3JsV3Provider](#interface-web3jsv3provider)* -*Defined in [ethereum-types/src/index.ts:11](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/ethereum-types/src/index.ts#L11)* +*Defined in [ethereum-types/src/index.ts:11](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L11)*
@@ -3451,7 +3344,7 @@ ___ • **DEFAULT_TOKEN_PRECISION**: *`18`* = 18 -*Defined in [orderbook/src/order_provider/base_order_provider.ts:9](https://github.com/0xProject/0x-monorepo/blob/34538f2ce/packages/orderbook/src/order_provider/base_order_provider.ts#L9)* +*Defined in [orderbook/src/order_provider/base_order_provider.ts:9](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L9)*
diff --git a/packages/asset-swapper/package-lock.json b/packages/asset-swapper/package-lock.json new file mode 100644 index 0000000000..5bfd95859e --- /dev/null +++ b/packages/asset-swapper/package-lock.json @@ -0,0 +1,1839 @@ +{ + "name": "@0x/asset-swapper", + "version": "2.1.0-beta.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + }, + "dependencies": { + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + } + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "dependencies": { + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + } + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "v8-compile-cache": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", + "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", + "dev": true + }, + "webpack-cli": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.9.tgz", + "integrity": "sha512-xwnSxWl8nZtBl/AFJCOn9pG7s5CYUYdZxmmukv+fAHLcBIHM36dImfpQg3WfShZXeArkWlf6QRw24Klcsv8a5A==", + "dev": true, + "requires": { + "chalk": "2.4.2", + "cross-spawn": "6.0.5", + "enhanced-resolve": "4.1.0", + "findup-sync": "3.0.0", + "global-modules": "2.0.0", + "import-local": "2.0.0", + "interpret": "1.2.0", + "loader-utils": "1.2.3", + "supports-color": "6.1.0", + "v8-compile-cache": "2.0.3", + "yargs": "13.2.4" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index 20b5a55c8b..17d67da85d 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -1,6 +1,6 @@ { "name": "@0x/asset-swapper", - "version": "2.1.0-beta.2", + "version": "2.1.0-beta.1", "engines": { "node": ">=6.12" }, @@ -40,28 +40,30 @@ }, "homepage": "https://0x.org/asset-swapper", "dependencies": { - "@0x/assert": "^2.2.0-beta.2", - "@0x/connect": "^5.1.0-beta.2", - "@0x/contract-addresses": "^3.3.0-beta.3", - "@0x/contract-wrappers": "^12.2.0-beta.2", - "@0x/dev-utils": "^2.4.0-beta.2", - "@0x/json-schemas": "^4.1.0-beta.2", - "@0x/migrations": "^4.4.0-beta.2", - "@0x/order-utils": "^8.5.0-beta.2", - "@0x/orderbook": "^0.1.0-beta.2", - "@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", + "@0x/assert": "^2.2.0-beta.1", + "@0x/contracts-dev-utils": "^0.1.0-beta.1", + "@0x/contracts-erc20": "^2.3.0-beta.1", + "@0x/contracts-exchange": "^2.2.0-beta.1", + "@0x/contracts-exchange-forwarder": "^3.1.0-beta.1", + "@0x/json-schemas": "^4.1.0-beta.1", + "@0x/order-utils": "^8.5.0-beta.1", + "@0x/orderbook": "^0.1.0-beta.1", + "@0x/types": "^2.5.0-beta.1", + "@0x/utils": "^4.6.0-beta.1", + "@0x/web3-wrapper": "^6.1.0-beta.1", + "ethereum-types": "^2.2.0-beta.1", "lodash": "^4.17.11" }, "devDependencies": { + "@0x/contract-addresses": "^3.3.0-beta.2", "@0x/contracts-test-utils": "^3.2.0-beta.2", + "@0x/dev-utils": "^2.4.0-beta.1", "@0x/mesh-rpc-client": "^7.0.4-beta-0xv3", + "@0x/migrations": "^4.4.0-beta.1", + "@0x/subproviders": "^5.1.0-beta.1", "@0x/ts-doc-gen": "^0.0.22", - "@0x/tslint-config": "^3.1.0-beta.2", + "@0x/tslint-config": "^3.1.0-beta.1", + "@0x/typescript-typings": "^4.4.0-beta.1", "@types/lodash": "4.14.104", "@types/mocha": "^5.2.7", "@types/node": "*", diff --git a/packages/asset-swapper/src/constants.ts b/packages/asset-swapper/src/constants.ts index 97a7eba606..fa7f1cf366 100644 --- a/packages/asset-swapper/src/constants.ts +++ b/packages/asset-swapper/src/constants.ts @@ -1,55 +1,68 @@ -import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { - ForwarderSwapQuoteExecutionOpts, - ForwarderSwapQuoteGetOutputOpts, - OrdersAndFillableAmounts, + ForwarderExtensionContractOpts, + OrderPrunerOpts, + OrderPrunerPermittedFeeTypes, + SwapQuoteExecutionOpts, + SwapQuoteGetOutputOpts, SwapQuoteRequestOpts, SwapQuoterOpts, } from './types'; +const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info'; const NULL_BYTES = '0x'; +const NULL_ERC20_ASSET_DATA = '0xf47261b00000000000000000000000000000000000000000000000000000000000000000'; const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'; const MAINNET_CHAIN_ID = 1; const ONE_SECOND_MS = 1000; const DEFAULT_PER_PAGE = 1000; +const PROTOCOL_FEE_MULTIPLIER = 150000; -const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = { - chainId: MAINNET_CHAIN_ID, - orderRefreshIntervalMs: 10000, // 10 seconds +const DEFAULT_ORDER_PRUNER_OPTS: OrderPrunerOpts = { expiryBufferMs: 120000, // 2 minutes + permittedOrderFeeTypes: new Set([ + OrderPrunerPermittedFeeTypes.NoFees, + OrderPrunerPermittedFeeTypes.MakerDenominatedTakerFee, + ]), // Default asset-swapper for CFL oriented fee types }; -const DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS: ForwarderSwapQuoteGetOutputOpts = { +const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = { + ...{ + chainId: MAINNET_CHAIN_ID, + orderRefreshIntervalMs: 10000, // 10 seconds + }, + ...DEFAULT_ORDER_PRUNER_OPTS, +}; + +const DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS: SwapQuoteGetOutputOpts & ForwarderExtensionContractOpts = { feePercentage: 0, feeRecipient: NULL_ADDRESS, }; -const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: ForwarderSwapQuoteExecutionOpts = DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS; +const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: SwapQuoteExecutionOpts & + ForwarderExtensionContractOpts = DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS; const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = { - shouldDisableRequestingFeeOrders: false, slippagePercentage: 0.2, // 20% slippage protection, }; -const EMPTY_ORDERS_AND_FILLABLE_AMOUNTS: OrdersAndFillableAmounts = { - orders: [] as SignedOrder[], - remainingFillableMakerAssetAmounts: [] as BigNumber[], -}; - export const constants = { + ETH_GAS_STATION_API_BASE_URL, NULL_BYTES, ZERO_AMOUNT: new BigNumber(0), NULL_ADDRESS, MAINNET_CHAIN_ID, + DEFAULT_ORDER_PRUNER_OPTS, ETHER_TOKEN_DECIMALS: 18, ONE_AMOUNT: new BigNumber(1), + MAX_AFFILIATE_FEE_PERCENTAGE: 0.05, ONE_SECOND_MS, DEFAULT_SWAP_QUOTER_OPTS, DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS, DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS, DEFAULT_SWAP_QUOTE_REQUEST_OPTS, - EMPTY_ORDERS_AND_FILLABLE_AMOUNTS, DEFAULT_PER_PAGE, + PROTOCOL_FEE_MULTIPLIER, + NULL_ERC20_ASSET_DATA, }; diff --git a/packages/asset-swapper/src/index.ts b/packages/asset-swapper/src/index.ts index fa5973fd22..fbd1e31f34 100644 --- a/packages/asset-swapper/src/index.ts +++ b/packages/asset-swapper/src/index.ts @@ -20,11 +20,13 @@ export { ConstructorStateMutability, } from 'ethereum-types'; -export { SignedOrder, AssetPairsItem, APIOrder, Asset } from '@0x/types'; +export { SignedOrder, AssetPairsItem, APIOrder, Asset, Order } from '@0x/types'; export { BigNumber } from '@0x/utils'; export { SwapQuoteConsumer } from './quote_consumers/swap_quote_consumer'; export { SwapQuoter } from './swap_quoter'; +export { protocolFeeUtils } from './utils/protocol_fee_utils'; +export { affiliateFeeUtils } from './utils/affiliate_fee_utils'; export { InsufficientAssetLiquidityError } from './errors'; export { @@ -34,21 +36,16 @@ export { SwapQuoteConsumerOpts, CalldataInfo, ExtensionContractType, + SwapQuoteConsumingOpts, + LiquidityForTakerMakerAssetDataPair, SwapQuoteGetOutputOpts, + PrunedSignedOrder, SwapQuoteExecutionOpts, SwapQuoteInfo, GetExtensionContractTypeOpts, - SwapQuoteExecutionOptsBase, - SwapQuoteGetOutputOptsBase, - ForwarderSwapQuoteExecutionOpts, - ForwarderSwapQuoteGetOutputOpts, SmartContractParamsInfo, MarketBuySwapQuote, MarketSellSwapQuote, - MarketBuySwapQuoteWithAffiliateFee, - MarketSellSwapQuoteWithAffiliateFee, - LiquidityForAssetData, - OrdersAndFillableAmounts, SwapQuoteConsumerBase, SwapQuoteRequestOpts, } from './types'; diff --git a/packages/asset-swapper/src/quote_consumers/exchange_swap_quote_consumer.ts b/packages/asset-swapper/src/quote_consumers/exchange_swap_quote_consumer.ts index 996a652c10..e8e796c5cd 100644 --- a/packages/asset-swapper/src/quote_consumers/exchange_swap_quote_consumer.ts +++ b/packages/asset-swapper/src/quote_consumers/exchange_swap_quote_consumer.ts @@ -1,5 +1,5 @@ -import { ContractError, ContractWrappers, ForwarderError } from '@0x/contract-wrappers'; -import { MarketOperation } from '@0x/types'; +import { ContractAddresses } from '@0x/contract-addresses'; +import { ExchangeContract } from '@0x/contracts-exchange'; import { AbiEncoder, providerUtils } from '@0x/utils'; import { SupportedProvider, ZeroExProvider } from '@0x/web3-wrapper'; import { MethodAbi } from 'ethereum-types'; @@ -9,13 +9,13 @@ import { constants } from '../constants'; import { CalldataInfo, ExchangeSmartContractParams, + MarketOperation, SmartContractParamsInfo, SwapQuote, SwapQuoteConsumerBase, - SwapQuoteConsumerError, SwapQuoteConsumerOpts, - SwapQuoteExecutionOptsBase, - SwapQuoteGetOutputOptsBase, + SwapQuoteExecutionOpts, + SwapQuoteGetOutputOpts, } from '../types'; import { assert } from '../utils/assert'; import { swapQuoteConsumerUtils } from '../utils/swap_quote_consumer_utils'; @@ -25,23 +25,24 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase = {}) { + constructor( + supportedProvider: SupportedProvider, + contractAddresses: ContractAddresses, + options: Partial = {}, + ) { const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options); assert.isNumber('chainId', chainId); - const provider = providerUtils.standardizeOrThrow(supportedProvider); this.provider = provider; this.chainId = chainId; - this._contractWrappers = new ContractWrappers(this.provider, { - chainId, - }); + this._exchangeContract = new ExchangeContract(contractAddresses.exchange, supportedProvider); } public async getCalldataOrThrowAsync( quote: SwapQuote, - opts: Partial, + opts: Partial, ): Promise { assert.isValidSwapQuote('quote', quote); @@ -69,7 +70,7 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase, + _opts: Partial = {}, ): Promise> { assert.isValidSwapQuote('quote', quote); @@ -77,8 +78,6 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase o.signature); - const optimizedOrders = swapQuoteConsumerUtils.optimizeOrdersForMarketExchangeOperation(orders, quote.type); - let params: ExchangeSmartContractParams; let methodName: string; @@ -86,45 +85,43 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase, + opts: Partial, ): Promise { assert.isValidSwapQuote('quote', quote); - const { takerAddress, gasLimit, gasPrice } = opts; + const { takerAddress, gasLimit, gasPrice, ethAmount } = opts; if (takerAddress !== undefined) { assert.isETHAddressHex('takerAddress', takerAddress); @@ -135,41 +132,38 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase o.signature)) - .sendTransactionAsync({ - from: finalTakerAddress, - gas: gasLimit, - gasPrice, - }); - } else { - const { takerAssetFillAmount } = quote; - txHash = await this._contractWrappers.exchange - .marketSellOrdersNoThrow(orders, takerAssetFillAmount, orders.map(o => o.signature)) - .sendTransactionAsync({ - from: finalTakerAddress, - gas: gasLimit, - gasPrice, - }); - } - return txHash; - } catch (err) { - if (_.includes(err.message, ContractError.SignatureRequestDenied)) { - throw new Error(SwapQuoteConsumerError.SignatureRequestDenied); - } else if (_.includes(err.message, ForwarderError.CompleteFillFailed)) { - throw new Error(SwapQuoteConsumerError.TransactionValueTooLow); - } else { - throw err; - } + const value = ethAmount || quote.worstCaseQuoteInfo.protocolFeeInEthAmount; + let txHash: string; + if (quote.type === MarketOperation.Buy) { + const { makerAssetFillAmount } = quote; + txHash = await this._exchangeContract + .marketBuyOrdersFillOrKill(orders, makerAssetFillAmount, orders.map(o => o.signature)) + .sendTransactionAsync({ + from: finalTakerAddress, + gas: gasLimit, + gasPrice, + value, + }); + } else { + const { takerAssetFillAmount } = quote; + txHash = await this._exchangeContract + .marketSellOrdersFillOrKill(orders, takerAssetFillAmount, orders.map(o => o.signature)) + .sendTransactionAsync({ + from: finalTakerAddress, + gas: gasLimit, + gasPrice, + value, + }); } + // TODO(dorothy-zbornak): Handle signature request denied + // (see contract-wrappers/decorators) + // and ExchangeRevertErrors.IncompleteFillError. + return txHash; } } diff --git a/packages/asset-swapper/src/quote_consumers/forwarder_swap_quote_consumer.ts b/packages/asset-swapper/src/quote_consumers/forwarder_swap_quote_consumer.ts index 82420df4a9..6d38db113e 100644 --- a/packages/asset-swapper/src/quote_consumers/forwarder_swap_quote_consumer.ts +++ b/packages/asset-swapper/src/quote_consumers/forwarder_swap_quote_consumer.ts @@ -1,5 +1,6 @@ -import { ContractError, ContractWrappers, ForwarderError } from '@0x/contract-wrappers'; -import { MarketOperation } from '@0x/types'; +import { ContractAddresses } from '@0x/contract-addresses'; +import { DevUtilsContract } from '@0x/contracts-dev-utils'; +import { ForwarderContract } from '@0x/contracts-exchange-forwarder'; import { AbiEncoder, providerUtils } from '@0x/utils'; import { SupportedProvider, ZeroExProvider } from '@0x/web3-wrapper'; import { MethodAbi } from 'ethereum-types'; @@ -8,14 +9,15 @@ import * as _ from 'lodash'; import { constants } from '../constants'; import { CalldataInfo, + ForwarderExtensionContractOpts, ForwarderSmartContractParams, - ForwarderSwapQuoteExecutionOpts, - ForwarderSwapQuoteGetOutputOpts, + MarketOperation, SmartContractParamsInfo, SwapQuote, SwapQuoteConsumerBase, - SwapQuoteConsumerError, SwapQuoteConsumerOpts, + SwapQuoteExecutionOpts, + SwapQuoteGetOutputOpts, } from '../types'; import { affiliateFeeUtils } from '../utils/affiliate_fee_utils'; import { assert } from '../utils/assert'; @@ -26,18 +28,23 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumerBase = {}) { + constructor( + supportedProvider: SupportedProvider, + contractAddresses: ContractAddresses, + options: Partial = {}, + ) { const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options); assert.isNumber('chainId', chainId); - const provider = providerUtils.standardizeOrThrow(supportedProvider); this.provider = provider; this.chainId = chainId; - this._contractWrappers = new ContractWrappers(this.provider, { - chainId, - }); + this._contractAddresses = contractAddresses; + this._forwarder = new ForwarderContract(contractAddresses.forwarder, supportedProvider); + this._devUtils = new DevUtilsContract(contractAddresses.devUtils, supportedProvider); } /** @@ -47,21 +54,21 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumerBase, + opts: Partial = {}, ): Promise { assert.isValidForwarderSwapQuote('quote', quote, await this._getEtherTokenAssetDataOrThrowAsync()); const { toAddress, methodAbi, ethAmount, params } = await this.getSmartContractParamsOrThrowAsync(quote, opts); const abiEncoder = new AbiEncoder.Method(methodAbi); - const { orders, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient } = params; + const { orders, signatures, feePercentage, feeRecipient } = params; let args: any[]; if (params.type === MarketOperation.Buy) { const { makerAssetFillAmount } = params; - args = [orders, makerAssetFillAmount, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient]; + args = [orders, makerAssetFillAmount, signatures, feePercentage, feeRecipient]; } else { - args = [orders, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient]; + args = [orders, signatures, feePercentage, feeRecipient]; } const calldataHexString = abiEncoder.encode(args, { shouldOptimize: true }); return { @@ -79,47 +86,42 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumerBase, + opts: Partial = {}, ): Promise> { assert.isValidForwarderSwapQuote('quote', quote, await this._getEtherTokenAssetDataOrThrowAsync()); - const { ethAmount, feeRecipient, feePercentage: unFormattedFeePercentage } = _.merge( + const { ethAmount: providedEthAmount, feeRecipient, feePercentage } = _.merge( {}, constants.DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS, opts, ); - assert.isValidPercentage('feePercentage', unFormattedFeePercentage); + assert.isValidPercentage('feePercentage', feePercentage); assert.isETHAddressHex('feeRecipient', feeRecipient); - if (ethAmount !== undefined) { - assert.isBigNumber('ethAmount', ethAmount); + if (providedEthAmount !== undefined) { + assert.isBigNumber('ethAmount', providedEthAmount); } - const quoteWithAffiliateFee = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(quote, unFormattedFeePercentage); - - const { orders, feeOrders, worstCaseQuoteInfo } = quoteWithAffiliateFee; + const { orders, worstCaseQuoteInfo } = quote; // lowercase input addresses const normalizedFeeRecipientAddress = feeRecipient.toLowerCase(); const signatures = _.map(orders, o => o.signature); - const feeSignatures = _.map(feeOrders, o => o.signature); - const feePercentage = utils.numberPercentageToEtherTokenAmountPercentage(unFormattedFeePercentage); + const formattedFeePercentage = utils.numberPercentageToEtherTokenAmountPercentage(feePercentage); let params: ForwarderSmartContractParams; let methodName: string; - if (quoteWithAffiliateFee.type === MarketOperation.Buy) { - const { makerAssetFillAmount } = quoteWithAffiliateFee; + if (quote.type === MarketOperation.Buy) { + const { makerAssetFillAmount } = quote; params = { orders, makerAssetFillAmount, signatures, - feeOrders, - feeSignatures, - feePercentage, + feePercentage: formattedFeePercentage, feeRecipient: normalizedFeeRecipientAddress, type: MarketOperation.Buy, }; @@ -129,23 +131,21 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumerBase, + opts: Partial, ): Promise { assert.isValidForwarderSwapQuote('quote', quote, await this._getEtherTokenAssetDataOrThrowAsync()); - const { ethAmount, takerAddress, gasLimit, gasPrice, feeRecipient, feePercentage } = _.merge( + const { ethAmount: providedEthAmount, takerAddress, gasLimit, gasPrice, feeRecipient, feePercentage } = _.merge( {}, constants.DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS, opts, @@ -169,8 +169,8 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumerBase o.signature), - formattedFeePercentage, - feeRecipient, - ) - .sendTransactionAsync({ - value, - from: finalTakerAddress.toLowerCase(), - gas: gasLimit, - gasPrice, - }); - } else { - txHash = await this._contractWrappers.forwarder - .marketSellOrdersWithEth(orders, 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(SwapQuoteConsumerError.SignatureRequestDenied); - } else if (_.includes(err.message, ForwarderError.CompleteFillFailed)) { - throw new Error(SwapQuoteConsumerError.TransactionValueTooLow); - } else { - throw err; - } + let txHash: string; + if (quote.type === MarketOperation.Buy) { + const { makerAssetFillAmount } = quote; + txHash = await this._forwarder + .marketBuyOrdersWithEth( + orders, + makerAssetFillAmount, + orders.map(o => o.signature), + formattedFeePercentage, + feeRecipient, + ) + .sendTransactionAsync({ + from: finalTakerAddress, + gas: gasLimit, + gasPrice, + value: providedEthAmount || ethAmountWithFees, + }); + } else { + txHash = await this._forwarder + .marketSellOrdersWithEth(orders, orders.map(o => o.signature), formattedFeePercentage, feeRecipient) + .sendTransactionAsync({ + from: finalTakerAddress, + gas: gasLimit, + gasPrice, + value: providedEthAmount || ethAmountWithFees, + }); } + // TODO(dorothy-zbornak): Handle signature request denied + // (see contract-wrappers/decorators) + // and ForwarderRevertErrors.CompleteBuyFailed. + return txHash; } private async _getEtherTokenAssetDataOrThrowAsync(): Promise { - return this._contractWrappers.devUtils - .encodeERC20AssetData(this._contractWrappers.contractAddresses.etherToken) - .callAsync(); + return this._devUtils.encodeERC20AssetData(this._contractAddresses.etherToken).callAsync(); } } diff --git a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts b/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts index a07b587e52..c184b768f4 100644 --- a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts +++ b/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts @@ -1,4 +1,4 @@ -import { ContractWrappers } from '@0x/contract-wrappers'; +import { ContractAddresses, getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; import { providerUtils } from '@0x/utils'; import { SupportedProvider, ZeroExProvider } from '@0x/web3-wrapper'; import * as _ from 'lodash'; @@ -13,6 +13,7 @@ import { SwapQuote, SwapQuoteConsumerBase, SwapQuoteConsumerOpts, + SwapQuoteConsumingOpts, SwapQuoteExecutionOpts, SwapQuoteGetOutputOpts, } from '../types'; @@ -28,7 +29,14 @@ export class SwapQuoteConsumer implements SwapQuoteConsumerBase = {}, + ): SwapQuoteConsumer { + return new SwapQuoteConsumer(supportedProvider, options); + } constructor(supportedProvider: SupportedProvider, options: Partial = {}) { const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options); @@ -37,22 +45,19 @@ export class SwapQuoteConsumer implements SwapQuoteConsumerBase = {}, + opts: Partial = {}, ): Promise { assert.isValidSwapQuote('quote', quote); const consumer = await this._getConsumerForSwapQuoteAsync(opts); @@ -60,13 +65,13 @@ export class SwapQuoteConsumer implements SwapQuoteConsumerBase = {}, + opts: Partial = {}, ): Promise> { assert.isValidSwapQuote('quote', quote); const consumer = await this._getConsumerForSwapQuoteAsync(opts); @@ -74,33 +79,38 @@ export class SwapQuoteConsumer implements SwapQuoteConsumerBase = {}, + opts: Partial = {}, ): Promise { assert.isValidSwapQuote('quote', quote); const consumer = await this._getConsumerForSwapQuoteAsync(opts); return consumer.executeSwapQuoteOrThrowAsync(quote, opts); } + /** + * Given a SwapQuote, returns optimal 0x protocol interface (extension or no extension) to perform the swap. + * @param quote An object that conforms to SwapQuote. See type definition for more information. + * @param opts Options for getting optimal exteion contract to fill quote. See type definition for more information. + */ public async getOptimalExtensionContractTypeAsync( quote: SwapQuote, opts: Partial = {}, ): Promise { return swapQuoteConsumerUtils.getExtensionContractTypeForSwapQuoteAsync( quote, - this._contractWrappers, + this._contractAddresses, this.provider, opts, ); } private async _getConsumerForSwapQuoteAsync( - opts: Partial, + opts: Partial, ): Promise> { if (opts.useExtensionContract === ExtensionContractType.Forwarder) { return this._forwarderConsumer; diff --git a/packages/asset-swapper/src/swap_quoter.ts b/packages/asset-swapper/src/swap_quoter.ts index 5d2d39bd8c..b0f3df7d81 100644 --- a/packages/asset-swapper/src/swap_quoter.ts +++ b/packages/asset-swapper/src/swap_quoter.ts @@ -1,18 +1,20 @@ -import { ContractWrappers } from '@0x/contract-wrappers'; +import { ContractAddresses, getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; +import { DevUtilsContract } from '@0x/contracts-dev-utils'; import { schemas } from '@0x/json-schemas'; -import { assetDataUtils, SignedOrder } from '@0x/order-utils'; +import { SignedOrder } from '@0x/order-utils'; import { MeshOrderProviderOpts, Orderbook, SRAPollingOrderProviderOpts } from '@0x/orderbook'; -import { MarketOperation } from '@0x/types'; import { BigNumber, providerUtils } from '@0x/utils'; import { SupportedProvider, ZeroExProvider } from 'ethereum-types'; import * as _ from 'lodash'; import { constants } from './constants'; import { - LiquidityForAssetData, + LiquidityForTakerMakerAssetDataPair, MarketBuySwapQuote, + MarketOperation, MarketSellSwapQuote, - OrdersAndFillableAmounts, + OrderPrunerPermittedFeeTypes, + PrunedSignedOrder, SwapQuote, SwapQuoteRequestOpts, SwapQuoterError, @@ -20,16 +22,19 @@ import { } from './types'; import { assert } from './utils/assert'; import { calculateLiquidity } from './utils/calculate_liquidity'; -import { orderProviderResponseProcessor } from './utils/order_provider_response_processor'; +import { OrderPruner } from './utils/order_prune_utils'; +import { protocolFeeUtils } from './utils/protocol_fee_utils'; +import { sortingUtils } from './utils/sorting_utils'; import { swapQuoteCalculator } from './utils/swap_quote_calculator'; -import { utils } from './utils/utils'; export class SwapQuoter { public readonly provider: ZeroExProvider; public readonly orderbook: Orderbook; public readonly expiryBufferMs: number; - private readonly _contractWrappers: ContractWrappers; - + public readonly permittedOrderFeeTypes: Set; + private readonly _contractAddresses: ContractAddresses; + private readonly _orderPruner: OrderPruner; + private readonly _devUtilsContract: DevUtilsContract; /** * Instantiates a new SwapQuoter 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. @@ -132,7 +137,11 @@ export class SwapQuoter { * @return An instance of SwapQuoter */ constructor(supportedProvider: SupportedProvider, orderbook: Orderbook, options: Partial = {}) { - const { chainId, expiryBufferMs } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options); + const { chainId, expiryBufferMs, permittedOrderFeeTypes } = _.merge( + {}, + constants.DEFAULT_SWAP_QUOTER_OPTS, + options, + ); const provider = providerUtils.standardizeOrThrow(supportedProvider); assert.isValidOrderbook('orderbook', orderbook); assert.isNumber('chainId', chainId); @@ -140,10 +149,15 @@ export class SwapQuoter { this.provider = provider; this.orderbook = orderbook; this.expiryBufferMs = expiryBufferMs; - this._contractWrappers = new ContractWrappers(this.provider, { - chainId, + this.permittedOrderFeeTypes = permittedOrderFeeTypes; + this._contractAddresses = getContractAddressesForChainOrThrow(chainId); + this._devUtilsContract = new DevUtilsContract(this._contractAddresses.devUtils, provider); + this._orderPruner = new OrderPruner(this._devUtilsContract, { + expiryBufferMs: this.expiryBufferMs, + permittedOrderFeeTypes: this.permittedOrderFeeTypes, }); } + /** * Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address. * You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption. @@ -214,12 +228,8 @@ export class SwapQuoter { assert.isETHAddressHex('makerTokenAddress', makerTokenAddress); assert.isETHAddressHex('takerTokenAddress', takerTokenAddress); assert.isBigNumber('makerAssetBuyAmount', makerAssetBuyAmount); - const makerAssetData = await this._contractWrappers.devUtils - .encodeERC20AssetData(makerTokenAddress) - .callAsync(); - const takerAssetData = await this._contractWrappers.devUtils - .encodeERC20AssetData(takerTokenAddress) - .callAsync(); + const makerAssetData = await this._devUtilsContract.encodeERC20AssetData(makerTokenAddress).callAsync(); + const takerAssetData = await this._devUtilsContract.encodeERC20AssetData(takerTokenAddress).callAsync(); const swapQuote = this.getMarketBuySwapQuoteForAssetDataAsync( makerAssetData, takerAssetData, @@ -248,12 +258,8 @@ export class SwapQuoter { assert.isETHAddressHex('makerTokenAddress', makerTokenAddress); assert.isETHAddressHex('takerTokenAddress', takerTokenAddress); assert.isBigNumber('takerAssetSellAmount', takerAssetSellAmount); - const makerAssetData = await this._contractWrappers.devUtils - .encodeERC20AssetData(makerTokenAddress) - .callAsync(); - const takerAssetData = await this._contractWrappers.devUtils - .encodeERC20AssetData(takerTokenAddress) - .callAsync(); + const makerAssetData = await this._devUtilsContract.encodeERC20AssetData(makerTokenAddress).callAsync(); + const takerAssetData = await this._devUtilsContract.encodeERC20AssetData(takerTokenAddress).callAsync(); const swapQuote = this.getMarketSellSwapQuoteForAssetDataAsync( makerAssetData, takerAssetData, @@ -269,27 +275,26 @@ export class SwapQuoter { * @param makerAssetData The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * @param takerAssetData The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * - * @return An object that conforms to LiquidityForAssetData that satisfies the request. See type definition for more information. + * @return An object that conforms to LiquidityForTakerMakerAssetDataPair that satisfies the request. See type definition for more information. */ public async getLiquidityForMakerTakerAssetDataPairAsync( makerAssetData: string, takerAssetData: string, - ): Promise { + ): Promise { assert.isString('makerAssetData', makerAssetData); assert.isString('takerAssetData', takerAssetData); - assetDataUtils.decodeAssetDataOrThrow(makerAssetData); - assetDataUtils.decodeAssetDataOrThrow(takerAssetData); - + await this._devUtilsContract.revertIfInvalidAssetData(takerAssetData).callAsync(); + await this._devUtilsContract.revertIfInvalidAssetData(makerAssetData).callAsync(); const assetPairs = await this.getAvailableMakerAssetDatasAsync(takerAssetData); if (!assetPairs.includes(makerAssetData)) { return { - makerTokensAvailableInBaseUnits: new BigNumber(0), - takerTokensAvailableInBaseUnits: new BigNumber(0), + makerAssetAvailableInBaseUnits: new BigNumber(0), + takerAssetAvailableInBaseUnits: new BigNumber(0), }; } - const ordersAndFillableAmounts = await this.getOrdersAndFillableAmountsAsync(makerAssetData, takerAssetData); - return calculateLiquidity(ordersAndFillableAmounts); + const prunedOrders = await this.getPrunedSignedOrdersAsync(makerAssetData, takerAssetData); + return calculateLiquidity(prunedOrders); } /** @@ -299,7 +304,7 @@ export class SwapQuoter { */ public async getAvailableTakerAssetDatasAsync(makerAssetData: string): Promise { assert.isString('makerAssetData', makerAssetData); - assetDataUtils.decodeAssetDataOrThrow(makerAssetData); + await this._devUtilsContract.revertIfInvalidAssetData(makerAssetData).callAsync(); const allAssetPairs = await this.orderbook.getAvailableAssetDatasAsync(); const assetPairs = allAssetPairs .filter(pair => pair.assetDataA.assetData === makerAssetData) @@ -314,7 +319,7 @@ export class SwapQuoter { */ public async getAvailableMakerAssetDatasAsync(takerAssetData: string): Promise { assert.isString('takerAssetData', takerAssetData); - assetDataUtils.decodeAssetDataOrThrow(takerAssetData); + await this._devUtilsContract.revertIfInvalidAssetData(takerAssetData).callAsync(); const allAssetPairs = await this.orderbook.getAvailableAssetDatasAsync(); const assetPairs = allAssetPairs .filter(pair => pair.assetDataB.assetData === takerAssetData) @@ -324,6 +329,8 @@ export class SwapQuoter { /** * Validates the taker + maker asset pair is available from the order provider provided to `SwapQuote`. + * @param makerAssetData The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). + * @param takerAssetData The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * * @return A boolean on if the taker, maker pair exists */ @@ -333,41 +340,31 @@ export class SwapQuoter { ): Promise { assert.isString('makerAssetData', makerAssetData); assert.isString('takerAssetData', takerAssetData); - assetDataUtils.decodeAssetDataOrThrow(makerAssetData); - assetDataUtils.decodeAssetDataOrThrow(takerAssetData); + await this._devUtilsContract.revertIfInvalidAssetData(makerAssetData).callAsync(); + await this._devUtilsContract.revertIfInvalidAssetData(takerAssetData).callAsync(); const availableMakerAssetDatas = await this.getAvailableMakerAssetDatasAsync(takerAssetData); return _.includes(availableMakerAssetDatas, makerAssetData); } /** - * Grab orders from the map, if there is a miss or it is time to refresh, fetch and process the orders + * Grab orders from the order provider, prunes for valid orders with provided OrderPruner options * @param makerAssetData The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * @param takerAssetData The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). */ - public async getOrdersAndFillableAmountsAsync( + public async getPrunedSignedOrdersAsync( makerAssetData: string, takerAssetData: string, - ): Promise { + ): Promise { assert.isString('makerAssetData', makerAssetData); assert.isString('takerAssetData', takerAssetData); - assetDataUtils.decodeAssetDataOrThrow(makerAssetData); - assetDataUtils.decodeAssetDataOrThrow(takerAssetData); - const zrxTokenAssetData = await this._getZrxTokenAssetDataOrThrowAsync(); + await this._devUtilsContract.revertIfInvalidAssetData(takerAssetData).callAsync(); + await this._devUtilsContract.revertIfInvalidAssetData(makerAssetData).callAsync(); // get orders - const response = await this.orderbook.getOrdersAsync(makerAssetData, takerAssetData); - const adaptedResponse = { orders: response.map(o => ({ ...o.order, ...o.metaData })) }; - // 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(adaptedResponse, makerAssetData, takerAssetData); - // process the responses into one object - const isMakerAssetZrxToken = makerAssetData === zrxTokenAssetData; - const ordersAndFillableAmounts = await orderProviderResponseProcessor.processAsync( - adaptedResponse, - isMakerAssetZrxToken, - this.expiryBufferMs, - this._contractWrappers.orderValidator, - ); - return ordersAndFillableAmounts; + const apiOrders = await this.orderbook.getOrdersAsync(makerAssetData, takerAssetData); + const orders = _.map(apiOrders, o => o.order); + const prunedOrders = await this._orderPruner.pruneSignedOrdersAsync(orders); + const sortedPrunedOrders = sortingUtils.sortOrders(prunedOrders); + return sortedPrunedOrders; } /** @@ -375,18 +372,16 @@ export class SwapQuoter { * @param swapQuote The swapQuote in question to check enough allowance enabled for 0x exchange contracts to conduct the swap. * @param takerAddress The address of the taker of the provided swapQuote */ - public async isTakerAddressAllowanceEnoughForBestAndWorstQuoteInfoAsync( + public async isSwapQuoteFillableByTakerAddressAsync( swapQuote: SwapQuote, takerAddress: string, ): Promise<[boolean, boolean]> { - const orderValidator = this._contractWrappers.orderValidator; - const balanceAndAllowance = await orderValidator - .getBalanceAndAllowance(takerAddress, swapQuote.takerAssetData) + const balanceAndAllowance = await this._devUtilsContract + .getBalanceAndAssetProxyAllowance(takerAddress, swapQuote.takerAssetData) .callAsync(); - const allowance = balanceAndAllowance[1]; return [ - allowance.isGreaterThanOrEqualTo(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount), - allowance.isGreaterThanOrEqualTo(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount), + balanceAndAllowance[1].isGreaterThanOrEqualTo(swapQuote.bestCaseQuoteInfo.totalTakerAssetAmount), + balanceAndAllowance[1].isGreaterThanOrEqualTo(swapQuote.worstCaseQuoteInfo.totalTakerAssetAmount), ]; } @@ -398,13 +393,10 @@ export class SwapQuoter { } /** - * Get the assetData that represents the ZRX token. - * Will throw if ZRX does not exist for the current chain. + * Utility function to get assetData for Ether token. */ - private async _getZrxTokenAssetDataOrThrowAsync(): Promise { - return this._contractWrappers.devUtils - .encodeERC20AssetData(this._contractWrappers.contractAddresses.zrxToken) - .callAsync(); + public async getEtherTokenAssetDataOrThrowAsync(): Promise { + return this._devUtilsContract.encodeERC20AssetData(this._contractAddresses.etherToken).callAsync(); } /** @@ -417,30 +409,20 @@ export class SwapQuoter { marketOperation: MarketOperation, options: Partial, ): Promise { - const { slippagePercentage, shouldDisableRequestingFeeOrders } = _.merge( - {}, - constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS, - options, - ); + const { slippagePercentage } = _.merge({}, constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS, options); assert.isString('makerAssetData', makerAssetData); assert.isString('takerAssetData', takerAssetData); assert.isNumber('slippagePercentage', slippagePercentage); - const zrxTokenAssetData = await this._getZrxTokenAssetDataOrThrowAsync(); - const isMakerAssetZrxToken = makerAssetData === zrxTokenAssetData; - // get the relevant orders for the makerAsset - const ordersAndFillableAmounts = await this.getOrdersAndFillableAmountsAsync(makerAssetData, takerAssetData); - const doesOrdersRequireFeeOrders = - !isMakerAssetZrxToken && utils.isFeeOrdersRequiredToFillOrders(ordersAndFillableAmounts); - const isRequestingFeeOrders = !shouldDisableRequestingFeeOrders && doesOrdersRequireFeeOrders; - let feeOrdersAndFillableAmounts = constants.EMPTY_ORDERS_AND_FILLABLE_AMOUNTS; - if (isRequestingFeeOrders) { - feeOrdersAndFillableAmounts = await this.getOrdersAndFillableAmountsAsync( - zrxTokenAssetData, - takerAssetData, - ); + let gasPrice: BigNumber; + if (!!options.gasPrice) { + gasPrice = options.gasPrice; + assert.isBigNumber('gasPrice', gasPrice); + } else { + gasPrice = await protocolFeeUtils.getGasPriceEstimationOrThrowAsync(); } - - if (ordersAndFillableAmounts.orders.length === 0) { + // get the relevant orders for the makerAsset + const prunedOrders = await this.getPrunedSignedOrdersAsync(makerAssetData, takerAssetData); + if (prunedOrders.length === 0) { throw new Error( `${ SwapQuoterError.AssetUnavailable @@ -448,33 +430,21 @@ export class SwapQuoter { ); } - if (isRequestingFeeOrders && feeOrdersAndFillableAmounts.orders.length === 0) { - throw new Error( - `${ - SwapQuoterError.FeeAssetUnavailable - }: For makerAssetdata ${makerAssetData} and takerAssetdata ${takerAssetData}`, - ); - } - let swapQuote: SwapQuote; if (marketOperation === MarketOperation.Buy) { swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote( - ordersAndFillableAmounts, - feeOrdersAndFillableAmounts, + prunedOrders, assetFillAmount, slippagePercentage, - isMakerAssetZrxToken, - shouldDisableRequestingFeeOrders, + gasPrice, ); } else { swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote( - ordersAndFillableAmounts, - feeOrdersAndFillableAmounts, + prunedOrders, assetFillAmount, slippagePercentage, - isMakerAssetZrxToken, - shouldDisableRequestingFeeOrders, + gasPrice, ); } diff --git a/packages/asset-swapper/src/types.ts b/packages/asset-swapper/src/types.ts index 1015b3d243..771157cf47 100644 --- a/packages/asset-swapper/src/types.ts +++ b/packages/asset-swapper/src/types.ts @@ -1,7 +1,27 @@ -import { MarketOperation, SignedOrder } from '@0x/types'; +import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { MethodAbi } from 'ethereum-types'; +/** + * expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m). + * permittedOrderFeeTypes: A set of all the takerFee types that OrderPruner will filter for + */ +export interface OrderPrunerOpts { + expiryBufferMs: number; + permittedOrderFeeTypes: Set; +} + +/** + * Represents the on-chain metadata of a signed order + */ +export interface OrderPrunerOnChainMetadata { + orderStatus: number; + orderHash: string; + orderTakerAssetFilledAmount: BigNumber; + fillableTakerAssetAmount: BigNumber; + isValidSignature: boolean; +} + /** * makerAssetData: The assetData representing the desired makerAsset. * takerAssetData: The assetData representing the desired takerAsset. @@ -12,18 +32,14 @@ export interface OrderProviderRequest { } /** - * orders: An array of orders with optional remaining fillable makerAsset amounts. See type for more info. + * fillableMakerAssetAmount: Amount of makerAsset that is fillable + * fillableTakerAssetAmount: Amount of takerAsset that is fillable + * fillableTakerFeeAmount: Amount of takerFee paid to fill fillableTakerAssetAmount */ -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; +export interface PrunedSignedOrder extends SignedOrder { + fillableMakerAssetAmount: BigNumber; + fillableTakerAssetAmount: BigNumber; + fillableTakerFeeAmount: BigNumber; } /** @@ -31,13 +47,13 @@ export interface SignedOrderWithRemainingFillableMakerAssetAmount extends Signed * calldataHexString: The hexstring of the calldata. * methodAbi: The ABI of the smart contract method to call. * toAddress: The contract address to call. - * ethAmount: If provided, the eth amount in wei to send with the smart contract call. + * ethAmount: The eth amount in wei to send with the smart contract call. */ export interface CalldataInfo { calldataHexString: string; methodAbi: MethodAbi; toAddress: string; - ethAmount?: BigNumber; + ethAmount: BigNumber; } /** @@ -50,7 +66,7 @@ export interface CalldataInfo { export interface SmartContractParamsInfo { params: T; toAddress: string; - ethAmount?: BigNumber; + ethAmount: BigNumber; methodAbi: MethodAbi; } @@ -95,14 +111,10 @@ export enum ExtensionContractType { export type ExchangeSmartContractParams = ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams; /** - * feeOrders: An array of objects conforming to SignedOrder. These orders can be used to cover the fees for the orders param above. - * feeSignatures: An array of signatures that attest that the maker of the orders in fact made the orders. * feePercentage: Optional affiliate fee percentage used to calculate the eth amount paid to fee recipient. * feeRecipient: The address where affiliate fees are sent. Defaults to null address (0x000...000). */ export interface ForwarderSmartContractParamsBase { - feeOrders: SignedOrder[]; - feeSignatures: string[]; feePercentage: BigNumber; feeRecipient: string; } @@ -137,12 +149,12 @@ export type SmartContractParams = ForwarderSmartContractParams | ExchangeSmartCo * executeSwapQuoteOrThrowAsync: Executes a web3 transaction to swap for tokens with provided SwapQuote. Throws if invalid SwapQuote is provided. */ export interface SwapQuoteConsumerBase { - getCalldataOrThrowAsync(quote: SwapQuote, opts: Partial): Promise; + getCalldataOrThrowAsync(quote: SwapQuote, opts: Partial): Promise; getSmartContractParamsOrThrowAsync( quote: SwapQuote, - opts: Partial, + opts: Partial, ): Promise>; - executeSwapQuoteOrThrowAsync(quote: SwapQuote, opts: Partial): Promise; + executeSwapQuoteOrThrowAsync(quote: SwapQuote, opts: Partial): Promise; } /** @@ -155,28 +167,37 @@ export interface SwapQuoteConsumerOpts { /** * Represents the options provided to a generic SwapQuoteConsumer */ -export interface SwapQuoteGetOutputOptsBase {} +export interface SwapQuoteGetOutputOpts {} /** * 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 + * ethAmount: The amount of eth sent with the execution of a swap */ -export interface SwapQuoteExecutionOptsBase extends SwapQuoteGetOutputOptsBase { +export interface SwapQuoteExecutionOpts extends SwapQuoteGetOutputOpts { takerAddress?: string; gasLimit?: number; gasPrice?: BigNumber; + ethAmount?: BigNumber; } /** + * ethAmount: The amount of eth (in Wei) sent to the forwarder contract. * feePercentage: percentage (up to 5%) of the taker asset paid to feeRecipient * feeRecipient: address of the receiver of the feePercentage of taker asset - * ethAmount: The amount of eth (in Wei) sent to the forwarder contract. */ -export interface ForwarderSwapQuoteGetOutputOpts extends SwapQuoteGetOutputOptsBase { +export interface ForwarderExtensionContractOpts { + ethAmount?: BigNumber; feePercentage: number; feeRecipient: string; - ethAmount?: BigNumber; +} + +/* + * Options for how SwapQuoteConsumer will generate output + */ +export interface SwapQuoteConsumingOpts { + useExtensionContract: ExtensionContractType; } export type SwapQuote = MarketBuySwapQuote | MarketSellSwapQuote; @@ -186,26 +207,10 @@ export interface GetExtensionContractTypeOpts { ethAmount?: BigNumber; } -/** - * takerAddress: The address to perform the buy. Defaults to the first available address from the provider. - * useConsumerType: If provided, defaults the SwapQuoteConsumer to create output consumed by ConsumerType. - */ -export interface SwapQuoteGetOutputOpts extends ForwarderSwapQuoteGetOutputOpts { - useExtensionContract: ExtensionContractType; -} - -export interface ForwarderSwapQuoteExecutionOpts extends ForwarderSwapQuoteGetOutputOpts, SwapQuoteExecutionOptsBase {} - -/** - * Represents the options for executing a swap quote with SwapQuoteConsumer - */ -export interface SwapQuoteExecutionOpts extends SwapQuoteGetOutputOpts, ForwarderSwapQuoteExecutionOpts {} - /** * takerAssetData: String that represents a specific taker asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * makerAssetData: String that represents a specific maker asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * orders: An array of objects conforming to SignedOrder. These orders can be used to cover the requested assetBuyAmount plus slippage. - * feeOrders: An array of objects conforming to SignedOrder. These orders can be used to cover the fees for the orders param above. * bestCaseQuoteInfo: Info about the best case price for the asset. * worstCaseQuoteInfo: Info about the worst case price for the asset. */ @@ -213,7 +218,6 @@ export interface SwapQuoteBase { takerAssetData: string; makerAssetData: string; orders: SignedOrder[]; - feeOrders: SignedOrder[]; bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; } @@ -236,36 +240,28 @@ export interface MarketBuySwapQuote extends SwapQuoteBase { type: MarketOperation.Buy; } -export interface SwapQuoteWithAffiliateFeeBase { - feePercentage: number; -} - -export interface MarketSellSwapQuoteWithAffiliateFee extends SwapQuoteWithAffiliateFeeBase, MarketSellSwapQuote {} - -export interface MarketBuySwapQuoteWithAffiliateFee extends SwapQuoteWithAffiliateFeeBase, MarketBuySwapQuote {} - -export type SwapQuoteWithAffiliateFee = MarketBuySwapQuoteWithAffiliateFee | MarketSellSwapQuoteWithAffiliateFee; - /** - * feeTakerTokenAmount: The amount of takerToken required any fee concerned with completing the swap. - * takerTokenAmount: The amount of takerToken required to conduct the swap. - * totalTakerTokenAmount: The total amount of takerToken required to complete the swap (filling orders, feeOrders, and paying affiliate fee) - * makerTokenAmount: The amount of makerToken that will be acquired through the swap. + * feeTakerAssetAmount: The amount of takerAsset reserved for paying takerFees when swapping for desired assets. + * takerAssetAmount: The amount of takerAsset swapped for desired makerAsset. + * totalTakerAssetAmount: The total amount of takerAsset required to complete the swap (filling orders, and paying takerFees). + * makerAssetAmount: The amount of makerAsset that will be acquired through the swap. + * protocolFeeInEthAmount: The amount of eth to pay as protocol fee to perform the swap for desired asset. */ export interface SwapQuoteInfo { - feeTakerTokenAmount: BigNumber; - totalTakerTokenAmount: BigNumber; - takerTokenAmount: BigNumber; - makerTokenAmount: BigNumber; + feeTakerAssetAmount: BigNumber; + takerAssetAmount: BigNumber; + totalTakerAssetAmount: BigNumber; + makerAssetAmount: BigNumber; + protocolFeeInEthAmount: BigNumber; } /** - * shouldDisableRequestingFeeOrders: If set to true, requesting a swapQuote will not perform any computation or requests for fees. * slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.2 (20%). + * gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount */ export interface SwapQuoteRequestOpts { - shouldDisableRequestingFeeOrders: boolean; slippagePercentage: number; + gasPrice?: BigNumber; } /** @@ -273,7 +269,7 @@ export interface SwapQuoteRequestOpts { * orderRefreshIntervalMs: The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s). * expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m). */ -export interface SwapQuoterOpts { +export interface SwapQuoterOpts extends OrderPrunerOpts { chainId: number; orderRefreshIntervalMs: number; expiryBufferMs: number; @@ -295,28 +291,33 @@ export enum SwapQuoteConsumerError { */ export enum SwapQuoterError { 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', - InvalidOrderProviderResponse = 'INVALID_ORDER_PROVIDER_RESPONSE', AssetUnavailable = 'ASSET_UNAVAILABLE', - FeeAssetUnavailable = 'FEE_ASSET_UNAVAILABLE', + NoGasPriceProvidedOrEstimated = 'NO_GAS_PRICE_PROVIDED_OR_ESTIMATED', } /** - * 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. + * Represents available liquidity for a given assetData. */ -export interface OrdersAndFillableAmounts { - orders: SignedOrder[]; - remainingFillableMakerAssetAmounts: BigNumber[]; +export interface LiquidityForTakerMakerAssetDataPair { + makerAssetAvailableInBaseUnits: BigNumber; + takerAssetAvailableInBaseUnits: BigNumber; } /** - * Represents available liquidity for a given assetData + * Represents two main market operations supported by asset-swapper. */ -export interface LiquidityForAssetData { - makerTokensAvailableInBaseUnits: BigNumber; - takerTokensAvailableInBaseUnits: BigNumber; +export enum MarketOperation { + Sell = 'Sell', + Buy = 'Buy', +} + +/** + * Represents varying order takerFee types that can be pruned for by OrderPruner. + */ +export enum OrderPrunerPermittedFeeTypes { + NoFees = 'NO_FEES', + MakerDenominatedTakerFee = 'MAKER_DENOMINATED_TAKER_FEE', + TakerDenominatedTakerFee = 'TAKER_DENOMINATED_TAKER_FEE', } diff --git a/packages/asset-swapper/src/utils/affiliate_fee_utils.ts b/packages/asset-swapper/src/utils/affiliate_fee_utils.ts index 7276a8dd92..9d595ac339 100644 --- a/packages/asset-swapper/src/utils/affiliate_fee_utils.ts +++ b/packages/asset-swapper/src/utils/affiliate_fee_utils.ts @@ -1,29 +1,24 @@ import { BigNumber } from '@0x/utils'; -import * as _ from 'lodash'; -import { SwapQuote, SwapQuoteInfo, SwapQuoteWithAffiliateFee } from '../types'; +import { constants } from '../constants'; +import { SwapQuoteInfo } from '../types'; + +import { assert } from './assert'; export const affiliateFeeUtils = { - getSwapQuoteWithAffiliateFee(quote: SwapQuote, feePercentage: number): SwapQuoteWithAffiliateFee { - const newQuote = _.clone(quote); - newQuote.bestCaseQuoteInfo = getSwapQuoteInfoWithAffiliateFee(newQuote.bestCaseQuoteInfo, feePercentage); - newQuote.worstCaseQuoteInfo = getSwapQuoteInfoWithAffiliateFee(newQuote.worstCaseQuoteInfo, feePercentage); - return { ...newQuote, ...{ feePercentage } }; + /** + * Get the amount of eth to send for a forwarder contract call (includes takerAssetAmount, protocol fees, and specified affiliate fee amount) + * @param swapQuoteInfo SwapQuoteInfo to generate total eth amount from + * @param feePercentage Percentage of additive fees to apply to totalTakerAssetAmount + protocol fee. (max 5%) + */ + getTotalEthAmountWithAffiliateFee(swapQuoteInfo: SwapQuoteInfo, feePercentage: number): BigNumber { + assert.assert( + feePercentage >= 0 && feePercentage <= constants.MAX_AFFILIATE_FEE_PERCENTAGE, + 'feePercentage must be between range 0-0.05 (inclusive)', + ); + const ethAmount = swapQuoteInfo.protocolFeeInEthAmount.plus(swapQuoteInfo.totalTakerAssetAmount); + const affiliateFeeAmount = ethAmount.multipliedBy(feePercentage); + const ethAmountWithFees = ethAmount.plus(affiliateFeeAmount); + return ethAmountWithFees; }, }; - -/** - * Adds a fee based on feePercentage of the takerTokenAmount and adds it to the feeTakerTokenAmount and totalTakerTokenAmount - * @param quoteInfo quote information to add fee to - * @param feePercentage the percentage of takerTokenAmount charged additionally as a fee - */ -const getSwapQuoteInfoWithAffiliateFee = (quoteInfo: SwapQuoteInfo, feePercentage: number): SwapQuoteInfo => { - const newQuoteInfo = _.clone(quoteInfo); - const affiliateFeeAmount = quoteInfo.takerTokenAmount - .multipliedBy(feePercentage) - .integerValue(BigNumber.ROUND_CEIL); - const newFeeAmount = quoteInfo.feeTakerTokenAmount.plus(affiliateFeeAmount); - newQuoteInfo.feeTakerTokenAmount = newFeeAmount; - newQuoteInfo.totalTakerTokenAmount = newFeeAmount.plus(quoteInfo.takerTokenAmount); - return newQuoteInfo; -}; diff --git a/packages/asset-swapper/src/utils/assert.ts b/packages/asset-swapper/src/utils/assert.ts index 1840d13913..99e2626cb0 100644 --- a/packages/asset-swapper/src/utils/assert.ts +++ b/packages/asset-swapper/src/utils/assert.ts @@ -1,10 +1,12 @@ import { assert as sharedAssert } from '@0x/assert'; import { schemas } from '@0x/json-schemas'; import { Orderbook } from '@0x/orderbook'; -import { MarketOperation, SignedOrder } from '@0x/types'; +import { Order, SignedOrder } from '@0x/types'; import * as _ from 'lodash'; -import { OrderProviderRequest, SwapQuote, SwapQuoteInfo } from '../types'; +import { MarketOperation, OrderProviderRequest, SwapQuote, SwapQuoteInfo } from '../types'; + +import { utils } from './utils'; export const assert = { ...sharedAssert, @@ -12,8 +14,7 @@ export const assert = { sharedAssert.isHexString(`${variableName}.takerAssetData`, swapQuote.takerAssetData); sharedAssert.isHexString(`${variableName}.makerAssetData`, swapQuote.makerAssetData); sharedAssert.doesConformToSchema(`${variableName}.orders`, swapQuote.orders, schemas.signedOrdersSchema); - sharedAssert.doesConformToSchema(`${variableName}.feeOrders`, swapQuote.feeOrders, schemas.signedOrdersSchema); - assert.isValidOrdersForSwapQuote( + assert.isValidSwapQuoteOrders( `${variableName}.orders`, swapQuote.orders, swapQuote.makerAssetData, @@ -27,7 +28,7 @@ export const assert = { sharedAssert.isBigNumber(`${variableName}.takerAssetFillAmount`, swapQuote.takerAssetFillAmount); } }, - isValidOrdersForSwapQuote( + isValidSwapQuoteOrders( variableName: string, orders: SignedOrder[], makerAssetData: string, @@ -48,10 +49,21 @@ export const assert = { ); }); }, + isValidOrdersForSwapQuoter(variableName: string, orders: T[]): void { + _.every(orders, (order: T, index: number) => { + assert.assert( + order.takerFee.isZero() || + utils.isOrderTakerFeePayableWithTakerAsset(order) || + utils.isOrderTakerFeePayableWithMakerAsset(order), + `Expected ${variableName}[${index}].takerFeeAssetData to be ${order.makerAssetData} or ${ + order.takerAssetData + } but found ${order.takerFeeAssetData}`, + ); + }); + }, isValidForwarderSwapQuote(variableName: string, swapQuote: SwapQuote, wethAssetData: string): void { assert.isValidSwapQuote(variableName, swapQuote); assert.isValidForwarderSignedOrders(`${variableName}.orders`, swapQuote.orders, wethAssetData); - assert.isValidForwarderSignedOrders(`${variableName}.feeOrders`, swapQuote.feeOrders, wethAssetData); }, isValidForwarderSignedOrders(variableName: string, orders: SignedOrder[], wethAssetData: string): void { _.forEach(orders, (o: SignedOrder, i: number) => { @@ -65,10 +77,10 @@ export const assert = { ); }, isValidSwapQuoteInfo(variableName: string, swapQuoteInfo: SwapQuoteInfo): void { - sharedAssert.isBigNumber(`${variableName}.feeTakerTokenAmount`, swapQuoteInfo.feeTakerTokenAmount); - sharedAssert.isBigNumber(`${variableName}.totalTakerTokenAmount`, swapQuoteInfo.totalTakerTokenAmount); - sharedAssert.isBigNumber(`${variableName}.takerTokenAmount`, swapQuoteInfo.takerTokenAmount); - sharedAssert.isBigNumber(`${variableName}.takerTokenAmount`, swapQuoteInfo.makerTokenAmount); + sharedAssert.isBigNumber(`${variableName}.feeTakerAssetAmount`, swapQuoteInfo.feeTakerAssetAmount); + sharedAssert.isBigNumber(`${variableName}.totalTakerAssetAmount`, swapQuoteInfo.totalTakerAssetAmount); + sharedAssert.isBigNumber(`${variableName}.takerAssetAmount`, swapQuoteInfo.takerAssetAmount); + sharedAssert.isBigNumber(`${variableName}.makerAssetAmount`, swapQuoteInfo.makerAssetAmount); }, isValidOrderbook(variableName: string, orderFetcher: Orderbook): void { sharedAssert.isFunction(`${variableName}.getOrdersAsync`, orderFetcher.getOrdersAsync); diff --git a/packages/asset-swapper/src/utils/calculate_liquidity.ts b/packages/asset-swapper/src/utils/calculate_liquidity.ts index 45a881961d..4cf1711cf0 100644 --- a/packages/asset-swapper/src/utils/calculate_liquidity.ts +++ b/packages/asset-swapper/src/utils/calculate_liquidity.ts @@ -1,40 +1,27 @@ -import { orderCalculationUtils } from '@0x/order-utils'; import { BigNumber } from '@0x/utils'; -import { LiquidityForAssetData, OrdersAndFillableAmounts } from '../types'; +import { LiquidityForTakerMakerAssetDataPair, PrunedSignedOrder } 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}`); - } +import { utils } from './utils'; - const makerTokensAvailableForCurrentOrder = availableMakerAssetAmount; - const takerTokensAvailableForCurrentOrder = orderCalculationUtils.getTakerFillAmount( - order, - makerTokensAvailableForCurrentOrder, - ); +export const calculateLiquidity = (prunedOrders: PrunedSignedOrder[]): LiquidityForTakerMakerAssetDataPair => { + const liquidityInBigNumbers = prunedOrders.reduce( + (acc, order) => { + const fillableMakerAssetAmount = utils.isOrderTakerFeePayableWithMakerAsset(order) + ? order.fillableMakerAssetAmount.minus(order.fillableTakerFeeAmount) + : order.fillableMakerAssetAmount; + const fillableTakerAssetAmount = utils.isOrderTakerFeePayableWithTakerAsset(order) + ? order.fillableTakerAssetAmount.plus(order.fillableTakerFeeAmount) + : order.fillableTakerAssetAmount; return { - makerTokensAvailableInBaseUnits: acc.makerTokensAvailableInBaseUnits.plus( - makerTokensAvailableForCurrentOrder, - ), - takerTokensAvailableInBaseUnits: acc.takerTokensAvailableInBaseUnits.plus( - takerTokensAvailableForCurrentOrder, - ), + makerAssetAvailableInBaseUnits: acc.makerAssetAvailableInBaseUnits.plus(fillableMakerAssetAmount), + takerAssetAvailableInBaseUnits: acc.takerAssetAvailableInBaseUnits.plus(fillableTakerAssetAmount), }; }, { - makerTokensAvailableInBaseUnits: new BigNumber(0), - takerTokensAvailableInBaseUnits: new BigNumber(0), + makerAssetAvailableInBaseUnits: new BigNumber(0), + takerAssetAvailableInBaseUnits: new BigNumber(0), }, ); - - // Turn into regular numbers - return { - makerTokensAvailableInBaseUnits: liquidityInBigNumbers.makerTokensAvailableInBaseUnits, - takerTokensAvailableInBaseUnits: liquidityInBigNumbers.takerTokensAvailableInBaseUnits, - }; + return liquidityInBigNumbers; }; diff --git a/packages/asset-swapper/src/utils/market_utils.ts b/packages/asset-swapper/src/utils/market_utils.ts new file mode 100644 index 0000000000..830f9da922 --- /dev/null +++ b/packages/asset-swapper/src/utils/market_utils.ts @@ -0,0 +1,92 @@ +import { BigNumber } from '@0x/utils'; +import * as _ from 'lodash'; + +import { constants } from '../constants'; +import { MarketOperation, PrunedSignedOrder } from '../types'; + +import { assert } from './assert'; +import { utils } from './utils'; + +export const marketUtils = { + findOrdersThatCoverTakerAssetFillAmount( + sortedOrders: PrunedSignedOrder[], + takerAssetFillAmount: BigNumber, + slippageBufferAmount: BigNumber = new BigNumber(0), + ): { resultOrders: PrunedSignedOrder[]; remainingFillAmount: BigNumber } { + return findOrdersThatCoverAssetFillAmount( + sortedOrders, + takerAssetFillAmount, + MarketOperation.Sell, + slippageBufferAmount, + ); + }, + findOrdersThatCoverMakerAssetFillAmount( + sortedOrders: PrunedSignedOrder[], + makerAssetFillAmount: BigNumber, + slippageBufferAmount: BigNumber = new BigNumber(0), + ): { resultOrders: PrunedSignedOrder[]; remainingFillAmount: BigNumber } { + return findOrdersThatCoverAssetFillAmount( + sortedOrders, + makerAssetFillAmount, + MarketOperation.Buy, + slippageBufferAmount, + ); + }, +}; + +function findOrdersThatCoverAssetFillAmount( + sortedOrders: PrunedSignedOrder[], + assetFillAmount: BigNumber, + operation: MarketOperation, + slippageBufferAmount: BigNumber, +): { resultOrders: PrunedSignedOrder[]; remainingFillAmount: BigNumber } { + assert.isValidBaseUnitAmount('slippageBufferAmount', slippageBufferAmount); + // calculate total amount of asset needed to be filled + const totalFillAmount = assetFillAmount.plus(slippageBufferAmount); + // iterate through the orders input from left to right until we have enough makerAsset to fill totalFillAmount + const result = _.reduce( + sortedOrders, + ({ resultOrders, remainingFillAmount }, order) => { + if (remainingFillAmount.isLessThanOrEqualTo(constants.ZERO_AMOUNT)) { + return { + resultOrders, + remainingFillAmount: constants.ZERO_AMOUNT, + }; + } else { + const assetAmountAvailable = getAssetAmountAvailable(order, operation); + const shouldIncludeOrder = assetAmountAvailable.gt(constants.ZERO_AMOUNT); + // if there is no assetAmountAvailable do not append order to resultOrders + // if we have exceeded the total amount we want to fill set remainingFillAmount to 0 + return { + resultOrders: shouldIncludeOrder ? _.concat(resultOrders, order) : resultOrders, + remainingFillAmount: BigNumber.max( + constants.ZERO_AMOUNT, + remainingFillAmount.minus(assetAmountAvailable), + ), + }; + } + }, + { + resultOrders: [] as PrunedSignedOrder[], + remainingFillAmount: totalFillAmount, + }, + ); + + return result; +} + +function getAssetAmountAvailable(order: PrunedSignedOrder, operation: MarketOperation): BigNumber { + if (operation === MarketOperation.Buy) { + if (utils.isOrderTakerFeePayableWithMakerAsset(order)) { + return order.fillableMakerAssetAmount.minus(order.fillableTakerFeeAmount); + } else { + return order.fillableMakerAssetAmount; + } + } else { + if (utils.isOrderTakerFeePayableWithTakerAsset(order)) { + return order.fillableTakerAssetAmount.plus(order.fillableTakerFeeAmount); + } else { + return order.fillableTakerAssetAmount; + } + } +} diff --git a/packages/asset-swapper/src/utils/order_provider_response_processor.ts b/packages/asset-swapper/src/utils/order_provider_response_processor.ts deleted file mode 100644 index 92442fa52c..0000000000 --- a/packages/asset-swapper/src/utils/order_provider_response_processor.ts +++ /dev/null @@ -1,182 +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 { - OrderProviderResponse, - OrdersAndFillableAmounts, - SignedOrderWithRemainingFillableMakerAssetAmount, - SwapQuoterError, -} from '../types'; - -export const orderProviderResponseProcessor = { - throwIfInvalidResponse(response: OrderProviderResponse, makerAssetData: string, takerAssetData: string): void { - _.forEach(response.orders, order => { - if (order.makerAssetData !== makerAssetData || order.takerAssetData !== takerAssetData) { - throw new Error(SwapQuoterError.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 an orderValidator is provided, attempt to grab fillable amounts from on-chain otherwise assume completely fillable - * - Sort by rate - */ - async processAsync( - orderProviderResponse: OrderProviderResponse, - isMakerAssetZrxToken: boolean, - expiryBufferMs: number, - orderValidator?: OrderValidatorContract, - ): Promise { - // drop orders that are expired or not open - const filteredOrders = filterOutExpiredAndNonOpenOrders( - orderProviderResponse.orders, - expiryBufferMs / constants.ONE_SECOND_MS, - ); - // 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: any[] = ordersInfo.map((orderInfo, index) => { - const singleOrderAndTraderInfo = { - orderInfo, - traderInfo: tradersInfo[index], - }; - return singleOrderAndTraderInfo; - }); - // 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[], - expiryBufferMs: number, -): SignedOrderWithRemainingFillableMakerAssetAmount[] { - const result = _.filter(orders, order => { - return ( - orderCalculationUtils.isOpenOrder(order) && - !orderCalculationUtils.willOrderExpire(order, expiryBufferMs / constants.ONE_SECOND_MS) - ); - }); - 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; -} diff --git a/packages/asset-swapper/src/utils/order_prune_utils.ts b/packages/asset-swapper/src/utils/order_prune_utils.ts new file mode 100644 index 0000000000..b5fbcdc580 --- /dev/null +++ b/packages/asset-swapper/src/utils/order_prune_utils.ts @@ -0,0 +1,96 @@ +import { DevUtilsContract } from '@0x/contracts-dev-utils'; +import { orderCalculationUtils } from '@0x/order-utils'; +import { OrderStatus, SignedOrder } from '@0x/types'; +import * as _ from 'lodash'; + +import { constants } from '../constants'; +import { OrderPrunerOnChainMetadata, OrderPrunerOpts, OrderPrunerPermittedFeeTypes, PrunedSignedOrder } from '../types'; +import { utils } from '../utils/utils'; + +export class OrderPruner { + public readonly expiryBufferMs: number; + public readonly permittedOrderFeeTypes: Set; + private readonly _devUtils: DevUtilsContract; + + // TODO(dave4506): OrderPruneCalculator can be more powerful if it takes in a specified takerAddress + constructor(devUtils: DevUtilsContract, opts: Partial = {}) { + const { expiryBufferMs, permittedOrderFeeTypes } = _.assign({}, constants.DEFAULT_ORDER_PRUNER_OPTS, opts); + + this.expiryBufferMs = expiryBufferMs; + this.permittedOrderFeeTypes = permittedOrderFeeTypes; + this._devUtils = devUtils; + } + + public async pruneSignedOrdersAsync(signedOrders: SignedOrder[]): Promise { + const unsortedOrders = this._filterForUsableOrders(signedOrders, this.expiryBufferMs); + + const signatures = _.map(unsortedOrders, o => o.signature); + const [ordersInfo, fillableTakerAssetAmounts, isValidSignatures] = await this._devUtils + .getOrderRelevantStates(unsortedOrders, signatures) + .callAsync(); + const ordersOnChainMetadata: OrderPrunerOnChainMetadata[] = ordersInfo.map((orderInfo, index) => { + return { + ...orderInfo, + fillableTakerAssetAmount: fillableTakerAssetAmounts[index], + isValidSignature: isValidSignatures[index], + }; + }); + // take orders + on chain information and find the valid orders and fillable makerAsset or takerAsset amounts + const prunedOrders = this._filterForFillableAndPermittedFeeTypeOrders(unsortedOrders, ordersOnChainMetadata); + + return prunedOrders; + } + + // tslint:disable-next-line: prefer-function-over-method + private _filterForFillableAndPermittedFeeTypeOrders( + orders: SignedOrder[], + ordersOnChainMetadata: OrderPrunerOnChainMetadata[], + ): PrunedSignedOrder[] { + const result = _.chain(orders) + .filter( + (order: SignedOrder, index: number): boolean => { + const { isValidSignature, orderStatus } = ordersOnChainMetadata[index]; + return ( + isValidSignature && + orderStatus === OrderStatus.Fillable && + ((this.permittedOrderFeeTypes.has(OrderPrunerPermittedFeeTypes.NoFees) && + order.takerFee.eq(constants.ZERO_AMOUNT)) || + (this.permittedOrderFeeTypes.has(OrderPrunerPermittedFeeTypes.TakerDenominatedTakerFee) && + utils.isOrderTakerFeePayableWithTakerAsset(order)) || + (this.permittedOrderFeeTypes.has(OrderPrunerPermittedFeeTypes.MakerDenominatedTakerFee) && + utils.isOrderTakerFeePayableWithMakerAsset(order))) + ); + }, + ) + .map( + (order: SignedOrder, index: number): PrunedSignedOrder => { + const { fillableTakerAssetAmount } = ordersOnChainMetadata[index]; + return { + ...order, + fillableTakerAssetAmount, + fillableMakerAssetAmount: orderCalculationUtils.getMakerFillAmount( + order, + fillableTakerAssetAmount, + ), + fillableTakerFeeAmount: orderCalculationUtils.getTakerFeeAmount( + order, + fillableTakerAssetAmount, + ), + }; + }, + ) + .value(); + return result; + } + + // tslint:disable-next-line: prefer-function-over-method + private _filterForUsableOrders(orders: SignedOrder[], expiryBufferMs: number): SignedOrder[] { + const result = _.filter(orders, order => { + return ( + orderCalculationUtils.isOpenOrder(order) && + !orderCalculationUtils.willOrderExpire(order, expiryBufferMs / constants.ONE_SECOND_MS) + ); + }); + return result; + } +} diff --git a/packages/asset-swapper/src/utils/protocol_fee_utils.ts b/packages/asset-swapper/src/utils/protocol_fee_utils.ts new file mode 100644 index 0000000000..784d5209ae --- /dev/null +++ b/packages/asset-swapper/src/utils/protocol_fee_utils.ts @@ -0,0 +1,31 @@ +import { Order } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import * as _ from 'lodash'; + +import { constants } from '../constants'; +import { SwapQuoterError } from '../types'; + +// tslint:disable:no-unnecessary-type-assertion +export const protocolFeeUtils = { + /** + * Gets 'fast' gas price from Eth Gas Station. + */ + async getGasPriceEstimationOrThrowAsync(): Promise { + try { + const res = await fetch(`${constants.ETH_GAS_STATION_API_BASE_URL}/json/ethgasAPI.json`); + const gasInfo = await res.json(); + // Eth Gas Station result is gwei * 10 + // tslint:disable-next-line:custom-no-magic-numbers + return new BigNumber(gasInfo.fast / 10); + } catch (e) { + throw new Error(SwapQuoterError.NoGasPriceProvidedOrEstimated); + } + }, + /** + * Calculates protocol fee with protofol fee multiplier for each fill. + */ + calculateWorstCaseProtocolFee(orders: T[], gasPrice: BigNumber): BigNumber { + const protocolFee = new BigNumber(orders.length * constants.PROTOCOL_FEE_MULTIPLIER).times(gasPrice); + return protocolFee; + }, +}; diff --git a/packages/asset-swapper/src/utils/sorting_utils.ts b/packages/asset-swapper/src/utils/sorting_utils.ts new file mode 100644 index 0000000000..4f41944379 --- /dev/null +++ b/packages/asset-swapper/src/utils/sorting_utils.ts @@ -0,0 +1,29 @@ +import { schemas } from '@0x/json-schemas'; +import { Order } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import * as _ from 'lodash'; + +import { assert } from './assert'; +import { utils } from './utils'; + +export const sortingUtils = { + sortOrders(orders: T[]): T[] { + assert.doesConformToSchema('orders', orders, schemas.ordersSchema); + assert.isValidOrdersForSwapQuoter('orders', orders); + const copiedOrders = _.cloneDeep(orders); + copiedOrders.sort((firstOrder, secondOrder) => { + const firstOrderRate = getTakerFeeAdjustedRateOfOrder(firstOrder); + const secondOrderRate = getTakerFeeAdjustedRateOfOrder(secondOrder); + return firstOrderRate.comparedTo(secondOrderRate); + }); + return copiedOrders; + }, +}; + +function getTakerFeeAdjustedRateOfOrder(order: Order): BigNumber { + const [adjustedMakerAssetAmount, adjustedTakerAssetAmount] = utils.getAdjustedMakerAndTakerAmountsFromTakerFees( + order, + ); + const rate = adjustedTakerAssetAmount.div(adjustedMakerAssetAmount); + return rate; +} diff --git a/packages/asset-swapper/src/utils/swap_quote_calculator.ts b/packages/asset-swapper/src/utils/swap_quote_calculator.ts index 9ae521f355..ec2ac25981 100644 --- a/packages/asset-swapper/src/utils/swap_quote_calculator.ts +++ b/packages/asset-swapper/src/utils/swap_quote_calculator.ts @@ -1,5 +1,4 @@ -import { marketUtils, orderCalculationUtils, SignedOrder } from '@0x/order-utils'; -import { MarketOperation } from '@0x/types'; +import { orderCalculationUtils } from '@0x/order-utils'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; @@ -7,106 +6,75 @@ import { constants } from '../constants'; import { InsufficientAssetLiquidityError } from '../errors'; import { MarketBuySwapQuote, + MarketOperation, MarketSellSwapQuote, - OrdersAndFillableAmounts, + PrunedSignedOrder, SwapQuote, SwapQuoteInfo, - SwapQuoterError, } from '../types'; +import { marketUtils } from './market_utils'; +import { protocolFeeUtils } from './protocol_fee_utils'; +import { utils } from './utils'; + // Calculates a swap quote for orders export const swapQuoteCalculator = { calculateMarketSellSwapQuote( - ordersAndFillableAmounts: OrdersAndFillableAmounts, - feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, + prunedOrders: PrunedSignedOrder[], takerAssetFillAmount: BigNumber, slippagePercentage: number, - isMakerAssetZrxToken: boolean, - shouldDisableFeeOrderCalculations: boolean, + gasPrice: BigNumber, ): MarketSellSwapQuote { return calculateSwapQuote( - ordersAndFillableAmounts, - feeOrdersAndFillableAmounts, + prunedOrders, takerAssetFillAmount, slippagePercentage, - isMakerAssetZrxToken, - shouldDisableFeeOrderCalculations, + gasPrice, MarketOperation.Sell, ) as MarketSellSwapQuote; }, calculateMarketBuySwapQuote( - ordersAndFillableAmounts: OrdersAndFillableAmounts, - feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, - makerAssetFillAmount: BigNumber, + prunedOrders: PrunedSignedOrder[], + takerAssetFillAmount: BigNumber, slippagePercentage: number, - isMakerAssetZrxToken: boolean, - shouldDisableFeeOrderCalculations: boolean, + gasPrice: BigNumber, ): MarketBuySwapQuote { return calculateSwapQuote( - ordersAndFillableAmounts, - feeOrdersAndFillableAmounts, - makerAssetFillAmount, + prunedOrders, + takerAssetFillAmount, slippagePercentage, - isMakerAssetZrxToken, - shouldDisableFeeOrderCalculations, + gasPrice, MarketOperation.Buy, ) as MarketBuySwapQuote; }, }; function calculateSwapQuote( - ordersAndFillableAmounts: OrdersAndFillableAmounts, - feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, + prunedOrders: PrunedSignedOrder[], assetFillAmount: BigNumber, slippagePercentage: number, - isMakerAssetZrxToken: boolean, - shouldDisableFeeOrderCalculations: boolean, + gasPrice: BigNumber, marketOperation: MarketOperation, ): SwapQuote { - const orders = ordersAndFillableAmounts.orders; - const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts; - const remainingFillableTakerAssetAmounts = remainingFillableMakerAssetAmounts.map( - (makerAssetAmount: BigNumber, index: number) => { - return orderCalculationUtils.getTakerFillAmount(orders[index], makerAssetAmount); - }, - ); - const feeOrders = feeOrdersAndFillableAmounts.orders; - const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts; - const slippageBufferAmount = assetFillAmount.multipliedBy(slippagePercentage).integerValue(); - let resultOrders: SignedOrder[]; + let resultOrders: PrunedSignedOrder[]; let remainingFillAmount: BigNumber; - let ordersRemainingFillableMakerAssetAmounts: BigNumber[]; if (marketOperation === MarketOperation.Buy) { // find the orders that cover the desired assetBuyAmount (with slippage) - ({ - resultOrders, - remainingFillAmount, - ordersRemainingFillableMakerAssetAmounts, - } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, assetFillAmount, { - remainingFillableMakerAssetAmounts, + ({ resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + prunedOrders, + assetFillAmount, slippageBufferAmount, - })); + )); } else { - let ordersRemainingFillableTakerAssetAmounts: BigNumber[]; // find the orders that cover the desired assetBuyAmount (with slippage) - ({ - resultOrders, - remainingFillAmount, - ordersRemainingFillableTakerAssetAmounts, - } = marketUtils.findOrdersThatCoverTakerAssetFillAmount(orders, assetFillAmount, { - remainingFillableTakerAssetAmounts, + ({ resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + prunedOrders, + assetFillAmount, slippageBufferAmount, - })); - - ordersRemainingFillableMakerAssetAmounts = _.map( - ordersRemainingFillableTakerAssetAmounts, - (takerAssetAmount: BigNumber, index: number) => { - return orderCalculationUtils.getMakerFillAmount(resultOrders[index], takerAssetAmount); - }, - ); + )); } // if we do not have enough orders to cover the desired assetBuyAmount, throw @@ -126,60 +94,16 @@ function calculateSwapQuote( 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 (!shouldDisableFeeOrderCalculations && !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(SwapQuoterError.InsufficientZrxLiquidity); - } - resultFeeOrders = feeOrdersAndRemainingFeeAmount.resultFeeOrders; - feeOrdersRemainingFillableMakerAssetAmounts = - feeOrdersAndRemainingFeeAmount.feeOrdersRemainingFillableMakerAssetAmounts; - } - // assetData information for the result - const takerAssetData = orders[0].takerAssetData; - const makerAssetData = orders[0].makerAssetData; + const takerAssetData = resultOrders[0].takerAssetData; + const makerAssetData = resultOrders[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, - assetFillAmount, - isMakerAssetZrxToken, - shouldDisableFeeOrderCalculations, - marketOperation, - ); + const bestCaseQuoteInfo = calculateQuoteInfo(resultOrders, assetFillAmount, gasPrice, marketOperation); // 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), + _.reverse(_.clone(resultOrders)), assetFillAmount, - isMakerAssetZrxToken, - shouldDisableFeeOrderCalculations, + gasPrice, marketOperation, ); @@ -187,7 +111,6 @@ function calculateSwapQuote( takerAssetData, makerAssetData, orders: resultOrders, - feeOrders: resultFeeOrders, bestCaseQuoteInfo, worstCaseQuoteInfo, }; @@ -208,199 +131,159 @@ function calculateSwapQuote( } function calculateQuoteInfo( - ordersAndFillableAmounts: OrdersAndFillableAmounts, - feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, - tokenAmount: BigNumber, - isMakerAssetZrxToken: boolean, - shouldDisableFeeOrderCalculations: boolean, - marketOperation: MarketOperation, + prunedOrders: PrunedSignedOrder[], + assetFillAmount: BigNumber, + gasPrice: BigNumber, + operation: MarketOperation, ): SwapQuoteInfo { - // find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right - let makerTokenAmount = marketOperation === MarketOperation.Buy ? tokenAmount : constants.ZERO_AMOUNT; - let takerTokenAmount = marketOperation === MarketOperation.Sell ? tokenAmount : constants.ZERO_AMOUNT; - let zrxTakerTokenAmount = constants.ZERO_AMOUNT; - - if (isMakerAssetZrxToken) { - if (marketOperation === MarketOperation.Buy) { - takerTokenAmount = findTakerTokenAmountNeededToBuyZrx(ordersAndFillableAmounts, makerTokenAmount); - } else { - makerTokenAmount = findZrxTokenAmountFromSellingTakerTokenAmount( - ordersAndFillableAmounts, - takerTokenAmount, - ); - } + if (operation === MarketOperation.Buy) { + return calculateMarketBuyQuoteInfo(prunedOrders, assetFillAmount, gasPrice); } else { - const findTokenAndZrxAmount = - marketOperation === MarketOperation.Buy - ? findTakerTokenAndZrxAmountNeededToBuyAsset - : findMakerTokenAmountReceivedAndZrxAmountNeededToSellAsset; - // find eth and zrx amounts needed to buy - const tokenAndZrxAmountToBuyAsset = findTokenAndZrxAmount( - ordersAndFillableAmounts, - marketOperation === MarketOperation.Buy ? makerTokenAmount : takerTokenAmount, - ); - if (marketOperation === MarketOperation.Buy) { - takerTokenAmount = tokenAndZrxAmountToBuyAsset[0]; - } else { - makerTokenAmount = tokenAndZrxAmountToBuyAsset[0]; - } - const zrxAmountToBuyAsset = tokenAndZrxAmountToBuyAsset[1]; - // find eth amount needed to buy zrx - zrxTakerTokenAmount = shouldDisableFeeOrderCalculations - ? constants.ZERO_AMOUNT - : findTakerTokenAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset); + return calculateMarketSellQuoteInfo(prunedOrders, assetFillAmount, gasPrice); } - - const feeTakerTokenAmount = zrxTakerTokenAmount; - - // eth amount needed in total is the sum of the amount needed for the asset and the amount needed for fees - const totalTakerTokenAmount = takerTokenAmount.plus(feeTakerTokenAmount); - return { - makerTokenAmount, - takerTokenAmount, - feeTakerTokenAmount, - totalTakerTokenAmount, - }; -} -// 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 findZrxTokenAmountFromSellingTakerTokenAmount( - feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, +function calculateMarketSellQuoteInfo( + prunedOrders: PrunedSignedOrder[], takerAssetSellAmount: BigNumber, -): BigNumber { - const { orders, remainingFillableMakerAssetAmounts } = feeOrdersAndFillableAmounts; + gasPrice: BigNumber, +): SwapQuoteInfo { const result = _.reduce( - orders, - (acc, order, index) => { - const { totalZrxTokenAmount, remainingTakerAssetFillAmount } = acc; - const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index]; - const remainingFillableTakerAssetAmount = orderCalculationUtils.getTakerFillAmount( - order, - remainingFillableMakerAssetAmount, + prunedOrders, + (acc, order) => { + const { + totalMakerAssetAmount, + totalTakerAssetAmount, + totalFeeTakerAssetAmount, + remainingTakerAssetFillAmount, + } = acc; + const [ + adjustedFillableMakerAssetAmount, + adjustedFillableTakerAssetAmount, + ] = utils.getAdjustedFillableMakerAndTakerAmountsFromTakerFees(order); + const takerAssetAmountWithFees = BigNumber.min( + remainingTakerAssetFillAmount, + adjustedFillableTakerAssetAmount, ); - const takerFillAmount = BigNumber.min(remainingTakerAssetFillAmount, remainingFillableTakerAssetAmount); - const makerFillAmount = orderCalculationUtils.getMakerFillAmount(order, takerFillAmount); - const feeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount); + const { takerAssetAmount, feeTakerAssetAmount } = getTakerAssetAmountBreakDown( + order, + takerAssetAmountWithFees, + ); + const makerAssetAmount = takerAssetAmountWithFees + .div(adjustedFillableTakerAssetAmount) + .multipliedBy(adjustedFillableMakerAssetAmount) + .integerValue(BigNumber.ROUND_CEIL); return { - totalZrxTokenAmount: totalZrxTokenAmount.plus(makerFillAmount).minus(feeAmount), + totalMakerAssetAmount: totalMakerAssetAmount.plus(makerAssetAmount), + totalTakerAssetAmount: totalTakerAssetAmount.plus(takerAssetAmount), + totalFeeTakerAssetAmount: totalFeeTakerAssetAmount.plus(feeTakerAssetAmount), remainingTakerAssetFillAmount: BigNumber.max( constants.ZERO_AMOUNT, - remainingTakerAssetFillAmount.minus(takerFillAmount), + remainingTakerAssetFillAmount.minus(takerAssetAmountWithFees), ), }; }, { - totalZrxTokenAmount: constants.ZERO_AMOUNT, + totalMakerAssetAmount: constants.ZERO_AMOUNT, + totalTakerAssetAmount: constants.ZERO_AMOUNT, + totalFeeTakerAssetAmount: constants.ZERO_AMOUNT, remainingTakerAssetFillAmount: takerAssetSellAmount, }, ); - return result.totalZrxTokenAmount; + return { + feeTakerAssetAmount: result.totalFeeTakerAssetAmount, + takerAssetAmount: result.totalTakerAssetAmount, + totalTakerAssetAmount: result.totalFeeTakerAssetAmount.plus(result.totalTakerAssetAmount), + makerAssetAmount: result.totalMakerAssetAmount, + protocolFeeInEthAmount: protocolFeeUtils.calculateWorstCaseProtocolFee(prunedOrders, gasPrice), + }; } -function findTakerTokenAmountNeededToBuyZrx( - feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, - zrxBuyAmount: BigNumber, -): BigNumber { - const { orders, remainingFillableMakerAssetAmounts } = feeOrdersAndFillableAmounts; - const result = _.reduce( - orders, - (acc, order, index) => { - const { totalTakerTokenAmount, 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 { - totalTakerTokenAmount: totalTakerTokenAmount.plus(takerFillAmount), - remainingZrxBuyAmount: BigNumber.max( - constants.ZERO_AMOUNT, - remainingZrxBuyAmount.minus(makerFillAmount).plus(extraFeeAmount), - ), - }; - }, - { - totalTakerTokenAmount: constants.ZERO_AMOUNT, - remainingZrxBuyAmount: zrxBuyAmount, - }, - ); - return result.totalTakerTokenAmount; -} - -function findTakerTokenAndZrxAmountNeededToBuyAsset( - ordersAndFillableAmounts: OrdersAndFillableAmounts, +function calculateMarketBuyQuoteInfo( + prunedOrders: PrunedSignedOrder[], makerAssetBuyAmount: BigNumber, -): [BigNumber, BigNumber] { - const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts; + gasPrice: BigNumber, +): SwapQuoteInfo { const result = _.reduce( - orders, - (acc, order, index) => { - const { totalTakerTokenAmount, totalZrxAmount, remainingmakerAssetFillAmount } = acc; - const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index]; - const makerFillAmount = BigNumber.min(acc.remainingmakerAssetFillAmount, remainingFillableMakerAssetAmount); - const takerFillAmount = orderCalculationUtils.getTakerFillAmount(order, makerFillAmount); - const takerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount); + prunedOrders, + (acc, order) => { + const { + totalMakerAssetAmount, + totalTakerAssetAmount, + totalFeeTakerAssetAmount, + remainingMakerAssetFillAmount, + } = acc; + const [ + adjustedFillableMakerAssetAmount, + adjustedFillableTakerAssetAmount, + ] = utils.getAdjustedFillableMakerAndTakerAmountsFromTakerFees(order); + const makerFillAmount = BigNumber.min(remainingMakerAssetFillAmount, adjustedFillableMakerAssetAmount); + const takerAssetAmountWithFees = makerFillAmount + .div(adjustedFillableMakerAssetAmount) + .multipliedBy(adjustedFillableTakerAssetAmount) + .integerValue(BigNumber.ROUND_CEIL); + const { takerAssetAmount, feeTakerAssetAmount } = getTakerAssetAmountBreakDown( + order, + takerAssetAmountWithFees, + ); return { - totalTakerTokenAmount: totalTakerTokenAmount.plus(takerFillAmount), - totalZrxAmount: totalZrxAmount.plus(takerFeeAmount), - remainingmakerAssetFillAmount: BigNumber.max( + totalMakerAssetAmount: totalMakerAssetAmount.plus(makerFillAmount), + totalTakerAssetAmount: totalTakerAssetAmount.plus(takerAssetAmount), + totalFeeTakerAssetAmount: totalFeeTakerAssetAmount.plus(feeTakerAssetAmount), + remainingMakerAssetFillAmount: BigNumber.max( constants.ZERO_AMOUNT, - remainingmakerAssetFillAmount.minus(makerFillAmount), + remainingMakerAssetFillAmount.minus(makerFillAmount), ), }; }, { - totalTakerTokenAmount: constants.ZERO_AMOUNT, - totalZrxAmount: constants.ZERO_AMOUNT, - remainingmakerAssetFillAmount: makerAssetBuyAmount, + totalMakerAssetAmount: constants.ZERO_AMOUNT, + totalTakerAssetAmount: constants.ZERO_AMOUNT, + totalFeeTakerAssetAmount: constants.ZERO_AMOUNT, + remainingMakerAssetFillAmount: makerAssetBuyAmount, }, ); - return [result.totalTakerTokenAmount, result.totalZrxAmount]; + return { + feeTakerAssetAmount: result.totalFeeTakerAssetAmount, + takerAssetAmount: result.totalTakerAssetAmount, + totalTakerAssetAmount: result.totalFeeTakerAssetAmount.plus(result.totalTakerAssetAmount), + makerAssetAmount: result.totalMakerAssetAmount, + protocolFeeInEthAmount: protocolFeeUtils.calculateWorstCaseProtocolFee(prunedOrders, gasPrice), + }; } -function findMakerTokenAmountReceivedAndZrxAmountNeededToSellAsset( - ordersAndFillableAmounts: OrdersAndFillableAmounts, - takerAssetSellAmount: BigNumber, -): [BigNumber, BigNumber] { - const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts; - const result = _.reduce( - orders, - (acc, order, index) => { - const { totalMakerTokenAmount, totalZrxAmount, remainingTakerAssetFillAmount } = acc; - const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index]; - const remainingFillableTakerAssetAmount = orderCalculationUtils.getTakerFillAmount( - order, - remainingFillableMakerAssetAmount, - ); - const takerFillAmount = BigNumber.min(acc.remainingTakerAssetFillAmount, remainingFillableTakerAssetAmount); - const makerFillAmount = orderCalculationUtils.getMakerFillAmount(order, takerFillAmount); - const takerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount); +function getTakerAssetAmountBreakDown( + order: PrunedSignedOrder, + takerAssetAmountWithFees: BigNumber, +): { feeTakerAssetAmount: BigNumber; takerAssetAmount: BigNumber } { + if (utils.isOrderTakerFeePayableWithTakerAsset(order)) { + const adjustedTakerAssetAmount = order.takerAssetAmount.plus(order.takerFee); + const filledRatio = takerAssetAmountWithFees.div(adjustedTakerAssetAmount); + const takerAssetAmount = filledRatio.multipliedBy(order.takerAssetAmount).integerValue(BigNumber.ROUND_CEIL); + return { + takerAssetAmount, + feeTakerAssetAmount: takerAssetAmountWithFees.minus(takerAssetAmount), + }; + } else if (utils.isOrderTakerFeePayableWithMakerAsset(order)) { + if (takerAssetAmountWithFees.isZero()) { return { - totalMakerTokenAmount: totalMakerTokenAmount.plus(makerFillAmount), - totalZrxAmount: totalZrxAmount.plus(takerFeeAmount), - remainingTakerAssetFillAmount: BigNumber.max( - constants.ZERO_AMOUNT, - remainingTakerAssetFillAmount.minus(takerFillAmount), - ), + takerAssetAmount: constants.ZERO_AMOUNT, + feeTakerAssetAmount: constants.ZERO_AMOUNT, }; - }, - { - totalMakerTokenAmount: constants.ZERO_AMOUNT, - totalZrxAmount: constants.ZERO_AMOUNT, - remainingTakerAssetFillAmount: takerAssetSellAmount, - }, - ); - return [result.totalMakerTokenAmount, result.totalZrxAmount]; + } + const takerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerAssetAmountWithFees); + const makerAssetFillAmount = orderCalculationUtils.getMakerFillAmount(order, takerAssetAmountWithFees); + const takerAssetAmount = takerFeeAmount + .div(makerAssetFillAmount) + .multipliedBy(takerAssetAmountWithFees) + .integerValue(BigNumber.ROUND_CEIL); + return { + takerAssetAmount, + feeTakerAssetAmount: takerAssetAmountWithFees.minus(takerAssetAmount), + }; + } + return { + feeTakerAssetAmount: constants.ZERO_AMOUNT, + takerAssetAmount: takerAssetAmountWithFees, + }; } diff --git a/packages/asset-swapper/src/utils/swap_quote_consumer_utils.ts b/packages/asset-swapper/src/utils/swap_quote_consumer_utils.ts index 87be11f339..5a1bf191ff 100644 --- a/packages/asset-swapper/src/utils/swap_quote_consumer_utils.ts +++ b/packages/asset-swapper/src/utils/swap_quote_consumer_utils.ts @@ -1,5 +1,7 @@ -import { ContractWrappers } from '@0x/contract-wrappers'; -import { MarketOperation, SignedOrder } from '@0x/types'; +import { ContractAddresses } from '@0x/contract-addresses'; +import { DevUtilsContract } from '@0x/contracts-dev-utils'; +import { WETH9Contract } from '@0x/contracts-erc20'; +import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { SupportedProvider, Web3Wrapper } from '@0x/web3-wrapper'; import { Provider } from 'ethereum-types'; @@ -47,19 +49,17 @@ export const swapQuoteConsumerUtils = { }, async getEthAndWethBalanceAsync( provider: SupportedProvider, - contractWrappers: ContractWrappers, + contractAddresses: ContractAddresses, takerAddress: string, ): Promise<[BigNumber, BigNumber]> { + const weth = new WETH9Contract(contractAddresses.etherToken, provider); const web3Wrapper = new Web3Wrapper(provider); const ethBalance = await web3Wrapper.getBalanceInWeiAsync(takerAddress); - const wethBalance = await contractWrappers.weth9.balanceOf(takerAddress).callAsync(); + const wethBalance = await weth.balanceOf(takerAddress).callAsync(); return [ethBalance, wethBalance]; }, isValidForwarderSwapQuote(swapQuote: SwapQuote, wethAssetData: string): boolean { - return ( - swapQuoteConsumerUtils.isValidForwarderSignedOrders(swapQuote.orders, wethAssetData) && - swapQuoteConsumerUtils.isValidForwarderSignedOrders(swapQuote.feeOrders, wethAssetData) - ); + return swapQuoteConsumerUtils.isValidForwarderSignedOrders(swapQuote.orders, wethAssetData); }, isValidForwarderSignedOrders(orders: SignedOrder[], wethAssetData: string): boolean { return _.every(orders, order => swapQuoteConsumerUtils.isValidForwarderSignedOrder(order, wethAssetData)); @@ -67,35 +67,25 @@ export const swapQuoteConsumerUtils = { isValidForwarderSignedOrder(order: SignedOrder, wethAssetData: string): boolean { return order.takerAssetData === wethAssetData; }, - optimizeOrdersForMarketExchangeOperation(orders: SignedOrder[], operation: MarketOperation): SignedOrder[] { - return _.map(orders, (order: SignedOrder, index: number) => { - const optimizedOrder = _.clone(order); - if (operation === MarketOperation.Sell && index !== 0) { - optimizedOrder.takerAssetData = constants.NULL_BYTES; - } else if (index !== 0) { - optimizedOrder.makerAssetData = constants.NULL_BYTES; - } - return optimizedOrder; - }); - }, async getExtensionContractTypeForSwapQuoteAsync( quote: SwapQuote, - contractWrappers: ContractWrappers, + contractAddresses: ContractAddresses, provider: Provider, opts: Partial, ): Promise { - const wethAssetData = await contractWrappers.devUtils - .encodeERC20AssetData(contractWrappers.contractAddresses.etherToken) - .callAsync(); + const devUtils = new DevUtilsContract(contractAddresses.devUtils, provider); + const wethAssetData = await devUtils.encodeERC20AssetData(contractAddresses.etherToken).callAsync(); if (swapQuoteConsumerUtils.isValidForwarderSwapQuote(quote, wethAssetData)) { if (opts.takerAddress !== undefined) { assert.isETHAddressHex('takerAddress', opts.takerAddress); } - const ethAmount = opts.ethAmount || quote.worstCaseQuoteInfo.totalTakerTokenAmount; + const ethAmount = + opts.ethAmount || + quote.worstCaseQuoteInfo.takerAssetAmount.plus(quote.worstCaseQuoteInfo.protocolFeeInEthAmount); const takerAddress = await swapQuoteConsumerUtils.getTakerAddressAsync(provider, opts); const takerEthAndWethBalance = takerAddress !== undefined - ? await swapQuoteConsumerUtils.getEthAndWethBalanceAsync(provider, contractWrappers, takerAddress) + ? await swapQuoteConsumerUtils.getEthAndWethBalanceAsync(provider, contractAddresses, takerAddress) : [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT]; // TODO(david): when considering if there is enough Eth balance, should account for gas costs. const isEnoughEthAndWethBalance = _.map(takerEthAndWethBalance, (balance: BigNumber) => diff --git a/packages/asset-swapper/src/utils/utils.ts b/packages/asset-swapper/src/utils/utils.ts index 5c2907be1b..14e12fb203 100644 --- a/packages/asset-swapper/src/utils/utils.ts +++ b/packages/asset-swapper/src/utils/utils.ts @@ -1,11 +1,11 @@ -import { SignedOrder } from '@0x/types'; +import { Order } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { AbiDefinition, ContractAbi, MethodAbi } from 'ethereum-types'; import * as _ from 'lodash'; import { constants } from '../constants'; -import { OrdersAndFillableAmounts } from '../types'; +import { PrunedSignedOrder } from '../types'; // tslint:disable:no-unnecessary-type-assertion export const utils = { @@ -27,15 +27,30 @@ export const utils = { }, ) as MethodAbi | undefined; }, - isFeeOrdersRequiredToFillOrders(ordersAndFillableAmounts: OrdersAndFillableAmounts): boolean { - const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts; - return _.some( - orders, - (order: SignedOrder, index: number): boolean => { - const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index]; - // If takerFee is a non zero value and order is still fillable, fee orders are required - return !order.takerFee.isZero() && !remainingFillableMakerAssetAmount.isZero(); - }, - ); + isOrderTakerFeePayableWithMakerAsset(order: T): boolean { + return order.takerFeeAssetData === order.makerAssetData; + }, + isOrderTakerFeePayableWithTakerAsset(order: T): boolean { + return order.takerFeeAssetData === order.takerAssetData; + }, + getAdjustedMakerAndTakerAmountsFromTakerFees(order: T): [BigNumber, BigNumber] { + const adjustedMakerAssetAmount = utils.isOrderTakerFeePayableWithMakerAsset(order) + ? order.makerAssetAmount.minus(order.takerFee) + : order.makerAssetAmount; + const adjustedTakerAssetAmount = utils.isOrderTakerFeePayableWithTakerAsset(order) + ? order.takerAssetAmount.plus(order.takerFee) + : order.takerAssetAmount; + return [adjustedMakerAssetAmount, adjustedTakerAssetAmount]; + }, + getAdjustedFillableMakerAndTakerAmountsFromTakerFees( + order: T, + ): [BigNumber, BigNumber] { + const adjustedFillableMakerAssetAmount = utils.isOrderTakerFeePayableWithMakerAsset(order) + ? order.fillableMakerAssetAmount.minus(order.fillableTakerFeeAmount) + : order.fillableMakerAssetAmount; + const adjustedFillableTakerAssetAmount = utils.isOrderTakerFeePayableWithTakerAsset(order) + ? order.fillableTakerAssetAmount.plus(order.fillableTakerFeeAmount) + : order.fillableTakerAssetAmount; + return [adjustedFillableMakerAssetAmount, adjustedFillableTakerAssetAmount]; }, }; diff --git a/packages/asset-swapper/test/affiliate_fee_utils_test.ts b/packages/asset-swapper/test/affiliate_fee_utils_test.ts deleted file mode 100644 index a321eb95a6..0000000000 --- a/packages/asset-swapper/test/affiliate_fee_utils_test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { MarketOperation } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import 'mocha'; - -import { constants } from '../src/constants'; -import { affiliateFeeUtils } from '../src/utils/affiliate_fee_utils'; - -import { chaiSetup } from './utils/chai_setup'; -import { - getFullyFillableSwapQuoteWithFees, - getFullyFillableSwapQuoteWithNoFees, - getPartialSignedOrdersWithFees, - getPartialSignedOrdersWithNoFees, -} from './utils/swap_quote'; - -chaiSetup.configure(); -const expect = chai.expect; - -const FAKE_TAKER_ASSET_DATA = '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48'; -const FAKE_MAKER_ASSET_DATA = '0xf47261b00000000000000000000000009f5B0C7e1623793bF0620569b9749e79DF6D0bC5'; -const NULL_ADDRESS = constants.NULL_ADDRESS; -const FEE_PERCENTAGE = 0.1; -const FILLABLE_AMOUNTS = [new BigNumber(2), new BigNumber(3), new BigNumber(5)]; -const FILLABLE_FEE_AMOUNTS = [new BigNumber(1), new BigNumber(1), new BigNumber(1)]; -const MARKET_OPERATION = MarketOperation.Sell; - -describe('affiliateFeeUtils', () => { - const fakeFeeOrders = getPartialSignedOrdersWithNoFees( - FAKE_MAKER_ASSET_DATA, - FAKE_TAKER_ASSET_DATA, - NULL_ADDRESS, - NULL_ADDRESS, - FILLABLE_FEE_AMOUNTS, - ); - const fakeOrders = getPartialSignedOrdersWithNoFees( - FAKE_MAKER_ASSET_DATA, - FAKE_TAKER_ASSET_DATA, - NULL_ADDRESS, - NULL_ADDRESS, - FILLABLE_AMOUNTS, - ); - - const fakeOrdersWithFees = getPartialSignedOrdersWithFees( - FAKE_MAKER_ASSET_DATA, - FAKE_TAKER_ASSET_DATA, - NULL_ADDRESS, - NULL_ADDRESS, - FILLABLE_AMOUNTS, - FILLABLE_FEE_AMOUNTS, - ); - - const fakeSwapQuote = getFullyFillableSwapQuoteWithNoFees( - FAKE_MAKER_ASSET_DATA, - FAKE_TAKER_ASSET_DATA, - fakeOrders, - MARKET_OPERATION, - ); - - const fakeSwapQuoteWithFees = getFullyFillableSwapQuoteWithFees( - FAKE_MAKER_ASSET_DATA, - FAKE_TAKER_ASSET_DATA, - fakeOrdersWithFees, - fakeFeeOrders, - MARKET_OPERATION, - ); - - describe('getSwapQuoteWithAffiliateFee', () => { - it('should return unchanged swapQuote if feePercentage is 0', () => { - const updatedSwapQuote = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(fakeSwapQuote, 0); - const fakeSwapQuoteWithAffiliateFees = { ...fakeSwapQuote, ...{ feePercentage: 0 } }; - expect(updatedSwapQuote).to.deep.equal(fakeSwapQuoteWithAffiliateFees); - }); - it('should return correct feeTakerToken and totalTakerToken amounts when provided SwapQuote with no fees', () => { - const updatedSwapQuote = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(fakeSwapQuote, FEE_PERCENTAGE); - expect(updatedSwapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.deep.equal(new BigNumber(1)); - expect(updatedSwapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.deep.equal(new BigNumber(11)); - expect(updatedSwapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.deep.equal(new BigNumber(1)); - expect(updatedSwapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.deep.equal(new BigNumber(11)); - }); - it('should return correct feeTakerToken and totalTakerToken amounts when provides SwapQuote with fees', () => { - const updatedSwapQuote = affiliateFeeUtils.getSwapQuoteWithAffiliateFee( - fakeSwapQuoteWithFees, - FEE_PERCENTAGE, - ); - expect(updatedSwapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.deep.equal(new BigNumber(4)); - expect(updatedSwapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.deep.equal(new BigNumber(14)); - expect(updatedSwapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.deep.equal(new BigNumber(4)); - expect(updatedSwapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.deep.equal(new BigNumber(14)); - }); - }); -}); diff --git a/packages/asset-swapper/test/calculate_liquidity_test.ts b/packages/asset-swapper/test/calculate_liquidity_test.ts new file mode 100644 index 0000000000..0c7c499e3e --- /dev/null +++ b/packages/asset-swapper/test/calculate_liquidity_test.ts @@ -0,0 +1,57 @@ +import * as chai from 'chai'; +import * as _ from 'lodash'; +import 'mocha'; + +import { calculateLiquidity } from '../src/utils/calculate_liquidity'; + +import { chaiSetup } from './utils/chai_setup'; +import { testOrders } from './utils/test_orders'; +import { baseUnitAmount } from './utils/utils'; + +chaiSetup.configure(); +const expect = chai.expect; +const { + PRUNED_SIGNED_ORDERS_FEELESS, + PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, + PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, +} = testOrders; + +// tslint:disable:custom-no-magic-numbers +describe('#calculateLiquidity', () => { + it('should provide correct liquidity result with feeless orders', () => { + const prunedSignedOrders = PRUNED_SIGNED_ORDERS_FEELESS; + const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity( + prunedSignedOrders, + ); + expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(10)); + expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(9)); + }); + it('should provide correct liquidity result with orders with takerFees in takerAsset', () => { + const prunedSignedOrders = PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET; + const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity( + prunedSignedOrders, + ); + expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(10)); + expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(15)); + }); + it('should provide correct liquidity result with orders with takerFees in makerAsset', () => { + const prunedSignedOrders = PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET; + const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity( + prunedSignedOrders, + ); + expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(5)); + expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(9)); + }); + it('should provide correct liquidity result with mixed orders with fees and no fees', () => { + const prunedSignedOrders = _.concat( + PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, + PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, + PRUNED_SIGNED_ORDERS_FEELESS, + ); + const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity( + prunedSignedOrders, + ); + expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(25)); + expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(33)); + }); +}); diff --git a/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts b/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts index 880435e249..87b74bd36d 100644 --- a/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts +++ b/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts @@ -1,7 +1,9 @@ -import { ContractAddresses, ContractWrappers, ERC20TokenContract } from '@0x/contract-wrappers'; +import { ContractAddresses } from '@0x/contract-addresses'; +import { DevUtilsContract } from '@0x/contracts-dev-utils'; +import { ERC20TokenContract } from '@0x/contracts-erc20'; +import { ExchangeContract } from '@0x/contracts-exchange'; import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils'; import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; -import { MarketOperation, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import 'mocha'; @@ -13,7 +15,9 @@ import { ExchangeMarketBuySmartContractParams, ExchangeMarketSellSmartContractParams, MarketBuySwapQuote, + MarketOperation, MarketSellSwapQuote, + PrunedSignedOrder, } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; @@ -25,16 +29,47 @@ chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +const GAS_PRICE = new BigNumber(devConstants.DEFAULT_GAS_PRICE); const ONE_ETH_IN_WEI = new BigNumber(1000000000000000000); -const TESTRPC_CHAIN_ID = 1337; -const FILLABLE_AMOUNTS = [new BigNumber(3), new BigNumber(2), new BigNumber(5)].map(value => - value.multipliedBy(ONE_ETH_IN_WEI), -); +const TESTRPC_CHAIN_ID = devConstants.TESTRPC_CHAIN_ID; +const UNLIMITED_ALLOWANCE = new BigNumber(2).pow(256).minus(1); // tslint:disable-line:custom-no-magic-numbers + +const PARTIAL_PRUNED_SIGNED_ORDERS_FEELESS: Array> = [ + { + takerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + }, + { + takerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI), + }, + { + takerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI), + }, +]; + +const expectMakerAndTakerBalancesAsyncFactory = ( + erc20TokenContract: ERC20TokenContract, + makerAddress: string, + takerAddress: string, +) => async (expectedMakerBalance: BigNumber, expectedTakerBalance: BigNumber) => { + const makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); + const takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); + expect(makerBalance).to.bignumber.equal(expectedMakerBalance); + expect(takerBalance).to.bignumber.equal(expectedTakerBalance); +}; describe('ExchangeSwapQuoteConsumer', () => { - let contractWrappers: ContractWrappers; let userAddresses: string[]; - let erc20TokenContract: ERC20TokenContract; + let erc20MakerTokenContract: ERC20TokenContract; + let erc20TakerTokenContract: ERC20TokenContract; let coinbaseAddress: string; let makerAddress: string; let takerAddress: string; @@ -46,32 +81,38 @@ describe('ExchangeSwapQuoteConsumer', () => { let takerAssetData: string; let wethAssetData: string; let contractAddresses: ContractAddresses; + let exchangeContract: ExchangeContract; const chainId = TESTRPC_CHAIN_ID; - let orders: SignedOrder[]; + let orders: PrunedSignedOrder[]; let marketSellSwapQuote: SwapQuote; let marketBuySwapQuote: SwapQuote; let swapQuoteConsumer: ExchangeSwapQuoteConsumer; + let expectMakerAndTakerBalancesForMakerAssetAsync: ( + expectedMakerBalance: BigNumber, + expectedTakerBalance: BigNumber, + ) => Promise; + let expectMakerAndTakerBalancesForTakerAssetAsync: ( + expectedMakerBalance: BigNumber, + expectedTakerBalance: BigNumber, + ) => Promise; before(async () => { contractAddresses = await migrateOnceAsync(); await blockchainLifecycle.startAsync(); userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - const config = { - chainId, - contractAddresses, - }; - contractWrappers = new ContractWrappers(provider, config); [coinbaseAddress, takerAddress, makerAddress, feeRecipient] = userAddresses; [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); - [makerAssetData, takerAssetData, wethAssetData] = [ - await contractWrappers.devUtils.encodeERC20AssetData(makerTokenAddress).callAsync(), - await contractWrappers.devUtils.encodeERC20AssetData(takerTokenAddress).callAsync(), - await contractWrappers.devUtils.encodeERC20AssetData(contractAddresses.etherToken).callAsync(), - ]; - erc20TokenContract = new ERC20TokenContract(makerTokenAddress, provider); - + const devUtils = new DevUtilsContract(contractAddresses.devUtils, provider); + [makerAssetData, takerAssetData, wethAssetData] = await Promise.all([ + devUtils.encodeERC20AssetData(makerTokenAddress).callAsync(), + devUtils.encodeERC20AssetData(takerTokenAddress).callAsync(), + devUtils.encodeERC20AssetData(contractAddresses.etherToken).callAsync(), + ]); + erc20MakerTokenContract = new ERC20TokenContract(makerTokenAddress, provider); + erc20TakerTokenContract = new ERC20TokenContract(takerTokenAddress, provider); + exchangeContract = new ExchangeContract(contractAddresses.exchange, provider); // Configure order defaults const defaultOrderParams = { ...devConstants.STATIC_ORDER_PARAMS, @@ -79,17 +120,26 @@ describe('ExchangeSwapQuoteConsumer', () => { takerAddress, makerAssetData, takerAssetData, - makerFeeAssetData: await contractWrappers.devUtils - .encodeERC20AssetData(contractAddresses.zrxToken) - .callAsync(), - takerFeeAssetData: await contractWrappers.devUtils - .encodeERC20AssetData(contractAddresses.zrxToken) - .callAsync(), + makerFeeAssetData: constants.NULL_ERC20_ASSET_DATA, + takerFeeAssetData: constants.NULL_ERC20_ASSET_DATA, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + feeRecipientAddress: feeRecipient, exchangeAddress: contractAddresses.exchange, chainId, }; const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)]; orderFactory = new OrderFactory(privateKey, defaultOrderParams); + expectMakerAndTakerBalancesForTakerAssetAsync = expectMakerAndTakerBalancesAsyncFactory( + erc20TakerTokenContract, + makerAddress, + takerAddress, + ); + expectMakerAndTakerBalancesForMakerAssetAsync = expectMakerAndTakerBalancesAsyncFactory( + erc20MakerTokenContract, + makerAddress, + takerAddress, + ); }); after(async () => { await blockchainLifecycle.revertAsync(); @@ -97,12 +147,13 @@ describe('ExchangeSwapQuoteConsumer', () => { beforeEach(async () => { await blockchainLifecycle.startAsync(); orders = []; - for (const fillableAmount of FILLABLE_AMOUNTS) { - const order = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: fillableAmount, - takerAssetAmount: fillableAmount, - }); - orders.push(order); + for (const partialOrder of PARTIAL_PRUNED_SIGNED_ORDERS_FEELESS) { + const order = await orderFactory.newSignedOrderAsync(partialOrder); + const prunedOrder = { + ...order, + ...partialOrder, + }; + orders.push(prunedOrder as PrunedSignedOrder); } marketSellSwapQuote = getFullyFillableSwapQuoteWithNoFees( @@ -110,6 +161,7 @@ describe('ExchangeSwapQuoteConsumer', () => { takerAssetData, orders, MarketOperation.Sell, + GAS_PRICE, ); marketBuySwapQuote = getFullyFillableSwapQuoteWithNoFees( @@ -117,45 +169,87 @@ describe('ExchangeSwapQuoteConsumer', () => { takerAssetData, orders, MarketOperation.Buy, + GAS_PRICE, ); - swapQuoteConsumer = new ExchangeSwapQuoteConsumer(provider, { + swapQuoteConsumer = new ExchangeSwapQuoteConsumer(provider, contractAddresses, { chainId, }); + + await erc20MakerTokenContract + .transfer(makerAddress, marketBuySwapQuote.worstCaseQuoteInfo.makerAssetAmount) + .sendTransactionAsync({ + from: coinbaseAddress, + }); + await erc20TakerTokenContract + .transfer(takerAddress, marketBuySwapQuote.worstCaseQuoteInfo.totalTakerAssetAmount) + .sendTransactionAsync({ + from: coinbaseAddress, + }); + await erc20MakerTokenContract + .approve(contractAddresses.erc20Proxy, UNLIMITED_ALLOWANCE) + .sendTransactionAsync({ from: makerAddress }); + await erc20TakerTokenContract + .approve(contractAddresses.erc20Proxy, UNLIMITED_ALLOWANCE) + .sendTransactionAsync({ from: takerAddress }); }); afterEach(async () => { await blockchainLifecycle.revertAsync(); }); - describe('executeSwapQuoteOrThrowAsync', () => { + describe('#executeSwapQuoteOrThrowAsync', () => { /* * Testing that SwapQuoteConsumer logic correctly performs a execution (doesn't throw or revert) * Does not test the validity of the state change performed by the forwarder smart contract */ it('should perform a marketSell execution when provided a MarketSell type swapQuote', async () => { - let makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - let takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - expect(makerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { takerAddress }); - makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - expect(takerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); + await expectMakerAndTakerBalancesForMakerAssetAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); + await expectMakerAndTakerBalancesForTakerAssetAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); + await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { + takerAddress, + gasPrice: GAS_PRICE, + gasLimit: 4000000, + }); + await expectMakerAndTakerBalancesForMakerAssetAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); + await expectMakerAndTakerBalancesForTakerAssetAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); }); it('should perform a marketBuy execution when provided a MarketBuy type swapQuote', async () => { - let makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - let takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - expect(makerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, { takerAddress }); - makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - expect(takerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); + await expectMakerAndTakerBalancesForMakerAssetAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); + await expectMakerAndTakerBalancesForTakerAssetAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); + await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, { + takerAddress, + gasPrice: GAS_PRICE, + gasLimit: 4000000, + }); + await expectMakerAndTakerBalancesForMakerAssetAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); + await expectMakerAndTakerBalancesForTakerAssetAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); }); }); - describe('getSmartContractParamsOrThrow', () => { + describe('#getSmartContractParamsOrThrow', () => { describe('valid swap quote', async () => { // TODO(david) Check for valid MethodAbi it('provide correct and optimized smart contract params for a marketSell SwapQuote', async () => { @@ -163,7 +257,7 @@ describe('ExchangeSwapQuoteConsumer', () => { marketSellSwapQuote, {}, ); - expect(toAddress).to.deep.equal(contractWrappers.exchange.address); + expect(toAddress).to.deep.equal(exchangeContract.address); const { takerAssetFillAmount, signatures, type } = params as ExchangeMarketSellSmartContractParams; expect(type).to.deep.equal(MarketOperation.Sell); expect(takerAssetFillAmount).to.bignumber.equal( @@ -172,12 +266,12 @@ describe('ExchangeSwapQuoteConsumer', () => { const orderSignatures = marketSellSwapQuote.orders.map(order => order.signature); expect(signatures).to.deep.equal(orderSignatures); }); - it('provide correct and optimized smart contract params for a marketBuy SwapQuote', async () => { + it('provide correct smart contract params for a marketBuy SwapQuote', async () => { const { toAddress, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync( marketBuySwapQuote, {}, ); - expect(toAddress).to.deep.equal(contractWrappers.exchange.address); + expect(toAddress).to.deep.equal(exchangeContract.address); const { makerAssetFillAmount, signatures, type } = params as ExchangeMarketBuySmartContractParams; expect(type).to.deep.equal(MarketOperation.Buy); expect(makerAssetFillAmount).to.bignumber.equal( @@ -189,49 +283,53 @@ describe('ExchangeSwapQuoteConsumer', () => { }); }); - describe('getCalldataOrThrow', () => { + describe('#getCalldataOrThrow', () => { describe('valid swap quote', async () => { it('provide correct and optimized calldata options with default options for a marketSell SwapQuote (no affiliate fees)', async () => { - let makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - let takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - expect(makerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - const { calldataHexString, toAddress } = await swapQuoteConsumer.getCalldataOrThrowAsync( + await expectMakerAndTakerBalancesForMakerAssetAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); + const { calldataHexString, toAddress, ethAmount } = await swapQuoteConsumer.getCalldataOrThrowAsync( marketSellSwapQuote, {}, ); - expect(toAddress).to.deep.equal(contractWrappers.exchange.address); + expect(toAddress).to.deep.equal(exchangeContract.address); await web3Wrapper.sendTransactionAsync({ from: takerAddress, to: toAddress, data: calldataHexString, gas: 4000000, + gasPrice: GAS_PRICE, + value: ethAmount, }); - makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - expect(takerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); + await expectMakerAndTakerBalancesForMakerAssetAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); }); it('provide correct and optimized calldata options with default options for a marketBuy SwapQuote (no affiliate fees)', async () => { - let makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - let takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - expect(makerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - const { calldataHexString, toAddress } = await swapQuoteConsumer.getCalldataOrThrowAsync( + await expectMakerAndTakerBalancesForMakerAssetAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); + const { calldataHexString, toAddress, ethAmount } = await swapQuoteConsumer.getCalldataOrThrowAsync( marketBuySwapQuote, {}, ); - expect(toAddress).to.deep.equal(contractWrappers.exchange.address); + expect(toAddress).to.deep.equal(exchangeContract.address); await web3Wrapper.sendTransactionAsync({ from: takerAddress, to: toAddress, data: calldataHexString, gas: 4000000, + gasPrice: GAS_PRICE, + value: ethAmount, }); - makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - expect(takerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); + await expectMakerAndTakerBalancesForMakerAssetAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); }); }); }); diff --git a/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts b/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts index 2cdbd90386..d9f5343a62 100644 --- a/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts +++ b/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts @@ -1,6 +1,9 @@ -import { ContractAddresses, ContractWrappers, ERC20TokenContract } from '@0x/contract-wrappers'; +import { ContractAddresses } from '@0x/contract-addresses'; +import { DevUtilsContract } from '@0x/contracts-dev-utils'; +import { ERC20TokenContract } from '@0x/contracts-erc20'; +import { ForwarderContract } from '@0x/contracts-exchange-forwarder'; +import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils'; import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; -import { MarketOperation, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import 'mocha'; @@ -12,28 +15,61 @@ import { ForwarderMarketBuySmartContractParams, ForwarderMarketSellSmartContractParams, MarketBuySwapQuote, + MarketOperation, + PrunedSignedOrder, } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; import { migrateOnceAsync } from './utils/migrate'; -import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFeesAsync } from './utils/swap_quote'; +import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote'; import { provider, web3Wrapper } from './utils/web3_wrapper'; chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +const GAS_PRICE = new BigNumber(devConstants.DEFAULT_GAS_PRICE); const ONE_ETH_IN_WEI = new BigNumber(1000000000000000000); -const TESTRPC_CHAIN_ID = 1337; -const MARKET_OPERATION = MarketOperation.Sell; +const TESTRPC_CHAIN_ID = devConstants.TESTRPC_CHAIN_ID; const FILLABLE_AMOUNTS = [new BigNumber(2), new BigNumber(3), new BigNumber(5)].map(value => value.multipliedBy(ONE_ETH_IN_WEI), ); const UNLIMITED_ALLOWANCE_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1); // tslint:disable-line:custom-no-magic-numbers +const PARTIAL_PRUNED_SIGNED_ORDERS_FEELESS: Array> = [ + { + takerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + }, + { + takerAssetAmount: new BigNumber(1).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(1).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI), + }, + { + takerAssetAmount: new BigNumber(1).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(1).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI), + }, +]; + +const expectMakerAndTakerBalancesAsyncFactory = ( + erc20TokenContract: ERC20TokenContract, + makerAddress: string, + takerAddress: string, +) => async (expectedMakerBalance: BigNumber, expectedTakerBalance: BigNumber) => { + const makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); + const takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); + expect(makerBalance).to.bignumber.equal(expectedMakerBalance); + expect(takerBalance).to.bignumber.equal(expectedTakerBalance); +}; + describe('ForwarderSwapQuoteConsumer', () => { - let contractWrappers: ContractWrappers; - let erc20Token: ERC20TokenContract; + const FEE_PERCENTAGE = 0.05; let userAddresses: string[]; let coinbaseAddress: string; let makerAddress: string; @@ -43,33 +79,68 @@ describe('ForwarderSwapQuoteConsumer', () => { let takerTokenAddress: string; let makerAssetData: string; let takerAssetData: string; + let orderFactory: OrderFactory; + let invalidOrderFactory: OrderFactory; let wethAssetData: string; let contractAddresses: ContractAddresses; + let erc20TokenContract: ERC20TokenContract; + let forwarderContract: ForwarderContract; - let orders: SignedOrder[]; + let orders: PrunedSignedOrder[]; + let invalidOrders: PrunedSignedOrder[]; let marketSellSwapQuote: SwapQuote; let marketBuySwapQuote: SwapQuote; + let invalidMarketBuySwapQuote: SwapQuote; let swapQuoteConsumer: ForwarderSwapQuoteConsumer; - let erc20ProxyAddress: string; - + let expectMakerAndTakerBalancesAsync: ( + expectedMakerBalance: BigNumber, + expectedTakerBalance: BigNumber, + ) => Promise; const chainId = TESTRPC_CHAIN_ID; + before(async () => { contractAddresses = await migrateOnceAsync(); await blockchainLifecycle.startAsync(); userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - const config = { - chainId, - contractAddresses, - }; - contractWrappers = new ContractWrappers(provider, config); [coinbaseAddress, takerAddress, makerAddress, feeRecipient] = userAddresses; [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); - erc20Token = new ERC20TokenContract(makerTokenAddress, provider); - [makerAssetData, takerAssetData, wethAssetData] = [ - await contractWrappers.devUtils.encodeERC20AssetData(makerTokenAddress).callAsync(), - await contractWrappers.devUtils.encodeERC20AssetData(takerTokenAddress).callAsync(), - await contractWrappers.devUtils.encodeERC20AssetData(contractAddresses.etherToken).callAsync(), - ]; + erc20TokenContract = new ERC20TokenContract(makerTokenAddress, provider); + forwarderContract = new ForwarderContract(contractAddresses.forwarder, provider); + const devUtils = new DevUtilsContract(contractAddresses.devUtils, provider); + [makerAssetData, takerAssetData, wethAssetData] = await Promise.all([ + devUtils.encodeERC20AssetData(makerTokenAddress).callAsync(), + devUtils.encodeERC20AssetData(takerTokenAddress).callAsync(), + devUtils.encodeERC20AssetData(contractAddresses.etherToken).callAsync(), + ]); + // Configure order defaults + const defaultOrderParams = { + ...devConstants.STATIC_ORDER_PARAMS, + makerAddress, + takerAddress: constants.NULL_ADDRESS, + makerAssetData, + takerAssetData: wethAssetData, + makerFeeAssetData: constants.NULL_ERC20_ASSET_DATA, + takerFeeAssetData: constants.NULL_ERC20_ASSET_DATA, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + feeRecipientAddress: feeRecipient, + exchangeAddress: contractAddresses.exchange, + chainId, + }; + const invalidDefaultOrderParams = { + ...defaultOrderParams, + ...{ + takerAssetData, + }, + }; + const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)]; + orderFactory = new OrderFactory(privateKey, defaultOrderParams); + expectMakerAndTakerBalancesAsync = expectMakerAndTakerBalancesAsyncFactory( + erc20TokenContract, + makerAddress, + takerAddress, + ); + invalidOrderFactory = new OrderFactory(privateKey, invalidDefaultOrderParams); }); after(async () => { await blockchainLifecycle.revertAsync(); @@ -77,35 +148,48 @@ describe('ForwarderSwapQuoteConsumer', () => { beforeEach(async () => { await blockchainLifecycle.startAsync(); const UNLIMITED_ALLOWANCE = UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - erc20ProxyAddress = contractAddresses.erc20Proxy; const totalFillableAmount = FILLABLE_AMOUNTS.reduce( (a: BigNumber, c: BigNumber) => a.plus(c), new BigNumber(0), ); - await erc20Token.transfer(makerAddress, totalFillableAmount).sendTransactionAsync({ + await erc20TokenContract.transfer(makerAddress, totalFillableAmount).sendTransactionAsync({ from: coinbaseAddress, }); - await erc20Token.approve(erc20ProxyAddress, UNLIMITED_ALLOWANCE).sendTransactionAsync({ - from: makerAddress, - }); - orders = await getSignedOrdersWithNoFeesAsync( - provider, - makerAssetData, - wethAssetData, - makerAddress, - takerAddress, - FILLABLE_AMOUNTS, - contractAddresses.exchange, - ); + await erc20TokenContract + .approve(contractAddresses.erc20Proxy, UNLIMITED_ALLOWANCE) + .sendTransactionAsync({ from: makerAddress }); + + await forwarderContract.approveMakerAssetProxy(makerAssetData).sendTransactionAsync({ from: makerAddress }); + + orders = []; + for (const partialOrder of PARTIAL_PRUNED_SIGNED_ORDERS_FEELESS) { + const order = await orderFactory.newSignedOrderAsync(partialOrder); + const prunedOrder = { + ...order, + ...partialOrder, + }; + orders.push(prunedOrder as PrunedSignedOrder); + } + + invalidOrders = []; + for (const partialOrder of PARTIAL_PRUNED_SIGNED_ORDERS_FEELESS) { + const order = await invalidOrderFactory.newSignedOrderAsync(partialOrder); + const prunedOrder = { + ...order, + ...partialOrder, + }; + invalidOrders.push(prunedOrder as PrunedSignedOrder); + } marketSellSwapQuote = getFullyFillableSwapQuoteWithNoFees( makerAssetData, wethAssetData, orders, MarketOperation.Sell, + GAS_PRICE, ); marketBuySwapQuote = getFullyFillableSwapQuoteWithNoFees( @@ -113,34 +197,29 @@ describe('ForwarderSwapQuoteConsumer', () => { wethAssetData, orders, MarketOperation.Buy, + GAS_PRICE, ); - swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, { + invalidMarketBuySwapQuote = getFullyFillableSwapQuoteWithNoFees( + makerAssetData, + takerAssetData, + invalidOrders, + MarketOperation.Buy, + GAS_PRICE, + ); + + swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, contractAddresses, { chainId, }); }); afterEach(async () => { await blockchainLifecycle.revertAsync(); }); - describe('executeSwapQuoteOrThrowAsync', () => { + describe('#executeSwapQuoteOrThrowAsync', () => { describe('validation', () => { - it('should throw if swapQuote provided is not a valid forwarder SwapQuote (taker asset is wEth', async () => { - const invalidSignedOrders = await getSignedOrdersWithNoFeesAsync( - provider, - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - FILLABLE_AMOUNTS, - ); - const invalidSwapQuote = getFullyFillableSwapQuoteWithNoFees( - makerAssetData, - takerAssetData, - invalidSignedOrders, - MARKET_OPERATION, - ); + it('should throw if swapQuote provided is not a valid forwarder SwapQuote (taker asset is wEth)', async () => { expect( - swapQuoteConsumer.executeSwapQuoteOrThrowAsync(invalidSwapQuote, { takerAddress }), + swapQuoteConsumer.executeSwapQuoteOrThrowAsync(invalidMarketBuySwapQuote, { takerAddress }), ).to.be.rejectedWith( `Expected quote.orders[0] to have takerAssetData set as ${wethAssetData}, but is ${takerAssetData}`, ); @@ -153,89 +232,98 @@ describe('ForwarderSwapQuoteConsumer', () => { * Testing that SwapQuoteConsumer logic correctly performs a execution (doesn't throw or revert) * Does not test the validity of the state change performed by the forwarder smart contract */ - it('should perform a marketSell execution when provided a MarketSell type swapQuote', async () => { - let makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - let takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); - expect(makerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { takerAddress }); - makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); - expect(makerBalance).to.bignumber.equal(new BigNumber(0.5).multipliedBy(ONE_ETH_IN_WEI)); - expect(takerBalance).to.bignumber.equal(new BigNumber(9.5).multipliedBy(ONE_ETH_IN_WEI)); + it('should perform a marketBuy execution when provided a MarketBuy type swapQuote', async () => { + await expectMakerAndTakerBalancesAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); + await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, { + takerAddress, + gasPrice: GAS_PRICE, + gasLimit: 4000000, + }); + await expectMakerAndTakerBalancesAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); }); - it('should perform a marketBuy execution when provided a MarketBuy type swapQuote', async () => { - let makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - let takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); - expect(makerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, { takerAddress }); - makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); - expect(takerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); + it('should perform a marketSell execution when provided a MarketSell type swapQuote', async () => { + await expectMakerAndTakerBalancesAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); + await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { + takerAddress, + gasPrice: GAS_PRICE, + gasLimit: 4000000, + }); + await expectMakerAndTakerBalancesAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); }); it('should perform a marketBuy execution with affiliate fees', async () => { - let makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - let takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); + await expectMakerAndTakerBalancesAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipient); - expect(makerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, { takerAddress, - feePercentage: 0.05, + gasPrice: GAS_PRICE, + gasLimit: 4000000, + feePercentage: FEE_PERCENTAGE, feeRecipient, }); - makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); + await expectMakerAndTakerBalancesAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipient); - expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - expect(takerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); + const totalEthSpent = marketBuySwapQuote.bestCaseQuoteInfo.totalTakerAssetAmount.plus( + marketBuySwapQuote.bestCaseQuoteInfo.protocolFeeInEthAmount, + ); expect(feeRecipientEthBalanceAfter.minus(feeRecipientEthBalanceBefore)).to.bignumber.equal( - new BigNumber(0.5).multipliedBy(ONE_ETH_IN_WEI), + new BigNumber(FEE_PERCENTAGE).times(totalEthSpent), ); }); - // TODO(david) Finish marketSell affiliate fee excution testing - // it('should perform a marketSell execution with affiliate fees', async () => { - // let makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - // let takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); - // const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipient); - // expect(makerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI)); - // expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - // console.log(makerBalance, takerBalance, feeRecipientEthBalanceBefore); - // await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { takerAddress, feePercentage: 0.05, feeRecipient }); - // makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - // takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); - // const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipient); - // console.log(makerBalance, takerBalance, feeRecipientEthBalanceAfter); - // expect(makerBalance).to.bignumber.equal((new BigNumber(0.5)).multipliedBy(ONE_ETH_IN_WEI)); - // expect(takerBalance).to.bignumber.equal((new BigNumber(9.5)).multipliedBy(ONE_ETH_IN_WEI)); - // expect(feeRecipientEthBalanceAfter.minus(feeRecipientEthBalanceBefore)).to.bignumber.equal((new BigNumber(0.5)).multipliedBy(ONE_ETH_IN_WEI)); - // }); + it('should perform a marketSell execution with affiliate fees', async () => { + await expectMakerAndTakerBalancesAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); + const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipient); + await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { + takerAddress, + feePercentage: FEE_PERCENTAGE, + feeRecipient, + gasPrice: GAS_PRICE, + gasLimit: 4000000, + }); + await expectMakerAndTakerBalancesAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); + const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipient); + const totalEthSpent = marketBuySwapQuote.bestCaseQuoteInfo.totalTakerAssetAmount.plus( + marketBuySwapQuote.bestCaseQuoteInfo.protocolFeeInEthAmount, + ); + expect(feeRecipientEthBalanceAfter.minus(feeRecipientEthBalanceBefore)).to.bignumber.equal( + new BigNumber(FEE_PERCENTAGE).times(totalEthSpent), + ); + }); }); }); - describe('getSmartContractParamsOrThrow', () => { + describe('#getSmartContractParamsOrThrow', () => { describe('validation', () => { it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => { - const invalidSignedOrders = await getSignedOrdersWithNoFeesAsync( - provider, - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - FILLABLE_AMOUNTS, - ); - const invalidSwapQuote = getFullyFillableSwapQuoteWithNoFees( - makerAssetData, - takerAssetData, - invalidSignedOrders, - MARKET_OPERATION, - ); - expect(swapQuoteConsumer.getSmartContractParamsOrThrowAsync(invalidSwapQuote, {})).to.be.rejectedWith( + expect( + swapQuoteConsumer.getSmartContractParamsOrThrowAsync(invalidMarketBuySwapQuote, {}), + ).to.be.rejectedWith( `Expected quote.orders[0] to have takerAssetData set as ${wethAssetData}, but is ${takerAssetData}`, ); }); @@ -247,9 +335,8 @@ describe('ForwarderSwapQuoteConsumer', () => { marketSellSwapQuote, {}, ); - expect(toAddress).to.deep.equal(contractWrappers.forwarder.address); + expect(toAddress).to.deep.equal(forwarderContract.address); const { - feeSignatures, feePercentage, feeRecipient: feeRecipientFromParams, signatures, @@ -260,17 +347,15 @@ describe('ForwarderSwapQuoteConsumer', () => { const orderSignatures = marketSellSwapQuote.orders.map(order => order.signature); expect(signatures).to.deep.equal(orderSignatures); expect(feePercentage).to.bignumber.equal(0); - expect(feeSignatures).to.deep.equal([]); }); it('provide correct and optimized smart contract params with default options for a marketBuy SwapQuote (no affiliate fees)', async () => { const { toAddress, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync( marketBuySwapQuote, {}, ); - expect(toAddress).to.deep.equal(contractWrappers.forwarder.address); + expect(toAddress).to.deep.equal(forwarderContract.address); const { makerAssetFillAmount, - feeSignatures, feePercentage, feeRecipient: feeRecipientFromParams, signatures, @@ -284,7 +369,6 @@ describe('ForwarderSwapQuoteConsumer', () => { const orderSignatures = marketBuySwapQuote.orders.map(order => order.signature); expect(signatures).to.deep.equal(orderSignatures); expect(feePercentage).to.bignumber.equal(0); - expect(feeSignatures).to.deep.equal([]); }); it('provide correct and optimized smart contract params with affiliate fees for a marketSell SwapQuote', async () => { const { toAddress, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync( @@ -294,9 +378,8 @@ describe('ForwarderSwapQuoteConsumer', () => { feeRecipient, }, ); - expect(toAddress).to.deep.equal(contractWrappers.forwarder.address); + expect(toAddress).to.deep.equal(forwarderContract.address); const { - feeSignatures, feePercentage, feeRecipient: feeRecipientFromParams, signatures, @@ -307,7 +390,6 @@ describe('ForwarderSwapQuoteConsumer', () => { const orderSignatures = marketSellSwapQuote.orders.map(order => order.signature); expect(signatures).to.deep.equal(orderSignatures); expect(feePercentage).to.bignumber.equal(new BigNumber(0.05).multipliedBy(ONE_ETH_IN_WEI)); - expect(feeSignatures).to.deep.equal([]); }); it('provide correct and optimized smart contract params with affiliate fees for a marketBuy SwapQuote', async () => { const { toAddress, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync( @@ -317,10 +399,9 @@ describe('ForwarderSwapQuoteConsumer', () => { feeRecipient, }, ); - expect(toAddress).to.deep.equal(contractWrappers.forwarder.address); + expect(toAddress).to.deep.equal(forwarderContract.address); const { makerAssetFillAmount, - feeSignatures, feePercentage, feeRecipient: feeRecipientFromParams, signatures, @@ -334,29 +415,14 @@ describe('ForwarderSwapQuoteConsumer', () => { const orderSignatures = marketBuySwapQuote.orders.map(order => order.signature); expect(signatures).to.deep.equal(orderSignatures); expect(feePercentage).to.bignumber.equal(new BigNumber(0.05).multipliedBy(ONE_ETH_IN_WEI)); - expect(feeSignatures).to.deep.equal([]); }); }); }); - describe('getCalldataOrThrow', () => { + describe('#getCalldataOrThrow', () => { describe('validation', () => { it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => { - const invalidSignedOrders = await getSignedOrdersWithNoFeesAsync( - provider, - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - FILLABLE_AMOUNTS, - ); - const invalidSwapQuote = getFullyFillableSwapQuoteWithNoFees( - makerAssetData, - takerAssetData, - invalidSignedOrders, - MARKET_OPERATION, - ); - expect(swapQuoteConsumer.getCalldataOrThrowAsync(invalidSwapQuote, {})).to.be.rejectedWith( + expect(swapQuoteConsumer.getCalldataOrThrowAsync(invalidMarketBuySwapQuote, {})).to.be.rejectedWith( `Expected quote.orders[0] to have takerAssetData set as ${wethAssetData}, but is ${takerAssetData}`, ); }); @@ -364,33 +430,34 @@ describe('ForwarderSwapQuoteConsumer', () => { describe('valid swap quote', async () => { it('provide correct and optimized calldata options with default options for a marketSell SwapQuote (no affiliate fees)', async () => { - let makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - let takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); - expect(makerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - const { calldataHexString, toAddress } = await swapQuoteConsumer.getCalldataOrThrowAsync( + await expectMakerAndTakerBalancesAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); + const { calldataHexString, toAddress, ethAmount } = await swapQuoteConsumer.getCalldataOrThrowAsync( marketSellSwapQuote, {}, ); - expect(toAddress).to.deep.equal(contractWrappers.forwarder.address); + expect(toAddress).to.deep.equal(forwarderContract.address); await web3Wrapper.sendTransactionAsync({ from: takerAddress, to: toAddress, data: calldataHexString, - value: marketSellSwapQuote.worstCaseQuoteInfo.totalTakerTokenAmount, + value: ethAmount, + gasPrice: GAS_PRICE, gas: 4000000, }); - makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); - expect(makerBalance).to.bignumber.equal(new BigNumber(0.5).multipliedBy(ONE_ETH_IN_WEI)); - expect(takerBalance).to.bignumber.equal(new BigNumber(9.5).multipliedBy(ONE_ETH_IN_WEI)); + await expectMakerAndTakerBalancesAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); }); it('provide correct and optimized calldata options with default options for a marketBuy SwapQuote (no affiliate fees)', async () => { - let makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - let takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); - expect(makerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - const { calldataHexString, toAddress } = await swapQuoteConsumer.getCalldataOrThrowAsync( + await expectMakerAndTakerBalancesAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); + const { calldataHexString, toAddress, ethAmount } = await swapQuoteConsumer.getCalldataOrThrowAsync( marketBuySwapQuote, {}, ); @@ -399,19 +466,84 @@ describe('ForwarderSwapQuoteConsumer', () => { from: takerAddress, to: toAddress, data: calldataHexString, - value: marketBuySwapQuote.worstCaseQuoteInfo.totalTakerTokenAmount, + value: ethAmount, + gasPrice: GAS_PRICE, gas: 4000000, }); - makerBalance = await erc20Token.balanceOf(makerAddress).callAsync(); - takerBalance = await erc20Token.balanceOf(takerAddress).callAsync(); - expect(takerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); + await expectMakerAndTakerBalancesAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); + }); + it('provide correct and optimized calldata options with affiliate fees for a marketSell SwapQuote', async () => { + await expectMakerAndTakerBalancesAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); + const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipient); + const { calldataHexString, toAddress, ethAmount } = await swapQuoteConsumer.getCalldataOrThrowAsync( + marketSellSwapQuote, + { + feePercentage: FEE_PERCENTAGE, + feeRecipient, + }, + ); + expect(toAddress).to.deep.equal(contractAddresses.forwarder); + await web3Wrapper.sendTransactionAsync({ + from: takerAddress, + to: toAddress, + data: calldataHexString, + value: ethAmount, + gasPrice: GAS_PRICE, + gas: 4000000, + }); + await expectMakerAndTakerBalancesAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); + const totalEthSpent = marketBuySwapQuote.bestCaseQuoteInfo.totalTakerAssetAmount.plus( + marketBuySwapQuote.bestCaseQuoteInfo.protocolFeeInEthAmount, + ); + const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipient); + expect(feeRecipientEthBalanceAfter.minus(feeRecipientEthBalanceBefore)).to.bignumber.equal( + new BigNumber(FEE_PERCENTAGE).times(totalEthSpent), + ); + }); + it('provide correct and optimized calldata options with affiliate fees for a marketBuy SwapQuote', async () => { + await expectMakerAndTakerBalancesAsync( + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + constants.ZERO_AMOUNT, + ); + const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipient); + const { calldataHexString, toAddress, ethAmount } = await swapQuoteConsumer.getCalldataOrThrowAsync( + marketBuySwapQuote, + { + feePercentage: FEE_PERCENTAGE, + feeRecipient, + }, + ); + expect(toAddress).to.deep.equal(contractAddresses.forwarder); + await web3Wrapper.sendTransactionAsync({ + from: takerAddress, + to: toAddress, + data: calldataHexString, + value: ethAmount, + gasPrice: GAS_PRICE, + gas: 4000000, + }); + await expectMakerAndTakerBalancesAsync( + constants.ZERO_AMOUNT, + new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI), + ); + const totalEthSpent = marketBuySwapQuote.bestCaseQuoteInfo.totalTakerAssetAmount.plus( + marketBuySwapQuote.bestCaseQuoteInfo.protocolFeeInEthAmount, + ); + const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipient); + expect(feeRecipientEthBalanceAfter.minus(feeRecipientEthBalanceBefore)).to.bignumber.equal( + new BigNumber(FEE_PERCENTAGE).times(totalEthSpent), + ); }); - // TODO(david) finish testing for affiliate fees calldata output - // it('provide correct and optimized calldata options with affiliate fees for a marketSell SwapQuote', async () => { - // }); - // it('provide correct and optimized calldata options with affiliate fees for a marketBuy SwapQuote', async () => { - // }); }); }); + // tslint:disable-next-line: max-file-line-count }); diff --git a/packages/asset-swapper/test/market_utils_test.ts b/packages/asset-swapper/test/market_utils_test.ts new file mode 100644 index 0000000000..3ac467c1ac --- /dev/null +++ b/packages/asset-swapper/test/market_utils_test.ts @@ -0,0 +1,374 @@ +import * as chai from 'chai'; +import * as _ from 'lodash'; +import 'mocha'; + +import { constants } from '../src/constants'; +import { marketUtils } from '../src/utils/market_utils'; + +import { chaiSetup } from './utils/chai_setup'; +import { testOrders } from './utils/test_orders'; +import { baseUnitAmount } from './utils/utils'; + +chaiSetup.configure(); +const expect = chai.expect; + +// tslint:disable:custom-no-magic-numbers +// tslint:disable: no-unused-expression +describe('marketUtils', () => { + describe('#findOrdersThatCoverTakerAssetFillAmount', () => { + describe('no orders', () => { + it('returns empty and unchanged remainingFillAmount', async () => { + const fillAmount = baseUnitAmount(9); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + [], + fillAmount, + ); + expect(resultOrders).to.be.empty; + expect(remainingFillAmount).to.be.bignumber.equal(fillAmount); + }); + }); + describe('orders do not have fees', () => { + const inputOrders = testOrders.PRUNED_SIGNED_ORDERS_FEELESS; + it('returns input orders and zero remainingFillAmount when input exactly matches requested fill amount', async () => { + const fillAmount = baseUnitAmount(5); + const slippageBufferAmount = baseUnitAmount(4); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and zero remainingFillAmount when input has more than requested fill amount', async () => { + const fillAmount = baseUnitAmount(6); + const slippageBufferAmount = baseUnitAmount(3); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and non-zero remainingFillAmount when input has less than requested fill amount', async () => { + const fillAmount = baseUnitAmount(10); + const slippageBufferAmount = baseUnitAmount(2); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(baseUnitAmount(3)); + }); + it('returns first order and zero remainingFillAmount when requested fill amount is exactly covered by the first order', async () => { + const fillAmount = baseUnitAmount(1); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns first two orders and zero remainingFillAmount when requested fill amount is over covered by the first two order', async () => { + const fillAmount = baseUnitAmount(6); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0], inputOrders[1]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + }); + describe('orders have fees in takerAsset', () => { + const inputOrders = testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET; + it('returns input orders and zero remainingFillAmount when input exactly matches requested fill amount', async () => { + const fillAmount = baseUnitAmount(10); + const slippageBufferAmount = baseUnitAmount(5); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and zero remainingFillAmount when input has more than requested fill amount', async () => { + const fillAmount = baseUnitAmount(6); + const slippageBufferAmount = baseUnitAmount(6); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and non-zero remainingFillAmount when input has less than requested fill amount', async () => { + const fillAmount = baseUnitAmount(10); + const slippageBufferAmount = baseUnitAmount(6); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(baseUnitAmount(1)); + }); + it('returns first order and zero remainingFillAmount when requested fill amount is exactly covered by the first order', async () => { + const fillAmount = baseUnitAmount(4); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns first two orders and zero remainingFillAmount when requested fill amount is over covered by the first two order', async () => { + const fillAmount = baseUnitAmount(9); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0], inputOrders[1]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + }); + describe('orders are feeless or have fees in takerAsset', () => { + const inputOrders = _.concat( + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, + testOrders.PRUNED_SIGNED_ORDERS_FEELESS, + ); + it('returns input orders and zero remainingFillAmount when input exactly matches requested fill amount', async () => { + const fillAmount = baseUnitAmount(20); + const slippageBufferAmount = baseUnitAmount(4); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and zero remainingFillAmount when input has more than requested fill amount', async () => { + const fillAmount = baseUnitAmount(10); + const slippageBufferAmount = baseUnitAmount(12); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and non-zero remainingFillAmount when input has less than requested fill amount', async () => { + const fillAmount = baseUnitAmount(20); + const slippageBufferAmount = baseUnitAmount(6); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(baseUnitAmount(2)); + }); + it('returns first order and zero remainingFillAmount when requested fill amount is exactly covered by the first order', async () => { + const fillAmount = baseUnitAmount(4); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns first four orders and zero remainingFillAmount when requested fill amount is over covered by the first two order', async () => { + const fillAmount = baseUnitAmount(16); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0], inputOrders[1], inputOrders[2], inputOrders[3]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + }); + }); + describe('#findOrdersThatCoverMakerAssetFillAmount', () => { + describe('no orders', () => { + it('returns empty and unchanged remainingFillAmount', async () => { + const fillAmount = baseUnitAmount(9); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + [], + fillAmount, + ); + expect(resultOrders).to.be.empty; + expect(remainingFillAmount).to.be.bignumber.equal(fillAmount); + }); + }); + describe('orders do not have fees', () => { + const inputOrders = testOrders.PRUNED_SIGNED_ORDERS_FEELESS; + it('returns input orders and zero remainingFillAmount when input exactly matches requested fill amount', async () => { + const fillAmount = baseUnitAmount(6); + const slippageBufferAmount = baseUnitAmount(4); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and zero remainingFillAmount when input has more than requested fill amount', async () => { + const fillAmount = baseUnitAmount(6); + const slippageBufferAmount = baseUnitAmount(3); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and non-zero remainingFillAmount when input has less than requested fill amount', async () => { + const fillAmount = baseUnitAmount(10); + const slippageBufferAmount = baseUnitAmount(2); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(baseUnitAmount(2)); + }); + it('returns first order and zero remainingFillAmount when requested fill amount is exactly covered by the first order', async () => { + const fillAmount = baseUnitAmount(6); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns first two orders and zero remainingFillAmount when requested fill amount is over covered by the first two order', async () => { + const fillAmount = baseUnitAmount(8); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0], inputOrders[1]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + }); + describe('orders have fees in makerAsset', () => { + const inputOrders = testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET; + it('returns input orders and zero remainingFillAmount when input exactly matches requested fill amount', async () => { + const fillAmount = baseUnitAmount(2); + const slippageBufferAmount = baseUnitAmount(3); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and zero remainingFillAmount when input has more than requested fill amount', async () => { + const fillAmount = baseUnitAmount(4); + const slippageBufferAmount = baseUnitAmount(0.5); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and non-zero remainingFillAmount when input has less than requested fill amount', async () => { + const fillAmount = baseUnitAmount(3); + const slippageBufferAmount = baseUnitAmount(3); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(baseUnitAmount(1)); + }); + it('returns first order and zero remainingFillAmount when requested fill amount is exactly covered by the first order', async () => { + const fillAmount = baseUnitAmount(1); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns first two orders and zero remainingFillAmount when requested fill amount is over covered by the first two order', async () => { + const fillAmount = baseUnitAmount(2); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0], inputOrders[1]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + }); + describe('orders are feeless or have fees in makerAsset', () => { + const inputOrders = _.concat( + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, + testOrders.PRUNED_SIGNED_ORDERS_FEELESS, + ); + it('returns input orders and zero remainingFillAmount when input exactly matches requested fill amount', async () => { + const fillAmount = baseUnitAmount(12); + const slippageBufferAmount = baseUnitAmount(3); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and zero remainingFillAmount when input has more than requested fill amount', async () => { + const fillAmount = baseUnitAmount(12); + const slippageBufferAmount = baseUnitAmount(2); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and non-zero remainingFillAmount when input has less than requested fill amount', async () => { + const fillAmount = baseUnitAmount(14); + const slippageBufferAmount = baseUnitAmount(4); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(baseUnitAmount(3)); + }); + it('returns first order and zero remainingFillAmount when requested fill amount is exactly covered by the first order', async () => { + const fillAmount = baseUnitAmount(1); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns first four orders and zero remainingFillAmount when requested fill amount is over covered by the first two order', async () => { + const fillAmount = baseUnitAmount(11); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0], inputOrders[1], inputOrders[2], inputOrders[3]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + }); + }); +}); diff --git a/packages/asset-swapper/test/order_prune_utils_test.ts b/packages/asset-swapper/test/order_prune_utils_test.ts new file mode 100644 index 0000000000..6baf3d49e0 --- /dev/null +++ b/packages/asset-swapper/test/order_prune_utils_test.ts @@ -0,0 +1,336 @@ +import { ContractAddresses } from '@0x/contract-addresses'; +import { DevUtilsContract } from '@0x/contracts-dev-utils'; +import { ERC20TokenContract } from '@0x/contracts-erc20'; +import { ExchangeContract } from '@0x/contracts-exchange'; +import { constants as devConstants, getLatestBlockTimestampAsync, OrderFactory } from '@0x/contracts-test-utils'; +import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; +import { assetDataUtils } from '@0x/order-utils'; +import { SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import * as chai from 'chai'; +import 'mocha'; + +import { constants } from '../src/constants'; +import { OrderPrunerPermittedFeeTypes } from '../src/types'; +import { OrderPruner } from '../src/utils/order_prune_utils'; + +import { chaiSetup } from './utils/chai_setup'; +import { migrateOnceAsync } from './utils/migrate'; +import { provider, web3Wrapper } from './utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +const ONE_ETH_IN_WEI = new BigNumber(1000000000000000000); +const TESTRPC_CHAIN_ID = devConstants.TESTRPC_CHAIN_ID; +const GAS_PRICE = new BigNumber(devConstants.DEFAULT_GAS_PRICE); +const PROTOCOL_FEE_PER_FILL = GAS_PRICE.times(constants.PROTOCOL_FEE_MULTIPLIER); +const UNLIMITED_ALLOWANCE_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1); // tslint:disable-line:custom-no-magic-numbers + +// tslint:disable: no-unused-expression +// tslint:disable: custom-no-magic-numbers +describe('OrderPruner', () => { + let erc20MakerTokenContract: ERC20TokenContract; + let erc20TakerTokenContract: ERC20TokenContract; + let exchangeContract: ExchangeContract; + let devUtilsContract: DevUtilsContract; + let userAddresses: string[]; + let coinbaseAddress: string; + let makerAddress: string; + let takerAddress: string; + let feeRecipient: string; + let makerTokenAddress: string; + let takerTokenAddress: string; + let makerAssetData: string; + let takerAssetData: string; + let orderFactory: OrderFactory; + let wethAssetData: string; + let contractAddresses: ContractAddresses; + let orderPruner: OrderPruner; + + let nonOpenSignedOrder: SignedOrder; + let expiredOpenSignedOrder: SignedOrder; + let invalidSignatureOpenSignedOrder: SignedOrder; + let fullyFillableOpenSignedOrder: SignedOrder; + let partiallyFilledOpenSignedOrderFeeless: SignedOrder; + let partiallyFilledOpenSignedOrderFeeInTakerAsset: SignedOrder; + let partiallyFilledOpenSignedOrderFeeInMakerAsset: SignedOrder; + let filledOpenSignedOrder: SignedOrder; + + const chainId = TESTRPC_CHAIN_ID; + const fillableAmount = new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI); + const partialFillAmount = new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI); + const takerFeeAmount = new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI); + + before(async () => { + contractAddresses = await migrateOnceAsync(); + await blockchainLifecycle.startAsync(); + userAddresses = await web3Wrapper.getAvailableAddressesAsync(); + [coinbaseAddress, takerAddress, makerAddress, feeRecipient] = userAddresses; + [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); + erc20MakerTokenContract = new ERC20TokenContract(makerTokenAddress, provider); + erc20TakerTokenContract = new ERC20TokenContract(takerTokenAddress, provider); + [makerAssetData, takerAssetData, wethAssetData] = [ + assetDataUtils.encodeERC20AssetData(makerTokenAddress), + assetDataUtils.encodeERC20AssetData(takerTokenAddress), + assetDataUtils.encodeERC20AssetData(contractAddresses.etherToken), + ]; + exchangeContract = new ExchangeContract(contractAddresses.exchange, provider); + devUtilsContract = new DevUtilsContract(contractAddresses.devUtils, provider); + // Configure order defaults + const defaultOrderParams = { + ...devConstants.STATIC_ORDER_PARAMS, + makerAddress, + takerAddress: constants.NULL_ADDRESS, + makerAssetData, + takerAssetData, + makerFeeAssetData: constants.NULL_ERC20_ASSET_DATA, + takerFeeAssetData: constants.NULL_ERC20_ASSET_DATA, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + feeRecipientAddress: feeRecipient, + exchangeAddress: contractAddresses.exchange, + chainId, + }; + const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)]; + orderFactory = new OrderFactory(privateKey, defaultOrderParams); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + + nonOpenSignedOrder = await orderFactory.newSignedOrderAsync({ + takerAddress, + }); + + expiredOpenSignedOrder = await orderFactory.newSignedOrderAsync({ + expirationTimeSeconds: new BigNumber(await getLatestBlockTimestampAsync()).minus(10), + }); + + invalidSignatureOpenSignedOrder = await orderFactory.newSignedOrderAsync({ + takerAddress, + }); + invalidSignatureOpenSignedOrder.signature = constants.NULL_BYTES; + + fullyFillableOpenSignedOrder = await orderFactory.newSignedOrderAsync({ + takerAssetAmount: fillableAmount, + makerAssetAmount: fillableAmount, + }); + + // give double fillableAmount to maker and taker as buffer + await erc20MakerTokenContract + .transfer(makerAddress, fillableAmount.multipliedBy(4)) + .sendTransactionAsync({ from: coinbaseAddress }); + await erc20TakerTokenContract + .transfer(takerAddress, fillableAmount.multipliedBy(4)) + .sendTransactionAsync({ from: coinbaseAddress }); + await erc20MakerTokenContract + .approve(contractAddresses.erc20Proxy, UNLIMITED_ALLOWANCE_IN_BASE_UNITS) + .sendTransactionAsync({ from: makerAddress }); + await erc20MakerTokenContract + .approve(contractAddresses.erc20Proxy, UNLIMITED_ALLOWANCE_IN_BASE_UNITS) + .sendTransactionAsync({ from: takerAddress }); + await erc20TakerTokenContract + .approve(contractAddresses.erc20Proxy, UNLIMITED_ALLOWANCE_IN_BASE_UNITS) + .sendTransactionAsync({ from: takerAddress }); + + partiallyFilledOpenSignedOrderFeeless = await orderFactory.newSignedOrderAsync({ + takerAssetAmount: fillableAmount, + makerAssetAmount: fillableAmount, + }); + + await exchangeContract + .fillOrKillOrder( + partiallyFilledOpenSignedOrderFeeless, + partialFillAmount, + partiallyFilledOpenSignedOrderFeeless.signature, + ) + .sendTransactionAsync({ + from: takerAddress, + gasPrice: GAS_PRICE, + gas: 4000000, + value: PROTOCOL_FEE_PER_FILL, + }); + + partiallyFilledOpenSignedOrderFeeInTakerAsset = await orderFactory.newSignedOrderAsync({ + takerAssetAmount: fillableAmount, + makerAssetAmount: fillableAmount, + takerFee: takerFeeAmount, + takerFeeAssetData: takerAssetData, + }); + + await exchangeContract + .fillOrKillOrder( + partiallyFilledOpenSignedOrderFeeInTakerAsset, + partialFillAmount, + partiallyFilledOpenSignedOrderFeeInTakerAsset.signature, + ) + .sendTransactionAsync({ + from: takerAddress, + gasPrice: GAS_PRICE, + gas: 4000000, + value: PROTOCOL_FEE_PER_FILL, + }); + + partiallyFilledOpenSignedOrderFeeInMakerAsset = await orderFactory.newSignedOrderAsync({ + takerAssetAmount: fillableAmount, + makerAssetAmount: fillableAmount, + takerFee: takerFeeAmount, + takerFeeAssetData: makerAssetData, + }); + + await exchangeContract + .fillOrKillOrder( + partiallyFilledOpenSignedOrderFeeInMakerAsset, + partialFillAmount, + partiallyFilledOpenSignedOrderFeeInMakerAsset.signature, + ) + .sendTransactionAsync({ + from: takerAddress, + gasPrice: GAS_PRICE, + gas: 4000000, + value: PROTOCOL_FEE_PER_FILL, + }); + + filledOpenSignedOrder = await orderFactory.newSignedOrderAsync({ + takerAssetAmount: fillableAmount, + makerAssetAmount: fillableAmount, + }); + + await exchangeContract + .fillOrKillOrder(filledOpenSignedOrder, fillableAmount, filledOpenSignedOrder.signature) + .sendTransactionAsync({ + from: takerAddress, + gasPrice: GAS_PRICE, + gas: 4000000, + value: PROTOCOL_FEE_PER_FILL, + }); + + orderPruner = new OrderPruner(devUtilsContract, { + permittedOrderFeeTypes: new Set([ + OrderPrunerPermittedFeeTypes.NoFees, + OrderPrunerPermittedFeeTypes.MakerDenominatedTakerFee, + OrderPrunerPermittedFeeTypes.TakerDenominatedTakerFee, + ]), + }); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('constructor options', () => { + it('should filter for only feeless orders if options permit only feeless orders', async () => { + orderPruner = new OrderPruner(devUtilsContract, { + permittedOrderFeeTypes: new Set([OrderPrunerPermittedFeeTypes.NoFees]), + }); + const orders = [ + partiallyFilledOpenSignedOrderFeeInMakerAsset, + partiallyFilledOpenSignedOrderFeeInTakerAsset, + partiallyFilledOpenSignedOrderFeeless, + ]; + const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders); + // checks for one order in results and check for signature of orders + expect(resultPrunedOrders.length).to.be.equal(1); + expect(resultPrunedOrders[0].signature).to.be.deep.equal(partiallyFilledOpenSignedOrderFeeless.signature); + }); + it('should filter for only takerFee in takerAsset orders if options permit only takerFee in takerAsset orders', async () => { + orderPruner = new OrderPruner(devUtilsContract, { + permittedOrderFeeTypes: new Set([ + OrderPrunerPermittedFeeTypes.TakerDenominatedTakerFee, + ]), + }); + const orders = [ + partiallyFilledOpenSignedOrderFeeInMakerAsset, + partiallyFilledOpenSignedOrderFeeInTakerAsset, + partiallyFilledOpenSignedOrderFeeless, + ]; + const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders); + // checks for one order in results and check for signature of orders + expect(resultPrunedOrders.length).to.be.equal(1); + expect(resultPrunedOrders[0].signature).to.be.deep.equal( + partiallyFilledOpenSignedOrderFeeInTakerAsset.signature, + ); + }); + it('should filter for only makerFee in takerAsset orders if options permit only makerFee orders', async () => { + orderPruner = new OrderPruner(devUtilsContract, { + permittedOrderFeeTypes: new Set([ + OrderPrunerPermittedFeeTypes.MakerDenominatedTakerFee, + ]), + }); + const orders = [ + partiallyFilledOpenSignedOrderFeeInMakerAsset, + partiallyFilledOpenSignedOrderFeeInTakerAsset, + partiallyFilledOpenSignedOrderFeeless, + ]; + const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders); + // checks for one order in results and check for signature of orders + expect(resultPrunedOrders.length).to.be.equal(1); + expect(resultPrunedOrders[0].signature).to.be.deep.equal( + partiallyFilledOpenSignedOrderFeeInMakerAsset.signature, + ); + }); + }); + describe('#pruneSignedOrdersAsync', () => { + it('should filter out non open orders', async () => { + const orders = [nonOpenSignedOrder]; + const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders); + expect(resultPrunedOrders).to.be.empty; + }); + it('should filter out expired orders', async () => { + const orders = [expiredOpenSignedOrder]; + const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders); + expect(resultPrunedOrders).to.be.empty; + }); + it('should filter out invalid signature orders', async () => { + const orders = [invalidSignatureOpenSignedOrder]; + const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders); + expect(resultPrunedOrders).to.be.empty; + }); + it('should filter out fully filled orders', async () => { + const orders = [filledOpenSignedOrder]; + const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders); + expect(resultPrunedOrders).to.be.empty; + }); + it('should provide correct pruned signed orders for fully fillable orders', async () => { + const orders = [fullyFillableOpenSignedOrder]; + const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders); + const prunedOrder = resultPrunedOrders[0]; + expect(prunedOrder.fillableMakerAssetAmount).to.bignumber.equal(fillableAmount); + expect(prunedOrder.fillableTakerAssetAmount).to.bignumber.equal(fillableAmount); + }); + it('should provide correct pruned signed orders for partially fillable orders', async () => { + const orders = [ + partiallyFilledOpenSignedOrderFeeless, + partiallyFilledOpenSignedOrderFeeInTakerAsset, + partiallyFilledOpenSignedOrderFeeInMakerAsset, + ]; + const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders); + expect(resultPrunedOrders[0].fillableMakerAssetAmount).to.bignumber.equal( + fillableAmount.minus(partialFillAmount), + ); + expect(resultPrunedOrders[0].fillableTakerAssetAmount).to.bignumber.equal( + fillableAmount.minus(partialFillAmount), + ); + expect(resultPrunedOrders[1].fillableMakerAssetAmount).to.bignumber.equal( + fillableAmount.minus(partialFillAmount), + ); + expect(resultPrunedOrders[1].fillableTakerAssetAmount).to.bignumber.equal( + fillableAmount.minus(partialFillAmount), + ); + expect(resultPrunedOrders[1].fillableTakerFeeAmount).to.bignumber.equal( + new BigNumber(1.6).multipliedBy(ONE_ETH_IN_WEI), + ); + expect(resultPrunedOrders[2].fillableMakerAssetAmount).to.bignumber.equal( + fillableAmount.minus(partialFillAmount), + ); + expect(resultPrunedOrders[2].fillableTakerAssetAmount).to.bignumber.equal( + fillableAmount.minus(partialFillAmount), + ); + expect(resultPrunedOrders[2].fillableTakerFeeAmount).to.bignumber.equal( + new BigNumber(1.6).multipliedBy(ONE_ETH_IN_WEI), + ); + }); + }); +}); diff --git a/packages/asset-swapper/test/sorting_utils_test.ts b/packages/asset-swapper/test/sorting_utils_test.ts new file mode 100644 index 0000000000..e11e03f35d --- /dev/null +++ b/packages/asset-swapper/test/sorting_utils_test.ts @@ -0,0 +1,134 @@ +import { BigNumber } from '@0x/utils'; +import * as chai from 'chai'; +import 'mocha'; + +import { sortingUtils } from '../src/utils/sorting_utils'; + +import { chaiSetup } from './utils/chai_setup'; +import { testOrderFactory } from './utils/test_order_factory'; + +chaiSetup.configure(); +const expect = chai.expect; + +const FAKE_ERC20_TAKER_ASSET_DATA = '0xf47261b22222222222222222222222222222222222222222222222222222222222222222'; +const FAKE_ERC20_MAKER_ASSET_DATA = '0xf47261b11111111111111111111111111111111111111111111111111111111111111111'; + +describe('sortingUtils', () => { + describe('#sortOrders', () => { + // rate: 2 takerAsset / makerAsset + const testOrder1 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(200), + takerAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + makerAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + }); + // rate: 1 takerAsset / makerAsset + const testOrder2 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(100), + takerAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + makerAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + }); + // rate: 2.5 takerAsset / makerAsset + const testOrder3 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(250), + takerAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + makerAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + }); + // rate: 2 takerAsset / makerAsset + const testOrderWithFeeInTakerAsset1 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(100), + takerFee: new BigNumber(100), + takerFeeAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + takerAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + makerAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + }); + // rate: 1 takerAsset / makerAsset + const testOrderWithFeeInTakerAsset2 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(50), + takerFee: new BigNumber(50), + takerFeeAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + takerAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + makerAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + }); + // rate: 2.5 takerAsset / makerAsset + const testOrderWithFeeInTakerAsset3 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(200), + takerFee: new BigNumber(50), + takerFeeAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + takerAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + makerAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + }); + // rate: 2 takerAsset / makerAsset + const testOrderWithFeeInMakerAsset1 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(200), + takerAssetAmount: new BigNumber(200), + takerFee: new BigNumber(100), + takerFeeAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + takerAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + makerAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + }); + // rate: 1 takerAsset / makerAsset + const testOrderWithFeeInMakerAsset2 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(150), + takerAssetAmount: new BigNumber(100), + takerFee: new BigNumber(50), + takerFeeAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + takerAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + makerAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + }); + // rate: 2.5 takerAsset / makerAsset + const testOrderWithFeeInMakerAsset3 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(150), + takerAssetAmount: new BigNumber(250), + takerFee: new BigNumber(50), + takerFeeAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + takerAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + makerAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + }); + it('correctly sorts by fee adjusted rate (feeless orders)', async () => { + const orders = [testOrder1, testOrder2, testOrder3]; + const sortedOrders = sortingUtils.sortOrders(orders); + expect(sortedOrders).to.deep.equal([testOrder2, testOrder1, testOrder3]); + }); + it('correctly sorts by fee adjusted rate (takerAsset denominated fee orders)', async () => { + const orders = [ + testOrderWithFeeInTakerAsset1, + testOrderWithFeeInTakerAsset2, + testOrderWithFeeInTakerAsset3, + ]; + const sortedOrders = sortingUtils.sortOrders(orders); + expect(sortedOrders).to.deep.equal([ + testOrderWithFeeInTakerAsset2, + testOrderWithFeeInTakerAsset1, + testOrderWithFeeInTakerAsset3, + ]); + }); + it('correctly sorts by fee adjusted rate (makerAsset denominated fee orders)', async () => { + const orders = [ + testOrderWithFeeInMakerAsset1, + testOrderWithFeeInMakerAsset2, + testOrderWithFeeInMakerAsset3, + ]; + const sortedOrders = sortingUtils.sortOrders(orders); + expect(sortedOrders).to.deep.equal([ + testOrderWithFeeInMakerAsset2, + testOrderWithFeeInMakerAsset1, + testOrderWithFeeInMakerAsset3, + ]); + }); + it('correctly sorts by fee adjusted rate (mixed orders)', async () => { + const orders = [testOrderWithFeeInMakerAsset1, testOrderWithFeeInTakerAsset2, testOrder3]; + const sortedOrders = sortingUtils.sortOrders(orders); + expect(sortedOrders).to.deep.equal([ + testOrderWithFeeInTakerAsset2, + testOrderWithFeeInMakerAsset1, + testOrder3, + ]); + }); + }); +}); diff --git a/packages/asset-swapper/test/swap_quote_calculator_test.ts b/packages/asset-swapper/test/swap_quote_calculator_test.ts index a46ebd3c75..013aad277f 100644 --- a/packages/asset-swapper/test/swap_quote_calculator_test.ts +++ b/packages/asset-swapper/test/swap_quote_calculator_test.ts @@ -1,753 +1,581 @@ -import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; -import { SignedOrder } from '@0x/types'; +import { constants as devConstants } from '@0x/contracts-test-utils'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; import 'mocha'; -import { constants } from '../src/constants'; -import { OrdersAndFillableAmounts, SwapQuoterError } from '../src/types'; import { swapQuoteCalculator } from '../src/utils/swap_quote_calculator'; import { chaiSetup } from './utils/chai_setup'; import { testHelpers } from './utils/test_helpers'; +import { testOrders } from './utils/test_orders'; +import { baseUnitAmount } from './utils/utils'; chaiSetup.configure(); const expect = chai.expect; +const GAS_PRICE = new BigNumber(devConstants.DEFAULT_GAS_PRICE); +const ONE_ETH_IN_WEI = new BigNumber(1000000000000000000); +const MIXED_TEST_ORDERS = _.concat( + testOrders.PRUNED_SIGNED_ORDERS_FEELESS, + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, +); + // tslint:disable:max-file-line-count // tslint:disable:custom-no-magic-numbers describe('swapQuoteCalculator', () => { describe('#calculateMarketSellSwapQuote', () => { - let firstOrder: SignedOrder; - let firstRemainingFillAmount: BigNumber; - let secondOrder: SignedOrder; - let secondRemainingFillAmount: BigNumber; - let ordersAndFillableAmounts: OrdersAndFillableAmounts; - let smallFeeOrderAndFillableAmount: OrdersAndFillableAmounts; - let allFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts; - 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({ - chainId: 42, - makerAssetAmount: new BigNumber(400), - takerAssetAmount: new BigNumber(100), - takerFee: new BigNumber(200), - }); - firstRemainingFillAmount = new BigNumber(200); - secondOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: new BigNumber(200), - takerAssetAmount: new BigNumber(100), - takerFee: new BigNumber(100), - }); - secondRemainingFillAmount = secondOrder.makerAssetAmount; - ordersAndFillableAmounts = { - orders: [firstOrder, secondOrder], - remainingFillableMakerAssetAmounts: [firstRemainingFillAmount, secondRemainingFillAmount], - }; - const smallFeeOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: new BigNumber(100), - takerAssetAmount: new BigNumber(100), - }); - smallFeeOrderAndFillableAmount = { - orders: [smallFeeOrder], - remainingFillableMakerAssetAmounts: [smallFeeOrder.makerAssetAmount], - }; - const largeFeeOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: new BigNumber(113), - takerAssetAmount: new BigNumber(200), - takerFee: new BigNumber(11), - }); - allFeeOrdersAndFillableAmounts = { - orders: [smallFeeOrder, largeFeeOrder], - remainingFillableMakerAssetAmounts: [ - smallFeeOrder.makerAssetAmount, - largeFeeOrder.makerAssetAmount.minus(largeFeeOrder.takerFee), - ], - }; - }); describe('InsufficientLiquidityError', () => { - it('should throw if not enough taker asset liquidity (multiple orders)', () => { - // we have 150 takerAsset units available to sell but attempt to calculate a quote for 200 takerAsset units + it('should throw if not enough taker asset liquidity (multiple feeless orders)', () => { const errorFunction = () => { swapQuoteCalculator.calculateMarketSellSwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, - new BigNumber(200), + testOrders.PRUNED_SIGNED_ORDERS_FEELESS, + baseUnitAmount(10), 0, - false, - false, + GAS_PRICE, ); }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(150)); + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(9)); }); - it('should throw if not enough taker asset liquidity (multiple orders with 20% slippage)', () => { - // we have 150 takerAsset units available to sell but attempt to calculate a quote for 200 takerAsset units + it('should throw if not enough taker asset liquidity (multiple feeless orders with 20% slippage)', () => { const errorFunction = () => { swapQuoteCalculator.calculateMarketSellSwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, - new BigNumber(200), + testOrders.PRUNED_SIGNED_ORDERS_FEELESS, + baseUnitAmount(10), 0.2, - false, - false, + GAS_PRICE, ); }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(125)); + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(7.5)); }); - it('should throw if not enough taker asset liquidity (multiple orders with 5% slippage)', () => { - // we have 150 takerAsset units available to fill but attempt to calculate a quote for 200 takerAsset units + it('should throw if not enough taker asset liquidity (multiple takerAsset denominated fee orders with no slippage)', () => { const errorFunction = () => { swapQuoteCalculator.calculateMarketSellSwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, - new BigNumber(200), - 0.05, - false, - false, - ); - }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(142)); - }); - it('should throw if not enough taker asset liquidity (partially filled order)', () => { - const firstOrderAndFillableAmount: OrdersAndFillableAmounts = { - orders: [firstOrder], - remainingFillableMakerAssetAmounts: [firstRemainingFillAmount], - }; - - const errorFunction = () => { - swapQuoteCalculator.calculateMarketSellSwapQuote( - firstOrderAndFillableAmount, - smallFeeOrderAndFillableAmount, - new BigNumber(51), + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, + baseUnitAmount(20), 0, - false, - false, + GAS_PRICE, ); }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(50)); + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(15)); }); - it('should throw if not enough taker asset liquidity (completely fillable order)', () => { - const completelyFillableOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: new BigNumber(123), - takerAssetAmount: new BigNumber(80), - takerFee: new BigNumber(200), - }); - const completelyFillableOrdersAndFillableAmount: OrdersAndFillableAmounts = { - orders: [completelyFillableOrder], - remainingFillableMakerAssetAmounts: [completelyFillableOrder.makerAssetAmount], - }; + it('should throw if not enough taker asset liquidity (multiple takerAsset denominated fee orders with 20% slippage)', () => { const errorFunction = () => { swapQuoteCalculator.calculateMarketSellSwapQuote( - completelyFillableOrdersAndFillableAmount, - smallFeeOrderAndFillableAmount, - new BigNumber(81), - 0, - false, - false, - ); - }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(80)); - }); - it('should throw with 1 amount available if no slippage', () => { - const smallOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: new BigNumber(1), - takerAssetAmount: new BigNumber(1), - takerFee: new BigNumber(0), - }); - const errorFunction = () => { - swapQuoteCalculator.calculateMarketSellSwapQuote( - { orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] }, - smallFeeOrderAndFillableAmount, - new BigNumber(100), - 0, - false, - 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({ - chainId: 42, - makerAssetAmount: new BigNumber(1), - takerAssetAmount: new BigNumber(1), - takerFee: new BigNumber(0), - }); - const errorFunction = () => { - swapQuoteCalculator.calculateMarketSellSwapQuote( - { orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] }, - smallFeeOrderAndFillableAmount, - new BigNumber(100), + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, + baseUnitAmount(20), 0.2, - false, - false, + GAS_PRICE, ); }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(0)); + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(12.5)); + }); + it('should throw if not enough taker asset liquidity (multiple makerAsset denominated fee orders with no slippage)', () => { + const errorFunction = () => { + swapQuoteCalculator.calculateMarketSellSwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, + baseUnitAmount(10), + 0, + GAS_PRICE, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(9)); + }); + it('should throw if not enough taker asset liquidity (multiple makerAsset denominated fee orders with 20% slippage)', () => { + const errorFunction = () => { + swapQuoteCalculator.calculateMarketSellSwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, + baseUnitAmount(10), + 0.2, + GAS_PRICE, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(7.5)); + }); + it('should throw if not enough taker asset liquidity (multiple mixed feeType orders with no slippage)', () => { + const errorFunction = () => { + swapQuoteCalculator.calculateMarketSellSwapQuote( + MIXED_TEST_ORDERS, + baseUnitAmount(40), + 0, + GAS_PRICE, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(33)); + }); + it('should throw if not enough taker asset liquidity (multiple mixed feeTyoe orders with 20% slippage)', () => { + const errorFunction = () => { + swapQuoteCalculator.calculateMarketSellSwapQuote( + MIXED_TEST_ORDERS, + baseUnitAmount(40), + 0.2, + GAS_PRICE, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(27.5)); }); }); - it('should not throw if order is fillable', () => { - expect(() => - swapQuoteCalculator.calculateMarketSellSwapQuote( - ordersAndFillableAmounts, - allFeeOrdersAndFillableAmounts, - new BigNumber(125), - 0, - false, - false, - ), - ).to.not.throw(); - }); - it('should throw if not enough ZRX liquidity', () => { - // we request 75 takerAsset units but the ZRX order is only enough to fill the first order, which only has 50 takerAsset units available - expect(() => - swapQuoteCalculator.calculateMarketSellSwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, - new BigNumber(75), - 0, - false, - false, - ), - ).to.throw(SwapQuoterError.InsufficientZrxLiquidity); - }); - it('calculates a correct swapQuote with no slippage', () => { - // we request 50 takerAsset 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 assetSellAmount = new BigNumber(50); + it('calculates a correct swapQuote with no slippage (feeless orders)', () => { + const assetSellAmount = baseUnitAmount(0.5); const slippagePercentage = 0; const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, + testOrders.PRUNED_SIGNED_ORDERS_FEELESS, assetSellAmount, slippagePercentage, - false, - false, + GAS_PRICE, ); // test if orders are correct - expect(swapQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]); - expect(swapQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]); + expect(swapQuote.orders).to.deep.equal([testOrders.PRUNED_SIGNED_ORDERS_FEELESS[0]]); + expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount); + // test if rates are correct + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(0), + takerAssetAmount: assetSellAmount, + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(3), + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(0), + takerAssetAmount: assetSellAmount, + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(3), + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); + }); + it('calculates a correct swapQuote with slippage (feeless orders)', () => { + const assetSellAmount = baseUnitAmount(1); + const slippagePercentage = 0.2; + const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEELESS, + assetSellAmount, + slippagePercentage, + GAS_PRICE, + ); + // test if orders are correct + expect(swapQuote.orders).to.deep.equal([ + testOrders.PRUNED_SIGNED_ORDERS_FEELESS[0], + testOrders.PRUNED_SIGNED_ORDERS_FEELESS[1], + ]); + expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount); + // test if rates are correct + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(0), + takerAssetAmount: assetSellAmount, + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(6), + protocolFeeInEthAmount: baseUnitAmount(30, 4), + }); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(0), + takerAssetAmount: assetSellAmount, + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(0.4), + protocolFeeInEthAmount: baseUnitAmount(30, 4), + }); + }); + it('calculates a correct swapQuote with no slippage (takerAsset denominated fee orders)', () => { + const assetSellAmount = baseUnitAmount(4); + const slippagePercentage = 0; + const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, + assetSellAmount, + slippagePercentage, + GAS_PRICE, + ); + // test if orders are correct + expect(swapQuote.orders).to.deep.equal([testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET[0]]); + expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount); + // test if rates are correct + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(3), + takerAssetAmount: assetSellAmount.minus(baseUnitAmount(3)), + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(6), + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(3), + takerAssetAmount: assetSellAmount.minus(baseUnitAmount(3)), + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(6), + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); + }); + it('calculates a correct swapQuote with slippage (takerAsset denominated fee orders)', () => { + const assetSellAmount = baseUnitAmount(3); + const slippagePercentage = 0.5; + const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, + assetSellAmount, + slippagePercentage, + GAS_PRICE, + ); + // test if orders are correct + expect(swapQuote.orders).to.deep.equal([ + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET[0], + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET[1], + ]); + expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount); + // test if rates are correct + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(2.25), + takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2.25)), + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(4.5), + protocolFeeInEthAmount: baseUnitAmount(30, 4), + }); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(0.5), + takerAssetAmount: assetSellAmount.minus(baseUnitAmount(0.5)), + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(1), + protocolFeeInEthAmount: baseUnitAmount(30, 4), + }); + }); + it('calculates a correct swapQuote with no slippage (makerAsset denominated fee orders)', () => { + const assetSellAmount = baseUnitAmount(4); + const slippagePercentage = 0; + const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, + assetSellAmount, + slippagePercentage, + GAS_PRICE, + ); + // test if orders are correct + expect(swapQuote.orders).to.deep.equal([testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET[0]]); + expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount); + // test if rates are correct + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(2), + takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)), + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(0.8), + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(2), + takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)), + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(0.8), + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); + }); + it('calculates a correct swapQuote with slippage (makerAsset denominated fee orders)', () => { + const assetSellAmount = baseUnitAmount(4); + const slippagePercentage = 0.5; + const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, + assetSellAmount, + slippagePercentage, + GAS_PRICE, + ); + // test if orders are correct + expect(swapQuote.orders).to.deep.equal([ + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET[0], + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET[1], + ]); + expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount); // test if rates are correct // 50 takerAsset units to fill the first order + 100 takerAsset units for fees - const expectedMakerAssetAmountForTakerAsset = new BigNumber(200); - const expectedTakerAssetAmountForZrxFees = new BigNumber(100); - const expectedTotalTakerAssetAmount = assetSellAmount.plus(expectedTakerAssetAmountForZrxFees); - expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount); - expect(swapQuote.bestCaseQuoteInfo.makerTokenAmount).to.bignumber.equal( - expectedMakerAssetAmountForTakerAsset, - ); - expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForZrxFees, - ); - expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount); - // because we have no slippage protection, minRate is equal to maxRate - expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount); - expect(swapQuote.worstCaseQuoteInfo.makerTokenAmount).to.bignumber.equal( - expectedMakerAssetAmountForTakerAsset, - ); - expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForZrxFees, - ); - expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal( - expectedTotalTakerAssetAmount, - ); - }); - it('calculates a correct swapQuote with slippage', () => { - // we request 50 takerAsset units which can be filled using the first order - // however with 50% slippage we are protecting the buy with 25 extra takerAssetUnits - // so we need enough orders to fill 75 takerAssetUnits - // 75 takerAssetUnits 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 assetSellAmount = new BigNumber(50); - const slippagePercentage = 0.5; - const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote( - ordersAndFillableAmounts, - allFeeOrdersAndFillableAmounts, - assetSellAmount, - slippagePercentage, - false, - false, - ); - // test if orders are correct - expect(swapQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders); - expect(swapQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders); - // test if rates are correct - const expectedMakerAssetAmountForTakerAsset = new BigNumber(200); - const expectedTakerAssetAmountForZrxFees = new BigNumber(100); - const expectedTotalTakerAssetAmount = assetSellAmount.plus(expectedTakerAssetAmountForZrxFees); - expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount); - expect(swapQuote.bestCaseQuoteInfo.makerTokenAmount).to.bignumber.equal( - expectedMakerAssetAmountForTakerAsset, - ); - expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForZrxFees, - ); - expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount); - - const expectedWorstMakerAssetAmountForTakerAsset = new BigNumber(100); - const expectedWorstTakerAssetAmountForZrxFees = new BigNumber(99); - const expectedWorstTotalTakerAssetAmount = assetSellAmount.plus(expectedWorstTakerAssetAmountForZrxFees); - expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount); - expect(swapQuote.worstCaseQuoteInfo.makerTokenAmount).to.bignumber.equal( - expectedWorstMakerAssetAmountForTakerAsset, - ); - expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( - expectedWorstTakerAssetAmountForZrxFees, - ); - expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal( - expectedWorstTotalTakerAssetAmount, - ); - }); - it('calculates a correct swapQuote (with fee calculations disabled) with no slippage', () => { - // we request 50 takerAsset units which can be filled using the first order - const assetSellAmount = new BigNumber(50); - const slippagePercentage = 0; - const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, - assetSellAmount, - slippagePercentage, - false, - true, - ); - // test if orders are correct - expect(swapQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]); - expect(swapQuote.feeOrders).to.deep.equal([]); - // test if rates are correct - const expectedMakerAssetAmountForTakerAsset = new BigNumber(200); - const expectedTotalTakerAssetAmount = assetSellAmount; - expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount); - expect(swapQuote.bestCaseQuoteInfo.makerTokenAmount).to.bignumber.equal( - expectedMakerAssetAmountForTakerAsset, - ); - expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(constants.ZERO_AMOUNT); - expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount); - // because we have no slippage protection, minRate is equal to maxRate - expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount); - expect(swapQuote.worstCaseQuoteInfo.makerTokenAmount).to.bignumber.equal( - expectedMakerAssetAmountForTakerAsset, - ); - expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(constants.ZERO_AMOUNT); - expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal( - expectedTotalTakerAssetAmount, - ); - }); - it('calculates a correct swapQuote (with fee calculatations disabled) with slippage', () => { - // we request 50 takerAsset units which can be filled using the first order - // however with 50% slippage we are protecting the buy with 25 extra takerAssetUnits - // so we need enough orders to fill 75 takerAssetUnits - // 50 takerAssetUnits 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 assetSellAmount = new BigNumber(50); - const slippagePercentage = 0.5; - const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote( - ordersAndFillableAmounts, - allFeeOrdersAndFillableAmounts, - assetSellAmount, - slippagePercentage, - false, - true, - ); - // test if orders are correct - expect(swapQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders); - expect(swapQuote.feeOrders).to.deep.equal([]); - // test if rates are correct - const expectedMakerAssetAmountForTakerAsset = new BigNumber(200); - const expectedTotalTakerAssetAmount = assetSellAmount; - expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount); - expect(swapQuote.bestCaseQuoteInfo.makerTokenAmount).to.bignumber.equal( - expectedMakerAssetAmountForTakerAsset, - ); - expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(constants.ZERO_AMOUNT); - expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount); - // 100 eth to fill the first order + 208 eth for fees - const expectedWorstMakerAssetAmountForTakerAsset = new BigNumber(100); - const expectedWorstTotalTakerAssetAmount = assetSellAmount; - expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount); - expect(swapQuote.worstCaseQuoteInfo.makerTokenAmount).to.bignumber.equal( - expectedWorstMakerAssetAmountForTakerAsset, - ); - expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(constants.ZERO_AMOUNT); - expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal( - expectedWorstTotalTakerAssetAmount, - ); + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(2), + takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)), + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(0.8), + protocolFeeInEthAmount: baseUnitAmount(30, 4), + }); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(2), + takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)), + totalTakerAssetAmount: assetSellAmount, + makerAssetAmount: baseUnitAmount(3.6), + protocolFeeInEthAmount: baseUnitAmount(30, 4), + }); }); }); describe('#calculateMarketBuySwapQuote', () => { - let firstOrder: SignedOrder; - let firstRemainingFillAmount: BigNumber; - let secondOrder: SignedOrder; - let secondRemainingFillAmount: BigNumber; - let ordersAndFillableAmounts: OrdersAndFillableAmounts; - let smallFeeOrderAndFillableAmount: OrdersAndFillableAmounts; - let allFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts; - 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({ - chainId: 42, - makerAssetAmount: new BigNumber(400), - takerAssetAmount: new BigNumber(100), - takerFee: new BigNumber(200), - }); - firstRemainingFillAmount = new BigNumber(200); - secondOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: new BigNumber(200), - takerAssetAmount: new BigNumber(100), - takerFee: new BigNumber(100), - }); - secondRemainingFillAmount = secondOrder.makerAssetAmount; - ordersAndFillableAmounts = { - orders: [firstOrder, secondOrder], - remainingFillableMakerAssetAmounts: [firstRemainingFillAmount, secondRemainingFillAmount], - }; - const smallFeeOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: new BigNumber(100), - takerAssetAmount: new BigNumber(100), - }); - smallFeeOrderAndFillableAmount = { - orders: [smallFeeOrder], - remainingFillableMakerAssetAmounts: [smallFeeOrder.makerAssetAmount], - }; - const largeFeeOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: new BigNumber(113), - takerAssetAmount: new BigNumber(200), - takerFee: new BigNumber(11), - }); - 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 + it('should throw if not enough maker asset liquidity (multiple feeless orders)', () => { const errorFunction = () => { swapQuoteCalculator.calculateMarketBuySwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, - new BigNumber(500), + testOrders.PRUNED_SIGNED_ORDERS_FEELESS, + baseUnitAmount(12), 0, - false, - false, + GAS_PRICE, ); }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(400)); + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(10)); }); - 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 + it('should throw if not enough taker asset liquidity (multiple feeless orders with 20% slippage)', () => { const errorFunction = () => { swapQuoteCalculator.calculateMarketBuySwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, - new BigNumber(500), - 0.2, - false, - false, + testOrders.PRUNED_SIGNED_ORDERS_FEELESS, + baseUnitAmount(10), + 0.6, + GAS_PRICE, ); }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(333)); + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(6.25)); }); - 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 + it('should throw if not enough taker asset liquidity (multiple takerAsset denominated fee orders with no slippage)', () => { const errorFunction = () => { swapQuoteCalculator.calculateMarketBuySwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, - new BigNumber(600), - 0.05, - false, - false, + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, + baseUnitAmount(12), + 0, + GAS_PRICE, ); }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(380)); + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(10)); }); - it('should throw if not enough maker asset liquidity (partially filled order)', () => { - const firstOrderAndFillableAmount: OrdersAndFillableAmounts = { - orders: [firstOrder], - remainingFillableMakerAssetAmounts: [firstRemainingFillAmount], + it('should throw if not enough taker asset liquidity (multiple takerAsset denominated fee orders with 20% slippage)', () => { + const errorFunction = () => { + swapQuoteCalculator.calculateMarketBuySwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, + baseUnitAmount(12), + 0.6, + GAS_PRICE, + ); }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(6.25)); + }); + it('should throw if not enough taker asset liquidity (multiple makerAsset denominated fee orders with no slippage)', () => { + const errorFunction = () => { + swapQuoteCalculator.calculateMarketBuySwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, + baseUnitAmount(6), + 0, + GAS_PRICE, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(5)); + }); + it('should throw if not enough taker asset liquidity (multiple makerAsset denominated fee orders with 20% slippage)', () => { + const errorFunction = () => { + swapQuoteCalculator.calculateMarketBuySwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, + baseUnitAmount(6), + 0.6, + GAS_PRICE, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(3.125)); + }); + it('should throw if not enough taker asset liquidity (multiple mixed feeType orders with no slippage)', () => { + const errorFunction = () => { + swapQuoteCalculator.calculateMarketBuySwapQuote( + MIXED_TEST_ORDERS, + baseUnitAmount(40), + 0, + GAS_PRICE, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(25)); + }); + it('should throw if not enough taker asset liquidity (multiple mixed feeTyoe orders with 20% slippage)', () => { + const errorFunction = () => { + swapQuoteCalculator.calculateMarketBuySwapQuote( + MIXED_TEST_ORDERS, + baseUnitAmount(40), + 0.6, + GAS_PRICE, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, baseUnitAmount(15.625)); + }); + }); + it('calculates a correct swapQuote with no slippage (feeless orders)', () => { + const assetBuyAmount = baseUnitAmount(3); + const slippagePercentage = 0; + const swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEELESS, + assetBuyAmount, + slippagePercentage, + GAS_PRICE, + ); + // test if orders are correct + expect(swapQuote.orders).to.deep.equal([testOrders.PRUNED_SIGNED_ORDERS_FEELESS[0]]); + expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount); + // test if rates are correct + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(0), + takerAssetAmount: baseUnitAmount(0.5), + totalTakerAssetAmount: baseUnitAmount(0.5), + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(0), + takerAssetAmount: baseUnitAmount(0.5), + totalTakerAssetAmount: baseUnitAmount(0.5), + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); + }); + it('calculates a correct swapQuote with slippage (feeless orders)', () => { + const assetBuyAmount = baseUnitAmount(5); + const slippagePercentage = 0.5; + const swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote( + testOrders.PRUNED_SIGNED_ORDERS_FEELESS, + assetBuyAmount, + slippagePercentage, + GAS_PRICE, + ); + // test if orders are correct + expect(swapQuote.orders).to.deep.equal([ + testOrders.PRUNED_SIGNED_ORDERS_FEELESS[0], + testOrders.PRUNED_SIGNED_ORDERS_FEELESS[1], + ]); + expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount); - const errorFunction = () => { - swapQuoteCalculator.calculateMarketBuySwapQuote( - firstOrderAndFillableAmount, - smallFeeOrderAndFillableAmount, - new BigNumber(201), - 0, - false, - false, - ); - }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(200)); + const takerAssetAmount = new BigNumber(5) + .div(new BigNumber(6)) + .multipliedBy(ONE_ETH_IN_WEI) + .integerValue(BigNumber.ROUND_CEIL); + // test if rates are correct + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(0), + takerAssetAmount, + totalTakerAssetAmount: takerAssetAmount, + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(30, 4), }); - it('should throw if not enough maker asset liquidity (completely fillable order)', () => { - const completelyFillableOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: new BigNumber(123), - takerAssetAmount: new BigNumber(100), - takerFee: new BigNumber(200), - }); - const completelyFillableOrdersAndFillableAmount: OrdersAndFillableAmounts = { - orders: [completelyFillableOrder], - remainingFillableMakerAssetAmounts: [completelyFillableOrder.makerAssetAmount], - }; - const errorFunction = () => { - swapQuoteCalculator.calculateMarketBuySwapQuote( - completelyFillableOrdersAndFillableAmount, - smallFeeOrderAndFillableAmount, - new BigNumber(124), - 0, - false, - false, - ); - }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(123)); - }); - it('should throw with 1 amount available if no slippage', () => { - const smallOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: new BigNumber(1), - takerAssetAmount: new BigNumber(1), - takerFee: new BigNumber(0), - }); - const errorFunction = () => { - swapQuoteCalculator.calculateMarketBuySwapQuote( - { orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] }, - smallFeeOrderAndFillableAmount, - new BigNumber(600), - 0, - false, - 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({ - chainId: 42, - makerAssetAmount: new BigNumber(1), - takerAssetAmount: new BigNumber(1), - takerFee: new BigNumber(0), - }); - const errorFunction = () => { - swapQuoteCalculator.calculateMarketBuySwapQuote( - { orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] }, - smallFeeOrderAndFillableAmount, - new BigNumber(600), - 0.2, - false, - false, - ); - }; - testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(0)); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(0), + takerAssetAmount: baseUnitAmount(5.5), + totalTakerAssetAmount: baseUnitAmount(5.5), + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(30, 4), }); }); - it('should not throw if order is fillable', () => { - expect(() => - swapQuoteCalculator.calculateMarketBuySwapQuote( - ordersAndFillableAmounts, - allFeeOrdersAndFillableAmounts, - new BigNumber(300), - 0, - false, - 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(() => - swapQuoteCalculator.calculateMarketBuySwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, - new BigNumber(300), - 0, - false, - false, - ), - ).to.throw(SwapQuoterError.InsufficientZrxLiquidity); - }); - it('calculates a correct swapQuote 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); + it('calculates a correct swapQuote with no slippage (takerAsset denominated fee orders)', () => { + const assetBuyAmount = baseUnitAmount(3); const slippagePercentage = 0; const swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, assetBuyAmount, slippagePercentage, - false, - false, + GAS_PRICE, ); // test if orders are correct - expect(swapQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]); - expect(swapQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]); + expect(swapQuote.orders).to.deep.equal([testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET[0]]); + expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount); // test if rates are correct - // 50 eth to fill the first order + 100 eth for fees - const expectedTakerAssetAmountForMakerAsset = new BigNumber(50); - const expectedTakerAssetAmountForZrxFees = new BigNumber(100); - const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset.plus( - expectedTakerAssetAmountForZrxFees, - ); - expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForMakerAsset, - ); - expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForZrxFees, - ); - expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount); - // because we have no slippage protection, minRate is equal to maxRate - expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForMakerAsset, - ); - expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForZrxFees, - ); - expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal( - expectedTotalTakerAssetAmount, - ); + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(1.5), + takerAssetAmount: baseUnitAmount(0.5), + totalTakerAssetAmount: baseUnitAmount(2), + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(1.5), + takerAssetAmount: baseUnitAmount(0.5), + totalTakerAssetAmount: baseUnitAmount(2), + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); }); - it('calculates a correct swapQuote 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); + it('calculates a correct swapQuote with slippage (takerAsset denominated fee orders)', () => { + const assetBuyAmount = baseUnitAmount(5); const slippagePercentage = 0.5; const swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote( - ordersAndFillableAmounts, - allFeeOrdersAndFillableAmounts, + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, assetBuyAmount, slippagePercentage, - false, - false, + GAS_PRICE, ); + const fiveSixthEthInWei = new BigNumber(5) + .div(new BigNumber(6)) + .multipliedBy(ONE_ETH_IN_WEI) + .integerValue(BigNumber.ROUND_CEIL); // test if orders are correct - expect(swapQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders); - expect(swapQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders); + expect(swapQuote.orders).to.deep.equal([ + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET[0], + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET[1], + ]); + expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount); // test if rates are correct - // 50 eth to fill the first order + 100 eth for fees - const expectedTakerAssetAmountForMakerAsset = new BigNumber(50); - const expectedTakerAssetAmountForZrxFees = new BigNumber(100); - const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset.plus( - expectedTakerAssetAmountForZrxFees, - ); - expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForMakerAsset, - ); - expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForZrxFees, - ); - expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount); - // 100 eth to fill the first order + 208 eth for fees - const expectedWorstTakerAssetAmountForMakerAsset = new BigNumber(100); - const expectedWorstTakerAssetAmountForZrxFees = new BigNumber(208); - const expectedWorstTotalTakerAssetAmount = expectedWorstTakerAssetAmountForMakerAsset.plus( - expectedWorstTakerAssetAmountForZrxFees, - ); - expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( - expectedWorstTakerAssetAmountForMakerAsset, - ); - expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( - expectedWorstTakerAssetAmountForZrxFees, - ); - expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal( - expectedWorstTotalTakerAssetAmount, - ); + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(2.5), + takerAssetAmount: fiveSixthEthInWei, + totalTakerAssetAmount: baseUnitAmount(2.5).plus(fiveSixthEthInWei), + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(30, 4), + }); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(2.5), + takerAssetAmount: baseUnitAmount(5.5), + totalTakerAssetAmount: baseUnitAmount(8), + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(30, 4), + }); }); - it('calculates a correct swapQuote (with fee calculations disabled) 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); + it('calculates a correct swapQuote with no slippage (makerAsset denominated fee orders)', () => { + const assetBuyAmount = baseUnitAmount(1); const slippagePercentage = 0; const swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, assetBuyAmount, slippagePercentage, - false, - true, + GAS_PRICE, ); // test if orders are correct - expect(swapQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]); - expect(swapQuote.feeOrders).to.deep.equal([]); + expect(swapQuote.orders).to.deep.equal([testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET[0]]); + expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount); // test if rates are correct - // 50 eth to fill the first order + 100 eth for fees - const expectedTakerAssetAmountForMakerAsset = new BigNumber(50); - const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset; - expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForMakerAsset, - ); - expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(constants.ZERO_AMOUNT); - expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount); - // because we have no slippage protection, minRate is equal to maxRate - expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForMakerAsset, - ); - expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(constants.ZERO_AMOUNT); - expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal( - expectedTotalTakerAssetAmount, - ); + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(2.5), + takerAssetAmount: baseUnitAmount(2.5), + totalTakerAssetAmount: baseUnitAmount(5), + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(2.5), + takerAssetAmount: baseUnitAmount(2.5), + totalTakerAssetAmount: baseUnitAmount(5), + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(15, 4), + }); }); - it('calculates a correct swapQuote (with fee calculations disabled) 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); + it('calculates a correct swapQuote with slippage (makerAsset denominated fee orders)', () => { + const assetBuyAmount = baseUnitAmount(2.5); const slippagePercentage = 0.5; const swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote( - ordersAndFillableAmounts, - allFeeOrdersAndFillableAmounts, + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, assetBuyAmount, slippagePercentage, - false, - true, + GAS_PRICE, ); + const totalTakerAssetAmount = new BigNumber(5) + .div(new BigNumber(6)) + .multipliedBy(ONE_ETH_IN_WEI) + .integerValue(BigNumber.ROUND_CEIL); // test if orders are correct - expect(swapQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders); - expect(swapQuote.feeOrders).to.deep.equal([]); + expect(swapQuote.orders).to.deep.equal([ + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET[0], + testOrders.PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET[1], + ]); + expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount); // test if rates are correct - // 50 eth to fill the first order + 100 eth for fees - const expectedTakerAssetAmountForMakerAsset = new BigNumber(50); - const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset; - expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( - expectedTakerAssetAmountForMakerAsset, - ); - expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(constants.ZERO_AMOUNT); - expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount); - // 100 eth to fill the first order + 208 eth for fees - const expectedWorstTakerAssetAmountForMakerAsset = new BigNumber(100); - const expectedWorstTotalTakerAssetAmount = expectedWorstTakerAssetAmountForMakerAsset; - expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( - expectedWorstTakerAssetAmountForMakerAsset, - ); - expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(constants.ZERO_AMOUNT); - expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal( - expectedWorstTotalTakerAssetAmount, - ); + // 50 takerAsset units to fill the first order + 100 takerAsset units for fees + expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: baseUnitAmount(2.75), + takerAssetAmount: baseUnitAmount(2.75), + totalTakerAssetAmount: baseUnitAmount(5.5), + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(30, 4), + }); + expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({ + feeTakerAssetAmount: totalTakerAssetAmount.div(2), + takerAssetAmount: totalTakerAssetAmount.div(2), + totalTakerAssetAmount, + makerAssetAmount: assetBuyAmount, + protocolFeeInEthAmount: baseUnitAmount(30, 4), + }); }); }); }); diff --git a/packages/asset-swapper/test/swap_quote_consumer_test.ts b/packages/asset-swapper/test/swap_quote_consumer_test.ts deleted file mode 100644 index 69cc975158..0000000000 --- a/packages/asset-swapper/test/swap_quote_consumer_test.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { ContractAddresses, ContractWrappers, ERC20TokenContract } from '@0x/contract-wrappers'; -import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; -import { MarketOperation, SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import 'mocha'; - -import { SwapQuote, SwapQuoteConsumer } from '../src'; -import { ExtensionContractType } from '../src/types'; - -import { chaiSetup } from './utils/chai_setup'; -import { migrateOnceAsync } from './utils/migrate'; -import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFeesAsync } from './utils/swap_quote'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -const ONE_ETH_IN_WEI = new BigNumber(1000000000000000000); -const TESTRPC_CHAIN_ID = 1337; -const FILLABLE_AMOUNTS = [new BigNumber(3), new BigNumber(2), new BigNumber(5)].map(value => - value.multipliedBy(ONE_ETH_IN_WEI), -); - -const UNLIMITED_ALLOWANCE_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1); // tslint:disable-line:custom-no-magic-numbers - -describe('SwapQuoteConsumer', () => { - let contractWrappers: ContractWrappers; - let erc20Token: ERC20TokenContract; - let userAddresses: string[]; - let coinbaseAddress: string; - let makerAddress: string; - let takerAddress: string; - let feeRecipient: string; - let makerTokenAddress: string; - let takerTokenAddress: string; - let makerAssetData: string; - let takerAssetData: string; - let wethAssetData: string; - let contractAddresses: ContractAddresses; - - const chainId = TESTRPC_CHAIN_ID; - - let orders: SignedOrder[]; - let marketSellSwapQuote: SwapQuote; - let swapQuoteConsumer: SwapQuoteConsumer; - let erc20ProxyAddress: string; - - before(async () => { - contractAddresses = await migrateOnceAsync(); - await blockchainLifecycle.startAsync(); - userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - const config = { - chainId, - contractAddresses, - }; - contractWrappers = new ContractWrappers(provider, config); - [coinbaseAddress, takerAddress, makerAddress, feeRecipient] = userAddresses; - [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); - erc20Token = new ERC20TokenContract(makerTokenAddress, provider); - [makerAssetData, takerAssetData, wethAssetData] = [ - await contractWrappers.devUtils.encodeERC20AssetData(makerTokenAddress).callAsync(), - await contractWrappers.devUtils.encodeERC20AssetData(takerTokenAddress).callAsync(), - await contractWrappers.devUtils.encodeERC20AssetData(contractAddresses.etherToken).callAsync(), - ]; - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - const UNLIMITED_ALLOWANCE = UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - erc20ProxyAddress = contractAddresses.erc20Proxy; - - const totalFillableAmount = FILLABLE_AMOUNTS.reduce( - (a: BigNumber, c: BigNumber) => a.plus(c), - new BigNumber(0), - ); - - await erc20Token.transfer(makerAddress, totalFillableAmount).sendTransactionAsync({ - from: coinbaseAddress, - }); - - await erc20Token.approve(erc20ProxyAddress, UNLIMITED_ALLOWANCE).sendTransactionAsync({ - from: makerAddress, - }); - orders = await getSignedOrdersWithNoFeesAsync( - provider, - makerAssetData, - wethAssetData, - makerAddress, - takerAddress, - FILLABLE_AMOUNTS, - contractAddresses.exchange, - ); - - marketSellSwapQuote = getFullyFillableSwapQuoteWithNoFees( - makerAssetData, - wethAssetData, - orders, - MarketOperation.Sell, - ); - - swapQuoteConsumer = new SwapQuoteConsumer(provider, { - chainId, - }); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - // TODO(david): write tests to ensure options work for executeSwapQuote - // describe('executeSwapQuoteOrThrowAsync', () => { - // /* - // * Testing that SwapQuoteConsumer logic correctly performs a execution (doesn't throw or revert) - // * Does not test the validity of the state change performed by the forwarder smart contract - // */ - // it('should perform an asset swap with Forwarder contract when provided corresponding useExtensionContract option', async () => { - // let makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - // let takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - // expect(makerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - // expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - // await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { takerAddress, useExtensionContract: ConsumerType.Forwarder }); - // makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - // takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - // expect(takerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - // expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - // }); - // it('should perform an asset swap with Exchange contract when provided corresponding useExtensionContract option', async () => { - // let makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - // let takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - // expect(makerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - // expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - // await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, { takerAddress }); - // makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync(); - // takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync(); - // expect(takerBalance).to.bignumber.equal(new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI)); - // expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); - // }); - // }); - - describe('getSmartContractParamsOrThrow', () => { - describe('valid swap quote', async () => { - // TODO(david) Check for valid MethodAbi - it('should provide correct and optimized smart contract params for Forwarder contract when provided corresponding useExtensionContract option', async () => { - const { toAddress } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(marketSellSwapQuote, { - useExtensionContract: ExtensionContractType.Forwarder, - }); - expect(toAddress).to.deep.equal(contractWrappers.forwarder.address); - }); - it('should provide correct and optimized smart contract params for Exchange contract when provided corresponding useExtensionContract option', async () => { - const { toAddress } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(marketSellSwapQuote, { - useExtensionContract: ExtensionContractType.None, - }); - expect(toAddress).to.deep.equal(contractWrappers.exchange.address); - }); - }); - }); - - describe('getCalldataOrThrow', () => { - describe('valid swap quote', async () => { - it('should provide correct and optimized calldata options for Forwarder contract when provided corresponding useExtensionContract option', async () => { - const { toAddress } = await swapQuoteConsumer.getCalldataOrThrowAsync(marketSellSwapQuote, { - useExtensionContract: ExtensionContractType.Forwarder, - }); - expect(toAddress).to.deep.equal(contractWrappers.forwarder.address); - }); - it('should provide correct and optimized smart contract params for Exchange contract when provided corresponding useExtensionContract option', async () => { - const { toAddress } = await swapQuoteConsumer.getCalldataOrThrowAsync(marketSellSwapQuote, { - useExtensionContract: ExtensionContractType.None, - }); - expect(toAddress).to.deep.equal(contractWrappers.exchange.address); - }); - }); - }); -}); diff --git a/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts b/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts index cc9b02728f..24bf29a785 100644 --- a/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts +++ b/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts @@ -1,16 +1,19 @@ -import { ContractAddresses, ContractWrappers } from '@0x/contract-wrappers'; +import { ContractAddresses } from '@0x/contract-addresses'; +import { DevUtilsContract } from '@0x/contracts-dev-utils'; +import { WETH9Contract } from '@0x/contracts-erc20'; +import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils'; import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils'; -import { MarketOperation, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import 'mocha'; import { SwapQuote, SwapQuoteConsumer } from '../src'; -import { ExtensionContractType } from '../src/types'; +import { constants } from '../src/constants'; +import { ExtensionContractType, MarketOperation, PrunedSignedOrder } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; import { migrateOnceAsync } from './utils/migrate'; -import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFeesAsync } from './utils/swap_quote'; +import { getFullyFillableSwapQuoteWithNoFees } from './utils/swap_quote'; import { provider, web3Wrapper } from './utils/web3_wrapper'; chaiSetup.configure(); @@ -19,15 +22,52 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); const ONE_ETH_IN_WEI = new BigNumber(1000000000000000000); const TESTRPC_CHAIN_ID = 1337; -const FILLABLE_AMOUNTS = [new BigNumber(2), new BigNumber(3), new BigNumber(5)].map(value => - value.multipliedBy(ONE_ETH_IN_WEI), -); -const LARGE_FILLABLE_AMOUNTS = [new BigNumber(20), new BigNumber(20), new BigNumber(20)].map(value => - value.multipliedBy(ONE_ETH_IN_WEI), -); +const GAS_PRICE = new BigNumber(devConstants.DEFAULT_GAS_PRICE); + +const PARTIAL_PRUNED_SIGNED_ORDERS: Array> = [ + { + takerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI), + }, + { + takerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI), + }, + { + takerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI), + }, +]; + +const PARTIAL_LARGE_PRUNED_SIGNED_ORDERS: Array> = [ + { + takerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + }, + { + takerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + }, + { + takerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + makerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + fillableTakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + fillableMakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI), + }, +]; describe('swapQuoteConsumerUtils', () => { - let contractWrappers: ContractWrappers; + let wethContract: WETH9Contract; let userAddresses: string[]; let makerAddress: string; let takerAddress: string; @@ -38,25 +78,48 @@ describe('swapQuoteConsumerUtils', () => { let wethAssetData: string; let contractAddresses: ContractAddresses; let swapQuoteConsumer: SwapQuoteConsumer; + let orderFactory: OrderFactory; + let forwarderOrderFactory: OrderFactory; const chainId = TESTRPC_CHAIN_ID; before(async () => { contractAddresses = await migrateOnceAsync(); await blockchainLifecycle.startAsync(); userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - const config = { - chainId, - contractAddresses, - }; - contractWrappers = new ContractWrappers(provider, config); + const devUtils = new DevUtilsContract(contractAddresses.devUtils, provider); + wethContract = new WETH9Contract(contractAddresses.etherToken, provider); [takerAddress, makerAddress] = userAddresses; [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); [makerAssetData, takerAssetData, wethAssetData] = [ - await contractWrappers.devUtils.encodeERC20AssetData(makerTokenAddress).callAsync(), - await contractWrappers.devUtils.encodeERC20AssetData(takerTokenAddress).callAsync(), - await contractWrappers.devUtils.encodeERC20AssetData(contractAddresses.etherToken).callAsync(), + await devUtils.encodeERC20AssetData(makerTokenAddress).callAsync(), + await devUtils.encodeERC20AssetData(takerTokenAddress).callAsync(), + await devUtils.encodeERC20AssetData(contractAddresses.etherToken).callAsync(), ]; + const defaultOrderParams = { + ...devConstants.STATIC_ORDER_PARAMS, + makerAddress, + takerAddress: constants.NULL_ADDRESS, + makerAssetData, + takerAssetData, + makerFeeAssetData: constants.NULL_ERC20_ASSET_DATA, + takerFeeAssetData: constants.NULL_ERC20_ASSET_DATA, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + feeRecipientAddress: constants.NULL_ADDRESS, + exchangeAddress: contractAddresses.exchange, + chainId, + }; + const defaultForwarderOrderParams = { + ...defaultOrderParams, + ...{ + takerAssetData: wethAssetData, + }, + }; + const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)]; + orderFactory = new OrderFactory(privateKey, defaultOrderParams); + forwarderOrderFactory = new OrderFactory(privateKey, defaultForwarderOrderParams); + swapQuoteConsumer = new SwapQuoteConsumer(provider, { chainId, }); @@ -72,46 +135,50 @@ describe('swapQuoteConsumerUtils', () => { }); describe('getConsumerTypeForSwapQuoteAsync', () => { - let forwarderOrders: SignedOrder[]; - let exchangeOrders: SignedOrder[]; - let largeForwarderOrders: SignedOrder[]; + let forwarderOrders: PrunedSignedOrder[]; + let exchangeOrders: PrunedSignedOrder[]; + let largeForwarderOrders: PrunedSignedOrder[]; let forwarderSwapQuote: SwapQuote; let exchangeSwapQuote: SwapQuote; let largeForwarderSwapQuote: SwapQuote; beforeEach(async () => { - exchangeOrders = await getSignedOrdersWithNoFeesAsync( - provider, - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - FILLABLE_AMOUNTS, - ); + exchangeOrders = []; + for (const partialOrder of PARTIAL_PRUNED_SIGNED_ORDERS) { + const order = await orderFactory.newSignedOrderAsync(partialOrder); + const prunedOrder = { + ...order, + ...partialOrder, + }; + exchangeOrders.push(prunedOrder as PrunedSignedOrder); + } - forwarderOrders = await getSignedOrdersWithNoFeesAsync( - provider, - makerAssetData, - wethAssetData, - makerAddress, - takerAddress, - FILLABLE_AMOUNTS, - ); + forwarderOrders = []; + for (const partialOrder of PARTIAL_PRUNED_SIGNED_ORDERS) { + const order = await forwarderOrderFactory.newSignedOrderAsync(partialOrder); + const prunedOrder = { + ...order, + ...partialOrder, + }; + forwarderOrders.push(prunedOrder as PrunedSignedOrder); + } - largeForwarderOrders = await getSignedOrdersWithNoFeesAsync( - provider, - makerAssetData, - wethAssetData, - makerAddress, - takerAddress, - LARGE_FILLABLE_AMOUNTS, - ); + largeForwarderOrders = []; + for (const partialOrder of PARTIAL_LARGE_PRUNED_SIGNED_ORDERS) { + const order = await forwarderOrderFactory.newSignedOrderAsync(partialOrder); + const prunedOrder = { + ...order, + ...partialOrder, + }; + largeForwarderOrders.push(prunedOrder as PrunedSignedOrder); + } forwarderSwapQuote = getFullyFillableSwapQuoteWithNoFees( makerAssetData, wethAssetData, forwarderOrders, MarketOperation.Sell, + GAS_PRICE, ); largeForwarderSwapQuote = getFullyFillableSwapQuoteWithNoFees( @@ -119,6 +186,7 @@ describe('swapQuoteConsumerUtils', () => { wethAssetData, largeForwarderOrders, MarketOperation.Sell, + GAS_PRICE, ); exchangeSwapQuote = getFullyFillableSwapQuoteWithNoFees( @@ -126,6 +194,7 @@ describe('swapQuoteConsumerUtils', () => { takerAssetData, exchangeOrders, MarketOperation.Sell, + GAS_PRICE, ); }); @@ -145,7 +214,7 @@ describe('swapQuoteConsumerUtils', () => { }); it('should return exchange consumer if takerAsset is wEth and taker has enough weth', async () => { const etherInWei = new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI); - await contractWrappers.weth9.deposit().sendTransactionAsync({ value: etherInWei, from: takerAddress }); + await wethContract.deposit().sendTransactionAsync({ value: etherInWei, from: takerAddress }); const extensionContractType = await swapQuoteConsumer.getOptimalExtensionContractTypeAsync( forwarderSwapQuote, { takerAddress }, @@ -154,7 +223,7 @@ describe('swapQuoteConsumerUtils', () => { }); it('should return forwarder consumer if takerAsset is wEth and takerAddress has no available balance in either weth or eth (defaulting behavior)', async () => { const etherInWei = new BigNumber(50).multipliedBy(ONE_ETH_IN_WEI); - await contractWrappers.weth9.deposit().sendTransactionAsync({ value: etherInWei, from: takerAddress }); + await wethContract.deposit().sendTransactionAsync({ value: etherInWei, from: takerAddress }); const extensionContractType = await swapQuoteConsumer.getOptimalExtensionContractTypeAsync( largeForwarderSwapQuote, { takerAddress }, diff --git a/packages/asset-swapper/test/swap_quoter_test.ts b/packages/asset-swapper/test/swap_quoter_test.ts index 8f3b4cd6c5..69f935c861 100644 --- a/packages/asset-swapper/test/swap_quoter_test.ts +++ b/packages/asset-swapper/test/swap_quoter_test.ts @@ -1,19 +1,19 @@ -import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; import { Orderbook } from '@0x/orderbook'; import { Web3ProviderEngine } from '@0x/subproviders'; import { AssetPairsItem, 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 { SwapQuoter } from '../src'; import { constants } from '../src/constants'; -import { LiquidityForAssetData, OrdersAndFillableAmounts } from '../src/types'; +import { LiquidityForTakerMakerAssetDataPair, PrunedSignedOrder } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; -import { mockAvailableAssetDatas, mockedSwapQuoterWithOrdersAndFillableAmounts, orderbookMock } from './utils/mocks'; +import { mockAvailableAssetDatas, mockedSwapQuoterWithPrunedSignedOrders, orderbookMock } from './utils/mocks'; +import { testOrderFactory } from './utils/test_order_factory'; +import { baseUnitAmount } from './utils/utils'; chaiSetup.configure(); const expect = chai.expect; @@ -27,10 +27,6 @@ const WETH_ASSET_DATA = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5 const WETH_DECIMALS = constants.ETHER_TOKEN_DECIMALS; const ZERO = new BigNumber(0); -const baseUnitAmount = (unitAmount: number, decimals = TOKEN_DECIMALS): BigNumber => { - return Web3Wrapper.toBaseUnitAmount(new BigNumber(unitAmount), decimals); -}; - const assetsToAssetPairItems = (makerAssetData: string, takerAssetData: string): AssetPairsItem[] => { const defaultAssetPairItem = { minAmount: ZERO, @@ -64,15 +60,15 @@ const assetsToAssetPairItems = (makerAssetData: string, takerAssetData: string): const expectLiquidityResult = async ( web3Provider: Web3ProviderEngine, orderbook: Orderbook, - ordersAndFillableAmounts: OrdersAndFillableAmounts, - expectedLiquidityResult: LiquidityForAssetData, + prunedOrders: PrunedSignedOrder[], + expectedLiquidityResult: LiquidityForTakerMakerAssetDataPair, ) => { - const mockedSwapQuoter = mockedSwapQuoterWithOrdersAndFillableAmounts( + const mockedSwapQuoter = mockedSwapQuoterWithPrunedSignedOrders( web3Provider, orderbook, FAKE_MAKER_ASSET_DATA, WETH_ASSET_DATA, - ordersAndFillableAmounts, + prunedOrders, ); const liquidityResult = await mockedSwapQuoter.object.getLiquidityForMakerTakerAssetDataPairAsync( FAKE_MAKER_ASSET_DATA, @@ -130,8 +126,8 @@ describe('SwapQuoter', () => { FAKE_TAKER_ASSET_DATA, ); expect(liquidityResult).to.deep.equal({ - makerTokensAvailableInBaseUnits: new BigNumber(0), - takerTokensAvailableInBaseUnits: new BigNumber(0), + makerAssetAvailableInBaseUnits: new BigNumber(0), + takerAssetAvailableInBaseUnits: new BigNumber(0), }); }); @@ -144,20 +140,15 @@ describe('SwapQuoter', () => { FAKE_TAKER_ASSET_DATA, ); expect(liquidityResult).to.deep.equal({ - makerTokensAvailableInBaseUnits: new BigNumber(0), - takerTokensAvailableInBaseUnits: new BigNumber(0), + makerAssetAvailableInBaseUnits: new BigNumber(0), + takerAssetAvailableInBaseUnits: new BigNumber(0), }); }); }); describe('assetData is supported', () => { // orders - const sellTwoTokensFor1Weth: SignedOrder = orderFactory.createSignedOrderFromPartial({ - makerAssetAmount: baseUnitAmount(2), - takerAssetAmount: baseUnitAmount(1, WETH_DECIMALS), - chainId: 42, - }); - const sellTenTokensFor10Weth: SignedOrder = orderFactory.createSignedOrderFromPartial({ + const sellTenTokensFor10Weth: SignedOrder = testOrderFactory.generateTestSignedOrder({ makerAssetAmount: baseUnitAmount(10), takerAssetAmount: baseUnitAmount(10, WETH_DECIMALS), chainId: 42, @@ -168,98 +159,145 @@ describe('SwapQuoter', () => { }); it('should return 0s when no orders available', async () => { - const ordersAndFillableAmounts: OrdersAndFillableAmounts = { - orders: [], - remainingFillableMakerAssetAmounts: [], - }; + const prunedOrders: PrunedSignedOrder[] = []; const expectedResult = { - makerTokensAvailableInBaseUnits: new BigNumber(0), - takerTokensAvailableInBaseUnits: new BigNumber(0), + makerAssetAvailableInBaseUnits: new BigNumber(0), + takerAssetAvailableInBaseUnits: new BigNumber(0), }; await expectLiquidityResult( mockWeb3Provider.object, mockOrderbook.object, - ordersAndFillableAmounts, + prunedOrders, 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 expectedMakerTokensAvailable = orders[0].makerAssetAmount.plus(orders[1].makerAssetAmount); - const expectedTakerTokensAvailable = orders[0].takerAssetAmount.plus(orders[1].takerAssetAmount); + const prunedOrders: PrunedSignedOrder[] = [ + { + ...sellTenTokensFor10Weth, + ...{ + fillableMakerAssetAmount: sellTenTokensFor10Weth.makerAssetAmount, + fillableTakerAssetAmount: sellTenTokensFor10Weth.takerAssetAmount, + fillableTakerFeeAmount: constants.ZERO_AMOUNT, + }, + }, + { + ...sellTenTokensFor10Weth, + ...{ + fillableMakerAssetAmount: sellTenTokensFor10Weth.makerAssetAmount, + fillableTakerAssetAmount: sellTenTokensFor10Weth.takerAssetAmount, + fillableTakerFeeAmount: constants.ZERO_AMOUNT, + }, + }, + ]; + const expectedMakerAssetAvailable = prunedOrders[0].makerAssetAmount.plus( + prunedOrders[1].makerAssetAmount, + ); + const expectedTakerAssetAvailable = prunedOrders[0].takerAssetAmount.plus( + prunedOrders[1].takerAssetAmount, + ); const expectedResult = { - makerTokensAvailableInBaseUnits: expectedMakerTokensAvailable, - takerTokensAvailableInBaseUnits: expectedTakerTokensAvailable, + makerAssetAvailableInBaseUnits: expectedMakerAssetAvailable, + takerAssetAvailableInBaseUnits: expectedTakerAssetAvailable, }; await expectLiquidityResult( mockWeb3Provider.object, mockOrderbook.object, - ordersAndFillableAmounts, + prunedOrders, expectedResult, ); }); it('should return correct computed value with one partial fillableAmounts', async () => { - const ordersAndFillableAmounts = { - orders: [sellTwoTokensFor1Weth], - remainingFillableMakerAssetAmounts: [baseUnitAmount(1)], - }; + const prunedOrders: PrunedSignedOrder[] = [ + { + ...sellTenTokensFor10Weth, + ...{ + fillableMakerAssetAmount: baseUnitAmount(1), + fillableTakerAssetAmount: baseUnitAmount(0.5, WETH_DECIMALS), + fillableTakerFeeAmount: constants.ZERO_AMOUNT, + }, + }, + ]; const expectedResult = { - makerTokensAvailableInBaseUnits: baseUnitAmount(1), - takerTokensAvailableInBaseUnits: baseUnitAmount(0.5, WETH_DECIMALS), + makerAssetAvailableInBaseUnits: baseUnitAmount(1), + takerAssetAvailableInBaseUnits: baseUnitAmount(0.5, WETH_DECIMALS), }; await expectLiquidityResult( mockWeb3Provider.object, mockOrderbook.object, - ordersAndFillableAmounts, + prunedOrders, 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 prunedOrders: PrunedSignedOrder[] = [ + { + ...sellTenTokensFor10Weth, + ...{ + fillableMakerAssetAmount: baseUnitAmount(1), + fillableTakerAssetAmount: baseUnitAmount(0.5, WETH_DECIMALS), + fillableTakerFeeAmount: constants.ZERO_AMOUNT, + }, + }, + { + ...sellTenTokensFor10Weth, + ...{ + fillableMakerAssetAmount: baseUnitAmount(3), + fillableTakerAssetAmount: baseUnitAmount(3, WETH_DECIMALS), + fillableTakerFeeAmount: constants.ZERO_AMOUNT, + }, + }, + ]; const expectedResult = { - makerTokensAvailableInBaseUnits: baseUnitAmount(4), - takerTokensAvailableInBaseUnits: baseUnitAmount(3.5, WETH_DECIMALS), + makerAssetAvailableInBaseUnits: baseUnitAmount(4), + takerAssetAvailableInBaseUnits: baseUnitAmount(3.5, WETH_DECIMALS), }; await expectLiquidityResult( mockWeb3Provider.object, mockOrderbook.object, - ordersAndFillableAmounts, + prunedOrders, expectedResult, ); }); it('should return 0s when no amounts fillable', async () => { - const ordersAndFillableAmounts = { - orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth], - remainingFillableMakerAssetAmounts: [baseUnitAmount(0), baseUnitAmount(0)], - }; + const prunedOrders: PrunedSignedOrder[] = [ + { + ...sellTenTokensFor10Weth, + ...{ + fillableMakerAssetAmount: constants.ZERO_AMOUNT, + fillableTakerAssetAmount: constants.ZERO_AMOUNT, + fillableTakerFeeAmount: constants.ZERO_AMOUNT, + }, + }, + { + ...sellTenTokensFor10Weth, + ...{ + fillableMakerAssetAmount: constants.ZERO_AMOUNT, + fillableTakerAssetAmount: constants.ZERO_AMOUNT, + fillableTakerFeeAmount: constants.ZERO_AMOUNT, + }, + }, + ]; const expectedResult = { - makerTokensAvailableInBaseUnits: baseUnitAmount(0), - takerTokensAvailableInBaseUnits: baseUnitAmount(0, WETH_DECIMALS), + makerAssetAvailableInBaseUnits: constants.ZERO_AMOUNT, + takerAssetAvailableInBaseUnits: constants.ZERO_AMOUNT, }; await expectLiquidityResult( mockWeb3Provider.object, mockOrderbook.object, - ordersAndFillableAmounts, + prunedOrders, expectedResult, ); }); diff --git a/packages/asset-swapper/test/utils/consumer_utils.ts b/packages/asset-swapper/test/utils/consumer_utils.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/asset-swapper/test/utils/mocks.ts b/packages/asset-swapper/test/utils/mocks.ts index 482be06634..6aa6ec724f 100644 --- a/packages/asset-swapper/test/utils/mocks.ts +++ b/packages/asset-swapper/test/utils/mocks.ts @@ -4,7 +4,7 @@ import { APIOrder, AssetPairsItem, SignedOrder } from '@0x/types'; import * as TypeMoq from 'typemoq'; import { SwapQuoter } from '../../src/swap_quoter'; -import { OrdersAndFillableAmounts } from '../../src/types'; +import { PrunedSignedOrder } from '../../src/types'; class OrderbookClass extends Orderbook { // tslint:disable-next-line:prefer-function-over-method @@ -49,26 +49,26 @@ const partiallyMockedSwapQuoter = (provider: Web3ProviderEngine, orderbook: Orde return mockedSwapQuoter; }; -const mockGetOrdersAndAvailableAmounts = ( +const mockGetPrunedSignedOrdersAsync = ( mockedSwapQuoter: TypeMoq.IMock, makerAssetData: string, takerAssetData: string, - ordersAndFillableAmounts: OrdersAndFillableAmounts, + prunedOrders: PrunedSignedOrder[], ): void => { mockedSwapQuoter - .setup(async a => a.getOrdersAndFillableAmountsAsync(makerAssetData, takerAssetData)) - .returns(async () => Promise.resolve(ordersAndFillableAmounts)) + .setup(async a => a.getPrunedSignedOrdersAsync(makerAssetData, takerAssetData)) + .returns(async () => Promise.resolve(prunedOrders)) .verifiable(TypeMoq.Times.once()); }; -export const mockedSwapQuoterWithOrdersAndFillableAmounts = ( +export const mockedSwapQuoterWithPrunedSignedOrders = ( provider: Web3ProviderEngine, orderbook: Orderbook, makerAssetData: string, takerAssetData: string, - ordersAndFillableAmounts: OrdersAndFillableAmounts, + prunedOrders: PrunedSignedOrder[], ): TypeMoq.IMock => { const mockedAssetQuoter = partiallyMockedSwapQuoter(provider, orderbook); - mockGetOrdersAndAvailableAmounts(mockedAssetQuoter, makerAssetData, takerAssetData, ordersAndFillableAmounts); + mockGetPrunedSignedOrdersAsync(mockedAssetQuoter, makerAssetData, takerAssetData, prunedOrders); return mockedAssetQuoter; }; diff --git a/packages/asset-swapper/test/utils/swap_quote.ts b/packages/asset-swapper/test/utils/swap_quote.ts index 46bc8b0e2f..1aab941a95 100644 --- a/packages/asset-swapper/test/utils/swap_quote.ts +++ b/packages/asset-swapper/test/utils/swap_quote.ts @@ -1,134 +1,40 @@ -import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; -import { MarketOperation, SignedOrder } from '@0x/types'; +import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; -import { SupportedProvider } from '@0x/web3-wrapper'; import * as _ from 'lodash'; import { constants } from '../../src/constants'; -import { SwapQuote } from '../../src/types'; - -const ZERO_BIG_NUMBER = new BigNumber(0); - -export const getSignedOrdersWithNoFeesAsync = async ( - provider: SupportedProvider, - makerAssetData: string, - takerAssetData: string, - makerAddress: string, - takerAddress: string, - fillableAmounts: BigNumber[], - exchangeAddress?: string, -): Promise => { - const promises = _.map(fillableAmounts, async (fillableAmount: BigNumber) => - orderFactory.createSignedOrderAsync( - provider, - makerAddress, - fillableAmount, - makerAssetData, - fillableAmount, - takerAssetData, - exchangeAddress || constants.NULL_ADDRESS, - ), - ); - return Promise.all(promises); -}; - -export const getPartialSignedOrdersWithNoFees = ( - makerAssetData: string, - takerAssetData: string, - makerAddress: string, - takerAddress: string, - fillableAmounts: BigNumber[], -): SignedOrder[] => { - return _.map(fillableAmounts, (fillableAmount: BigNumber) => - orderFactory.createSignedOrderFromPartial({ - makerAddress, - makerAssetAmount: fillableAmount, - makerAssetData, - takerAssetAmount: fillableAmount, - takerAssetData, - chainId: 42, - }), - ); -}; - -export const getPartialSignedOrdersWithFees = ( - makerAssetData: string, - takerAssetData: string, - makerAddress: string, - takerAddress: string, - fillableAmounts: BigNumber[], - takerFees: BigNumber[], -): SignedOrder[] => { - const orders = getPartialSignedOrdersWithNoFees( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmounts, - ); - return _.map(orders, (order: SignedOrder, index: number) => - orderFactory.createSignedOrderFromPartial({ - ...order, - ...{ takerFee: takerFees[index] }, - chainId: 42, - }), - ); -}; - -export const getFullyFillableSwapQuoteWithFees = ( - makerAssetData: string, - takerAssetData: string, - orders: SignedOrder[], - feeOrders: SignedOrder[], - operation: MarketOperation, -) => { - const swapQuote = getFullyFillableSwapQuoteWithNoFees(makerAssetData, takerAssetData, orders, operation); - swapQuote.feeOrders = feeOrders; - const totalFeeTakerTokenAmount = _.reduce( - feeOrders, - (a: BigNumber, c: SignedOrder) => a.plus(c.takerAssetAmount), - ZERO_BIG_NUMBER, - ); - // Adds fees to the SwapQuoteInfos assuming all feeOrders will be filled - swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount = totalFeeTakerTokenAmount; - swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount = totalFeeTakerTokenAmount; - swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount = swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount.plus( - totalFeeTakerTokenAmount, - ); - swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount = swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount.plus( - totalFeeTakerTokenAmount, - ); - return swapQuote; -}; +import { MarketOperation, PrunedSignedOrder, SwapQuote } from '../../src/types'; +import { protocolFeeUtils } from '../../src/utils/protocol_fee_utils'; export const getFullyFillableSwapQuoteWithNoFees = ( makerAssetData: string, takerAssetData: string, - orders: SignedOrder[], + orders: PrunedSignedOrder[], operation: MarketOperation, + gasPrice: BigNumber, ): SwapQuote => { const makerAssetFillAmount = _.reduce( orders, (a: BigNumber, c: SignedOrder) => a.plus(c.makerAssetAmount), - ZERO_BIG_NUMBER, + constants.ZERO_AMOUNT, ); - const totalTakerTokenAmount = _.reduce( + const totalTakerAssetAmount = _.reduce( orders, (a: BigNumber, c: SignedOrder) => a.plus(c.takerAssetAmount), - ZERO_BIG_NUMBER, + constants.ZERO_AMOUNT, ); const quoteInfo = { - makerTokenAmount: makerAssetFillAmount, - takerTokenAmount: totalTakerTokenAmount, - feeTakerTokenAmount: ZERO_BIG_NUMBER, - totalTakerTokenAmount, + makerAssetAmount: makerAssetFillAmount, + feeTakerAssetAmount: constants.ZERO_AMOUNT, + takerAssetAmount: totalTakerAssetAmount, + totalTakerAssetAmount, + protocolFeeInEthAmount: protocolFeeUtils.calculateWorstCaseProtocolFee(orders, gasPrice), }; const quoteBase = { makerAssetData, takerAssetData, orders, - feeOrders: [], bestCaseQuoteInfo: quoteInfo, worstCaseQuoteInfo: quoteInfo, }; @@ -143,7 +49,7 @@ export const getFullyFillableSwapQuoteWithNoFees = ( return { ...quoteBase, type: MarketOperation.Sell, - takerAssetFillAmount: totalTakerTokenAmount, + takerAssetFillAmount: totalTakerAssetAmount, }; } }; diff --git a/packages/asset-swapper/test/utils/test_order_factory.ts b/packages/asset-swapper/test/utils/test_order_factory.ts new file mode 100644 index 0000000000..96c3881d0d --- /dev/null +++ b/packages/asset-swapper/test/utils/test_order_factory.ts @@ -0,0 +1,63 @@ +import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; +import { Order, SignedOrder } from '@0x/types'; +import * as _ from 'lodash'; + +import { constants } from '../../src/constants'; +import { PrunedSignedOrder } from '../../src/types'; + +const CHAIN_ID = 1337; +const BASE_TEST_ORDER: Order = orderFactory.createOrder( + constants.NULL_ADDRESS, + constants.ZERO_AMOUNT, + constants.NULL_ERC20_ASSET_DATA, + constants.ZERO_AMOUNT, + constants.NULL_ERC20_ASSET_DATA, + constants.NULL_ADDRESS, + CHAIN_ID, +); + +const BASE_TEST_SIGNED_ORDER: SignedOrder = { + ...BASE_TEST_ORDER, + signature: constants.NULL_BYTES, +}; + +const BASE_TEST_PRUNED_SIGNED_ORDER: PrunedSignedOrder = { + ...BASE_TEST_SIGNED_ORDER, + fillableMakerAssetAmount: constants.ZERO_AMOUNT, + fillableTakerAssetAmount: constants.ZERO_AMOUNT, + fillableTakerFeeAmount: constants.ZERO_AMOUNT, +}; + +export const testOrderFactory = { + generateTestSignedOrder(partialOrder: Partial): SignedOrder { + return transformObject(BASE_TEST_SIGNED_ORDER, partialOrder); + }, + generateIdenticalTestSignedOrders(partialOrder: Partial, numOrders: number): SignedOrder[] { + const baseTestOrders = _.map(_.range(numOrders), () => BASE_TEST_SIGNED_ORDER); + return _.map(baseTestOrders, order => transformObject(order, partialOrder)); + }, + generateTestSignedOrders(partialOrders: Array>): SignedOrder[] { + return _.map(partialOrders, partialOrder => transformObject(BASE_TEST_SIGNED_ORDER, partialOrder)); + }, + generateTestPrunedSignedOrder(partialOrder: Partial): PrunedSignedOrder { + return transformObject(BASE_TEST_PRUNED_SIGNED_ORDER, partialOrder); + }, + generateIdenticalTestPrunedSignedOrders( + partialOrder: Partial, + numOrders: number, + ): PrunedSignedOrder[] { + const baseTestOrders = _.map(_.range(numOrders), () => BASE_TEST_PRUNED_SIGNED_ORDER); + return _.map(baseTestOrders, (baseOrder): PrunedSignedOrder => transformObject(baseOrder, partialOrder)); + }, + generateTestPrunedSignedOrders(partialOrders: Array>): PrunedSignedOrder[] { + return _.map( + partialOrders, + (partialOrder): PrunedSignedOrder => transformObject(BASE_TEST_PRUNED_SIGNED_ORDER, partialOrder), + ); + }, +}; + +function transformObject(input: T, transformation: Partial): T { + const copy = _.cloneDeep(input); + return _.assign(copy, transformation); +} diff --git a/packages/asset-swapper/test/utils/test_orders.ts b/packages/asset-swapper/test/utils/test_orders.ts new file mode 100644 index 0000000000..0dbbbba579 --- /dev/null +++ b/packages/asset-swapper/test/utils/test_orders.ts @@ -0,0 +1,146 @@ +import { PrunedSignedOrder } from '../../src/types'; + +import { testOrderFactory } from './test_order_factory'; +import { baseUnitAmount } from './utils'; + +// tslint:disable:custom-no-magic-numbers + +const FAKE_ERC20_TAKER_ASSET_DATA = '0xf47261b22222222222222222222222222222222222222222222222222222222222222222'; +const FAKE_ERC20_MAKER_ASSET_DATA = '0xf47261b11111111111111111111111111111111111111111111111111111111111111111'; + +const PARTIAL_ORDER: Partial = { + takerAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + makerAssetData: FAKE_ERC20_MAKER_ASSET_DATA, +}; + +const PARTIAL_ORDER_FEE_IN_TAKER_ASSET: Partial = { + ...{ + takerFeeAssetData: FAKE_ERC20_TAKER_ASSET_DATA, + }, + ...PARTIAL_ORDER, +}; + +const PARTIAL_ORDER_FEE_IN_MAKER_ASSET: Partial = { + ...{ + takerFeeAssetData: FAKE_ERC20_MAKER_ASSET_DATA, + }, + ...PARTIAL_ORDER, +}; + +const PARTIAL_PRUNED_SIGNED_ORDERS_FEELESS: Array> = [ + { + ...{ + takerAssetAmount: baseUnitAmount(1), + makerAssetAmount: baseUnitAmount(6), + fillableTakerAssetAmount: baseUnitAmount(1), + fillableMakerAssetAmount: baseUnitAmount(6), + }, + ...PARTIAL_ORDER, + }, + { + ...{ + takerAssetAmount: baseUnitAmount(10), + makerAssetAmount: baseUnitAmount(4), + fillableTakerAssetAmount: baseUnitAmount(5), + fillableMakerAssetAmount: baseUnitAmount(2), + }, + ...PARTIAL_ORDER, + }, + { + ...{ + takerAssetAmount: baseUnitAmount(6), + makerAssetAmount: baseUnitAmount(6), + fillableTakerAssetAmount: baseUnitAmount(3), + fillableMakerAssetAmount: baseUnitAmount(2), + }, + ...PARTIAL_ORDER, + }, +]; + +const PARTIAL_PRUNED_SIGNED_FEE_IN_TAKER_ASSET: Array> = [ + { + ...{ + takerAssetAmount: baseUnitAmount(1), + makerAssetAmount: baseUnitAmount(6), + takerFee: baseUnitAmount(3), + fillableTakerAssetAmount: baseUnitAmount(1), + fillableMakerAssetAmount: baseUnitAmount(6), + fillableTakerFeeAmount: baseUnitAmount(3), + }, + ...PARTIAL_ORDER_FEE_IN_TAKER_ASSET, + }, + { + ...{ + takerAssetAmount: baseUnitAmount(10), + makerAssetAmount: baseUnitAmount(4), + takerFee: baseUnitAmount(2), + fillableTakerAssetAmount: baseUnitAmount(5), + fillableMakerAssetAmount: baseUnitAmount(2), + fillableTakerFeeAmount: baseUnitAmount(1), + }, + ...PARTIAL_ORDER_FEE_IN_TAKER_ASSET, + }, + { + ...{ + takerAssetAmount: baseUnitAmount(6), + makerAssetAmount: baseUnitAmount(6), + takerFee: baseUnitAmount(4), + fillableTakerAssetAmount: baseUnitAmount(3), + fillableMakerAssetAmount: baseUnitAmount(2), + fillableTakerFeeAmount: baseUnitAmount(2), + }, + ...PARTIAL_ORDER_FEE_IN_TAKER_ASSET, + }, +]; + +const PARTIAL_PRUNED_SIGNED_FEE_IN_MAKER_ASSET: Array> = [ + { + ...{ + takerAssetAmount: baseUnitAmount(5), + makerAssetAmount: baseUnitAmount(2), + takerFee: baseUnitAmount(1), + fillableTakerAssetAmount: baseUnitAmount(5), + fillableMakerAssetAmount: baseUnitAmount(2), + fillableTakerFeeAmount: baseUnitAmount(1), + }, + ...PARTIAL_ORDER_FEE_IN_MAKER_ASSET, + }, + { + ...{ + takerAssetAmount: baseUnitAmount(2), + makerAssetAmount: baseUnitAmount(12), + takerFee: baseUnitAmount(6), + fillableTakerAssetAmount: baseUnitAmount(1), + fillableMakerAssetAmount: baseUnitAmount(6), + fillableTakerFeeAmount: baseUnitAmount(3), + }, + ...PARTIAL_ORDER_FEE_IN_MAKER_ASSET, + }, + { + ...{ + takerAssetAmount: baseUnitAmount(3), + makerAssetAmount: baseUnitAmount(3), + takerFee: baseUnitAmount(2), + fillableTakerAssetAmount: baseUnitAmount(3), + fillableMakerAssetAmount: baseUnitAmount(3), + fillableTakerFeeAmount: baseUnitAmount(2), + }, + ...PARTIAL_ORDER_FEE_IN_MAKER_ASSET, + }, +]; + +const PRUNED_SIGNED_ORDERS_FEELESS = testOrderFactory.generateTestPrunedSignedOrders( + PARTIAL_PRUNED_SIGNED_ORDERS_FEELESS, +); +const PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET = testOrderFactory.generateTestPrunedSignedOrders( + PARTIAL_PRUNED_SIGNED_FEE_IN_TAKER_ASSET, +); +const PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET = testOrderFactory.generateTestPrunedSignedOrders( + PARTIAL_PRUNED_SIGNED_FEE_IN_MAKER_ASSET, +); + +export const testOrders = { + PRUNED_SIGNED_ORDERS_FEELESS, + PRUNED_SIGNED_ORDERS_FEE_IN_TAKER_ASSET, + PRUNED_SIGNED_ORDERS_FEE_IN_MAKER_ASSET, +}; diff --git a/packages/asset-swapper/test/utils/utils.ts b/packages/asset-swapper/test/utils/utils.ts new file mode 100644 index 0000000000..1e01d59f0e --- /dev/null +++ b/packages/asset-swapper/test/utils/utils.ts @@ -0,0 +1,9 @@ +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; + +const TOKEN_DECIMALS = 18; + +// tslint:disable:custom-no-magic-numbers +export const baseUnitAmount = (unitAmount: number, decimals = TOKEN_DECIMALS): BigNumber => { + return Web3Wrapper.toBaseUnitAmount(new BigNumber(unitAmount), decimals); +}; diff --git a/packages/asset-swapper/test/utils_test.ts b/packages/asset-swapper/test/utils_test.ts deleted file mode 100644 index 801ce09761..0000000000 --- a/packages/asset-swapper/test/utils_test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; -import { SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; -import 'mocha'; - -import { constants } from '../src/constants'; -import { utils } from '../src/utils/utils'; - -import { chaiSetup } from './utils/chai_setup'; - -chaiSetup.configure(); -const expect = chai.expect; - -const TOKEN_DECIMALS = 18; -const WETH_DECIMALS = constants.ETHER_TOKEN_DECIMALS; - -const baseUnitAmount = (unitAmount: number, decimals = TOKEN_DECIMALS): BigNumber => { - return Web3Wrapper.toBaseUnitAmount(new BigNumber(unitAmount), decimals); -}; - -// tslint:disable:custom-no-magic-numbers -describe('utils', () => { - // orders - const sellTwoTokensFor1Weth: SignedOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: baseUnitAmount(2), - takerAssetAmount: baseUnitAmount(1, WETH_DECIMALS), - }); - const sellTenTokensFor10Weth: SignedOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: baseUnitAmount(10), - takerAssetAmount: baseUnitAmount(10, WETH_DECIMALS), - }); - const sellTwoTokensFor1WethWithTwoTokenFee: SignedOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: baseUnitAmount(2), - takerAssetAmount: baseUnitAmount(1, WETH_DECIMALS), - takerFee: baseUnitAmount(2), - }); - const sellTenTokensFor1WethWithFourTokenFee: SignedOrder = orderFactory.createSignedOrderFromPartial({ - chainId: 42, - makerAssetAmount: baseUnitAmount(2), - takerAssetAmount: baseUnitAmount(1, WETH_DECIMALS), - takerFee: baseUnitAmount(4), - }); - describe('isFeeOrdersRequiredToFillOrders', async () => { - it('should return true if ordersAndFillableAmounts is completed unfilled and has fees', () => { - const ordersAndFillableAmounts = { - orders: [sellTwoTokensFor1WethWithTwoTokenFee, sellTenTokensFor1WethWithFourTokenFee], - remainingFillableMakerAssetAmounts: [baseUnitAmount(1), baseUnitAmount(10)], - }; - const isFeeOrdersRequired = utils.isFeeOrdersRequiredToFillOrders(ordersAndFillableAmounts); - expect(isFeeOrdersRequired).to.equal(true); - }); - it('should return true if ordersAndFillableAmounts is partially unfilled and has fees', () => { - const ordersAndFillableAmounts = { - orders: [sellTwoTokensFor1WethWithTwoTokenFee, sellTenTokensFor1WethWithFourTokenFee], - remainingFillableMakerAssetAmounts: [baseUnitAmount(0), baseUnitAmount(5)], - }; - const isFeeOrdersRequired = utils.isFeeOrdersRequiredToFillOrders(ordersAndFillableAmounts); - expect(isFeeOrdersRequired).to.equal(true); - }); - it('should return false if ordersAndFillableAmounts is completed filled and has fees', () => { - const ordersAndFillableAmounts = { - orders: [sellTwoTokensFor1WethWithTwoTokenFee, sellTenTokensFor1WethWithFourTokenFee], - remainingFillableMakerAssetAmounts: [baseUnitAmount(0), baseUnitAmount(0)], - }; - const isFeeOrdersRequired = utils.isFeeOrdersRequiredToFillOrders(ordersAndFillableAmounts); - expect(isFeeOrdersRequired).to.equal(false); - }); - it("should return false if ordersAndFillableAmounts is completely unfilled and doesn't have fees", () => { - const ordersAndFillableAmounts = { - orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth], - remainingFillableMakerAssetAmounts: [baseUnitAmount(1), baseUnitAmount(10)], - }; - const isFeeOrdersRequired = utils.isFeeOrdersRequiredToFillOrders(ordersAndFillableAmounts); - expect(isFeeOrdersRequired).to.equal(false); - }); - }); -}); diff --git a/packages/migrations/CHANGELOG.json b/packages/migrations/CHANGELOG.json index cdff0cfb68..6fe2a90e20 100644 --- a/packages/migrations/CHANGELOG.json +++ b/packages/migrations/CHANGELOG.json @@ -16,7 +16,7 @@ }, { "note": "Deploy Forwarder AFTER staking is hooked up", - "pr": "TODO" + "pr": 2350 } ] },