aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorfragosti <francesco.agosti93@gmail.com>2018-10-05 06:03:14 +0800
committerfragosti <francesco.agosti93@gmail.com>2018-10-05 06:03:14 +0800
commit1e3ab76e9a6e4f50b105c62f7930408fae1cc661 (patch)
tree96df72933dd182f32b625fdb8099d40d5eba68e2 /packages
parentba2ba628e815c996582c6ead81f657a14a00abd0 (diff)
parentab28e6af3507d7eea163e37730e68a101b525237 (diff)
downloaddexon-sol-tools-1e3ab76e9a6e4f50b105c62f7930408fae1cc661.tar
dexon-sol-tools-1e3ab76e9a6e4f50b105c62f7930408fae1cc661.tar.gz
dexon-sol-tools-1e3ab76e9a6e4f50b105c62f7930408fae1cc661.tar.bz2
dexon-sol-tools-1e3ab76e9a6e4f50b105c62f7930408fae1cc661.tar.lz
dexon-sol-tools-1e3ab76e9a6e4f50b105c62f7930408fae1cc661.tar.xz
dexon-sol-tools-1e3ab76e9a6e4f50b105c62f7930408fae1cc661.tar.zst
dexon-sol-tools-1e3ab76e9a6e4f50b105c62f7930408fae1cc661.zip
Merge branch 'development' of https://github.com/0xProject/0x-monorepo into feature/instant/redux-styles-container
Diffstat (limited to 'packages')
-rw-r--r--packages/0x.js/package.json7
-rw-r--r--packages/0x.js/webpack.config.js31
-rw-r--r--packages/asset-buyer/CHANGELOG.json4
-rw-r--r--packages/asset-buyer/src/asset_buyer.ts24
-rw-r--r--packages/asset-buyer/src/types.ts27
-rw-r--r--packages/asset-buyer/src/utils/assert.ts11
-rw-r--r--packages/asset-buyer/src/utils/buy_quote_calculator.ts159
-rw-r--r--packages/asset-buyer/test/buy_quote_calculator_test.ts160
-rw-r--r--packages/contract-wrappers/package.json1
-rw-r--r--packages/order-utils/src/order_factory.ts39
-rw-r--r--packages/order-watcher/package.json2
-rw-r--r--packages/sol-compiler/src/compiler.ts87
-rw-r--r--packages/subproviders/package.json2
-rw-r--r--packages/testnet-faucets/package.json6
-rw-r--r--packages/website/README.md2
-rw-r--r--packages/website/package.json29
-rw-r--r--packages/website/ts/index.tsx30
-rw-r--r--packages/website/ts/lazy_component.tsx2
-rw-r--r--packages/website/webpack.config.js93
19 files changed, 547 insertions, 169 deletions
diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json
index c89a3e613..268972a4f 100644
--- a/packages/0x.js/package.json
+++ b/packages/0x.js/package.json
@@ -52,13 +52,12 @@
"@types/node": "*",
"@types/sinon": "^2.2.2",
"@types/web3-provider-engine": "^14.0.0",
- "awesome-typescript-loader": "^3.1.3",
+ "awesome-typescript-loader": "^5.2.1",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^2.0.1",
"copyfiles": "^2.0.0",
"dirty-chai": "^2.0.1",
- "json-loader": "^0.5.4",
"make-promises-safe": "^1.1.0",
"mocha": "^4.1.0",
"npm-run-all": "^4.1.2",
@@ -70,8 +69,8 @@
"tslint": "5.11.0",
"typedoc": "0.12.0",
"typescript": "3.0.1",
- "uglifyjs-webpack-plugin": "^1.3.0",
- "webpack": "^3.1.0"
+ "uglifyjs-webpack-plugin": "^2.0.1",
+ "webpack": "^4.20.2"
},
"dependencies": {
"@0xproject/assert": "^1.0.12",
diff --git a/packages/0x.js/webpack.config.js b/packages/0x.js/webpack.config.js
index 1ad0a79ec..397faa76f 100644
--- a/packages/0x.js/webpack.config.js
+++ b/packages/0x.js/webpack.config.js
@@ -2,8 +2,7 @@
* This is to generate the umd bundle only
*/
const _ = require('lodash');
-const webpack = require('webpack');
-const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
+const TerserPlugin = require('terser-webpack-plugin');
const path = require('path');
const production = process.env.NODE_ENV === 'production';
@@ -16,6 +15,7 @@ if (production) {
module.exports = {
entry,
+ mode: 'production',
output: {
path: path.resolve(__dirname, '_bundles'),
filename: '[name].js',
@@ -27,19 +27,18 @@ module.exports = {
extensions: ['.ts', '.js', '.json'],
},
devtool: 'source-map',
- plugins: [
- // TODO: Revert to webpack bundled version with webpack v4.
- // The v3 series bundled version does not support ES6 and
- // fails to build.
- new UglifyJsPlugin({
- sourceMap: true,
- uglifyOptions: {
- mangle: {
- reserved: ['BigNumber'],
+ optimization: {
+ minimizer: [
+ new TerserPlugin({
+ sourceMap: true,
+ terserOptions: {
+ mangle: {
+ reserved: ['BigNumber'],
+ },
},
- },
- }),
- ],
+ }),
+ ],
+ },
module: {
rules: [
{
@@ -59,10 +58,6 @@ module.exports = {
],
exclude: /node_modules/,
},
- {
- test: /\.json$/,
- loader: 'json-loader',
- },
],
},
};
diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json
index 8c2c7a8d2..df67059cb 100644
--- a/packages/asset-buyer/CHANGELOG.json
+++ b/packages/asset-buyer/CHANGELOG.json
@@ -5,6 +5,10 @@
{
"note": "Expand AssetBuyer to work with multiple assets at once",
"pr": 1086
+ },
+ {
+ "note": "Fix minRate and maxRate calculation",
+ "pr": 1113
}
]
},
diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts
index 0bb757f52..7ec39e012 100644
--- a/packages/asset-buyer/src/asset_buyer.ts
+++ b/packages/asset-buyer/src/asset_buyer.ts
@@ -123,7 +123,7 @@ export class AssetBuyer {
public async getBuyQuoteAsync(
assetData: string,
assetBuyAmount: BigNumber,
- options: Partial<BuyQuoteRequestOpts>,
+ options: Partial<BuyQuoteRequestOpts> = {},
): Promise<BuyQuote> {
const { feePercentage, shouldForceOrderRefresh, slippagePercentage } = {
...constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS,
@@ -164,7 +164,7 @@ export class AssetBuyer {
public async getBuyQuoteForERC20TokenAddressAsync(
tokenAddress: string,
assetBuyAmount: BigNumber,
- options: Partial<BuyQuoteRequestOpts>,
+ options: Partial<BuyQuoteRequestOpts> = {},
): Promise<BuyQuote> {
assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.isBigNumber('assetBuyAmount', assetBuyAmount);
@@ -179,20 +179,23 @@ export class AssetBuyer {
*
* @return A promise of the txHash.
*/
- public async executeBuyQuoteAsync(buyQuote: BuyQuote, options: Partial<BuyQuoteExecutionOpts>): Promise<string> {
- const { rate, takerAddress, feeRecipient } = {
+ public async executeBuyQuoteAsync(
+ buyQuote: BuyQuote,
+ options: Partial<BuyQuoteExecutionOpts> = {},
+ ): Promise<string> {
+ const { ethAmount, takerAddress, feeRecipient } = {
...constants.DEFAULT_BUY_QUOTE_EXECUTION_OPTS,
...options,
};
assert.isValidBuyQuote('buyQuote', buyQuote);
- if (!_.isUndefined(rate)) {
- assert.isBigNumber('rate', rate);
+ if (!_.isUndefined(ethAmount)) {
+ assert.isBigNumber('ethAmount', ethAmount);
}
if (!_.isUndefined(takerAddress)) {
assert.isETHAddressHex('takerAddress', takerAddress);
}
assert.isETHAddressHex('feeRecipient', feeRecipient);
- const { orders, feeOrders, feePercentage, assetBuyAmount, maxRate } = buyQuote;
+ const { orders, feeOrders, feePercentage, assetBuyAmount, worstCaseQuoteInfo } = buyQuote;
// if no takerAddress is provided, try to get one from the provider
let finalTakerAddress;
if (!_.isUndefined(takerAddress)) {
@@ -207,15 +210,12 @@ export class AssetBuyer {
throw new Error(AssetBuyerError.NoAddressAvailable);
}
}
- // if no rate is provided, default to the maxRate from buyQuote
- const desiredRate = rate || maxRate;
- // calculate how much eth is required to buy assetBuyAmount at the desired rate
- const ethAmount = assetBuyAmount.dividedToIntegerBy(desiredRate);
+ // if no ethAmount is provided, default to the worst ethAmount from buyQuote
const txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
orders,
assetBuyAmount,
finalTakerAddress,
- ethAmount,
+ ethAmount || worstCaseQuoteInfo.totalEthAmount,
feeOrders,
feePercentage,
feeRecipient,
diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts
index 8d3dcbfe6..b96795bb6 100644
--- a/packages/asset-buyer/src/types.ts
+++ b/packages/asset-buyer/src/types.ts
@@ -35,21 +35,32 @@ export interface OrderProvider {
/**
* assetData: String that represents a specific asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
+ * assetBuyAmount: The amount of asset to buy.
* orders: An array of objects conforming to SignedOrder. These orders can be used to cover the requested assetBuyAmount plus slippage.
* feeOrders: An array of objects conforming to SignedOrder. These orders can be used to cover the fees for the orders param above.
- * minRate: Min rate that needs to be paid in order to execute the buy.
- * maxRate: Max rate that can be paid in order to execute the buy.
- * assetBuyAmount: The amount of asset to buy.
* feePercentage: Optional affiliate fee percentage used to calculate the eth amounts above.
+ * bestCaseQuoteInfo: Info about the best case price for the asset.
+ * worstCaseQuoteInfo: Info about the worst case price for the asset.
*/
export interface BuyQuote {
assetData: string;
+ assetBuyAmount: BigNumber;
orders: SignedOrder[];
feeOrders: SignedOrder[];
- minRate: BigNumber;
- maxRate: BigNumber;
- assetBuyAmount: BigNumber;
feePercentage?: number;
+ bestCaseQuoteInfo: BuyQuoteInfo;
+ worstCaseQuoteInfo: BuyQuoteInfo;
+}
+
+/**
+ * ethPerAssetPrice: The price of one unit of the desired asset in ETH
+ * feeEthAmount: The amount of eth required to pay the affiliate fee.
+ * totalEthAmount: the total amount of eth required to complete the buy. (Filling orders, feeOrders, and paying affiliate fee)
+ */
+export interface BuyQuoteInfo {
+ ethPerAssetPrice: BigNumber;
+ feeEthAmount: BigNumber;
+ totalEthAmount: BigNumber;
}
/**
@@ -64,12 +75,12 @@ export interface BuyQuoteRequestOpts {
}
/**
- * rate: The desired rate to execute the buy at. Affects the amount of ETH sent with the transaction, defaults to buyQuote.maxRate.
+ * ethAmount: The desired amount of eth to spend. Defaults to buyQuote.worstCaseQuoteInfo.totalEthAmount.
* takerAddress: The address to perform the buy. Defaults to the first available address from the provider.
* feeRecipient: The address where affiliate fees are sent. Defaults to null address (0x000...000).
*/
export interface BuyQuoteExecutionOpts {
- rate?: BigNumber;
+ ethAmount?: BigNumber;
takerAddress?: string;
feeRecipient: string;
}
diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts
index 04f425237..d43b71fee 100644
--- a/packages/asset-buyer/src/utils/assert.ts
+++ b/packages/asset-buyer/src/utils/assert.ts
@@ -3,7 +3,7 @@ import { schemas } from '@0xproject/json-schemas';
import { SignedOrder } from '@0xproject/types';
import * as _ from 'lodash';
-import { BuyQuote, OrderProvider, OrderProviderRequest } from '../types';
+import { BuyQuote, BuyQuoteInfo, OrderProvider, OrderProviderRequest } from '../types';
export const assert = {
...sharedAssert,
@@ -11,13 +11,18 @@ export const assert = {
sharedAssert.isHexString(`${variableName}.assetData`, buyQuote.assetData);
sharedAssert.doesConformToSchema(`${variableName}.orders`, buyQuote.orders, schemas.signedOrdersSchema);
sharedAssert.doesConformToSchema(`${variableName}.feeOrders`, buyQuote.feeOrders, schemas.signedOrdersSchema);
- sharedAssert.isBigNumber(`${variableName}.minRate`, buyQuote.minRate);
- sharedAssert.isBigNumber(`${variableName}.maxRate`, buyQuote.maxRate);
+ assert.isValidBuyQuoteInfo(`${variableName}.bestCaseQuoteInfo`, buyQuote.bestCaseQuoteInfo);
+ assert.isValidBuyQuoteInfo(`${variableName}.worstCaseQuoteInfo`, buyQuote.worstCaseQuoteInfo);
sharedAssert.isBigNumber(`${variableName}.assetBuyAmount`, buyQuote.assetBuyAmount);
if (!_.isUndefined(buyQuote.feePercentage)) {
sharedAssert.isNumber(`${variableName}.feePercentage`, buyQuote.feePercentage);
}
},
+ isValidBuyQuoteInfo(variableName: string, buyQuoteInfo: BuyQuoteInfo): void {
+ sharedAssert.isBigNumber(`${variableName}.ethPerAssetPrice`, buyQuoteInfo.ethPerAssetPrice);
+ sharedAssert.isBigNumber(`${variableName}.feeEthAmount`, buyQuoteInfo.feeEthAmount);
+ sharedAssert.isBigNumber(`${variableName}.totalEthAmount`, buyQuoteInfo.totalEthAmount);
+ },
isValidOrderProvider(variableName: string, orderFetcher: OrderProvider): void {
sharedAssert.isFunction(`${variableName}.getOrdersAsync`, orderFetcher.getOrdersAsync);
},
diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts
index b706ea143..cb0fd128c 100644
--- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts
+++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts
@@ -1,11 +1,9 @@
-import { marketUtils } from '@0xproject/order-utils';
+import { marketUtils, rateUtils } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import { constants } from '../constants';
-import { AssetBuyerError, BuyQuote, OrdersAndFillableAmounts } from '../types';
-
-import { orderUtils } from './order_utils';
+import { AssetBuyerError, BuyQuote, BuyQuoteInfo, OrdersAndFillableAmounts } from '../types';
// Calculates a buy quote for orders that have WETH as the takerAsset
export const buyQuoteCalculator = {
@@ -21,6 +19,7 @@ export const buyQuoteCalculator = {
const feeOrders = feeOrdersAndFillableAmounts.orders;
const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts;
const slippageBufferAmount = assetBuyAmount.mul(slippagePercentage).round();
+ // find the orders that cover the desired assetBuyAmount (with slippage)
const {
resultOrders,
remainingFillAmount,
@@ -29,9 +28,11 @@ export const buyQuoteCalculator = {
remainingFillableMakerAssetAmounts,
slippageBufferAmount,
});
+ // if we do not have enough orders to cover the desired assetBuyAmount, throw
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
throw new Error(AssetBuyerError.InsufficientAssetLiquidity);
}
+ // 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
@@ -40,49 +41,139 @@ export const buyQuoteCalculator = {
remainingFeeAmount,
feeOrdersRemainingFillableMakerAssetAmounts,
} = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(resultOrders, feeOrders, {
- remainingFillableMakerAssetAmounts,
+ remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
remainingFillableFeeAmounts,
});
+ // if we do not have enough feeOrders to cover the fees, throw
if (remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
throw new Error(AssetBuyerError.InsufficientZrxLiquidity);
}
+ // assetData information for the result
const assetData = orders[0].makerAssetData;
-
- // calculate minRate and maxRate by calculating min and max eth usage and then dividing into
- // assetBuyAmount to get assetData / WETH, needs to take into account feePercentage as well
- // minEthAmount = (sum(takerAssetAmount[i]) until sum(makerAssetAmount[i]) >= assetBuyAmount ) * (1 + feePercentage)
- // maxEthAmount = (sum(takerAssetAmount[i]) until i == orders.length) * (1 + feePercentage)
- const allOrders = _.concat(resultOrders, resultFeeOrders);
- const allRemainingAmounts = _.concat(
- ordersRemainingFillableMakerAssetAmounts,
- feeOrdersRemainingFillableMakerAssetAmounts,
+ // compile the resulting trimmed set of orders for makerAsset and feeOrders that are needed for assetBuyAmount
+ const trimmedOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
+ orders: resultOrders,
+ remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
+ };
+ const trimmedFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
+ orders: resultFeeOrders,
+ remainingFillableMakerAssetAmounts: feeOrdersRemainingFillableMakerAssetAmounts,
+ };
+ const bestCaseQuoteInfo = calculateQuoteInfo(
+ trimmedOrdersAndFillableAmounts,
+ trimmedFeeOrdersAndFillableAmounts,
+ assetBuyAmount,
+ feePercentage,
);
- let minEthAmount = constants.ZERO_AMOUNT;
- let maxEthAmount = constants.ZERO_AMOUNT;
- let cumulativeMakerAmount = constants.ZERO_AMOUNT;
- _.forEach(allOrders, (order, index) => {
- const remainingFillableMakerAssetAmount = allRemainingAmounts[index];
- const claimableTakerAssetAmount = orderUtils.calculateRemainingTakerAssetAmount(
- order,
- remainingFillableMakerAssetAmount,
- );
- // taker asset is always assumed to be WETH
- maxEthAmount = maxEthAmount.plus(claimableTakerAssetAmount);
- if (cumulativeMakerAmount.lessThan(assetBuyAmount)) {
- minEthAmount = minEthAmount.plus(claimableTakerAssetAmount);
- }
- cumulativeMakerAmount = cumulativeMakerAmount.plus(remainingFillableMakerAssetAmount);
- });
- const feeAdjustedMinRate = minEthAmount.mul(feePercentage + 1).div(assetBuyAmount);
- const feeAdjustedMaxRate = minEthAmount.mul(feePercentage + 1).div(assetBuyAmount);
+ // in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
+ const worstCaseQuoteInfo = calculateQuoteInfo(
+ reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
+ reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
+ assetBuyAmount,
+ feePercentage,
+ );
+
return {
assetData,
orders: resultOrders,
feeOrders: resultFeeOrders,
- minRate: feeAdjustedMinRate,
- maxRate: feeAdjustedMaxRate,
+ bestCaseQuoteInfo,
+ worstCaseQuoteInfo,
assetBuyAmount,
feePercentage,
};
},
};
+
+function calculateQuoteInfo(
+ ordersAndFillableAmounts: OrdersAndFillableAmounts,
+ feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
+ assetBuyAmount: BigNumber,
+ feePercentage: number,
+): BuyQuoteInfo {
+ // find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right
+ const [ethAmountToBuyAsset, zrxAmountToBuyAsset] = findEthAndZrxAmountNeededToBuyAsset(
+ ordersAndFillableAmounts,
+ assetBuyAmount,
+ );
+ // find the total eth needed to buy fees
+ const ethAmountToBuyFees = findEthAmountNeededToBuyFees(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
+ const ethAmountBeforeAffiliateFee = ethAmountToBuyAsset.plus(ethAmountToBuyFees);
+ const totalEthAmount = ethAmountBeforeAffiliateFee.mul(feePercentage + 1);
+ // divide into the assetBuyAmount in order to find rate of makerAsset / WETH
+ const ethPerAssetPrice = ethAmountBeforeAffiliateFee.div(assetBuyAmount);
+ return {
+ totalEthAmount,
+ feeEthAmount: totalEthAmount.minus(ethAmountBeforeAffiliateFee),
+ ethPerAssetPrice,
+ };
+}
+
+// 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 findEthAmountNeededToBuyFees(
+ feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
+ feeAmount: BigNumber,
+): BigNumber {
+ const { orders, remainingFillableMakerAssetAmounts } = feeOrdersAndFillableAmounts;
+ const result = _.reduce(
+ orders,
+ (acc, order, index) => {
+ const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
+ const amountToFill = BigNumber.min(acc.remainingFeeAmount, remainingFillableMakerAssetAmount);
+ const feeAdjustedRate = rateUtils.getFeeAdjustedRateOfFeeOrder(order);
+ const ethAmountForThisOrder = feeAdjustedRate.mul(amountToFill);
+ return {
+ ethAmount: acc.ethAmount.plus(ethAmountForThisOrder),
+ remainingFeeAmount: BigNumber.max(constants.ZERO_AMOUNT, acc.remainingFeeAmount.minus(amountToFill)),
+ };
+ },
+ {
+ ethAmount: constants.ZERO_AMOUNT,
+ remainingFeeAmount: feeAmount,
+ },
+ );
+ return result.ethAmount;
+}
+
+function findEthAndZrxAmountNeededToBuyAsset(
+ ordersAndFillableAmounts: OrdersAndFillableAmounts,
+ assetBuyAmount: BigNumber,
+): [BigNumber, BigNumber] {
+ const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts;
+ const result = _.reduce(
+ orders,
+ (acc, order, index) => {
+ const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
+ const amountToFill = BigNumber.min(acc.remainingAssetBuyAmount, remainingFillableMakerAssetAmount);
+ // find the amount of eth required to fill amountToFill (amountToFill / makerAssetAmount) * takerAssetAmount
+ const ethAmountForThisOrder = amountToFill
+ .mul(order.takerAssetAmount)
+ .dividedToIntegerBy(order.makerAssetAmount);
+ // find the amount of zrx required to fill fees for amountToFill (amountToFill / makerAssetAmount) * takerFee
+ const zrxAmountForThisOrder = amountToFill.mul(order.takerFee).dividedToIntegerBy(order.makerAssetAmount);
+ return {
+ ethAmount: acc.ethAmount.plus(ethAmountForThisOrder),
+ zrxAmount: acc.zrxAmount.plus(zrxAmountForThisOrder),
+ remainingAssetBuyAmount: BigNumber.max(
+ constants.ZERO_AMOUNT,
+ acc.remainingAssetBuyAmount.minus(amountToFill),
+ ),
+ };
+ },
+ {
+ ethAmount: constants.ZERO_AMOUNT,
+ zrxAmount: constants.ZERO_AMOUNT,
+ remainingAssetBuyAmount: assetBuyAmount,
+ },
+ );
+ return [result.ethAmount, result.zrxAmount];
+}
diff --git a/packages/asset-buyer/test/buy_quote_calculator_test.ts b/packages/asset-buyer/test/buy_quote_calculator_test.ts
new file mode 100644
index 000000000..b987b45a8
--- /dev/null
+++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts
@@ -0,0 +1,160 @@
+import { orderFactory } from '@0xproject/order-utils/lib/src/order_factory';
+import { BigNumber } from '@0xproject/utils';
+import * as chai from 'chai';
+import * as _ from 'lodash';
+import 'mocha';
+
+import { AssetBuyerError, OrdersAndFillableAmounts } from '../src/types';
+import { buyQuoteCalculator } from '../src/utils/buy_quote_calculator';
+
+import { chaiSetup } from './utils/chai_setup';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+// tslint:disable:custom-no-magic-numbers
+describe('buyQuoteCalculator', () => {
+ describe('#calculate', () => {
+ 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)
+ const firstOrder = orderFactory.createSignedOrderFromPartial({
+ makerAssetAmount: new BigNumber(400),
+ takerAssetAmount: new BigNumber(100),
+ takerFee: new BigNumber(200),
+ });
+ const firstRemainingFillAmount = new BigNumber(200);
+ const secondOrder = orderFactory.createSignedOrderFromPartial({
+ makerAssetAmount: new BigNumber(200),
+ takerAssetAmount: new BigNumber(100),
+ takerFee: new BigNumber(100),
+ });
+ const secondRemainingFillAmount = secondOrder.makerAssetAmount;
+ ordersAndFillableAmounts = {
+ orders: [firstOrder, secondOrder],
+ remainingFillableMakerAssetAmounts: [firstRemainingFillAmount, secondRemainingFillAmount],
+ };
+ const smallFeeOrder = orderFactory.createSignedOrderFromPartial({
+ makerAssetAmount: new BigNumber(100),
+ takerAssetAmount: new BigNumber(100),
+ });
+ smallFeeOrderAndFillableAmount = {
+ orders: [smallFeeOrder],
+ remainingFillableMakerAssetAmounts: [smallFeeOrder.makerAssetAmount],
+ };
+ const largeFeeOrder = orderFactory.createSignedOrderFromPartial({
+ makerAssetAmount: new BigNumber(110),
+ takerAssetAmount: new BigNumber(200),
+ takerFee: new BigNumber(10),
+ });
+ allFeeOrdersAndFillableAmounts = {
+ orders: [smallFeeOrder, largeFeeOrder],
+ remainingFillableMakerAssetAmounts: [
+ smallFeeOrder.makerAssetAmount,
+ largeFeeOrder.makerAssetAmount.minus(largeFeeOrder.takerFee),
+ ],
+ };
+ });
+ it('should throw if not enough maker asset liquidity', () => {
+ // we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
+ expect(() =>
+ buyQuoteCalculator.calculate(
+ ordersAndFillableAmounts,
+ smallFeeOrderAndFillableAmount,
+ new BigNumber(500),
+ 0,
+ 0,
+ ),
+ ).to.throw(AssetBuyerError.InsufficientAssetLiquidity);
+ });
+ it('should throw if not enough ZRX liquidity', () => {
+ // we request 300 makerAsset units but the ZRX order is only enough to fill the first order, which only has 200 makerAssetUnits available
+ expect(() =>
+ buyQuoteCalculator.calculate(
+ ordersAndFillableAmounts,
+ smallFeeOrderAndFillableAmount,
+ new BigNumber(300),
+ 0,
+ 0,
+ ),
+ ).to.throw(AssetBuyerError.InsufficientZrxLiquidity);
+ });
+ it('calculates a correct buyQuote with no slippage', () => {
+ // we request 200 makerAsset units which can be filled using the first order
+ // the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder
+ const assetBuyAmount = new BigNumber(200);
+ const feePercentage = 0.02;
+ const slippagePercentage = 0;
+ const buyQuote = buyQuoteCalculator.calculate(
+ ordersAndFillableAmounts,
+ smallFeeOrderAndFillableAmount,
+ assetBuyAmount,
+ feePercentage,
+ slippagePercentage,
+ );
+ // test if orders are correct
+ expect(buyQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
+ expect(buyQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]);
+ // test if rates are correct
+ // 50 eth to fill the first order + 100 eth for fees
+ const expectedFillEthAmount = new BigNumber(150);
+ const expectedTotalEthAmount = expectedFillEthAmount.mul(feePercentage + 1);
+ const expectedFeeEthAmount = expectedTotalEthAmount.minus(expectedFillEthAmount);
+ const expectedEthPerAssetPrice = expectedFillEthAmount.div(assetBuyAmount);
+ expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
+ expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
+ expect(buyQuote.bestCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice);
+ // because we have no slippage protection, minRate is equal to maxRate
+ expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
+ expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
+ expect(buyQuote.worstCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice);
+ // test if feePercentage gets passed through
+ expect(buyQuote.feePercentage).to.equal(feePercentage);
+ });
+ it('calculates a correct buyQuote with with slippage', () => {
+ // we request 200 makerAsset units which can be filled using the first order
+ // however with 50% slippage we are protecting the buy with 100 extra makerAssetUnits
+ // so we need enough orders to fill 300 makerAssetUnits
+ // 300 makerAssetUnits can only be filled using both orders
+ // the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder
+ const assetBuyAmount = new BigNumber(200);
+ const feePercentage = 0.02;
+ const slippagePercentage = 0.5;
+ const buyQuote = buyQuoteCalculator.calculate(
+ ordersAndFillableAmounts,
+ allFeeOrdersAndFillableAmounts,
+ assetBuyAmount,
+ feePercentage,
+ slippagePercentage,
+ );
+ // test if orders are correct
+ expect(buyQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
+ expect(buyQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders);
+ // test if rates are correct
+ // 50 eth to fill the first order + 100 eth for fees
+ const expectedFillEthAmount = new BigNumber(150);
+ const expectedTotalEthAmount = expectedFillEthAmount.mul(feePercentage + 1);
+ const expectedFeeEthAmount = expectedTotalEthAmount.minus(expectedFillEthAmount);
+ const expectedEthPerAssetPrice = expectedFillEthAmount.div(assetBuyAmount);
+ expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
+ expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
+ expect(buyQuote.bestCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice);
+ // 100 eth to fill the first order + 200 eth for fees
+ const expectedWorstFillEthAmount = new BigNumber(300);
+ const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.mul(feePercentage + 1);
+ const expectedWorstFeeEthAmount = expectedWorstTotalEthAmount.minus(expectedWorstFillEthAmount);
+ const expectedWorstEthPerAssetPrice = expectedWorstFillEthAmount.div(assetBuyAmount);
+ expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedWorstFeeEthAmount);
+ expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedWorstTotalEthAmount);
+ expect(buyQuote.worstCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedWorstEthPerAssetPrice);
+ // test if feePercentage gets passed through
+ expect(buyQuote.feePercentage).to.equal(feePercentage);
+ });
+ });
+});
diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json
index 22dd6521c..c345d0a6a 100644
--- a/packages/contract-wrappers/package.json
+++ b/packages/contract-wrappers/package.json
@@ -53,7 +53,6 @@
"@types/sinon": "^2.2.2",
"@types/uuid": "^3.4.2",
"@types/web3-provider-engine": "^14.0.0",
- "awesome-typescript-loader": "^3.1.3",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^2.0.1",
diff --git a/packages/order-utils/src/order_factory.ts b/packages/order-utils/src/order_factory.ts
index 46a69ae4d..b1292903a 100644
--- a/packages/order-utils/src/order_factory.ts
+++ b/packages/order-utils/src/order_factory.ts
@@ -8,8 +8,21 @@ import { orderHashUtils } from './order_hash';
import { generatePseudoRandomSalt } from './salt';
import { signatureUtils } from './signature_utils';
import { CreateOrderOpts } from './types';
-
export const orderFactory = {
+ createOrderFromPartial(partialOrder: Partial<Order>): Order {
+ const defaultOrder = generateEmptyOrder();
+ return {
+ ...defaultOrder,
+ ...partialOrder,
+ };
+ },
+ createSignedOrderFromPartial(partialSignedOrder: Partial<SignedOrder>): SignedOrder {
+ const defaultOrder = generateEmptySignedOrder();
+ return {
+ ...defaultOrder,
+ ...partialSignedOrder,
+ };
+ },
createOrder(
makerAddress: string,
makerAssetAmount: BigNumber,
@@ -69,6 +82,30 @@ export const orderFactory = {
},
};
+function generateEmptySignedOrder(): SignedOrder {
+ return {
+ ...generateEmptyOrder(),
+ signature: constants.NULL_BYTES,
+ };
+}
+function generateEmptyOrder(): Order {
+ return {
+ senderAddress: constants.NULL_ADDRESS,
+ makerAddress: constants.NULL_ADDRESS,
+ takerAddress: constants.NULL_ADDRESS,
+ makerFee: constants.ZERO_AMOUNT,
+ takerFee: constants.ZERO_AMOUNT,
+ makerAssetAmount: constants.ZERO_AMOUNT,
+ takerAssetAmount: constants.ZERO_AMOUNT,
+ makerAssetData: constants.NULL_BYTES,
+ takerAssetData: constants.NULL_BYTES,
+ salt: generatePseudoRandomSalt(),
+ exchangeAddress: constants.NULL_ADDRESS,
+ feeRecipientAddress: constants.NULL_ADDRESS,
+ expirationTimeSeconds: constants.INFINITE_TIMESTAMP_SEC,
+ };
+}
+
function generateDefaultCreateOrderOpts(): {
takerAddress: string;
senderAddress: string;
diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json
index 9f57dd6fd..de5f7ba09 100644
--- a/packages/order-watcher/package.json
+++ b/packages/order-watcher/package.json
@@ -52,13 +52,11 @@
"@types/mocha": "^2.2.42",
"@types/node": "*",
"@types/sinon": "^2.2.2",
- "awesome-typescript-loader": "^3.1.3",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^2.0.1",
"copyfiles": "^2.0.0",
"dirty-chai": "^2.0.1",
- "json-loader": "^0.5.4",
"make-promises-safe": "^1.1.0",
"mocha": "^4.1.0",
"npm-run-all": "^4.1.2",
diff --git a/packages/sol-compiler/src/compiler.ts b/packages/sol-compiler/src/compiler.ts
index a29367485..7eefc1474 100644
--- a/packages/sol-compiler/src/compiler.ts
+++ b/packages/sol-compiler/src/compiler.ts
@@ -296,13 +296,16 @@ export class Compiler {
compilerOutput: solc.StandardOutput,
): Promise<void> {
const compiledContract = compilerOutput.contracts[contractPath][contractName];
- const sourceCodes = _.mapValues(
- compilerOutput.sources,
- (_1, sourceFilePath) => this._resolver.resolve(sourceFilePath).source,
- );
+
+ // need to gather sourceCodes for this artifact, but compilerOutput.sources (the list of contract modules)
+ // contains listings for for every contract compiled during the compiler invocation that compiled the contract
+ // to be persisted, which could include many that are irrelevant to the contract at hand. So, gather up only
+ // the relevant sources:
+ const { sourceCodes, sources } = this._getSourcesWithDependencies(contractPath, compilerOutput.sources);
+
const contractVersion: ContractVersionData = {
compilerOutput: compiledContract,
- sources: compilerOutput.sources,
+ sources,
sourceCodes,
sourceTreeHashHex,
compiler: {
@@ -333,6 +336,80 @@ export class Compiler {
await fsWrapper.writeFileAsync(currentArtifactPath, artifactString);
logUtils.warn(`${contractName} artifact saved!`);
}
+ /**
+ * For the given @param contractPath, populates JSON objects to be used in the ContractVersionData interface's
+ * properties `sources` (source code file names mapped to ID numbers) and `sourceCodes` (source code content of
+ * contracts) for that contract. The source code pointed to by contractPath is read and parsed directly (via
+ * `this._resolver.resolve().source`), as are its imports, recursively. The ID numbers for @return `sources` are
+ * taken from the corresponding ID's in @param fullSources, and the content for @return sourceCodes is read from
+ * disk (via the aforementioned `resolver.source`).
+ */
+ private _getSourcesWithDependencies(
+ contractPath: string,
+ fullSources: { [sourceName: string]: { id: number } },
+ ): { sourceCodes: { [sourceName: string]: string }; sources: { [sourceName: string]: { id: number } } } {
+ const sources = { [contractPath]: { id: fullSources[contractPath].id } };
+ const sourceCodes = { [contractPath]: this._resolver.resolve(contractPath).source };
+ this._recursivelyGatherDependencySources(
+ contractPath,
+ sourceCodes[contractPath],
+ fullSources,
+ sources,
+ sourceCodes,
+ );
+ return { sourceCodes, sources };
+ }
+ private _recursivelyGatherDependencySources(
+ contractPath: string,
+ contractSource: string,
+ fullSources: { [sourceName: string]: { id: number } },
+ sourcesToAppendTo: { [sourceName: string]: { id: number } },
+ sourceCodesToAppendTo: { [sourceName: string]: string },
+ ): void {
+ const importStatementMatches = contractSource.match(/\nimport[^;]*;/g);
+ if (importStatementMatches === null) {
+ return;
+ }
+ for (const importStatementMatch of importStatementMatches) {
+ const importPathMatches = importStatementMatch.match(/\"([^\"]*)\"/);
+ if (importPathMatches === null || importPathMatches.length === 0) {
+ continue;
+ }
+
+ let importPath = importPathMatches[1];
+ // HACK(ablrow): We have, e.g.:
+ //
+ // importPath = "../../utils/LibBytes/LibBytes.sol"
+ // contractPath = "2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol"
+ //
+ // Resolver doesn't understand "../" so we want to pass
+ // "2.0.0/utils/LibBytes/LibBytes.sol" to resolver.
+ //
+ // This hack involves using path.resolve. But path.resolve returns
+ // absolute directories by default. We trick it into thinking that
+ // contractPath is a root directory by prepending a '/' and then
+ // removing the '/' the end.
+ //
+ // path.resolve("/a/b/c", ""../../d/e") === "/a/d/e"
+ //
+ const lastPathSeparatorPos = contractPath.lastIndexOf('/');
+ const contractFolder = lastPathSeparatorPos === -1 ? '' : contractPath.slice(0, lastPathSeparatorPos + 1);
+ importPath = path.resolve('/' + contractFolder, importPath).replace('/', '');
+
+ if (_.isUndefined(sourcesToAppendTo[importPath])) {
+ sourcesToAppendTo[importPath] = { id: fullSources[importPath].id };
+ sourceCodesToAppendTo[importPath] = this._resolver.resolve(importPath).source;
+
+ this._recursivelyGatherDependencySources(
+ importPath,
+ this._resolver.resolve(importPath).source,
+ fullSources,
+ sourcesToAppendTo,
+ sourceCodesToAppendTo,
+ );
+ }
+ }
+ }
private _compile(solcInstance: solc.SolcInstance, standardInput: solc.StandardInput): solc.StandardOutput {
const compiled: solc.StandardOutput = JSON.parse(
solcInstance.compileStandardWrapper(JSON.stringify(standardInput), importPath => {
diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json
index f9063228f..491f079d2 100644
--- a/packages/subproviders/package.json
+++ b/packages/subproviders/package.json
@@ -76,7 +76,7 @@
"tslint": "5.11.0",
"typedoc": "0.12.0",
"typescript": "3.0.1",
- "webpack": "^3.1.0"
+ "webpack": "^4.20.2"
},
"optionalDependencies": {
"@ledgerhq/hw-transport-node-hid": "^4.3.0"
diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json
index 33f557538..8213f896f 100644
--- a/packages/testnet-faucets/package.json
+++ b/packages/testnet-faucets/package.json
@@ -36,15 +36,15 @@
"@types/body-parser": "^1.16.1",
"@types/express": "^4.0.35",
"@types/lodash": "4.14.104",
- "awesome-typescript-loader": "^3.1.3",
+ "awesome-typescript-loader": "^5.2.1",
"gulp": "^3.9.1",
"make-promises-safe": "^1.1.0",
"nodemon": "^1.11.0",
"shx": "^0.2.2",
- "source-map-loader": "^0.1.6",
+ "source-map-loader": "^0.2.4",
"tslint": "5.11.0",
"typescript": "3.0.1",
- "webpack": "^3.1.0",
+ "webpack": "^4.20.2",
"webpack-node-externals": "^1.6.0"
}
}
diff --git a/packages/website/README.md b/packages/website/README.md
index 7115a3b5c..2edc17cd9 100644
--- a/packages/website/README.md
+++ b/packages/website/README.md
@@ -14,7 +14,7 @@ Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting
## Local Dev Setup
-Requires Node version 6.9.5 or higher.
+Requires Node version 6.9.5 or higher
Add the following to your `/etc/hosts` file:
diff --git a/packages/website/package.json b/packages/website/package.json
index 8c115d8a0..3cf243645 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -7,11 +7,11 @@
"private": true,
"description": "Website and 0x portal dapp",
"scripts": {
- "build": "NODE_ENV=production node --max_old_space_size=8192 ../../node_modules/.bin/webpack",
- "build:ci": "yarn build",
+ "build": "node --max_old_space_size=8192 ../../node_modules/.bin/webpack --mode production",
+ "build:dev": "../../node_modules/.bin/webpack --mode development",
"clean": "shx rm -f public/bundle*",
"lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'",
- "watch_without_deps": "webpack-dev-server --content-base public --https",
+ "dev": "webpack-dev-server --mode development --content-base public --https",
"deploy_dogfood": "npm run build; aws s3 sync ./public/. s3://dogfood.0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers",
"deploy_staging": "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers",
"deploy_live": "DEPLOY_ROLLBAR_SOURCEMAPS=true npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --exclude *.map.js"
@@ -87,26 +87,23 @@
"@types/react-tap-event-plugin": "0.0.30",
"@types/redux": "^3.6.0",
"@types/web3-provider-engine": "^14.0.0",
- "awesome-typescript-loader": "^3.1.3",
- "copy-webpack-plugin": "^4.0.1",
+ "awesome-typescript-loader": "^5.2.1",
"copyfiles": "^2.0.0",
"css-loader": "0.23.x",
- "exports-loader": "0.6.x",
- "imports-loader": "0.6.x",
- "json-loader": "^0.5.4",
- "less-loader": "^2.2.3",
+ "less-loader": "^4.1.0",
"make-promises-safe": "^1.1.0",
"raw-loader": "^0.5.1",
- "rollbar-sourcemap-webpack-plugin": "^2.3.0",
+ "rollbar-sourcemap-webpack-plugin": "^2.4.0",
"shx": "^0.2.2",
- "source-map-loader": "^0.1.6",
- "style-loader": "0.13.x",
+ "source-map-loader": "^0.2.4",
+ "style-loader": "0.23.x",
+ "terser-webpack-plugin": "^1.1.0",
"tslint": "5.11.0",
"tslint-config-0xproject": "^0.0.2",
"typescript": "3.0.1",
- "uglifyjs-webpack-plugin": "^1.2.5",
- "webpack": "^3.1.0",
- "webpack-dev-middleware": "^1.10.0",
- "webpack-dev-server": "^2.5.0"
+ "uglifyjs-webpack-plugin": "^2.0.1",
+ "webpack": "^4.20.2",
+ "webpack-cli": "3.1.2",
+ "webpack-dev-server": "^3.1.9"
}
}
diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx
index 9e59b00ac..d4a79cc4f 100644
--- a/packages/website/ts/index.tsx
+++ b/packages/website/ts/index.tsx
@@ -26,47 +26,47 @@ import 'less/all.less';
// We pass modulePromise returning lambda instead of module promise,
// cause we only want to import the module when the user navigates to the page.
-// At the same time webpack statically parses for System.import() to determine bundle chunk split points
-// so each lazy import needs it's own `System.import()` declaration.
+// At the same time webpack statically parses for import() to determine bundle chunk split points
+// so each lazy import needs it's own `import()` declaration.
const LazyPortal = createLazyComponent('Portal', async () =>
- System.import<any>(/* webpackChunkName: "portal" */ 'ts/containers/portal'),
+ import(/* webpackChunkName: "portal" */ 'ts/containers/portal'),
);
const LazyZeroExJSDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'),
+ import(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'),
);
const LazyContractWrappersDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "contractWrapperDocs" */ 'ts/containers/contract_wrappers_documentation'),
+ import(/* webpackChunkName: "contractWrapperDocs" */ 'ts/containers/contract_wrappers_documentation'),
);
const LazyOrderWatcherDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "orderWatcherDocs" */ 'ts/containers/order_watcher_documentation'),
+ import(/* webpackChunkName: "orderWatcherDocs" */ 'ts/containers/order_watcher_documentation'),
);
const LazySmartContractsDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "smartContractDocs" */ 'ts/containers/smart_contracts_documentation'),
+ import(/* webpackChunkName: "smartContractDocs" */ 'ts/containers/smart_contracts_documentation'),
);
const LazyConnectDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "connectDocs" */ 'ts/containers/connect_documentation'),
+ import(/* webpackChunkName: "connectDocs" */ 'ts/containers/connect_documentation'),
);
const LazyWeb3WrapperDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "web3WrapperDocs" */ 'ts/containers/web3_wrapper_documentation'),
+ import(/* webpackChunkName: "web3WrapperDocs" */ 'ts/containers/web3_wrapper_documentation'),
);
const LazySolCompilerDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "solCompilerDocs" */ 'ts/containers/sol_compiler_documentation'),
+ import(/* webpackChunkName: "solCompilerDocs" */ 'ts/containers/sol_compiler_documentation'),
);
const LazyJSONSchemasDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "jsonSchemasDocs" */ 'ts/containers/json_schemas_documentation'),
+ import(/* webpackChunkName: "jsonSchemasDocs" */ 'ts/containers/json_schemas_documentation'),
);
const LazySolCovDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "solCovDocs" */ 'ts/containers/sol_cov_documentation'),
+ import(/* webpackChunkName: "solCovDocs" */ 'ts/containers/sol_cov_documentation'),
);
const LazySubprovidersDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "subproviderDocs" */ 'ts/containers/subproviders_documentation'),
+ import(/* webpackChunkName: "subproviderDocs" */ 'ts/containers/subproviders_documentation'),
);
const LazyOrderUtilsDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "orderUtilsDocs" */ 'ts/containers/order_utils_documentation'),
+ import(/* webpackChunkName: "orderUtilsDocs" */ 'ts/containers/order_utils_documentation'),
);
const LazyEthereumTypesDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "ethereumTypesDocs" */ 'ts/containers/ethereum_types_documentation'),
+ import(/* webpackChunkName: "ethereumTypesDocs" */ 'ts/containers/ethereum_types_documentation'),
);
const DOCUMENT_TITLE = '0x: The Protocol for Trading Tokens';
diff --git a/packages/website/ts/lazy_component.tsx b/packages/website/ts/lazy_component.tsx
index dce06ed8d..9d3b9944a 100644
--- a/packages/website/ts/lazy_component.tsx
+++ b/packages/website/ts/lazy_component.tsx
@@ -49,7 +49,7 @@ export class LazyComponent extends React.Component<LazyComponentProps, LazyCompo
* @param componentName name of exported component
* @param lazyImport lambda returning module promise
* we pass a lambda because we only want to require a module if it's used
- * @example `const LazyPortal = createLazyComponent('Portal', () => System.import<any>('ts/containers/portal'));``
+ * @example `const LazyPortal = createLazyComponent('Portal', () => import<any>('ts/containers/portal'));``
*/
export const createLazyComponent = (componentName: string, lazyImport: () => Promise<any>) => {
return (props: any) => {
diff --git a/packages/website/webpack.config.js b/packages/website/webpack.config.js
index 8653196a6..ec265be93 100644
--- a/packages/website/webpack.config.js
+++ b/packages/website/webpack.config.js
@@ -1,6 +1,6 @@
const path = require('path');
const webpack = require('webpack');
-const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
+const TerserPlugin = require('terser-webpack-plugin');
const RollbarSourceMapPlugin = require('rollbar-sourcemap-webpack-plugin');
const childProcess = require('child_process');
@@ -9,44 +9,7 @@ const GIT_SHA = childProcess
.toString()
.trim();
-const generatePlugins = () => {
- let plugins = [];
- if (process.env.NODE_ENV === 'production') {
- plugins = plugins.concat([
- // Since we do not use moment's locale feature, we exclude them from the bundle.
- // This reduces the bundle size by 0.4MB.
- new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
- new webpack.DefinePlugin({
- 'process.env': {
- NODE_ENV: JSON.stringify(process.env.NODE_ENV),
- GIT_SHA: JSON.stringify(GIT_SHA),
- },
- }),
- // TODO: Revert to webpack bundled version with webpack v4.
- // The v3 series bundled version does not support ES6 and
- // fails to build.
- new UglifyJsPlugin({
- sourceMap: true,
- uglifyOptions: {
- mangle: {
- reserved: ['BigNumber'],
- },
- },
- }),
- ]);
- if (process.env.DEPLOY_ROLLBAR_SOURCEMAPS === 'true') {
- plugins = plugins.concat([
- new RollbarSourceMapPlugin({
- accessToken: '32c39bfa4bb6440faedc1612a9c13d28',
- version: GIT_SHA,
- publicPath: 'https://0xproject.com/',
- }),
- ]);
- }
- }
- return plugins;
-};
-module.exports = {
+const config = {
entry: ['./ts/index.tsx'],
output: {
path: path.join(__dirname, '/public'),
@@ -92,10 +55,18 @@ module.exports = {
test: /\.css$/,
loaders: ['style-loader', 'css-loader'],
},
- {
- test: /\.json$/,
- loader: 'json-loader',
- },
+ ],
+ },
+ optimization: {
+ minimizer: [
+ new TerserPlugin({
+ sourceMap: true,
+ terserOptions: {
+ mangle: {
+ reserved: ['BigNumber'],
+ },
+ },
+ }),
],
},
devServer: {
@@ -115,5 +86,39 @@ module.exports = {
},
disableHostCheck: true,
},
- plugins: generatePlugins(),
+};
+
+module.exports = (_env, argv) => {
+ let plugins = [];
+ if (argv.mode === 'development') {
+ config.mode = 'development';
+ } else {
+ config.mode = 'production';
+ plugins = plugins.concat([
+ // Since we do not use moment's locale feature, we exclude them from the bundle.
+ // This reduces the bundle size by 0.4MB.
+ new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
+ new webpack.DefinePlugin({
+ 'process.env': {
+ NODE_ENV: JSON.stringify(process.env.NODE_ENV),
+ GIT_SHA: JSON.stringify(GIT_SHA),
+ },
+ }),
+ ]);
+ if (process.env.DEPLOY_ROLLBAR_SOURCEMAPS === 'true') {
+ plugins = plugins.concat([
+ new RollbarSourceMapPlugin({
+ accessToken: '32c39bfa4bb6440faedc1612a9c13d28',
+ version: GIT_SHA,
+ publicPath: 'https://0xproject.com/',
+ }),
+ ]);
+ }
+ }
+ console.log('i 「atl」: Mode: ', config.mode);
+
+ config.plugins = plugins;
+ console.log('i 「atl」: Plugin Count: ', config.plugins.length);
+
+ return config;
};