aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CODEOWNERS20
-rw-r--r--README.md4
-rw-r--r--packages/asset-buyer/src/asset_buyer.ts8
-rw-r--r--packages/asset-buyer/src/constants.ts9
-rw-r--r--packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts2
-rw-r--r--packages/asset-buyer/src/utils/buy_quote_calculator.ts120
-rw-r--r--packages/asset-buyer/src/utils/order_provider_response_processor.ts5
-rw-r--r--packages/asset-buyer/src/utils/order_utils.ts68
-rw-r--r--packages/asset-buyer/test/buy_quote_calculator_test.ts12
-rw-r--r--packages/contract-addresses/CHANGELOG.json9
-rw-r--r--packages/contract-addresses/src/index.ts6
-rw-r--r--packages/contract-artifacts/CHANGELOG.json9
-rw-r--r--packages/contract-artifacts/artifacts/Forwarder.json887
-rw-r--r--packages/instant/package.json5
-rw-r--r--packages/instant/src/components/amount_input.tsx49
-rw-r--r--packages/instant/src/components/animations/full_rotation.tsx24
-rw-r--r--packages/instant/src/components/animations/slide_animations.tsx10
-rw-r--r--packages/instant/src/components/asset_amount_input.tsx39
-rw-r--r--packages/instant/src/components/buy_button.tsx58
-rw-r--r--packages/instant/src/components/buy_order_state_button.tsx26
-rw-r--r--packages/instant/src/components/erc20_asset_amount_input.tsx84
-rw-r--r--packages/instant/src/components/instant_heading.tsx64
-rw-r--r--packages/instant/src/components/placing_order_button.tsx19
-rw-r--r--packages/instant/src/components/retry_button.tsx11
-rw-r--r--packages/instant/src/components/scaling_amount_input.tsx52
-rw-r--r--packages/instant/src/components/scaling_input.tsx170
-rw-r--r--packages/instant/src/components/secondary_button.tsx26
-rw-r--r--packages/instant/src/components/ui/container.tsx15
-rw-r--r--packages/instant/src/components/ui/flex.tsx13
-rw-r--r--packages/instant/src/components/ui/icon.tsx58
-rw-r--r--packages/instant/src/components/ui/input.tsx9
-rw-r--r--packages/instant/src/components/ui/spinner.tsx30
-rw-r--r--packages/instant/src/components/ui/text.tsx11
-rw-r--r--packages/instant/src/components/view_transaction_button.tsx11
-rw-r--r--packages/instant/src/components/zero_ex_instant_container.tsx6
-rw-r--r--packages/instant/src/constants.ts1
-rw-r--r--packages/instant/src/containers/selected_asset_buy_button.ts46
-rw-r--r--packages/instant/src/containers/selected_asset_buy_order_state_button.tsx20
-rw-r--r--packages/instant/src/containers/selected_asset_instant_heading.ts4
-rw-r--r--packages/instant/src/containers/selected_asset_retry_button.tsx26
-rw-r--r--packages/instant/src/containers/selected_asset_view_transaction_button.tsx38
-rw-r--r--packages/instant/src/containers/selected_erc20_asset_amount_input.ts (renamed from packages/instant/src/containers/selected_asset_amount_input.ts)31
-rw-r--r--packages/instant/src/redux/actions.ts14
-rw-r--r--packages/instant/src/redux/reducer.ts74
-rw-r--r--packages/instant/src/style/fonts.ts10
-rw-r--r--packages/instant/src/style/theme.ts6
-rw-r--r--packages/instant/src/types.ts18
-rw-r--r--packages/instant/src/util/asset.ts12
-rw-r--r--packages/instant/src/util/big_number_input.ts29
-rw-r--r--packages/instant/src/util/error.ts4
-rw-r--r--packages/instant/src/util/etherscan.ts24
-rw-r--r--packages/instant/src/util/format.ts2
-rw-r--r--packages/instant/test/util/format.test.ts8
-rw-r--r--packages/migrations/README.md27
-rw-r--r--packages/order-utils/test/asset_data_utils_test.ts25
-rw-r--r--packages/website/ts/components/fill_order.tsx5
-rw-r--r--python-packages/order_utils/LICENSE13
-rw-r--r--python-packages/order_utils/README.md45
-rwxr-xr-xpython-packages/order_utils/setup.py68
-rw-r--r--python-packages/order_utils/src/conf.py4
-rw-r--r--python-packages/order_utils/src/doc_static/.gitkeep0
-rw-r--r--python-packages/order_utils/src/doc_templates/.gitkeep0
-rw-r--r--python-packages/order_utils/src/index.rst8
-rw-r--r--python-packages/order_utils/src/zero_ex/__init__.py1
-rw-r--r--python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py15
-rw-r--r--python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py77
-rw-r--r--python-packages/order_utils/test/test_asset_data_utils.py39
-rw-r--r--python-packages/order_utils/tox.ini13
-rw-r--r--readthedocs.yaml7
-rw-r--r--requirements.readthedocs.txt1
-rw-r--r--yarn.lock104
71 files changed, 1926 insertions, 842 deletions
diff --git a/CODEOWNERS b/CODEOWNERS
index ba40af31a..3cf75fb2d 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -10,20 +10,26 @@ packages/instant/ @BMillman19 @fragosti @steveklebanoff
packages/website/ @BMillman19 @fragosti @fabioberger @steveklebanoff
# Dev tools & setup
+.circleci/ @LogvinovLeon
packages/abi-gen/ @LogvinovLeon
packages/base-contract/ @LogvinovLeon
+packages/connect/ @fragosti
packages/contract_templates/ @LogvinovLeon
+packages/contract-addresses/ @albrow
+packages/contract-artifacts/ @albrow
packages/dev-utils/ @LogvinovLeon @fabioberger
-
+packages/devnet/ @albrow
packages/ethereum-types/ @LogvinovLeon
packages/metacoin/ @LogvinovLeon
+packages/monorepo-scripts/ @fabioberger
+packages/order-utils/ @fabioberger @LogvinovLeon
packages/sol-compiler/ @LogvinovLeon
packages/sol-cov/ @LogvinovLeon
packages/sol-resolver/ @LogvinovLeon
-packages/web3-wrapper/ @LogvinovLeon @fabioberger
-.circleci/ @LogvinovLeon
packages/subproviders/ @fabioberger @dekz
-packages/connect/ @fragosti
-packages/monorepo-scripts/ @fabioberger
-packages/order-utils/ @fabioberger @LogvinovLeon
-python-packages/ @feuGeneA \ No newline at end of file
+packages/verdaccio/ @albrow
+packages/web3-wrapper/ @LogvinovLeon @fabioberger
+python-packages/ @feuGeneA
+
+# Protocol/smart contracts
+packages/contracts/test/ @albrow
diff --git a/README.md b/README.md
index 78e1689d0..5ceae41ba 100644
--- a/README.md
+++ b/README.md
@@ -94,6 +94,10 @@ We strongly recommend that the community help us make improvements and determine
Make sure you are using Yarn v1.9.4. To install using brew:
+```bash
+brew install yarn@1.9.4
+```
+
Then install dependencies
```bash
diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts
index 2f9a3108e..74f3cb471 100644
--- a/packages/asset-buyer/src/asset_buyer.ts
+++ b/packages/asset-buyer/src/asset_buyer.ts
@@ -135,9 +135,14 @@ export class AssetBuyer {
assert.isBoolean('shouldForceOrderRefresh', shouldForceOrderRefresh);
assert.isNumber('slippagePercentage', slippagePercentage);
const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow();
+ const isMakerAssetZrxToken = assetData === zrxTokenAssetData;
+ // get the relevant orders for the makerAsset and fees
+ // if the requested assetData is ZRX, don't get the fee info
const [ordersAndFillableAmounts, feeOrdersAndFillableAmounts] = await Promise.all([
this._getOrdersAndFillableAmountsAsync(assetData, shouldForceOrderRefresh),
- this._getOrdersAndFillableAmountsAsync(zrxTokenAssetData, shouldForceOrderRefresh),
+ isMakerAssetZrxToken
+ ? Promise.resolve(constants.EMPTY_ORDERS_AND_FILLABLE_AMOUNTS)
+ : this._getOrdersAndFillableAmountsAsync(zrxTokenAssetData, shouldForceOrderRefresh),
shouldForceOrderRefresh,
]);
if (ordersAndFillableAmounts.orders.length === 0) {
@@ -149,6 +154,7 @@ export class AssetBuyer {
assetBuyAmount,
feePercentage,
slippagePercentage,
+ isMakerAssetZrxToken,
);
return buyQuote;
}
diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts
index 55effdb23..c912253bd 100644
--- a/packages/asset-buyer/src/constants.ts
+++ b/packages/asset-buyer/src/constants.ts
@@ -1,6 +1,7 @@
+import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
-import { AssetBuyerOpts, BuyQuoteExecutionOpts, BuyQuoteRequestOpts } from './types';
+import { AssetBuyerOpts, BuyQuoteExecutionOpts, BuyQuoteRequestOpts, OrdersAndFillableAmounts } from './types';
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
const MAINNET_NETWORK_ID = 1;
@@ -22,6 +23,11 @@ const DEFAULT_BUY_QUOTE_EXECUTION_OPTS: BuyQuoteExecutionOpts = {
feeRecipient: NULL_ADDRESS,
};
+const EMPTY_ORDERS_AND_FILLABLE_AMOUNTS: OrdersAndFillableAmounts = {
+ orders: [] as SignedOrder[],
+ remainingFillableMakerAssetAmounts: [] as BigNumber[],
+};
+
export const constants = {
ZERO_AMOUNT: new BigNumber(0),
NULL_ADDRESS,
@@ -31,4 +37,5 @@ export const constants = {
DEFAULT_BUY_QUOTE_EXECUTION_OPTS,
DEFAULT_BUY_QUOTE_REQUEST_OPTS,
MAX_PER_PAGE: 10000,
+ EMPTY_ORDERS_AND_FILLABLE_AMOUNTS,
};
diff --git a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts
index 91682b089..41fd1fb30 100644
--- a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts
+++ b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts
@@ -30,7 +30,7 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider {
'remainingTakerAssetAmount',
order.takerAssetAmount,
);
- const remainingFillableMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount(
+ const remainingFillableMakerAssetAmount = orderUtils.getRemainingMakerAmount(
order,
remainingFillableTakerAssetAmount,
);
diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts
index b0cb4a547..f94ab3fa4 100644
--- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts
+++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts
@@ -1,10 +1,12 @@
-import { marketUtils, rateUtils } from '@0x/order-utils';
+import { marketUtils, SignedOrder } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { constants } from '../constants';
import { AssetBuyerError, BuyQuote, BuyQuoteInfo, OrdersAndFillableAmounts } from '../types';
+import { orderUtils } from './order_utils';
+
// Calculates a buy quote for orders that have WETH as the takerAsset
export const buyQuoteCalculator = {
calculate(
@@ -13,6 +15,7 @@ export const buyQuoteCalculator = {
assetBuyAmount: BigNumber,
feePercentage: number,
slippagePercentage: number,
+ isMakerAssetZrxToken: boolean,
): BuyQuote {
const orders = ordersAndFillableAmounts.orders;
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
@@ -32,22 +35,31 @@ export const buyQuoteCalculator = {
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
throw new Error(AssetBuyerError.InsufficientAssetLiquidity);
}
+ // if we are not buying ZRX:
// given the orders calculated above, find the fee-orders that cover the desired assetBuyAmount (with slippage)
// TODO(bmillman): optimization
// update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to
// finding order that cover all fees, this will help with estimating ETH and minimizing gas usage
- const {
- resultFeeOrders,
- remainingFeeAmount,
- feeOrdersRemainingFillableMakerAssetAmounts,
- } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(resultOrders, feeOrders, {
- remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
- remainingFillableFeeAmounts,
- });
- // if we do not have enough feeOrders to cover the fees, throw
- if (remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
- throw new Error(AssetBuyerError.InsufficientZrxLiquidity);
+ let resultFeeOrders = [] as SignedOrder[];
+ let feeOrdersRemainingFillableMakerAssetAmounts = [] as BigNumber[];
+ if (!isMakerAssetZrxToken) {
+ const feeOrdersAndRemainingFeeAmount = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
+ resultOrders,
+ feeOrders,
+ {
+ remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
+ remainingFillableFeeAmounts,
+ },
+ );
+ // if we do not have enough feeOrders to cover the fees, throw
+ if (feeOrdersAndRemainingFeeAmount.remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
+ throw new Error(AssetBuyerError.InsufficientZrxLiquidity);
+ }
+ resultFeeOrders = feeOrdersAndRemainingFeeAmount.resultFeeOrders;
+ feeOrdersRemainingFillableMakerAssetAmounts =
+ feeOrdersAndRemainingFeeAmount.feeOrdersRemainingFillableMakerAssetAmounts;
}
+
// assetData information for the result
const assetData = orders[0].makerAssetData;
// compile the resulting trimmed set of orders for makerAsset and feeOrders that are needed for assetBuyAmount
@@ -64,6 +76,7 @@ export const buyQuoteCalculator = {
trimmedFeeOrdersAndFillableAmounts,
assetBuyAmount,
feePercentage,
+ isMakerAssetZrxToken,
);
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
const worstCaseQuoteInfo = calculateQuoteInfo(
@@ -71,6 +84,7 @@ export const buyQuoteCalculator = {
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
assetBuyAmount,
feePercentage,
+ isMakerAssetZrxToken,
);
return {
assetData,
@@ -89,22 +103,30 @@ function calculateQuoteInfo(
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
assetBuyAmount: BigNumber,
feePercentage: number,
+ isMakerAssetZrxToken: boolean,
): BuyQuoteInfo {
// find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right
- const [ethAmountToBuyAsset, zrxAmountToBuyAsset] = findEthAndZrxAmountNeededToBuyAsset(
- ordersAndFillableAmounts,
- assetBuyAmount,
- );
- // find the total eth needed to buy fees
- const ethAmountToBuyFees = findEthAmountNeededToBuyFees(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
- const affiliateFeeEthAmount = ethAmountToBuyAsset.mul(feePercentage);
- const totalEthAmountWithoutAffiliateFee = ethAmountToBuyAsset.plus(ethAmountToBuyFees);
- const totalEthAmount = totalEthAmountWithoutAffiliateFee.plus(affiliateFeeEthAmount);
+ let ethAmountToBuyAsset = constants.ZERO_AMOUNT;
+ let ethAmountToBuyZrx = constants.ZERO_AMOUNT;
+ if (isMakerAssetZrxToken) {
+ ethAmountToBuyAsset = findEthAmountNeededToBuyZrx(ordersAndFillableAmounts, assetBuyAmount);
+ } else {
+ // find eth and zrx amounts needed to buy
+ const ethAndZrxAmountToBuyAsset = findEthAndZrxAmountNeededToBuyAsset(ordersAndFillableAmounts, assetBuyAmount);
+ ethAmountToBuyAsset = ethAndZrxAmountToBuyAsset[0];
+ const zrxAmountToBuyAsset = ethAndZrxAmountToBuyAsset[1];
+ // find eth amount needed to buy zrx
+ ethAmountToBuyZrx = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
+ }
+ /// find the eth amount needed to buy the affiliate fee
+ const ethAmountToBuyAffiliateFee = ethAmountToBuyAsset.mul(feePercentage);
+ const totalEthAmountWithoutAffiliateFee = ethAmountToBuyAsset.plus(ethAmountToBuyZrx);
+ const ethAmountTotal = totalEthAmountWithoutAffiliateFee.plus(ethAmountToBuyAffiliateFee);
// divide into the assetBuyAmount in order to find rate of makerAsset / WETH
const ethPerAssetPrice = totalEthAmountWithoutAffiliateFee.div(assetBuyAmount);
return {
- totalEthAmount,
- feeEthAmount: affiliateFeeEthAmount,
+ totalEthAmount: ethAmountTotal,
+ feeEthAmount: ethAmountToBuyAffiliateFee,
ethPerAssetPrice,
};
}
@@ -119,29 +141,38 @@ function reverseOrdersAndFillableAmounts(ordersAndFillableAmounts: OrdersAndFill
};
}
-function findEthAmountNeededToBuyFees(
+function findEthAmountNeededToBuyZrx(
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
- feeAmount: BigNumber,
+ zrxBuyAmount: BigNumber,
): BigNumber {
const { orders, remainingFillableMakerAssetAmounts } = feeOrdersAndFillableAmounts;
const result = _.reduce(
orders,
(acc, order, index) => {
+ const { totalEthAmount, remainingZrxBuyAmount } = acc;
const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
- const amountToFill = BigNumber.min(acc.remainingFeeAmount, remainingFillableMakerAssetAmount);
- const feeAdjustedRate = rateUtils.getFeeAdjustedRateOfFeeOrder(order);
- const ethAmountForThisOrder = feeAdjustedRate.mul(amountToFill);
+ const makerFillAmount = BigNumber.min(remainingZrxBuyAmount, remainingFillableMakerAssetAmount);
+ const [takerFillAmount, adjustedMakerFillAmount] = orderUtils.getTakerFillAmountForFeeOrder(
+ order,
+ makerFillAmount,
+ );
+ const extraFeeAmount = remainingFillableMakerAssetAmount.greaterThanOrEqualTo(adjustedMakerFillAmount)
+ ? constants.ZERO_AMOUNT
+ : adjustedMakerFillAmount.sub(makerFillAmount);
return {
- ethAmount: acc.ethAmount.plus(ethAmountForThisOrder),
- remainingFeeAmount: BigNumber.max(constants.ZERO_AMOUNT, acc.remainingFeeAmount.minus(amountToFill)),
+ totalEthAmount: totalEthAmount.plus(takerFillAmount),
+ remainingZrxBuyAmount: BigNumber.max(
+ constants.ZERO_AMOUNT,
+ remainingZrxBuyAmount.minus(makerFillAmount).plus(extraFeeAmount),
+ ),
};
},
{
- ethAmount: constants.ZERO_AMOUNT,
- remainingFeeAmount: feeAmount,
+ totalEthAmount: constants.ZERO_AMOUNT,
+ remainingZrxBuyAmount: zrxBuyAmount,
},
);
- return result.ethAmount;
+ return result.totalEthAmount;
}
function findEthAndZrxAmountNeededToBuyAsset(
@@ -152,28 +183,25 @@ function findEthAndZrxAmountNeededToBuyAsset(
const result = _.reduce(
orders,
(acc, order, index) => {
+ const { totalEthAmount, totalZrxAmount, remainingAssetBuyAmount } = acc;
const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
- const amountToFill = BigNumber.min(acc.remainingAssetBuyAmount, remainingFillableMakerAssetAmount);
- // find the amount of eth required to fill amountToFill (amountToFill / makerAssetAmount) * takerAssetAmount
- const ethAmountForThisOrder = amountToFill
- .mul(order.takerAssetAmount)
- .dividedToIntegerBy(order.makerAssetAmount);
- // find the amount of zrx required to fill fees for amountToFill (amountToFill / makerAssetAmount) * takerFee
- const zrxAmountForThisOrder = amountToFill.mul(order.takerFee).dividedToIntegerBy(order.makerAssetAmount);
+ const makerFillAmount = BigNumber.min(acc.remainingAssetBuyAmount, remainingFillableMakerAssetAmount);
+ const takerFillAmount = orderUtils.getTakerFillAmount(order, makerFillAmount);
+ const takerFeeAmount = orderUtils.getTakerFeeAmount(order, takerFillAmount);
return {
- ethAmount: acc.ethAmount.plus(ethAmountForThisOrder),
- zrxAmount: acc.zrxAmount.plus(zrxAmountForThisOrder),
+ totalEthAmount: totalEthAmount.plus(takerFillAmount),
+ totalZrxAmount: totalZrxAmount.plus(takerFeeAmount),
remainingAssetBuyAmount: BigNumber.max(
constants.ZERO_AMOUNT,
- acc.remainingAssetBuyAmount.minus(amountToFill),
+ remainingAssetBuyAmount.minus(makerFillAmount),
),
};
},
{
- ethAmount: constants.ZERO_AMOUNT,
- zrxAmount: constants.ZERO_AMOUNT,
+ totalEthAmount: constants.ZERO_AMOUNT,
+ totalZrxAmount: constants.ZERO_AMOUNT,
remainingAssetBuyAmount: assetBuyAmount,
},
);
- return [result.ethAmount, result.zrxAmount];
+ return [result.totalEthAmount, result.totalZrxAmount];
}
diff --git a/packages/asset-buyer/src/utils/order_provider_response_processor.ts b/packages/asset-buyer/src/utils/order_provider_response_processor.ts
index 81464d945..28f684f3c 100644
--- a/packages/asset-buyer/src/utils/order_provider_response_processor.ts
+++ b/packages/asset-buyer/src/utils/order_provider_response_processor.ts
@@ -110,10 +110,7 @@ function getValidOrdersWithRemainingFillableMakerAssetAmountsFromOnChain(
traderInfo.makerZrxBalance,
]);
const remainingTakerAssetAmount = order.takerAssetAmount.minus(orderInfo.orderTakerAssetFilledAmount);
- const remainingMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount(
- order,
- remainingTakerAssetAmount,
- );
+ const remainingMakerAssetAmount = orderUtils.getRemainingMakerAmount(order, remainingTakerAssetAmount);
const remainingFillableCalculator = new RemainingFillableCalculator(
order.makerFee,
order.makerAssetAmount,
diff --git a/packages/asset-buyer/src/utils/order_utils.ts b/packages/asset-buyer/src/utils/order_utils.ts
index ff47eb7c5..1cc2cf95f 100644
--- a/packages/asset-buyer/src/utils/order_utils.ts
+++ b/packages/asset-buyer/src/utils/order_utils.ts
@@ -12,19 +12,63 @@ export const orderUtils = {
const currentUnixTimestampSec = new BigNumber(Date.now() / millisecondsInSecond).round();
return order.expirationTimeSeconds.lessThan(currentUnixTimestampSec.plus(secondsFromNow));
},
- calculateRemainingMakerAssetAmount(order: SignedOrder, remainingTakerAssetAmount: BigNumber): BigNumber {
- if (remainingTakerAssetAmount.eq(0)) {
- return constants.ZERO_AMOUNT;
- }
- return remainingTakerAssetAmount.times(order.makerAssetAmount).dividedToIntegerBy(order.takerAssetAmount);
- },
- calculateRemainingTakerAssetAmount(order: SignedOrder, remainingMakerAssetAmount: BigNumber): BigNumber {
- if (remainingMakerAssetAmount.eq(0)) {
- return constants.ZERO_AMOUNT;
- }
- return remainingMakerAssetAmount.times(order.takerAssetAmount).dividedToIntegerBy(order.makerAssetAmount);
- },
isOpenOrder(order: SignedOrder): boolean {
return order.takerAddress === constants.NULL_ADDRESS;
},
+ // given a remaining amount of takerAsset, calculate how much makerAsset is available
+ getRemainingMakerAmount(order: SignedOrder, remainingTakerAmount: BigNumber): BigNumber {
+ const remainingMakerAmount = remainingTakerAmount
+ .times(order.makerAssetAmount)
+ .div(order.takerAssetAmount)
+ .floor();
+ return remainingMakerAmount;
+ },
+ // given a desired amount of makerAsset, calculate how much takerAsset is required to fill that amount
+ getTakerFillAmount(order: SignedOrder, makerFillAmount: BigNumber): BigNumber {
+ // Round up because exchange rate favors Maker
+ const takerFillAmount = makerFillAmount
+ .mul(order.takerAssetAmount)
+ .div(order.makerAssetAmount)
+ .ceil();
+ return takerFillAmount;
+ },
+ // given a desired amount of takerAsset to fill, calculate how much fee is required by the taker to fill that amount
+ getTakerFeeAmount(order: SignedOrder, takerFillAmount: BigNumber): BigNumber {
+ // Round down because Taker fee rate favors Taker
+ const takerFeeAmount = takerFillAmount
+ .mul(order.takerFee)
+ .div(order.takerAssetAmount)
+ .floor();
+ return takerFeeAmount;
+ },
+ // given a desired amount of takerAsset to fill, calculate how much makerAsset will be filled
+ getMakerFillAmount(order: SignedOrder, takerFillAmount: BigNumber): BigNumber {
+ // Round down because exchange rate favors Maker
+ const makerFillAmount = takerFillAmount
+ .mul(order.makerAssetAmount)
+ .div(order.takerAssetAmount)
+ .floor();
+ return makerFillAmount;
+ },
+ // given a desired amount of makerAsset, calculate how much fee is required by the maker to fill that amount
+ getMakerFeeAmount(order: SignedOrder, makerFillAmount: BigNumber): BigNumber {
+ // Round down because Maker fee rate favors Maker
+ const makerFeeAmount = makerFillAmount
+ .mul(order.makerFee)
+ .div(order.makerAssetAmount)
+ .floor();
+ return makerFeeAmount;
+ },
+ // given a desired amount of ZRX from a fee order, calculate how much takerAsset is required to fill that amount
+ // also calculate how much ZRX needs to be bought in order fill the desired amount + takerFee
+ getTakerFillAmountForFeeOrder(order: SignedOrder, makerFillAmount: BigNumber): [BigNumber, BigNumber] {
+ // For each unit of TakerAsset we buy (MakerAsset - TakerFee)
+ const adjustedTakerFillAmount = makerFillAmount
+ .mul(order.takerAssetAmount)
+ .div(order.makerAssetAmount.sub(order.takerFee))
+ .ceil();
+ // The amount that we buy will be greater than makerFillAmount, since we buy some amount for fees.
+ const adjustedMakerFillAmount = orderUtils.getMakerFillAmount(order, adjustedTakerFillAmount);
+ return [adjustedTakerFillAmount, adjustedMakerFillAmount];
+ },
};
diff --git a/packages/asset-buyer/test/buy_quote_calculator_test.ts b/packages/asset-buyer/test/buy_quote_calculator_test.ts
index 0f516a0f7..0ea371982 100644
--- a/packages/asset-buyer/test/buy_quote_calculator_test.ts
+++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts
@@ -49,9 +49,9 @@ describe('buyQuoteCalculator', () => {
remainingFillableMakerAssetAmounts: [smallFeeOrder.makerAssetAmount],
};
const largeFeeOrder = orderFactory.createSignedOrderFromPartial({
- makerAssetAmount: new BigNumber(110),
+ makerAssetAmount: new BigNumber(113),
takerAssetAmount: new BigNumber(200),
- takerFee: new BigNumber(10),
+ takerFee: new BigNumber(11),
});
allFeeOrdersAndFillableAmounts = {
orders: [smallFeeOrder, largeFeeOrder],
@@ -70,6 +70,7 @@ describe('buyQuoteCalculator', () => {
new BigNumber(500),
0,
0,
+ false,
),
).to.throw(AssetBuyerError.InsufficientAssetLiquidity);
});
@@ -82,6 +83,7 @@ describe('buyQuoteCalculator', () => {
new BigNumber(300),
0,
0,
+ false,
),
).to.throw(AssetBuyerError.InsufficientZrxLiquidity);
});
@@ -97,6 +99,7 @@ describe('buyQuoteCalculator', () => {
assetBuyAmount,
feePercentage,
slippagePercentage,
+ false,
);
// test if orders are correct
expect(buyQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
@@ -134,6 +137,7 @@ describe('buyQuoteCalculator', () => {
assetBuyAmount,
feePercentage,
slippagePercentage,
+ false,
);
// test if orders are correct
expect(buyQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
@@ -149,9 +153,9 @@ describe('buyQuoteCalculator', () => {
expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
expect(buyQuote.bestCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice);
- // 100 eth to fill the first order + 200 eth for fees
+ // 100 eth to fill the first order + 208 eth for fees
const expectedWorstEthAmountForAsset = new BigNumber(100);
- const expectedWorstEthAmountForZrxFees = new BigNumber(200);
+ const expectedWorstEthAmountForZrxFees = new BigNumber(208);
const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset.plus(expectedWorstEthAmountForZrxFees);
const expectedWorstFeeEthAmount = expectedWorstEthAmountForAsset.mul(feePercentage);
const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.plus(expectedWorstFeeEthAmount);
diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json
index c2b44ede8..2727cd42b 100644
--- a/packages/contract-addresses/CHANGELOG.json
+++ b/packages/contract-addresses/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "version": "1.1.0",
+ "changes": [
+ {
+ "pr": 1192,
+ "note": "Update Forwarder addresses"
+ }
+ ]
+ },
+ {
"version": "1.0.1",
"changes": [
{
diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts
index 1ea8c61dd..f5fd8d0be 100644
--- a/packages/contract-addresses/src/index.ts
+++ b/packages/contract-addresses/src/index.ts
@@ -25,7 +25,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
etherToken: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
exchange: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b',
assetProxyOwner: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6',
- forwarder: '0x7afc2d5107af94c462a194d2c21b5bdd238709d6',
+ forwarder: '0x5468a1dc173652ee28d249c271fa9933144746b1',
orderValidator: '0x9463e518dea6810309563c81d5266c1b1d149138',
},
3: {
@@ -35,7 +35,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
etherToken: '0xc778417e063141139fce010982780140aa0cd5ab',
exchange: '0x4530c0483a1633c7a1c97d2c53721caff2caaaaf',
assetProxyOwner: '0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b',
- forwarder: '0x3983e204b12b3c02fb0638caf2cd406a62e0ead3',
+ forwarder: '0x2240dab907db71e64d3e0dba4800c83b5c502d4e',
orderValidator: '0x90431a90516ab49af23a0530e04e8c7836e7122f',
},
42: {
@@ -45,7 +45,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
etherToken: '0xd0a1e359811322d97991e03f863a0c30c2cf029c',
exchange: '0x35dd2932454449b14cee11a94d3674a936d5d7b2',
assetProxyOwner: '0x2c824d2882baa668e0d5202b1e7f2922278703f8',
- forwarder: '0xd85e2fa7e7e252b27b01bf0d65c946959d2f45b8',
+ forwarder: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6',
orderValidator: '0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d',
},
};
diff --git a/packages/contract-artifacts/CHANGELOG.json b/packages/contract-artifacts/CHANGELOG.json
index c2b44ede8..e6a6d02c0 100644
--- a/packages/contract-artifacts/CHANGELOG.json
+++ b/packages/contract-artifacts/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "version": "1.1.0",
+ "changes": [
+ {
+ "pr": 1192,
+ "note": "Update Forwarder artifact"
+ }
+ ]
+ },
+ {
"version": "1.0.1",
"changes": [
{
diff --git a/packages/contract-artifacts/artifacts/Forwarder.json b/packages/contract-artifacts/artifacts/Forwarder.json
index 4f93b231b..a7bd62f8e 100644
--- a/packages/contract-artifacts/artifacts/Forwarder.json
+++ b/packages/contract-artifacts/artifacts/Forwarder.json
@@ -1,448 +1,447 @@
{
- "schemaVersion": "2.0.0",
- "contractName": "Forwarder",
- "compilerOutput": {
- "abi": [
- {
- "constant": false,
- "inputs": [
- {
- "components": [
- {
- "name": "makerAddress",
- "type": "address"
- },
- {
- "name": "takerAddress",
- "type": "address"
- },
- {
- "name": "feeRecipientAddress",
- "type": "address"
- },
- {
- "name": "senderAddress",
- "type": "address"
- },
- {
- "name": "makerAssetAmount",
- "type": "uint256"
- },
- {
- "name": "takerAssetAmount",
- "type": "uint256"
- },
- {
- "name": "makerFee",
- "type": "uint256"
- },
- {
- "name": "takerFee",
- "type": "uint256"
- },
- {
- "name": "expirationTimeSeconds",
- "type": "uint256"
- },
- {
- "name": "salt",
- "type": "uint256"
- },
- {
- "name": "makerAssetData",
- "type": "bytes"
- },
- {
- "name": "takerAssetData",
- "type": "bytes"
- }
- ],
- "name": "orders",
- "type": "tuple[]"
- },
- {
- "name": "makerAssetFillAmount",
- "type": "uint256"
- },
- {
- "name": "signatures",
- "type": "bytes[]"
- },
- {
- "components": [
- {
- "name": "makerAddress",
- "type": "address"
- },
- {
- "name": "takerAddress",
- "type": "address"
- },
- {
- "name": "feeRecipientAddress",
- "type": "address"
- },
- {
- "name": "senderAddress",
- "type": "address"
- },
- {
- "name": "makerAssetAmount",
- "type": "uint256"
- },
- {
- "name": "takerAssetAmount",
- "type": "uint256"
- },
- {
- "name": "makerFee",
- "type": "uint256"
- },
- {
- "name": "takerFee",
- "type": "uint256"
- },
- {
- "name": "expirationTimeSeconds",
- "type": "uint256"
- },
- {
- "name": "salt",
- "type": "uint256"
- },
- {
- "name": "makerAssetData",
- "type": "bytes"
- },
- {
- "name": "takerAssetData",
- "type": "bytes"
- }
- ],
- "name": "feeOrders",
- "type": "tuple[]"
- },
- {
- "name": "feeSignatures",
- "type": "bytes[]"
- },
- {
- "name": "feePercentage",
- "type": "uint256"
- },
- {
- "name": "feeRecipient",
- "type": "address"
- }
- ],
- "name": "marketBuyOrdersWithEth",
- "outputs": [
- {
- "components": [
- {
- "name": "makerAssetFilledAmount",
- "type": "uint256"
- },
- {
- "name": "takerAssetFilledAmount",
- "type": "uint256"
- },
- {
- "name": "makerFeePaid",
- "type": "uint256"
- },
- {
- "name": "takerFeePaid",
- "type": "uint256"
- }
- ],
- "name": "orderFillResults",
- "type": "tuple"
- },
- {
- "components": [
- {
- "name": "makerAssetFilledAmount",
- "type": "uint256"
- },
- {
- "name": "takerAssetFilledAmount",
- "type": "uint256"
- },
- {
- "name": "makerFeePaid",
- "type": "uint256"
- },
- {
- "name": "takerFeePaid",
- "type": "uint256"
- }
- ],
- "name": "feeOrderFillResults",
- "type": "tuple"
- }
- ],
- "payable": true,
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "assetData",
- "type": "bytes"
- },
- {
- "name": "amount",
- "type": "uint256"
- }
- ],
- "name": "withdrawAsset",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "owner",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "components": [
- {
- "name": "makerAddress",
- "type": "address"
- },
- {
- "name": "takerAddress",
- "type": "address"
- },
- {
- "name": "feeRecipientAddress",
- "type": "address"
- },
- {
- "name": "senderAddress",
- "type": "address"
- },
- {
- "name": "makerAssetAmount",
- "type": "uint256"
- },
- {
- "name": "takerAssetAmount",
- "type": "uint256"
- },
- {
- "name": "makerFee",
- "type": "uint256"
- },
- {
- "name": "takerFee",
- "type": "uint256"
- },
- {
- "name": "expirationTimeSeconds",
- "type": "uint256"
- },
- {
- "name": "salt",
- "type": "uint256"
- },
- {
- "name": "makerAssetData",
- "type": "bytes"
- },
- {
- "name": "takerAssetData",
- "type": "bytes"
- }
- ],
- "name": "orders",
- "type": "tuple[]"
- },
- {
- "name": "signatures",
- "type": "bytes[]"
- },
- {
- "components": [
- {
- "name": "makerAddress",
- "type": "address"
- },
- {
- "name": "takerAddress",
- "type": "address"
- },
- {
- "name": "feeRecipientAddress",
- "type": "address"
- },
- {
- "name": "senderAddress",
- "type": "address"
- },
- {
- "name": "makerAssetAmount",
- "type": "uint256"
- },
- {
- "name": "takerAssetAmount",
- "type": "uint256"
- },
- {
- "name": "makerFee",
- "type": "uint256"
- },
- {
- "name": "takerFee",
- "type": "uint256"
- },
- {
- "name": "expirationTimeSeconds",
- "type": "uint256"
- },
- {
- "name": "salt",
- "type": "uint256"
- },
- {
- "name": "makerAssetData",
- "type": "bytes"
- },
- {
- "name": "takerAssetData",
- "type": "bytes"
- }
- ],
- "name": "feeOrders",
- "type": "tuple[]"
- },
- {
- "name": "feeSignatures",
- "type": "bytes[]"
- },
- {
- "name": "feePercentage",
- "type": "uint256"
- },
- {
- "name": "feeRecipient",
- "type": "address"
- }
- ],
- "name": "marketSellOrdersWithEth",
- "outputs": [
- {
- "components": [
- {
- "name": "makerAssetFilledAmount",
- "type": "uint256"
- },
- {
- "name": "takerAssetFilledAmount",
- "type": "uint256"
- },
- {
- "name": "makerFeePaid",
- "type": "uint256"
- },
- {
- "name": "takerFeePaid",
- "type": "uint256"
- }
- ],
- "name": "orderFillResults",
- "type": "tuple"
- },
- {
- "components": [
- {
- "name": "makerAssetFilledAmount",
- "type": "uint256"
- },
- {
- "name": "takerAssetFilledAmount",
- "type": "uint256"
- },
- {
- "name": "makerFeePaid",
- "type": "uint256"
- },
- {
- "name": "takerFeePaid",
- "type": "uint256"
- }
- ],
- "name": "feeOrderFillResults",
- "type": "tuple"
- }
- ],
- "payable": true,
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "transferOwnership",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "name": "_exchange",
- "type": "address"
- },
- {
- "name": "_zrxAssetData",
- "type": "bytes"
- },
- {
- "name": "_wethAssetData",
- "type": "bytes"
- }
- ],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "constructor"
- },
- {
- "payable": true,
- "stateMutability": "payable",
- "type": "fallback"
- }
- ],
- "evm": {
- "bytecode": {
- "linkReferences": {},
- "object":
- "0x60806040523480156200001157600080fd5b5060405162002cdb38038062002cdb83398101806040526200003791908101906200051d565b6000805433600160a060020a031991821617825560018054909116600160a060020a0386161790558251849084908490849081906200007e906004906020870190620003d0565b50825162000094906005906020860190620003d0565b50620000b0836010640100000000620018f66200036f82021704565b9150620000cd846010640100000000620018f66200036f82021704565b60028054600160a060020a03948516600160a060020a031991821617909155600380549285169290911691909117905550600154604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130181207f6070410800000000000000000000000000000000000000000000000000000000825291909216945063607041089350620001739250906004016200068e565b602060405180830381600087803b1580156200018e57600080fd5b505af1158015620001a3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620001c99190810190620004f4565b9050600160a060020a038116151562000219576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200021090620006b0565b60405180910390fd5b6002546040517f095ea7b3000000000000000000000000000000000000000000000000000000008152600160a060020a039091169063095ea7b39062000268908490600019906004016200066f565b602060405180830381600087803b1580156200028357600080fd5b505af115801562000298573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620002be9190810190620005a1565b506003546040517f095ea7b3000000000000000000000000000000000000000000000000000000008152600160a060020a039091169063095ea7b3906200030e908490600019906004016200066f565b602060405180830381600087803b1580156200032957600080fd5b505af11580156200033e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620003649190810190620005a1565b50505050506200077a565b600081601401835110151515620003b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000210906200069e565b506014818301810151910190600160a060020a03165b92915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200041357805160ff191683800117855562000443565b8280016001018555821562000443579182015b828111156200044357825182559160200191906001019062000426565b506200045192915062000455565b5090565b6200047291905b808211156200045157600081556001016200045c565b90565b600062000483825162000711565b9392505050565b600062000483825162000742565b6000601f82018313620004aa57600080fd5b8151620004c1620004bb82620006e9565b620006c2565b91508082526020830160208301858383011115620004de57600080fd5b620004eb83828462000747565b50505092915050565b6000602082840312156200050757600080fd5b600062000515848462000475565b949350505050565b6000806000606084860312156200053357600080fd5b600062000541868662000475565b93505060208401516001604060020a038111156200055e57600080fd5b6200056c8682870162000498565b92505060408401516001604060020a038111156200058957600080fd5b620005978682870162000498565b9150509250925092565b600060208284031215620005b457600080fd5b60006200051584846200048a565b620005cd8162000711565b82525050565b620005cd816200071d565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601881527f554e524547495354455245445f41535345545f50524f58590000000000000000602082015260400190565b620005cd8162000472565b604081016200067f8285620005c2565b62000483602083018462000664565b60208101620003ca8284620005d3565b60208082528101620003ca81620005de565b60208082528101620003ca8162000634565b6040518181016001604060020a0381118282101715620006e157600080fd5b604052919050565b60006001604060020a038211156200070057600080fd5b506020601f91909101601f19160190565b600160a060020a031690565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b151590565b60005b83811015620007645781810151838201526020016200074a565b8381111562000774576000848401525b50505050565b612551806200078a6000396000f30060806040526004361061006c5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166318978e8281146100c8578063630f1e6c146100f25780638da5cb5b146101125780639395525c14610134578063f2fde38b14610147575b60025473ffffffffffffffffffffffffffffffffffffffff1633146100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612337565b60405180910390fd5b005b6100db6100d6366004611da0565b610167565b6040516100e9929190612437565b60405180910390f35b3480156100fe57600080fd5b506100c661010d366004611e9b565b6102f7565b34801561011e57600080fd5b50610127610388565b6040516100e991906122e6565b6100db610142366004611cba565b6103a4565b34801561015357600080fd5b506100c6610162366004611c94565b61050a565b61016f6119a9565b6101776119a9565b6000806101826105bb565b60048054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100600188161502019095169490940493840181900481028201810190925282815261025c939092909183018282801561022d5780601f106102025761010080835404028352916020019161022d565b820191906000526020600020905b81548152906001019060200180831161021057829003601f168201915b50505050508c600081518110151561024157fe5b6020908102909101015161014001519063ffffffff61069616565b156102875761026c8b8b8b6107c3565b935061028084600001518560600151610acb565b90506102ae565b6102928b8b8b610b0d565b9350836060015191506102a68883896107c3565b845190935090505b6102c2846020015184602001518888610d1f565b6102e98b60008151811015156102d457fe5b90602001906020020151610140015182610f33565b505097509795505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610348576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123e7565b61038383838080601f01602080910402602001604051908101604052809392919081815260200183838082843750879450610f339350505050565b505050565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b6103ac6119a9565b6103b46119a9565b60008060006103c16105bb565b60048054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152610441939092909183018282801561022d5780601f106102025761010080835404028352916020019161022d565b156104925761046a670de0b6b3a7640000610464670de0b6b3a76400008a61104f565b34611099565b92506104778b848c6110f1565b945061048b85600001518660600151610acb565b90506104d6565b6104ad670d2f13f7789f0000670de0b6b3a764000034611099565b92506104ba8b848c6110f1565b9450846060015191506104ce89838a6107c3565b855190945090505b6104ea856020015185602001518989610d1f565b6104fc8b60008151811015156102d457fe5b505050965096945050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461055b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123e7565b73ffffffffffffffffffffffffffffffffffffffff8116156105b857600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83161790555b50565b600034116105f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612347565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016000604051808303818588803b15801561067b57600080fd5b505af115801561068f573d6000803e3d6000fd5b5050505050565b6000815183511480156107ba5750816040518082805190602001908083835b602083106106f257805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016106b5565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0180199092169116179052604051919093018190038120885190955088945090928392508401908083835b6020831061078757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161074a565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916145b90505b92915050565b6107cb6119a9565b60608060008060008060006107de6119a9565b8a15156107ea57610abc565b6004805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561088e5780601f106108635761010080835404028352916020019161088e565b820191906000526020600020905b81548152906001019060200180831161087157829003601f168201915b505060058054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152969e509194509250840190508282801561093d5780601f106109125761010080835404028352916020019161093d565b820191906000526020600020905b81548152906001019060200180831161092057829003601f168201915b50505050509650600095508b519450600093505b838514610a8257878c8581518110151561096757fe5b6020908102909101015161014001528b5187908d908690811061098657fe5b60209081029091010151610160015261099f8b87610acb565b9250610a068c858151811015156109b257fe5b9060200190602002015160a00151610a008e878151811015156109d157fe5b90602001906020020151608001518f888151811015156109ed57fe5b9060200190602002015160e00151610acb565b85611099565b9150610a4b8c85815181101515610a1957fe5b90602001906020020151610a2e84600161104f565b8c87815181101515610a3c57fe5b90602001906020020151611295565b9050610a57898261130d565b610a6989600001518a60600151610acb565b95508a8610610a7757610a82565b600190930192610951565b8a861015610abc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123c7565b50505050505050509392505050565b600082821115610b07576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612367565b50900390565b610b156119a9565b606080600080600080610b266119a9565b60008b6000815181101515610b3757fe5b6020908102919091018101516101400151600580546040805160026001841615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190931692909204601f8101869004860283018601909152808252929b5092909190830182828015610bef5780601f10610bc457610100808354040283529160200191610bef565b820191906000526020600020905b815481529060010190602001808311610bd257829003601f168201915b505050505096508b519550600094505b848614610ce557878c86815181101515610c1557fe5b6020908102909101015161014001528b5187908d9087908110610c3457fe5b6020908102909101015161016001528851610c50908c90610acb565b9350610c938c86815181101515610c6357fe5b9060200190602002015160a001518d87815181101515610c7f57fe5b906020019060200201516080015186611099565b9250610cbf8c86815181101515610ca657fe5b90602001906020020151848c88815181101515610a3c57fe5b9150610ccb898361130d565b5087518a8110610cda57610ce5565b600190940193610bff565b8a811015610abc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123c7565b600080808066b1a2bc2ec50000861115610d65576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123f7565b610d6f888861104f565b935034841115610dab576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612357565b610db53485610acb565b9250610dca86670de0b6b3a76400008a611099565b915082821115610e06576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123d7565b6000831115610f29576002546040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690632e1a7d4d90610e65908690600401612453565b600060405180830381600087803b158015610e7f57600080fd5b505af1158015610e93573d6000803e3d6000fd5b505050506000821115610ee55760405173ffffffffffffffffffffffffffffffffffffffff86169083156108fc029084906000818181858888f19350505050158015610ee3573d6000803e3d6000fd5b505b610eef8383610acb565b90506000811115610f2957604051339082156108fc029083906000818181858888f19350505050158015610f27573d6000803e3d6000fd5b505b5050505050505050565b6000610f45838263ffffffff61136f16565b604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130190209091507fffffffff0000000000000000000000000000000000000000000000000000000080831691161415610fb557610fb083836113dc565b610383565b604080517f455243373231546f6b656e28616464726573732c75696e7432353629000000008152905190819003601c0190207fffffffff000000000000000000000000000000000000000000000000000000008281169116141561101d57610fb083836115ca565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123a7565b60008282018381101561108e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612397565b8091505b5092915050565b60008083116110d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612387565b6110e76110e185846116b2565b8461170d565b90505b9392505050565b6110f96119a9565b60608060008060006111096119a9565b89600081518110151561111857fe5b6020908102919091018101516101400151600580546040805160026001841615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190931692909204601f8101869004860283018601909152808252929950929091908301828280156111d05780601f106111a5576101008083540402835291602001916111d0565b820191906000526020600020905b8154815290600101906020018083116111b357829003601f168201915b5050505050945089519350600092505b82841461128857858a848151811015156111f657fe5b602090810290910101516101400152895185908b908590811061121557fe5b906020019060200201516101600181905250611235898860200151610acb565b91506112618a8481518110151561124857fe5b90602001906020020151838a86815181101515610a3c57fe5b905061126d878261130d565b6020870151891161127d57611288565b6001909201916111e0565b5050505050509392505050565b61129d6119a9565b606060006112ac868686611724565b600154815191935073ffffffffffffffffffffffffffffffffffffffff1691506080908390602082016000855af1801561130357825184526020830151602085015260408301516040850152606083015160608501525b5050509392505050565b8151815161131b919061104f565b825260208083015190820151611331919061104f565b60208301526040808301519082015161134a919061104f565b604083015260608083015190820151611363919061104f565b60609092019190915250565b6000816004018351101515156113b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612417565b5001602001517fffffffff000000000000000000000000000000000000000000000000000000001690565b6000806113f084601063ffffffff6118f616565b604080517f7472616e7366657228616464726573732c75696e7432353629000000000000008152905190819003601901812091935073ffffffffffffffffffffffffffffffffffffffff8416919061144e903390879060240161231c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931783525181519192909182919080838360005b838110156114f25781810151838201526020016114da565b50505050905090810190601f16801561151f5780820380516001836020036101000a031916815260200191505b509150506000604051808303816000865af19250505080151561156e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123b7565b3d1561158b575060003d6020141561158b5760206000803e506000515b8015156115c4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123b7565b50505050565b60008060018314611607576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612427565b61161884601063ffffffff6118f616565b915061162b84602463ffffffff61195716565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff8316906323b872dd90611684903090339086906004016122f4565b600060405180830381600087803b15801561169e57600080fd5b505af1158015610f29573d6000803e3d6000fd5b6000808315156116c55760009150611092565b508282028284828115156116d557fe5b041461108e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612397565b600080828481151561171b57fe5b04949350505050565b604080517fb4be83d5000000000000000000000000000000000000000000000000000000006020808301919091526060602483018181528751608485019081528884015160a48601529488015160c48501529087015160e4840152608087015161010484015260a087015161012484015260c087015161014484015260e08701516101648401526101008701516101848401526101208701516101a4840152610140870180516101c485019081526101608901516101e4860152610180905251805161020485018190529394919384936044870192849261022489019291820191601f82010460005b8181101561182b57835185526020948501949093019260010161180d565b50505050818103610160808401919091528a0151805180835260209283019291820191601f82010460005b81811015611874578351855260209485019490930192600101611856565b50505089845250848103602093840190815288518083529093918201918981019190601f82010460005b818110156118bc57835185526020948501949093019260010161189e565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08883030188525060405250505050509392505050565b600081601401835110151515611938576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612407565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b60006107ba83836000816020018351101515156119a0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612377565b50016020015190565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b60006107ba82356124ef565b6000601f820183136119ef57600080fd5b8135611a026119fd82612488565b612461565b81815260209384019390925082018360005b83811015611a405781358601611a2a8882611af0565b8452506020928301929190910190600101611a14565b5050505092915050565b6000601f82018313611a5b57600080fd5b8135611a696119fd82612488565b81815260209384019390925082018360005b83811015611a405781358601611a918882611b3f565b8452506020928301929190910190600101611a7b565b600080601f83018413611ab957600080fd5b50813567ffffffffffffffff811115611ad157600080fd5b602083019150836001820283011115611ae957600080fd5b9250929050565b6000601f82018313611b0157600080fd5b8135611b0f6119fd826124a9565b91508082526020830160208301858383011115611b2b57600080fd5b611b3683828461250b565b50505092915050565b60006101808284031215611b5257600080fd5b611b5d610180612461565b90506000611b6b84846119d2565b8252506020611b7c848483016119d2565b6020830152506040611b90848285016119d2565b6040830152506060611ba4848285016119d2565b6060830152506080611bb884828501611c88565b60808301525060a0611bcc84828501611c88565b60a08301525060c0611be084828501611c88565b60c08301525060e0611bf484828501611c88565b60e083015250610100611c0984828501611c88565b61010083015250610120611c1f84828501611c88565b6101208301525061014082013567ffffffffffffffff811115611c4157600080fd5b611c4d84828501611af0565b6101408301525061016082013567ffffffffffffffff811115611c6f57600080fd5b611c7b84828501611af0565b6101608301525092915050565b60006107ba8235612508565b600060208284031215611ca657600080fd5b6000611cb284846119d2565b949350505050565b60008060008060008060c08789031215611cd357600080fd5b863567ffffffffffffffff811115611cea57600080fd5b611cf689828a01611a4a565b965050602087013567ffffffffffffffff811115611d1357600080fd5b611d1f89828a016119de565b955050604087013567ffffffffffffffff811115611d3c57600080fd5b611d4889828a01611a4a565b945050606087013567ffffffffffffffff811115611d6557600080fd5b611d7189828a016119de565b9350506080611d8289828a01611c88565b92505060a0611d9389828a016119d2565b9150509295509295509295565b600080600080600080600060e0888a031215611dbb57600080fd5b873567ffffffffffffffff811115611dd257600080fd5b611dde8a828b01611a4a565b9750506020611def8a828b01611c88565b965050604088013567ffffffffffffffff811115611e0c57600080fd5b611e188a828b016119de565b955050606088013567ffffffffffffffff811115611e3557600080fd5b611e418a828b01611a4a565b945050608088013567ffffffffffffffff811115611e5e57600080fd5b611e6a8a828b016119de565b93505060a0611e7b8a828b01611c88565b92505060c0611e8c8a828b016119d2565b91505092959891949750929550565b600080600060408486031215611eb057600080fd5b833567ffffffffffffffff811115611ec757600080fd5b611ed386828701611aa7565b93509350506020611ee686828701611c88565b9150509250925092565b611ef9816124ef565b82525050565b602381527f44454641554c545f46554e4354494f4e5f574554485f434f4e54524143545f4f60208201527f4e4c590000000000000000000000000000000000000000000000000000000000604082015260600190565b601181527f494e56414c49445f4d53475f56414c5545000000000000000000000000000000602082015260400190565b600d81527f4f564552534f4c445f5745544800000000000000000000000000000000000000602082015260400190565b601181527f55494e543235365f554e444552464c4f57000000000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601081527f4449564953494f4e5f42595f5a45524f00000000000000000000000000000000602082015260400190565b601081527f55494e543235365f4f564552464c4f5700000000000000000000000000000000602082015260400190565b601781527f554e535550504f525445445f41535345545f50524f5859000000000000000000602082015260400190565b600f81527f5452414e534645525f4641494c45440000000000000000000000000000000000602082015260400190565b601481527f434f4d504c4554455f46494c4c5f4641494c4544000000000000000000000000602082015260400190565b601a81527f494e53554646494349454e545f4554485f52454d41494e494e47000000000000602082015260400190565b601381527f4f4e4c595f434f4e54524143545f4f574e455200000000000000000000000000602082015260400190565b601881527f4645455f50455243454e544147455f544f4f5f4c415247450000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b602581527f475245415445525f4f525f455155414c5f544f5f345f4c454e4754485f52455160208201527f5549524544000000000000000000000000000000000000000000000000000000604082015260600190565b600e81527f494e56414c49445f414d4f554e54000000000000000000000000000000000000602082015260400190565b805160808301906122a884826122dd565b5060208201516122bb60208501826122dd565b5060408201516122ce60408501826122dd565b5060608201516115c460608501825b611ef981612508565b602081016107bd8284611ef0565b606081016123028286611ef0565b61230f6020830185611ef0565b611cb260408301846122dd565b6040810161232a8285611ef0565b6110ea60208301846122dd565b602080825281016107bd81611eff565b602080825281016107bd81611f55565b602080825281016107bd81611f85565b602080825281016107bd81611fb5565b602080825281016107bd81611fe5565b602080825281016107bd8161203b565b602080825281016107bd8161206b565b602080825281016107bd8161209b565b602080825281016107bd816120cb565b602080825281016107bd816120fb565b602080825281016107bd8161212b565b602080825281016107bd8161215b565b602080825281016107bd8161218b565b602080825281016107bd816121bb565b602080825281016107bd81612211565b602080825281016107bd81612267565b61010081016124468285612297565b6110ea6080830184612297565b602081016107bd82846122dd565b60405181810167ffffffffffffffff8111828210171561248057600080fd5b604052919050565b600067ffffffffffffffff82111561249f57600080fd5b5060209081020190565b600067ffffffffffffffff8211156124c057600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b828183375060009101525600a265627a7a72305820185af4a5e8525b0c20983bf22c09773a1cab3f704716cba4c8da3b112601cd666c6578706572696d656e74616cf50037"
+ "schemaVersion": "2.0.0",
+ "contractName": "Forwarder",
+ "compilerOutput": {
+ "abi": [
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "components": [
+ {
+ "name": "makerAddress",
+ "type": "address"
+ },
+ {
+ "name": "takerAddress",
+ "type": "address"
+ },
+ {
+ "name": "feeRecipientAddress",
+ "type": "address"
+ },
+ {
+ "name": "senderAddress",
+ "type": "address"
+ },
+ {
+ "name": "makerAssetAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "takerAssetAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "makerFee",
+ "type": "uint256"
+ },
+ {
+ "name": "takerFee",
+ "type": "uint256"
+ },
+ {
+ "name": "expirationTimeSeconds",
+ "type": "uint256"
+ },
+ {
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "name": "takerAssetData",
+ "type": "bytes"
+ }
+ ],
+ "name": "orders",
+ "type": "tuple[]"
+ },
+ {
+ "name": "makerAssetFillAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "signatures",
+ "type": "bytes[]"
+ },
+ {
+ "components": [
+ {
+ "name": "makerAddress",
+ "type": "address"
+ },
+ {
+ "name": "takerAddress",
+ "type": "address"
+ },
+ {
+ "name": "feeRecipientAddress",
+ "type": "address"
+ },
+ {
+ "name": "senderAddress",
+ "type": "address"
+ },
+ {
+ "name": "makerAssetAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "takerAssetAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "makerFee",
+ "type": "uint256"
+ },
+ {
+ "name": "takerFee",
+ "type": "uint256"
+ },
+ {
+ "name": "expirationTimeSeconds",
+ "type": "uint256"
+ },
+ {
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "name": "takerAssetData",
+ "type": "bytes"
+ }
+ ],
+ "name": "feeOrders",
+ "type": "tuple[]"
+ },
+ {
+ "name": "feeSignatures",
+ "type": "bytes[]"
+ },
+ {
+ "name": "feePercentage",
+ "type": "uint256"
+ },
+ {
+ "name": "feeRecipient",
+ "type": "address"
+ }
+ ],
+ "name": "marketBuyOrdersWithEth",
+ "outputs": [
+ {
+ "components": [
+ {
+ "name": "makerAssetFilledAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "takerAssetFilledAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "makerFeePaid",
+ "type": "uint256"
+ },
+ {
+ "name": "takerFeePaid",
+ "type": "uint256"
+ }
+ ],
+ "name": "orderFillResults",
+ "type": "tuple"
+ },
+ {
+ "components": [
+ {
+ "name": "makerAssetFilledAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "takerAssetFilledAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "makerFeePaid",
+ "type": "uint256"
+ },
+ {
+ "name": "takerFeePaid",
+ "type": "uint256"
+ }
+ ],
+ "name": "feeOrderFillResults",
+ "type": "tuple"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "assetData",
+ "type": "bytes"
+ },
+ {
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "withdrawAsset",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "components": [
+ {
+ "name": "makerAddress",
+ "type": "address"
+ },
+ {
+ "name": "takerAddress",
+ "type": "address"
+ },
+ {
+ "name": "feeRecipientAddress",
+ "type": "address"
+ },
+ {
+ "name": "senderAddress",
+ "type": "address"
+ },
+ {
+ "name": "makerAssetAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "takerAssetAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "makerFee",
+ "type": "uint256"
+ },
+ {
+ "name": "takerFee",
+ "type": "uint256"
+ },
+ {
+ "name": "expirationTimeSeconds",
+ "type": "uint256"
+ },
+ {
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "name": "takerAssetData",
+ "type": "bytes"
+ }
+ ],
+ "name": "orders",
+ "type": "tuple[]"
+ },
+ {
+ "name": "signatures",
+ "type": "bytes[]"
+ },
+ {
+ "components": [
+ {
+ "name": "makerAddress",
+ "type": "address"
+ },
+ {
+ "name": "takerAddress",
+ "type": "address"
+ },
+ {
+ "name": "feeRecipientAddress",
+ "type": "address"
+ },
+ {
+ "name": "senderAddress",
+ "type": "address"
+ },
+ {
+ "name": "makerAssetAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "takerAssetAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "makerFee",
+ "type": "uint256"
+ },
+ {
+ "name": "takerFee",
+ "type": "uint256"
+ },
+ {
+ "name": "expirationTimeSeconds",
+ "type": "uint256"
+ },
+ {
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "name": "takerAssetData",
+ "type": "bytes"
+ }
+ ],
+ "name": "feeOrders",
+ "type": "tuple[]"
+ },
+ {
+ "name": "feeSignatures",
+ "type": "bytes[]"
+ },
+ {
+ "name": "feePercentage",
+ "type": "uint256"
+ },
+ {
+ "name": "feeRecipient",
+ "type": "address"
+ }
+ ],
+ "name": "marketSellOrdersWithEth",
+ "outputs": [
+ {
+ "components": [
+ {
+ "name": "makerAssetFilledAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "takerAssetFilledAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "makerFeePaid",
+ "type": "uint256"
+ },
+ {
+ "name": "takerFeePaid",
+ "type": "uint256"
+ }
+ ],
+ "name": "orderFillResults",
+ "type": "tuple"
+ },
+ {
+ "components": [
+ {
+ "name": "makerAssetFilledAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "takerAssetFilledAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "makerFeePaid",
+ "type": "uint256"
+ },
+ {
+ "name": "takerFeePaid",
+ "type": "uint256"
+ }
+ ],
+ "name": "feeOrderFillResults",
+ "type": "tuple"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "name": "_exchange",
+ "type": "address"
+ },
+ {
+ "name": "_zrxAssetData",
+ "type": "bytes"
+ },
+ {
+ "name": "_wethAssetData",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "constructor"
+ },
+ {
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "fallback"
+ }
+ ],
+ "evm": {
+ "bytecode": {
+ "linkReferences": {},
+ "object": "0x60806040523480156200001157600080fd5b5060405162002d2c38038062002d2c83398101806040526200003791908101906200051d565b6000805433600160a060020a031991821617825560018054909116600160a060020a0386161790558251849084908490849081906200007e906004906020870190620003d0565b50825162000094906005906020860190620003d0565b50620000b0836010640100000000620019476200036f82021704565b9150620000cd846010640100000000620019476200036f82021704565b60028054600160a060020a03948516600160a060020a031991821617909155600380549285169290911691909117905550600154604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130181207f6070410800000000000000000000000000000000000000000000000000000000825291909216945063607041089350620001739250906004016200068e565b602060405180830381600087803b1580156200018e57600080fd5b505af1158015620001a3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620001c99190810190620004f4565b9050600160a060020a038116151562000219576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200021090620006b0565b60405180910390fd5b6002546040517f095ea7b3000000000000000000000000000000000000000000000000000000008152600160a060020a039091169063095ea7b39062000268908490600019906004016200066f565b602060405180830381600087803b1580156200028357600080fd5b505af115801562000298573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620002be9190810190620005a1565b506003546040517f095ea7b3000000000000000000000000000000000000000000000000000000008152600160a060020a039091169063095ea7b3906200030e908490600019906004016200066f565b602060405180830381600087803b1580156200032957600080fd5b505af11580156200033e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620003649190810190620005a1565b50505050506200077a565b600081601401835110151515620003b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000210906200069e565b506014818301810151910190600160a060020a03165b92915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200041357805160ff191683800117855562000443565b8280016001018555821562000443579182015b828111156200044357825182559160200191906001019062000426565b506200045192915062000455565b5090565b6200047291905b808211156200045157600081556001016200045c565b90565b600062000483825162000711565b9392505050565b600062000483825162000742565b6000601f82018313620004aa57600080fd5b8151620004c1620004bb82620006e9565b620006c2565b91508082526020830160208301858383011115620004de57600080fd5b620004eb83828462000747565b50505092915050565b6000602082840312156200050757600080fd5b600062000515848462000475565b949350505050565b6000806000606084860312156200053357600080fd5b600062000541868662000475565b93505060208401516001604060020a038111156200055e57600080fd5b6200056c8682870162000498565b92505060408401516001604060020a038111156200058957600080fd5b620005978682870162000498565b9150509250925092565b600060208284031215620005b457600080fd5b60006200051584846200048a565b620005cd8162000711565b82525050565b620005cd816200071d565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601881527f554e524547495354455245445f41535345545f50524f58590000000000000000602082015260400190565b620005cd8162000472565b604081016200067f8285620005c2565b62000483602083018462000664565b60208101620003ca8284620005d3565b60208082528101620003ca81620005de565b60208082528101620003ca8162000634565b6040518181016001604060020a0381118282101715620006e157600080fd5b604052919050565b60006001604060020a038211156200070057600080fd5b506020601f91909101601f19160190565b600160a060020a031690565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b151590565b60005b83811015620007645781810151838201526020016200074a565b8381111562000774576000848401525b50505050565b6125a2806200078a6000396000f30060806040526004361061006c5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166318978e8281146100c8578063630f1e6c146100f25780638da5cb5b146101125780639395525c14610134578063f2fde38b14610147575b60025473ffffffffffffffffffffffffffffffffffffffff1633146100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612388565b60405180910390fd5b005b6100db6100d6366004611df1565b610167565b6040516100e9929190612488565b60405180910390f35b3480156100fe57600080fd5b506100c661010d366004611eec565b6102f7565b34801561011e57600080fd5b50610127610388565b6040516100e99190612337565b6100db610142366004611d0b565b6103a4565b34801561015357600080fd5b506100c6610162366004611ce5565b61050a565b61016f6119fa565b6101776119fa565b6000806101826105bb565b60048054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100600188161502019095169490940493840181900481028201810190925282815261025c939092909183018282801561022d5780601f106102025761010080835404028352916020019161022d565b820191906000526020600020905b81548152906001019060200180831161021057829003601f168201915b50505050508c600081518110151561024157fe5b6020908102909101015161014001519063ffffffff61069616565b156102875761026c8b8b8b6107c3565b935061028084600001518560600151610ac1565b90506102ae565b6102928b8b8b610b03565b9350836060015191506102a68883896107c3565b845190935090505b6102c2846020015184602001518888610d15565b6102e98b60008151811015156102d457fe5b90602001906020020151610140015182610f29565b505097509795505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610348576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612438565b61038383838080601f01602080910402602001604051908101604052809392919081815260200183838082843750879450610f299350505050565b505050565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b6103ac6119fa565b6103b46119fa565b60008060006103c16105bb565b60048054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152610441939092909183018282801561022d5780601f106102025761010080835404028352916020019161022d565b156104925761046a670de0b6b3a7640000610464670de0b6b3a76400008a611045565b3461108f565b92506104778b848c6110e7565b945061048b85600001518660600151610ac1565b90506104d6565b6104ad670d2f13f7789f0000670de0b6b3a76400003461108f565b92506104ba8b848c6110e7565b9450846060015191506104ce89838a6107c3565b855190945090505b6104ea856020015185602001518989610d15565b6104fc8b60008151811015156102d457fe5b505050965096945050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461055b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612438565b73ffffffffffffffffffffffffffffffffffffffff8116156105b857600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83161790555b50565b600034116105f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612398565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016000604051808303818588803b15801561067b57600080fd5b505af115801561068f573d6000803e3d6000fd5b5050505050565b6000815183511480156107ba5750816040518082805190602001908083835b602083106106f257805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016106b5565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0180199092169116179052604051919093018190038120885190955088945090928392508401908083835b6020831061078757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161074a565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916145b90505b92915050565b6107cb6119fa565b60608060008060008060006107de6119fa565b8a15156107ea57610ab2565b6004805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561088e5780601f106108635761010080835404028352916020019161088e565b820191906000526020600020905b81548152906001019060200180831161087157829003601f168201915b505060058054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152969e509194509250840190508282801561093d5780601f106109125761010080835404028352916020019161093d565b820191906000526020600020905b81548152906001019060200180831161092057829003601f168201915b50505050509650600095508b519450600093505b838514610a7857878c8581518110151561096757fe5b6020908102909101015161014001528b5187908d908690811061098657fe5b60209081029091010151610160015261099f8b87610ac1565b9250610a068c858151811015156109b257fe5b9060200190602002015160a00151610a008e878151811015156109d157fe5b90602001906020020151608001518f888151811015156109ed57fe5b9060200190602002015160e00151610ac1565b8561128b565b9150610a418c85815181101515610a1957fe5b90602001906020020151838c87815181101515610a3257fe5b906020019060200201516112e6565b9050610a4d898261135e565b610a5f89600001518a60600151610ac1565b95508a8610610a6d57610a78565b600190930192610951565b8a861015610ab2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612418565b50505050505050509392505050565b600082821115610afd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123b8565b50900390565b610b0b6119fa565b606080600080600080610b1c6119fa565b60008b6000815181101515610b2d57fe5b6020908102919091018101516101400151600580546040805160026001841615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190931692909204601f8101869004860283018601909152808252929b5092909190830182828015610be55780601f10610bba57610100808354040283529160200191610be5565b820191906000526020600020905b815481529060010190602001808311610bc857829003601f168201915b505050505096508b519550600094505b848614610cdb57878c86815181101515610c0b57fe5b6020908102909101015161014001528b5187908d9087908110610c2a57fe5b6020908102909101015161016001528851610c46908c90610ac1565b9350610c898c86815181101515610c5957fe5b9060200190602002015160a001518d87815181101515610c7557fe5b90602001906020020151608001518661128b565b9250610cb58c86815181101515610c9c57fe5b90602001906020020151848c88815181101515610a3257fe5b9150610cc1898361135e565b5087518a8110610cd057610cdb565b600190940193610bf5565b8a811015610ab2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612418565b600080808066b1a2bc2ec50000861115610d5b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612448565b610d658888611045565b935034841115610da1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123a8565b610dab3485610ac1565b9250610dc086670de0b6b3a76400008a61108f565b915082821115610dfc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612428565b6000831115610f1f576002546040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690632e1a7d4d90610e5b9086906004016124a4565b600060405180830381600087803b158015610e7557600080fd5b505af1158015610e89573d6000803e3d6000fd5b505050506000821115610edb5760405173ffffffffffffffffffffffffffffffffffffffff86169083156108fc029084906000818181858888f19350505050158015610ed9573d6000803e3d6000fd5b505b610ee58383610ac1565b90506000811115610f1f57604051339082156108fc029083906000818181858888f19350505050158015610f1d573d6000803e3d6000fd5b505b5050505050505050565b6000610f3b838263ffffffff6113c016565b604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130190209091507fffffffff0000000000000000000000000000000000000000000000000000000080831691161415610fab57610fa6838361142d565b610383565b604080517f455243373231546f6b656e28616464726573732c75696e7432353629000000008152905190819003601c0190207fffffffff000000000000000000000000000000000000000000000000000000008281169116141561101357610fa6838361161b565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123f8565b600082820183811015611084576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123e8565b8091505b5092915050565b60008083116110ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123d8565b6110dd6110d78584611703565b8461175e565b90505b9392505050565b6110ef6119fa565b60608060008060006110ff6119fa565b89600081518110151561110e57fe5b6020908102919091018101516101400151600580546040805160026001841615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190931692909204601f8101869004860283018601909152808252929950929091908301828280156111c65780601f1061119b576101008083540402835291602001916111c6565b820191906000526020600020905b8154815290600101906020018083116111a957829003601f168201915b5050505050945089519350600092505b82841461127e57858a848151811015156111ec57fe5b602090810290910101516101400152895185908b908590811061120b57fe5b90602001906020020151610160018190525061122b898860200151610ac1565b91506112578a8481518110151561123e57fe5b90602001906020020151838a86815181101515610a3257fe5b9050611263878261135e565b602087015189116112735761127e565b6001909201916111d6565b5050505050509392505050565b60008083116112c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123d8565b6110dd6110d76112d68685611703565b6112e1866001610ac1565b611045565b6112ee6119fa565b606060006112fd868686611775565b600154815191935073ffffffffffffffffffffffffffffffffffffffff1691506080908390602082016000855af1801561135457825184526020830151602085015260408301516040850152606083015160608501525b5050509392505050565b8151815161136c9190611045565b8252602080830151908201516113829190611045565b60208301526040808301519082015161139b9190611045565b6040830152606080830151908201516113b49190611045565b60609092019190915250565b600081600401835110151515611402576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612468565b5001602001517fffffffff000000000000000000000000000000000000000000000000000000001690565b60008061144184601063ffffffff61194716565b604080517f7472616e7366657228616464726573732c75696e7432353629000000000000008152905190819003601901812091935073ffffffffffffffffffffffffffffffffffffffff8416919061149f903390879060240161236d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931783525181519192909182919080838360005b8381101561154357818101518382015260200161152b565b50505050905090810190601f1680156115705780820380516001836020036101000a031916815260200191505b509150506000604051808303816000865af1925050508015156115bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612408565b3d156115dc575060003d602014156115dc5760206000803e506000515b801515611615576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612408565b50505050565b60008060018314611658576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612478565b61166984601063ffffffff61194716565b915061167c84602463ffffffff6119a816565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff8316906323b872dd906116d590309033908690600401612345565b600060405180830381600087803b1580156116ef57600080fd5b505af1158015610f1f573d6000803e3d6000fd5b6000808315156117165760009150611088565b5082820282848281151561172657fe5b0414611084576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123e8565b600080828481151561176c57fe5b04949350505050565b604080517fb4be83d5000000000000000000000000000000000000000000000000000000006020808301919091526060602483018181528751608485019081528884015160a48601529488015160c48501529087015160e4840152608087015161010484015260a087015161012484015260c087015161014484015260e08701516101648401526101008701516101848401526101208701516101a4840152610140870180516101c485019081526101608901516101e4860152610180905251805161020485018190529394919384936044870192849261022489019291820191601f82010460005b8181101561187c57835185526020948501949093019260010161185e565b50505050818103610160808401919091528a0151805180835260209283019291820191601f82010460005b818110156118c55783518552602094850194909301926001016118a7565b50505089845250848103602093840190815288518083529093918201918981019190601f82010460005b8181101561190d5783518552602094850194909301926001016118ef565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08883030188525060405250505050509392505050565b600081601401835110151515611989576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612458565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b60006107ba83836000816020018351101515156119f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123c8565b50016020015190565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b60006107ba8235612540565b6000601f82018313611a4057600080fd5b8135611a53611a4e826124d9565b6124b2565b81815260209384019390925082018360005b83811015611a915781358601611a7b8882611b41565b8452506020928301929190910190600101611a65565b5050505092915050565b6000601f82018313611aac57600080fd5b8135611aba611a4e826124d9565b81815260209384019390925082018360005b83811015611a915781358601611ae28882611b90565b8452506020928301929190910190600101611acc565b600080601f83018413611b0a57600080fd5b50813567ffffffffffffffff811115611b2257600080fd5b602083019150836001820283011115611b3a57600080fd5b9250929050565b6000601f82018313611b5257600080fd5b8135611b60611a4e826124fa565b91508082526020830160208301858383011115611b7c57600080fd5b611b8783828461255c565b50505092915050565b60006101808284031215611ba357600080fd5b611bae6101806124b2565b90506000611bbc8484611a23565b8252506020611bcd84848301611a23565b6020830152506040611be184828501611a23565b6040830152506060611bf584828501611a23565b6060830152506080611c0984828501611cd9565b60808301525060a0611c1d84828501611cd9565b60a08301525060c0611c3184828501611cd9565b60c08301525060e0611c4584828501611cd9565b60e083015250610100611c5a84828501611cd9565b61010083015250610120611c7084828501611cd9565b6101208301525061014082013567ffffffffffffffff811115611c9257600080fd5b611c9e84828501611b41565b6101408301525061016082013567ffffffffffffffff811115611cc057600080fd5b611ccc84828501611b41565b6101608301525092915050565b60006107ba8235612559565b600060208284031215611cf757600080fd5b6000611d038484611a23565b949350505050565b60008060008060008060c08789031215611d2457600080fd5b863567ffffffffffffffff811115611d3b57600080fd5b611d4789828a01611a9b565b965050602087013567ffffffffffffffff811115611d6457600080fd5b611d7089828a01611a2f565b955050604087013567ffffffffffffffff811115611d8d57600080fd5b611d9989828a01611a9b565b945050606087013567ffffffffffffffff811115611db657600080fd5b611dc289828a01611a2f565b9350506080611dd389828a01611cd9565b92505060a0611de489828a01611a23565b9150509295509295509295565b600080600080600080600060e0888a031215611e0c57600080fd5b873567ffffffffffffffff811115611e2357600080fd5b611e2f8a828b01611a9b565b9750506020611e408a828b01611cd9565b965050604088013567ffffffffffffffff811115611e5d57600080fd5b611e698a828b01611a2f565b955050606088013567ffffffffffffffff811115611e8657600080fd5b611e928a828b01611a9b565b945050608088013567ffffffffffffffff811115611eaf57600080fd5b611ebb8a828b01611a2f565b93505060a0611ecc8a828b01611cd9565b92505060c0611edd8a828b01611a23565b91505092959891949750929550565b600080600060408486031215611f0157600080fd5b833567ffffffffffffffff811115611f1857600080fd5b611f2486828701611af8565b93509350506020611f3786828701611cd9565b9150509250925092565b611f4a81612540565b82525050565b602381527f44454641554c545f46554e4354494f4e5f574554485f434f4e54524143545f4f60208201527f4e4c590000000000000000000000000000000000000000000000000000000000604082015260600190565b601181527f494e56414c49445f4d53475f56414c5545000000000000000000000000000000602082015260400190565b600d81527f4f564552534f4c445f5745544800000000000000000000000000000000000000602082015260400190565b601181527f55494e543235365f554e444552464c4f57000000000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601081527f4449564953494f4e5f42595f5a45524f00000000000000000000000000000000602082015260400190565b601081527f55494e543235365f4f564552464c4f5700000000000000000000000000000000602082015260400190565b601781527f554e535550504f525445445f41535345545f50524f5859000000000000000000602082015260400190565b600f81527f5452414e534645525f4641494c45440000000000000000000000000000000000602082015260400190565b601481527f434f4d504c4554455f46494c4c5f4641494c4544000000000000000000000000602082015260400190565b601a81527f494e53554646494349454e545f4554485f52454d41494e494e47000000000000602082015260400190565b601381527f4f4e4c595f434f4e54524143545f4f574e455200000000000000000000000000602082015260400190565b601881527f4645455f50455243454e544147455f544f4f5f4c415247450000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b602581527f475245415445525f4f525f455155414c5f544f5f345f4c454e4754485f52455160208201527f5549524544000000000000000000000000000000000000000000000000000000604082015260600190565b600e81527f494e56414c49445f414d4f554e54000000000000000000000000000000000000602082015260400190565b805160808301906122f9848261232e565b50602082015161230c602085018261232e565b50604082015161231f604085018261232e565b50606082015161161560608501825b611f4a81612559565b602081016107bd8284611f41565b606081016123538286611f41565b6123606020830185611f41565b611d03604083018461232e565b6040810161237b8285611f41565b6110e0602083018461232e565b602080825281016107bd81611f50565b602080825281016107bd81611fa6565b602080825281016107bd81611fd6565b602080825281016107bd81612006565b602080825281016107bd81612036565b602080825281016107bd8161208c565b602080825281016107bd816120bc565b602080825281016107bd816120ec565b602080825281016107bd8161211c565b602080825281016107bd8161214c565b602080825281016107bd8161217c565b602080825281016107bd816121ac565b602080825281016107bd816121dc565b602080825281016107bd8161220c565b602080825281016107bd81612262565b602080825281016107bd816122b8565b610100810161249782856122e8565b6110e060808301846122e8565b602081016107bd828461232e565b60405181810167ffffffffffffffff811182821017156124d157600080fd5b604052919050565b600067ffffffffffffffff8211156124f057600080fd5b5060209081020190565b600067ffffffffffffffff82111561251157600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b828183375060009101525600a265627a7a72305820d9f418f11e0f91f06f6f9d22924be0add925495eeb76a6388b5417adb505eeb36c6578706572696d656e74616cf50037"
}
}
},
- "networks": {}
-}
+ "networks": {}
+} \ No newline at end of file
diff --git a/packages/instant/package.json b/packages/instant/package.json
index 26c6c4e08..0329c3078 100644
--- a/packages/instant/package.json
+++ b/packages/instant/package.json
@@ -58,12 +58,12 @@
"react-redux": "^5.0.7",
"redux": "^4.0.0",
"redux-devtools-extension": "^2.13.5",
- "styled-components": "^3.4.9",
+ "styled-components": "^4.0.2",
"ts-optchain": "^0.1.1"
},
"devDependencies": {
- "@static/discharge": "^1.2.2",
"@0x/tslint-config": "^1.0.9",
+ "@static/discharge": "^1.2.2",
"@types/enzyme": "^3.1.14",
"@types/enzyme-adapter-react-16": "^1.0.3",
"@types/jest": "^23.3.5",
@@ -73,6 +73,7 @@
"@types/react-dom": "^16.0.8",
"@types/react-redux": "^6.0.9",
"@types/redux": "^3.6.0",
+ "@types/styled-components": "^4.0.1",
"awesome-typescript-loader": "^5.2.1",
"enzyme": "^3.6.0",
"enzyme-adapter-react-16": "^1.5.0",
diff --git a/packages/instant/src/components/amount_input.tsx b/packages/instant/src/components/amount_input.tsx
deleted file mode 100644
index c89fb05ad..000000000
--- a/packages/instant/src/components/amount_input.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { BigNumber } from '@0x/utils';
-import * as _ from 'lodash';
-import * as React from 'react';
-
-import { ColorOption } from '../style/theme';
-import { util } from '../util/util';
-
-import { Container, Input } from './ui';
-
-export interface AmountInputProps {
- fontColor?: ColorOption;
- fontSize?: string;
- value?: BigNumber;
- onChange: (value?: BigNumber) => void;
-}
-
-export class AmountInput extends React.Component<AmountInputProps> {
- public static defaultProps = {
- onChange: util.boundNoop,
- };
- public render(): React.ReactNode {
- const { fontColor, fontSize, value } = this.props;
- return (
- <Container borderBottom="1px solid rgba(255,255,255,0.3)" display="inline-block">
- <Input
- fontColor={fontColor}
- fontSize={fontSize}
- onChange={this._handleChange}
- value={!_.isUndefined(value) ? value.toString() : ''}
- placeholder="0.00"
- width="2.2em"
- />
- </Container>
- );
- }
- private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
- const value = event.target.value;
- let bigNumberValue;
- if (!_.isEmpty(value)) {
- try {
- bigNumberValue = new BigNumber(event.target.value);
- } catch {
- // We don't want to allow values that can't be a BigNumber, so don't even call onChange.
- return;
- }
- }
- this.props.onChange(bigNumberValue);
- };
-}
diff --git a/packages/instant/src/components/animations/full_rotation.tsx b/packages/instant/src/components/animations/full_rotation.tsx
new file mode 100644
index 000000000..9adb565f9
--- /dev/null
+++ b/packages/instant/src/components/animations/full_rotation.tsx
@@ -0,0 +1,24 @@
+import { keyframes, styled } from '../../style/theme';
+
+interface FullRotationProps {
+ height: string;
+ width: string;
+}
+const rotatingKeyframes = keyframes`
+from {
+ transform: rotate(0deg);
+}
+
+to {
+ transform: rotate(360deg);
+}
+`;
+
+export const FullRotation =
+ styled.div <
+ FullRotationProps >
+ `
+ animation: ${rotatingKeyframes} 2s linear infinite;
+ height: ${props => props.height};
+ width: ${props => props.width};
+`;
diff --git a/packages/instant/src/components/animations/slide_animations.tsx b/packages/instant/src/components/animations/slide_animations.tsx
index 1f10a2ed6..84280372b 100644
--- a/packages/instant/src/components/animations/slide_animations.tsx
+++ b/packages/instant/src/components/animations/slide_animations.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
+import { Keyframes } from 'styled-components';
-import { keyframes, styled } from '../../style/theme';
+import { css, keyframes, styled } from '../../style/theme';
const slideKeyframeGenerator = (fromY: string, toY: string) => keyframes`
from {
@@ -15,7 +16,7 @@ const slideKeyframeGenerator = (fromY: string, toY: string) => keyframes`
`;
export interface SlideAnimationProps {
- keyframes: string;
+ keyframes: Keyframes;
animationType: string;
animationDirection?: string;
}
@@ -24,7 +25,10 @@ export const SlideAnimation =
styled.div <
SlideAnimationProps >
`
- animation-name: ${props => props.keyframes};
+ animation-name: ${props =>
+ css`
+ ${props.keyframes};
+ `};
animation-duration: 0.3s;
animation-timing-function: ${props => props.animationType};
animation-delay: 0s;
diff --git a/packages/instant/src/components/asset_amount_input.tsx b/packages/instant/src/components/asset_amount_input.tsx
deleted file mode 100644
index c03ef1cf3..000000000
--- a/packages/instant/src/components/asset_amount_input.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { BigNumber } from '@0x/utils';
-import * as _ from 'lodash';
-import * as React from 'react';
-
-import { ColorOption } from '../style/theme';
-import { ERC20Asset } from '../types';
-import { assetUtils } from '../util/asset';
-import { util } from '../util/util';
-
-import { AmountInput, AmountInputProps } from './amount_input';
-import { Container, Text } from './ui';
-
-// Asset amounts only apply to ERC20 assets
-export interface AssetAmountInputProps extends AmountInputProps {
- asset?: ERC20Asset;
- onChange: (value?: BigNumber, asset?: ERC20Asset) => void;
-}
-
-export class AssetAmountInput extends React.Component<AssetAmountInputProps> {
- public static defaultProps = {
- onChange: util.boundNoop,
- };
- public render(): React.ReactNode {
- const { asset, onChange, ...rest } = this.props;
- return (
- <Container>
- <AmountInput {...rest} onChange={this._handleChange} />
- <Container display="inline-block" marginLeft="10px">
- <Text fontSize={rest.fontSize} fontColor={ColorOption.white} textTransform="uppercase">
- {assetUtils.bestNameForAsset(asset)}
- </Text>
- </Container>
- </Container>
- );
- }
- private readonly _handleChange = (value?: BigNumber): void => {
- this.props.onChange(value, this.props.asset);
- };
-}
diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx
index adc32f071..a70269dde 100644
--- a/packages/instant/src/components/buy_button.tsx
+++ b/packages/instant/src/components/buy_button.tsx
@@ -1,20 +1,22 @@
-import { AssetBuyer, BuyQuote } from '@0x/asset-buyer';
+import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer';
import * as _ from 'lodash';
import * as React from 'react';
+import { WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX } from '../constants';
import { ColorOption } from '../style/theme';
import { util } from '../util/util';
import { web3Wrapper } from '../util/web3_wrapper';
-import { Button, Container, Text } from './ui';
+import { Button, Text } from './ui';
export interface BuyButtonProps {
buyQuote?: BuyQuote;
assetBuyer?: AssetBuyer;
- onClick: (buyQuote: BuyQuote) => void;
- onBuySuccess: (buyQuote: BuyQuote, txnHash: string) => void;
- onBuyFailure: (buyQuote: BuyQuote, tnxHash?: string) => void;
- text: string;
+ onAwaitingSignature: (buyQuote: BuyQuote) => void;
+ onSignatureDenied: (buyQuote: BuyQuote, preventedError: Error) => void;
+ onBuyProcessing: (buyQuote: BuyQuote, txHash: string) => void;
+ onBuySuccess: (buyQuote: BuyQuote, txHash: string) => void;
+ onBuyFailure: (buyQuote: BuyQuote, txHash: string) => void;
}
export class BuyButton extends React.Component<BuyButtonProps> {
@@ -26,28 +28,42 @@ export class BuyButton extends React.Component<BuyButtonProps> {
public render(): React.ReactNode {
const shouldDisableButton = _.isUndefined(this.props.buyQuote) || _.isUndefined(this.props.assetBuyer);
return (
- <Container padding="20px" width="100%">
- <Button width="100%" onClick={this._handleClick} isDisabled={shouldDisableButton}>
- <Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
- {this.props.text}
- </Text>
- </Button>
- </Container>
+ <Button width="100%" onClick={this._handleClick} isDisabled={shouldDisableButton}>
+ <Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
+ Buy
+ </Text>
+ </Button>
);
}
private readonly _handleClick = async () => {
// The button is disabled when there is no buy quote anyway.
- if (_.isUndefined(this.props.buyQuote) || _.isUndefined(this.props.assetBuyer)) {
+ const { buyQuote, assetBuyer } = this.props;
+ if (_.isUndefined(buyQuote) || _.isUndefined(assetBuyer)) {
return;
}
- this.props.onClick(this.props.buyQuote);
- let txnHash;
+
+ let txHash: string | undefined;
+ this.props.onAwaitingSignature(buyQuote);
+ try {
+ txHash = await assetBuyer.executeBuyQuoteAsync(buyQuote);
+ } catch (e) {
+ if (e instanceof Error && e.message === AssetBuyerError.SignatureRequestDenied) {
+ this.props.onSignatureDenied(buyQuote, e);
+ return;
+ }
+ throw e;
+ }
+
+ this.props.onBuyProcessing(buyQuote, txHash);
try {
- txnHash = await this.props.assetBuyer.executeBuyQuoteAsync(this.props.buyQuote);
- await web3Wrapper.awaitTransactionSuccessAsync(txnHash);
- this.props.onBuySuccess(this.props.buyQuote, txnHash);
- } catch {
- this.props.onBuyFailure(this.props.buyQuote, txnHash);
+ await web3Wrapper.awaitTransactionSuccessAsync(txHash);
+ } catch (e) {
+ if (e instanceof Error && e.message.startsWith(WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX)) {
+ this.props.onBuyFailure(buyQuote, txHash);
+ return;
+ }
+ throw e;
}
+ this.props.onBuySuccess(buyQuote, txHash);
};
}
diff --git a/packages/instant/src/components/buy_order_state_button.tsx b/packages/instant/src/components/buy_order_state_button.tsx
new file mode 100644
index 000000000..44115e5a1
--- /dev/null
+++ b/packages/instant/src/components/buy_order_state_button.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+
+import { PlacingOrderButton } from '../components/placing_order_button';
+import { SelectedAssetBuyButton } from '../containers/selected_asset_buy_button';
+import { SelectedAssetRetryButton } from '../containers/selected_asset_retry_button';
+import { SelectedAssetViewTransactionButton } from '../containers/selected_asset_view_transaction_button';
+import { OrderProcessState } from '../types';
+
+export interface BuyOrderStateButtonProps {
+ buyOrderProcessingState: OrderProcessState;
+}
+
+export const BuyOrderStateButton: React.StatelessComponent<BuyOrderStateButtonProps> = props => {
+ if (props.buyOrderProcessingState === OrderProcessState.FAILURE) {
+ return <SelectedAssetRetryButton />;
+ } else if (
+ props.buyOrderProcessingState === OrderProcessState.SUCCESS ||
+ props.buyOrderProcessingState === OrderProcessState.PROCESSING
+ ) {
+ return <SelectedAssetViewTransactionButton />;
+ } else if (props.buyOrderProcessingState === OrderProcessState.AWAITING_SIGNATURE) {
+ return <PlacingOrderButton />;
+ }
+
+ return <SelectedAssetBuyButton />;
+};
diff --git a/packages/instant/src/components/erc20_asset_amount_input.tsx b/packages/instant/src/components/erc20_asset_amount_input.tsx
new file mode 100644
index 000000000..583fad28b
--- /dev/null
+++ b/packages/instant/src/components/erc20_asset_amount_input.tsx
@@ -0,0 +1,84 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { ColorOption, transparentWhite } from '../style/theme';
+import { ERC20Asset } from '../types';
+import { assetUtils } from '../util/asset';
+import { BigNumberInput } from '../util/big_number_input';
+import { util } from '../util/util';
+
+import { ScalingAmountInput } from './scaling_amount_input';
+import { Container, Text } from './ui';
+
+// Asset amounts only apply to ERC20 assets
+export interface ERC20AssetAmountInputProps {
+ asset?: ERC20Asset;
+ value?: BigNumberInput;
+ onChange: (value?: BigNumberInput, asset?: ERC20Asset) => void;
+ startingFontSizePx: number;
+ fontColor?: ColorOption;
+}
+
+export interface ERC20AssetAmountInputState {
+ currentFontSizePx: number;
+}
+
+export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInputProps, ERC20AssetAmountInputState> {
+ public static defaultProps = {
+ onChange: util.boundNoop,
+ };
+ constructor(props: ERC20AssetAmountInputProps) {
+ super(props);
+ this.state = {
+ currentFontSizePx: props.startingFontSizePx,
+ };
+ }
+ public render(): React.ReactNode {
+ const { asset, onChange, ...rest } = this.props;
+ return (
+ <Container whiteSpace="nowrap">
+ <Container borderBottom={`1px solid ${transparentWhite}`} display="inline-block">
+ <ScalingAmountInput
+ {...rest}
+ textLengthThreshold={this._textLengthThresholdForAsset(asset)}
+ maxFontSizePx={this.props.startingFontSizePx}
+ onChange={this._handleChange}
+ onFontSizeChange={this._handleFontSizeChange}
+ />
+ </Container>
+ <Container display="inline-flex" marginLeft="10px" title={assetUtils.bestNameForAsset(asset)}>
+ <Text
+ fontSize={`${this.state.currentFontSizePx}px`}
+ fontColor={ColorOption.white}
+ textTransform="uppercase"
+ >
+ {assetUtils.formattedSymbolForAsset(asset)}
+ </Text>
+ </Container>
+ </Container>
+ );
+ }
+ private readonly _handleChange = (value?: BigNumberInput): void => {
+ this.props.onChange(value, this.props.asset);
+ };
+ private readonly _handleFontSizeChange = (fontSizePx: number): void => {
+ this.setState({
+ currentFontSizePx: fontSizePx,
+ });
+ };
+ // For assets with symbols of different length,
+ // start scaling the input at different character lengths
+ private readonly _textLengthThresholdForAsset = (asset?: ERC20Asset): number => {
+ if (_.isUndefined(asset)) {
+ return 3;
+ }
+ const symbol = asset.metaData.symbol;
+ if (symbol.length <= 3) {
+ return 5;
+ }
+ if (symbol.length === 5) {
+ return 3;
+ }
+ return 4;
+ };
+}
diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx
index 63d2138a5..1ef276ff3 100644
--- a/packages/instant/src/components/instant_heading.tsx
+++ b/packages/instant/src/components/instant_heading.tsx
@@ -2,24 +2,32 @@ import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import * as React from 'react';
-import { SelectedAssetAmountInput } from '../containers/selected_asset_amount_input';
+import { SelectedERC20AssetAmountInput } from '../containers/selected_erc20_asset_amount_input';
import { ColorOption } from '../style/theme';
-import { AsyncProcessState } from '../types';
+import { AsyncProcessState, OrderProcessState, OrderState } from '../types';
import { format } from '../util/format';
import { AmountPlaceholder } from './amount_placeholder';
import { Container, Flex, Text } from './ui';
+import { Icon } from './ui/icon';
+import { Spinner } from './ui/spinner';
export interface InstantHeadingProps {
selectedAssetAmount?: BigNumber;
totalEthBaseAmount?: BigNumber;
ethUsdPrice?: BigNumber;
quoteRequestState: AsyncProcessState;
+ buyOrderState: OrderState;
}
-const placeholderColor = ColorOption.white;
+const PLACEHOLDER_COLOR = ColorOption.white;
+const ICON_WIDTH = 34;
+const ICON_HEIGHT = 34;
+const ICON_COLOR = ColorOption.white;
+
export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
public render(): React.ReactNode {
+ const iconOrAmounts = this._renderIcon() || this._renderAmountsSection();
return (
<Container
backgroundColor={ColorOption.primaryColor}
@@ -36,26 +44,62 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
textTransform="uppercase"
fontSize="12px"
>
- I want to buy
+ {this._renderTopText()}
</Text>
</Container>
<Flex direction="row" justify="space-between">
- <SelectedAssetAmountInput fontSize="45px" />
+ <Flex height="60px">
+ <SelectedERC20AssetAmountInput startingFontSizePx={38} />
+ </Flex>
<Flex direction="column" justify="space-between">
- <Container marginBottom="5px">{this._placeholderOrAmount(this._ethAmount)}</Container>
- <Container opacity={0.7}>{this._placeholderOrAmount(this._dollarAmount)}</Container>
+ {iconOrAmounts}
</Flex>
</Flex>
</Container>
);
}
+ private _renderAmountsSection(): React.ReactNode {
+ return (
+ <Container>
+ <Container marginBottom="5px">{this._placeholderOrAmount(this._ethAmount)}</Container>
+ <Container opacity={0.7}>{this._placeholderOrAmount(this._dollarAmount)}</Container>
+ </Container>
+ );
+ }
+
+ private _renderIcon(): React.ReactNode {
+ const processState = this.props.buyOrderState.processState;
+
+ if (processState === OrderProcessState.FAILURE) {
+ return <Icon icon={'failed'} width={ICON_WIDTH} height={ICON_HEIGHT} color={ICON_COLOR} />;
+ } else if (processState === OrderProcessState.PROCESSING) {
+ return <Spinner widthPx={ICON_HEIGHT} heightPx={ICON_HEIGHT} />;
+ } else if (processState === OrderProcessState.SUCCESS) {
+ return <Icon icon={'success'} width={ICON_WIDTH} height={ICON_HEIGHT} color={ICON_COLOR} />;
+ }
+ return undefined;
+ }
+
+ private _renderTopText(): React.ReactNode {
+ const processState = this.props.buyOrderState.processState;
+ if (processState === OrderProcessState.FAILURE) {
+ return 'Order failed';
+ } else if (processState === OrderProcessState.PROCESSING) {
+ return 'Processing Order...';
+ } else if (processState === OrderProcessState.SUCCESS) {
+ return 'Tokens received!';
+ }
+
+ return 'I want to buy';
+ }
+
private _placeholderOrAmount(amountFunction: () => React.ReactNode): React.ReactNode {
if (this.props.quoteRequestState === AsyncProcessState.PENDING) {
- return <AmountPlaceholder isPulsating={true} color={placeholderColor} />;
+ return <AmountPlaceholder isPulsating={true} color={PLACEHOLDER_COLOR} />;
}
if (_.isUndefined(this.props.selectedAssetAmount)) {
- return <AmountPlaceholder isPulsating={false} color={placeholderColor} />;
+ return <AmountPlaceholder isPulsating={false} color={PLACEHOLDER_COLOR} />;
}
return amountFunction();
}
@@ -66,7 +110,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
{format.ethBaseAmount(
this.props.totalEthBaseAmount,
4,
- <AmountPlaceholder isPulsating={false} color={placeholderColor} />,
+ <AmountPlaceholder isPulsating={false} color={PLACEHOLDER_COLOR} />,
)}
</Text>
);
diff --git a/packages/instant/src/components/placing_order_button.tsx b/packages/instant/src/components/placing_order_button.tsx
new file mode 100644
index 000000000..4232e6c22
--- /dev/null
+++ b/packages/instant/src/components/placing_order_button.tsx
@@ -0,0 +1,19 @@
+import * as React from 'react';
+
+import { ColorOption } from '../style/theme';
+
+import { Button } from './ui/button';
+import { Container } from './ui/container';
+import { Spinner } from './ui/spinner';
+import { Text } from './ui/text';
+
+export const PlacingOrderButton: React.StatelessComponent<{}> = props => (
+ <Button isDisabled={true} width="100%">
+ <Container display="inline-block" position="relative" top="3px" marginRight="8px">
+ <Spinner widthPx={20} heightPx={20} />
+ </Container>
+ <Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
+ Placing Order&hellip;
+ </Text>
+ </Button>
+);
diff --git a/packages/instant/src/components/retry_button.tsx b/packages/instant/src/components/retry_button.tsx
new file mode 100644
index 000000000..0d6188e6a
--- /dev/null
+++ b/packages/instant/src/components/retry_button.tsx
@@ -0,0 +1,11 @@
+import * as React from 'react';
+
+import { SecondaryButton } from './secondary_button';
+
+export interface RetryButtonProps {
+ onClick: () => void;
+}
+
+export const RetryButton: React.StatelessComponent<RetryButtonProps> = props => {
+ return <SecondaryButton onClick={props.onClick}>Try Again</SecondaryButton>;
+};
diff --git a/packages/instant/src/components/scaling_amount_input.tsx b/packages/instant/src/components/scaling_amount_input.tsx
new file mode 100644
index 000000000..655ae2b74
--- /dev/null
+++ b/packages/instant/src/components/scaling_amount_input.tsx
@@ -0,0 +1,52 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { ColorOption } from '../style/theme';
+import { BigNumberInput } from '../util/big_number_input';
+import { util } from '../util/util';
+
+import { ScalingInput } from './scaling_input';
+
+export interface ScalingAmountInputProps {
+ maxFontSizePx: number;
+ textLengthThreshold: number;
+ fontColor?: ColorOption;
+ value?: BigNumberInput;
+ onChange: (value?: BigNumberInput) => void;
+ onFontSizeChange: (fontSizePx: number) => void;
+}
+
+export class ScalingAmountInput extends React.Component<ScalingAmountInputProps> {
+ public static defaultProps = {
+ onChange: util.boundNoop,
+ onFontSizeChange: util.boundNoop,
+ };
+ public render(): React.ReactNode {
+ const { textLengthThreshold, fontColor, maxFontSizePx, value, onFontSizeChange } = this.props;
+ return (
+ <ScalingInput
+ maxFontSizePx={maxFontSizePx}
+ textLengthThreshold={textLengthThreshold}
+ onFontSizeChange={onFontSizeChange}
+ fontColor={fontColor}
+ onChange={this._handleChange}
+ value={!_.isUndefined(value) ? value.toDisplayString() : ''}
+ placeholder="0.00"
+ emptyInputWidthCh={3.5}
+ />
+ );
+ }
+ private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
+ const value = event.target.value;
+ let bigNumberValue;
+ if (!_.isEmpty(value)) {
+ try {
+ bigNumberValue = new BigNumberInput(value);
+ } catch {
+ // We don't want to allow values that can't be a BigNumber, so don't even call onChange.
+ return;
+ }
+ }
+ this.props.onChange(bigNumberValue);
+ };
+}
diff --git a/packages/instant/src/components/scaling_input.tsx b/packages/instant/src/components/scaling_input.tsx
new file mode 100644
index 000000000..34cb0b5fd
--- /dev/null
+++ b/packages/instant/src/components/scaling_input.tsx
@@ -0,0 +1,170 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { ColorOption } from '../style/theme';
+import { util } from '../util/util';
+
+import { Input } from './ui';
+
+export enum ScalingInputPhase {
+ FixedFontSize,
+ ScalingFontSize,
+}
+
+export interface ScalingSettings {
+ percentageToReduceFontSizePerCharacter: number;
+ constantPxToIncreaseWidthPerCharacter: number;
+}
+
+export interface ScalingInputProps {
+ textLengthThreshold: number;
+ maxFontSizePx: number;
+ value: string;
+ emptyInputWidthCh: number;
+ onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
+ onFontSizeChange: (fontSizePx: number) => void;
+ fontColor?: ColorOption;
+ placeholder?: string;
+ maxLength?: number;
+ scalingSettings: ScalingSettings;
+}
+
+export interface ScalingInputState {
+ inputWidthPxAtPhaseChange?: number;
+}
+
+export interface ScalingInputSnapshot {
+ inputWidthPx: number;
+}
+
+// These are magic numbers that were determined experimentally.
+const defaultScalingSettings: ScalingSettings = {
+ percentageToReduceFontSizePerCharacter: 0.125,
+ constantPxToIncreaseWidthPerCharacter: 4,
+};
+
+export class ScalingInput extends React.Component<ScalingInputProps, ScalingInputState> {
+ public static defaultProps = {
+ onChange: util.boundNoop,
+ onFontSizeChange: util.boundNoop,
+ maxLength: 7,
+ scalingSettings: defaultScalingSettings,
+ };
+ public state: ScalingInputState = {
+ inputWidthPxAtPhaseChange: undefined,
+ };
+ private readonly _inputRef = React.createRef<HTMLInputElement>();
+ public static getPhase(textLengthThreshold: number, value: string): ScalingInputPhase {
+ if (value.length <= textLengthThreshold) {
+ return ScalingInputPhase.FixedFontSize;
+ }
+ return ScalingInputPhase.ScalingFontSize;
+ }
+ public static getPhaseFromProps(props: ScalingInputProps): ScalingInputPhase {
+ const { value, textLengthThreshold } = props;
+ return ScalingInput.getPhase(textLengthThreshold, value);
+ }
+ public static calculateFontSize(
+ textLengthThreshold: number,
+ maxFontSizePx: number,
+ phase: ScalingInputPhase,
+ value: string,
+ percentageToReduceFontSizePerCharacter: number,
+ ): number {
+ if (phase !== ScalingInputPhase.ScalingFontSize) {
+ return maxFontSizePx;
+ }
+ const charactersOverMax = value.length - textLengthThreshold;
+ const scalingFactor = (1 - percentageToReduceFontSizePerCharacter) ** charactersOverMax;
+ const fontSize = scalingFactor * maxFontSizePx;
+ return fontSize;
+ }
+ public static calculateFontSizeFromProps(props: ScalingInputProps, phase: ScalingInputPhase): number {
+ const { textLengthThreshold, value, maxFontSizePx, scalingSettings } = props;
+ return ScalingInput.calculateFontSize(
+ textLengthThreshold,
+ maxFontSizePx,
+ phase,
+ value,
+ scalingSettings.percentageToReduceFontSizePerCharacter,
+ );
+ }
+ public getSnapshotBeforeUpdate(): ScalingInputSnapshot {
+ return {
+ inputWidthPx: this._getInputWidthInPx(),
+ };
+ }
+ public componentDidUpdate(
+ prevProps: ScalingInputProps,
+ prevState: ScalingInputState,
+ snapshot: ScalingInputSnapshot,
+ ): void {
+ const prevPhase = ScalingInput.getPhaseFromProps(prevProps);
+ const curPhase = ScalingInput.getPhaseFromProps(this.props);
+ // if we went from fixed to scaling, save the width from the transition
+ if (prevPhase !== ScalingInputPhase.ScalingFontSize && curPhase === ScalingInputPhase.ScalingFontSize) {
+ this.setState({
+ inputWidthPxAtPhaseChange: snapshot.inputWidthPx,
+ });
+ }
+ // if we went from scaling to fixed, revert back to scaling using `ch`
+ if (prevPhase === ScalingInputPhase.ScalingFontSize && curPhase !== ScalingInputPhase.ScalingFontSize) {
+ this.setState({
+ inputWidthPxAtPhaseChange: undefined,
+ });
+ }
+ const prevFontSize = ScalingInput.calculateFontSizeFromProps(prevProps, prevPhase);
+ const curFontSize = ScalingInput.calculateFontSizeFromProps(this.props, curPhase);
+ // If font size has changed, notify.
+ if (prevFontSize !== curFontSize) {
+ this.props.onFontSizeChange(curFontSize);
+ }
+ }
+ public render(): React.ReactNode {
+ const { fontColor, onChange, placeholder, value, maxLength } = this.props;
+ const phase = ScalingInput.getPhaseFromProps(this.props);
+ return (
+ <Input
+ ref={this._inputRef as any}
+ fontColor={fontColor}
+ onChange={onChange}
+ value={value}
+ placeholder={placeholder}
+ fontSize={`${this._calculateFontSize(phase)}px`}
+ width={this._calculateWidth(phase)}
+ maxLength={maxLength}
+ />
+ );
+ }
+ private readonly _calculateWidth = (phase: ScalingInputPhase): string => {
+ const { value, textLengthThreshold, scalingSettings } = this.props;
+ if (_.isEmpty(value)) {
+ return `${this.props.emptyInputWidthCh}ch`;
+ }
+ switch (phase) {
+ case ScalingInputPhase.FixedFontSize:
+ return `${value.length}ch`;
+ case ScalingInputPhase.ScalingFontSize:
+ const { inputWidthPxAtPhaseChange } = this.state;
+ if (!_.isUndefined(inputWidthPxAtPhaseChange)) {
+ const charactersOverMax = value.length - textLengthThreshold;
+ const scalingAmount = scalingSettings.constantPxToIncreaseWidthPerCharacter * charactersOverMax;
+ const width = inputWidthPxAtPhaseChange + scalingAmount;
+ return `${width}px`;
+ }
+ return `${textLengthThreshold}ch`;
+ default:
+ return '1ch';
+ }
+ };
+ private readonly _calculateFontSize = (phase: ScalingInputPhase): number => {
+ return ScalingInput.calculateFontSizeFromProps(this.props, phase);
+ };
+ private readonly _getInputWidthInPx = (): number => {
+ const ref = this._inputRef.current;
+ if (!ref) {
+ return 0;
+ }
+ return ref.getBoundingClientRect().width;
+ };
+}
diff --git a/packages/instant/src/components/secondary_button.tsx b/packages/instant/src/components/secondary_button.tsx
new file mode 100644
index 000000000..3c139a233
--- /dev/null
+++ b/packages/instant/src/components/secondary_button.tsx
@@ -0,0 +1,26 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { ColorOption } from '../style/theme';
+
+import { Button, ButtonProps } from './ui/button';
+import { Text } from './ui/text';
+
+export interface SecondaryButtonProps extends ButtonProps {}
+
+export const SecondaryButton: React.StatelessComponent<SecondaryButtonProps> = props => {
+ const buttonProps = _.omit(props, 'text');
+ return (
+ <Button
+ backgroundColor={ColorOption.white}
+ borderColor={ColorOption.lightGrey}
+ width="100%"
+ onClick={props.onClick}
+ {...buttonProps}
+ >
+ <Text fontColor={ColorOption.primaryColor} fontWeight={600} fontSize="16px">
+ {props.children}
+ </Text>
+ </Button>
+ );
+};
diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx
index 5e2218c68..76b570de7 100644
--- a/packages/instant/src/components/ui/container.tsx
+++ b/packages/instant/src/components/ui/container.tsx
@@ -1,5 +1,3 @@
-import * as React from 'react';
-
import { ColorOption, styled } from '../../style/theme';
import { cssRuleIfExists } from '../../style/util';
@@ -11,6 +9,7 @@ export interface ContainerProps {
bottom?: string;
left?: string;
width?: string;
+ height?: string;
maxWidth?: string;
margin?: string;
marginTop?: string;
@@ -27,14 +26,14 @@ export interface ContainerProps {
backgroundColor?: ColorOption;
hasBoxShadow?: boolean;
zIndex?: number;
+ whiteSpace?: string;
opacity?: number;
}
-const PlainContainer: React.StatelessComponent<ContainerProps> = ({ children, className }) => (
- <div className={className}>{children}</div>
-);
-
-export const Container = styled(PlainContainer)`
+export const Container =
+ styled.div <
+ ContainerProps >
+ `
box-sizing: border-box;
${props => cssRuleIfExists(props, 'display')}
${props => cssRuleIfExists(props, 'position')}
@@ -43,6 +42,7 @@ export const Container = styled(PlainContainer)`
${props => cssRuleIfExists(props, 'bottom')}
${props => cssRuleIfExists(props, 'left')}
${props => cssRuleIfExists(props, 'width')}
+ ${props => cssRuleIfExists(props, 'height')}
${props => cssRuleIfExists(props, 'max-width')}
${props => cssRuleIfExists(props, 'margin')}
${props => cssRuleIfExists(props, 'margin-top')}
@@ -55,6 +55,7 @@ export const Container = styled(PlainContainer)`
${props => cssRuleIfExists(props, 'border-top')}
${props => cssRuleIfExists(props, 'border-bottom')}
${props => cssRuleIfExists(props, 'z-index')}
+ ${props => cssRuleIfExists(props, 'white-space')}
${props => cssRuleIfExists(props, 'opacity')}
${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')};
background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
diff --git a/packages/instant/src/components/ui/flex.tsx b/packages/instant/src/components/ui/flex.tsx
index 327e91926..5fa3fc95b 100644
--- a/packages/instant/src/components/ui/flex.tsx
+++ b/packages/instant/src/components/ui/flex.tsx
@@ -1,5 +1,3 @@
-import * as React from 'react';
-
import { ColorOption, styled } from '../../style/theme';
import { cssRuleIfExists } from '../../style/util';
@@ -9,21 +7,22 @@ export interface FlexProps {
justify?: 'flex-start' | 'center' | 'space-around' | 'space-between' | 'space-evenly' | 'flex-end';
align?: 'flex-start' | 'center' | 'space-around' | 'space-between' | 'space-evenly' | 'flex-end';
width?: string;
+ height?: string;
backgroundColor?: ColorOption;
className?: string;
}
-const PlainFlex: React.StatelessComponent<FlexProps> = ({ children, className }) => (
- <div className={className}>{children}</div>
-);
-
-export const Flex = styled(PlainFlex)`
+export const Flex =
+ styled.div <
+ FlexProps >
+ `
display: flex;
flex-direction: ${props => props.direction};
flex-wrap: ${props => props.flexWrap};
justify-content: ${props => props.justify};
align-items: ${props => props.align};
${props => cssRuleIfExists(props, 'width')}
+ ${props => cssRuleIfExists(props, 'height')}
background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
`;
diff --git a/packages/instant/src/components/ui/icon.tsx b/packages/instant/src/components/ui/icon.tsx
new file mode 100644
index 000000000..7373c3acd
--- /dev/null
+++ b/packages/instant/src/components/ui/icon.tsx
@@ -0,0 +1,58 @@
+import * as React from 'react';
+
+import { ColorOption } from '../../style/theme';
+
+type svgRule = 'evenodd' | 'nonzero' | 'inherit';
+interface IconInfo {
+ viewBox: string;
+ fillRule?: svgRule;
+ clipRule?: svgRule;
+ path: string;
+}
+interface IconInfoMapping {
+ failed: IconInfo;
+ success: IconInfo;
+}
+const ICONS: IconInfoMapping = {
+ failed: {
+ viewBox: '0 0 34 34',
+ fillRule: 'evenodd',
+ clipRule: 'evenodd',
+ path:
+ 'M6.65771 26.4362C9.21777 29.2406 12.9033 31 17 31C24.7319 31 31 24.7319 31 17C31 14.4468 30.3164 12.0531 29.1226 9.99219L6.65771 26.4362ZM4.88281 24.0173C3.68555 21.9542 3 19.5571 3 17C3 9.26807 9.26807 3 17 3C21.1006 3 24.7891 4.76294 27.3496 7.57214L4.88281 24.0173ZM0 17C0 26.3888 7.61133 34 17 34C26.3887 34 34 26.3888 34 17C34 7.61121 26.3887 0 17 0C7.61133 0 0 7.61121 0 17Z',
+ },
+ success: {
+ viewBox: '0 0 34 34',
+ fillRule: 'evenodd',
+ clipRule: 'evenodd',
+ path:
+ 'M17 34C26.3887 34 34 26.3888 34 17C34 7.61121 26.3887 0 17 0C7.61133 0 0 7.61121 0 17C0 26.3888 7.61133 34 17 34ZM25.7539 13.0977C26.2969 12.4718 26.2295 11.5244 25.6035 10.9817C24.9775 10.439 24.0303 10.5063 23.4878 11.1323L15.731 20.0771L12.3936 16.7438C11.8071 16.1583 10.8574 16.1589 10.272 16.7451C9.68652 17.3313 9.6875 18.281 10.2734 18.8665L14.75 23.3373L15.8887 24.4746L16.9434 23.2587L25.7539 13.0977Z',
+ },
+};
+
+export interface IconProps {
+ width: number;
+ height: number;
+ color: ColorOption;
+ icon: keyof IconInfoMapping;
+}
+export const Icon: React.SFC<IconProps> = props => {
+ const iconInfo = ICONS[props.icon];
+
+ return (
+ <svg
+ width={props.width}
+ height={props.height}
+ viewBox={iconInfo.viewBox}
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d={iconInfo.path}
+ fill={props.color}
+ fillRule={iconInfo.fillRule || 'nonzero'}
+ clipRule={iconInfo.clipRule || 'nonzero'}
+ />
+ </svg>
+ );
+};
diff --git a/packages/instant/src/components/ui/input.tsx b/packages/instant/src/components/ui/input.tsx
index f8c6b6ef6..a884ff7cb 100644
--- a/packages/instant/src/components/ui/input.tsx
+++ b/packages/instant/src/components/ui/input.tsx
@@ -12,11 +12,10 @@ export interface InputProps {
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
}
-const PlainInput: React.StatelessComponent<InputProps> = ({ value, className, placeholder, onChange }) => (
- <input className={className} value={value} onChange={onChange} placeholder={placeholder} />
-);
-
-export const Input = styled(PlainInput)`
+export const Input =
+ styled.input <
+ InputProps >
+ `
font-size: ${props => props.fontSize};
width: ${props => props.width};
padding: 0.1em 0em;
diff --git a/packages/instant/src/components/ui/spinner.tsx b/packages/instant/src/components/ui/spinner.tsx
new file mode 100644
index 000000000..28ebc2598
--- /dev/null
+++ b/packages/instant/src/components/ui/spinner.tsx
@@ -0,0 +1,30 @@
+import * as React from 'react';
+
+import { FullRotation } from '../animations/full_rotation';
+
+export interface SpinnerProps {
+ widthPx: number;
+ heightPx: number;
+}
+export const Spinner: React.StatelessComponent<SpinnerProps> = props => {
+ return (
+ <FullRotation width={`${props.widthPx}px`} height={`${props.heightPx}px`}>
+ <svg
+ width={props.widthPx}
+ height={props.heightPx}
+ viewBox="0 0 34 34"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <circle cx="17" cy="17" r="15" stroke="white" strokeOpacity="0.2" strokeWidth="4" />
+ <path
+ d="M17 32C25.2843 32 32 25.2843 32 17C32 8.71573 25.2843 2 17 2"
+ stroke="white"
+ strokeWidth="4"
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ />
+ </svg>
+ </FullRotation>
+ );
+};
diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx
index 9fb8ea26f..fd72f6cc8 100644
--- a/packages/instant/src/components/ui/text.tsx
+++ b/packages/instant/src/components/ui/text.tsx
@@ -23,14 +23,11 @@ export interface TextProps {
display?: string;
}
-const PlainText: React.StatelessComponent<TextProps> = ({ children, className, onClick }) => (
- <div className={className} onClick={onClick}>
- {children}
- </div>
-);
-
const darkenOnHoverAmount = 0.3;
-export const Text = styled(PlainText)`
+export const Text =
+ styled.div <
+ TextProps >
+ `
font-family: ${props => props.fontFamily};
font-style: ${props => props.fontStyle};
font-weight: ${props => props.fontWeight};
diff --git a/packages/instant/src/components/view_transaction_button.tsx b/packages/instant/src/components/view_transaction_button.tsx
new file mode 100644
index 000000000..7aa44e657
--- /dev/null
+++ b/packages/instant/src/components/view_transaction_button.tsx
@@ -0,0 +1,11 @@
+import * as React from 'react';
+
+import { SecondaryButton } from './secondary_button';
+
+export interface ViewTransactionButtonProps {
+ onClick: () => void;
+}
+
+export const ViewTransactionButton: React.StatelessComponent<ViewTransactionButtonProps> = props => {
+ return <SecondaryButton onClick={props.onClick}>View Transaction</SecondaryButton>;
+};
diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx
index cf918d890..1d17ed12a 100644
--- a/packages/instant/src/components/zero_ex_instant_container.tsx
+++ b/packages/instant/src/components/zero_ex_instant_container.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import { LatestBuyQuoteOrderDetails } from '../containers/latest_buy_quote_order_details';
import { LatestError } from '../containers/latest_error';
-import { SelectedAssetBuyButton } from '../containers/selected_asset_buy_button';
+import { SelectedAssetBuyOrderStateButton } from '../containers/selected_asset_buy_order_state_button';
import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading';
import { ColorOption } from '../style/theme';
@@ -26,7 +26,9 @@ export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantConta
<Flex direction="column" justify="flex-start">
<SelectedAssetInstantHeading />
<LatestBuyQuoteOrderDetails />
- <SelectedAssetBuyButton />
+ <Container padding="20px" width="100%">
+ <SelectedAssetBuyOrderStateButton />
+ </Container>
</Flex>
</Container>
</Container>
diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts
index 31491c80a..48d0d4aa2 100644
--- a/packages/instant/src/constants.ts
+++ b/packages/instant/src/constants.ts
@@ -2,3 +2,4 @@ import { BigNumber } from '@0x/utils';
export const BIG_NUMBER_ZERO = new BigNumber(0);
export const ethDecimals = 18;
export const DEFAULT_ZERO_EX_CONTAINER_SELECTOR = '#zeroExInstantContainer';
+export const WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX = 'Transaction failed';
diff --git a/packages/instant/src/containers/selected_asset_buy_button.ts b/packages/instant/src/containers/selected_asset_buy_button.ts
index 8189a5377..adcbd61bc 100644
--- a/packages/instant/src/containers/selected_asset_buy_button.ts
+++ b/packages/instant/src/containers/selected_asset_buy_button.ts
@@ -6,7 +6,7 @@ import { Dispatch } from 'redux';
import { Action, actions } from '../redux/actions';
import { State } from '../redux/reducer';
-import { AsyncProcessState } from '../types';
+import { OrderProcessState, OrderState } from '../types';
import { BuyButton } from '../components/buy_button';
@@ -14,41 +14,39 @@ export interface SelectedAssetBuyButtonProps {}
interface ConnectedState {
assetBuyer?: AssetBuyer;
- text: string;
buyQuote?: BuyQuote;
}
interface ConnectedDispatch {
- onClick: (buyQuote: BuyQuote) => void;
- onBuySuccess: (buyQuote: BuyQuote) => void;
- onBuyFailure: (buyQuote: BuyQuote) => void;
+ onAwaitingSignature: (buyQuote: BuyQuote) => void;
+ onSignatureDenied: (buyQuote: BuyQuote, error: Error) => void;
+ onBuyProcessing: (buyQuote: BuyQuote, txHash: string) => void;
+ onBuySuccess: (buyQuote: BuyQuote, txHash: string) => void;
+ onBuyFailure: (buyQuote: BuyQuote, txHash: string) => void;
}
-const textForState = (state: AsyncProcessState): string => {
- switch (state) {
- case AsyncProcessState.NONE:
- return 'Buy';
- case AsyncProcessState.PENDING:
- return '...Loading';
- case AsyncProcessState.SUCCESS:
- return 'Success!';
- case AsyncProcessState.FAILURE:
- return 'Failed';
- default:
- return 'Buy';
- }
-};
-
const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyButtonProps): ConnectedState => ({
assetBuyer: state.assetBuyer,
- text: textForState(state.buyOrderState),
buyQuote: state.latestBuyQuote,
});
const mapDispatchToProps = (dispatch: Dispatch<Action>, ownProps: SelectedAssetBuyButtonProps): ConnectedDispatch => ({
- onClick: buyQuote => dispatch(actions.updateBuyOrderState(AsyncProcessState.PENDING)),
- onBuySuccess: buyQuote => dispatch(actions.updateBuyOrderState(AsyncProcessState.SUCCESS)),
- onBuyFailure: buyQuote => dispatch(actions.updateBuyOrderState(AsyncProcessState.FAILURE)),
+ onAwaitingSignature: (buyQuote: BuyQuote) => {
+ const newOrderState: OrderState = { processState: OrderProcessState.AWAITING_SIGNATURE };
+ dispatch(actions.updateBuyOrderState(newOrderState));
+ },
+ onBuyProcessing: (buyQuote: BuyQuote, txHash: string) => {
+ const newOrderState: OrderState = { processState: OrderProcessState.PROCESSING, txHash };
+ dispatch(actions.updateBuyOrderState(newOrderState));
+ },
+ onBuySuccess: (buyQuote: BuyQuote, txHash: string) =>
+ dispatch(actions.updateBuyOrderState({ processState: OrderProcessState.SUCCESS, txHash })),
+ onBuyFailure: (buyQuote: BuyQuote, txHash: string) =>
+ dispatch(actions.updateBuyOrderState({ processState: OrderProcessState.FAILURE, txHash })),
+ onSignatureDenied: (buyQuote, error) => {
+ dispatch(actions.resetAmount());
+ dispatch(actions.setError(error));
+ },
});
export const SelectedAssetBuyButton: React.ComponentClass<SelectedAssetBuyButtonProps> = connect(
diff --git a/packages/instant/src/containers/selected_asset_buy_order_state_button.tsx b/packages/instant/src/containers/selected_asset_buy_order_state_button.tsx
new file mode 100644
index 000000000..7faa79912
--- /dev/null
+++ b/packages/instant/src/containers/selected_asset_buy_order_state_button.tsx
@@ -0,0 +1,20 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import { connect } from 'react-redux';
+
+import { State } from '../redux/reducer';
+import { OrderProcessState } from '../types';
+
+import { BuyOrderStateButton } from '../components/buy_order_state_button';
+
+interface ConnectedState {
+ buyOrderProcessingState: OrderProcessState;
+}
+export interface SelectedAssetButtonProps {}
+const mapStateToProps = (state: State, _ownProps: SelectedAssetButtonProps): ConnectedState => ({
+ buyOrderProcessingState: state.buyOrderState.processState,
+});
+
+export const SelectedAssetBuyOrderStateButton: React.ComponentClass<SelectedAssetButtonProps> = connect(
+ mapStateToProps,
+)(BuyOrderStateButton);
diff --git a/packages/instant/src/containers/selected_asset_instant_heading.ts b/packages/instant/src/containers/selected_asset_instant_heading.ts
index 43127582c..6b2a29b07 100644
--- a/packages/instant/src/containers/selected_asset_instant_heading.ts
+++ b/packages/instant/src/containers/selected_asset_instant_heading.ts
@@ -5,7 +5,7 @@ import { connect } from 'react-redux';
import { oc } from 'ts-optchain';
import { State } from '../redux/reducer';
-import { AsyncProcessState } from '../types';
+import { AsyncProcessState, OrderState } from '../types';
import { InstantHeading } from '../components/instant_heading';
@@ -16,6 +16,7 @@ interface ConnectedState {
totalEthBaseAmount?: BigNumber;
ethUsdPrice?: BigNumber;
quoteRequestState: AsyncProcessState;
+ buyOrderState: OrderState;
}
const mapStateToProps = (state: State, _ownProps: InstantHeadingProps): ConnectedState => ({
@@ -23,6 +24,7 @@ const mapStateToProps = (state: State, _ownProps: InstantHeadingProps): Connecte
totalEthBaseAmount: oc(state).latestBuyQuote.worstCaseQuoteInfo.totalEthAmount(),
ethUsdPrice: state.ethUsdPrice,
quoteRequestState: state.quoteRequestState,
+ buyOrderState: state.buyOrderState,
});
export const SelectedAssetInstantHeading: React.ComponentClass<InstantHeadingProps> = connect(mapStateToProps)(
diff --git a/packages/instant/src/containers/selected_asset_retry_button.tsx b/packages/instant/src/containers/selected_asset_retry_button.tsx
new file mode 100644
index 000000000..b2b140be6
--- /dev/null
+++ b/packages/instant/src/containers/selected_asset_retry_button.tsx
@@ -0,0 +1,26 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Dispatch } from 'redux';
+
+import { Action, actions } from '../redux/actions';
+
+import { RetryButton } from '../components/retry_button';
+
+export interface SelectedAssetRetryButtonProps {}
+
+interface ConnectedDispatch {
+ onClick: () => void;
+}
+
+const mapDispatchToProps = (
+ dispatch: Dispatch<Action>,
+ _ownProps: SelectedAssetRetryButtonProps,
+): ConnectedDispatch => ({
+ onClick: () => dispatch(actions.resetAmount()),
+});
+
+export const SelectedAssetRetryButton: React.ComponentClass<SelectedAssetRetryButtonProps> = connect(
+ undefined,
+ mapDispatchToProps,
+)(RetryButton);
diff --git a/packages/instant/src/containers/selected_asset_view_transaction_button.tsx b/packages/instant/src/containers/selected_asset_view_transaction_button.tsx
new file mode 100644
index 000000000..064b877be
--- /dev/null
+++ b/packages/instant/src/containers/selected_asset_view_transaction_button.tsx
@@ -0,0 +1,38 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import { connect } from 'react-redux';
+
+import { State } from '../redux/reducer';
+
+import { ViewTransactionButton } from '../components/view_transaction_button';
+import { OrderProcessState } from '../types';
+import { etherscanUtil } from '../util/etherscan';
+
+export interface SelectedAssetViewTransactionButtonProps {}
+
+interface ConnectedState {
+ onClick: () => void;
+}
+
+const mapStateToProps = (state: State, _ownProps: {}): ConnectedState => ({
+ onClick: () => {
+ if (
+ state.assetBuyer &&
+ (state.buyOrderState.processState === OrderProcessState.PROCESSING ||
+ state.buyOrderState.processState === OrderProcessState.SUCCESS)
+ ) {
+ const etherscanUrl = etherscanUtil.getEtherScanTxnAddressIfExists(
+ state.buyOrderState.txHash,
+ state.assetBuyer.networkId,
+ );
+ if (etherscanUrl) {
+ window.open(etherscanUrl, '_blank');
+ return;
+ }
+ }
+ },
+});
+
+export const SelectedAssetViewTransactionButton: React.ComponentClass<
+ SelectedAssetViewTransactionButtonProps
+> = connect(mapStateToProps)(ViewTransactionButton);
diff --git a/packages/instant/src/containers/selected_asset_amount_input.ts b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
index 0d847cf02..ee76e9d66 100644
--- a/packages/instant/src/containers/selected_asset_amount_input.ts
+++ b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
@@ -10,35 +10,36 @@ import { Dispatch } from 'redux';
import { Action, actions } from '../redux/actions';
import { State } from '../redux/reducer';
import { ColorOption } from '../style/theme';
-import { AsyncProcessState, ERC20Asset } from '../types';
+import { ERC20Asset, OrderProcessState } from '../types';
+import { BigNumberInput } from '../util/big_number_input';
import { errorUtil } from '../util/error';
-import { AssetAmountInput } from '../components/asset_amount_input';
+import { ERC20AssetAmountInput } from '../components/erc20_asset_amount_input';
-export interface SelectedAssetAmountInputProps {
+export interface SelectedERC20AssetAmountInputProps {
fontColor?: ColorOption;
- fontSize?: string;
+ startingFontSizePx: number;
}
interface ConnectedState {
assetBuyer?: AssetBuyer;
- value?: BigNumber;
+ value?: BigNumberInput;
asset?: ERC20Asset;
}
interface ConnectedDispatch {
- updateBuyQuote: (assetBuyer?: AssetBuyer, value?: BigNumber, asset?: ERC20Asset) => void;
+ updateBuyQuote: (assetBuyer?: AssetBuyer, value?: BigNumberInput, asset?: ERC20Asset) => void;
}
interface ConnectedProps {
- value?: BigNumber;
+ value?: BigNumberInput;
asset?: ERC20Asset;
- onChange: (value?: BigNumber, asset?: ERC20Asset) => void;
+ onChange: (value?: BigNumberInput, asset?: ERC20Asset) => void;
}
-type FinalProps = ConnectedProps & SelectedAssetAmountInputProps;
+type FinalProps = ConnectedProps & SelectedERC20AssetAmountInputProps;
-const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps): ConnectedState => {
+const mapStateToProps = (state: State, _ownProps: SelectedERC20AssetAmountInputProps): ConnectedState => {
const selectedAsset = state.selectedAsset;
if (_.isUndefined(selectedAsset) || selectedAsset.metaData.assetProxyId !== AssetProxyId.ERC20) {
return {
@@ -82,7 +83,7 @@ const debouncedUpdateBuyQuoteAsync = _.debounce(updateBuyQuoteAsync, 200, { trai
const mapDispatchToProps = (
dispatch: Dispatch<Action>,
- _ownProps: SelectedAssetAmountInputProps,
+ _ownProps: SelectedERC20AssetAmountInputProps,
): ConnectedDispatch => ({
updateBuyQuote: (assetBuyer, value, asset) => {
// Update the input
@@ -90,7 +91,7 @@ const mapDispatchToProps = (
// invalidate the last buy quote.
dispatch(actions.updateLatestBuyQuote(undefined));
// reset our buy state
- dispatch(actions.updateBuyOrderState(AsyncProcessState.NONE));
+ dispatch(actions.updateBuyOrderState({ processState: OrderProcessState.NONE }));
if (!_.isUndefined(value) && !_.isUndefined(asset) && !_.isUndefined(assetBuyer)) {
// even if it's debounced, give them the illusion it's loading
@@ -104,7 +105,7 @@ const mapDispatchToProps = (
const mergeProps = (
connectedState: ConnectedState,
connectedDispatch: ConnectedDispatch,
- ownProps: SelectedAssetAmountInputProps,
+ ownProps: SelectedERC20AssetAmountInputProps,
): FinalProps => {
return {
...ownProps,
@@ -116,8 +117,8 @@ const mergeProps = (
};
};
-export const SelectedAssetAmountInput: React.ComponentClass<SelectedAssetAmountInputProps> = connect(
+export const SelectedERC20AssetAmountInput: React.ComponentClass<SelectedERC20AssetAmountInputProps> = connect(
mapStateToProps,
mapDispatchToProps,
mergeProps,
-)(AssetAmountInput);
+)(ERC20AssetAmountInput);
diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts
index 2c430ff83..46045024b 100644
--- a/packages/instant/src/redux/actions.ts
+++ b/packages/instant/src/redux/actions.ts
@@ -2,7 +2,9 @@ import { BuyQuote } from '@0x/asset-buyer';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
-import { ActionsUnion, AsyncProcessState } from '../types';
+import { BigNumberInput } from '../util/big_number_input';
+
+import { ActionsUnion, OrderState } from '../types';
export interface PlainAction<T extends string> {
type: T;
@@ -23,7 +25,7 @@ function createAction<T extends string, P>(type: T, data?: P): PlainAction<T> |
export enum ActionTypes {
UPDATE_ETH_USD_PRICE = 'UPDATE_ETH_USD_PRICE',
UPDATE_SELECTED_ASSET_AMOUNT = 'UPDATE_SELECTED_ASSET_AMOUNT',
- UPDATE_SELECTED_ASSET_BUY_STATE = 'UPDATE_SELECTED_ASSET_BUY_STATE',
+ UPDATE_BUY_ORDER_STATE = 'UPDATE_BUY_ORDER_STATE',
UPDATE_LATEST_BUY_QUOTE = 'UPDATE_LATEST_BUY_QUOTE',
UPDATE_SELECTED_ASSET = 'UPDATE_SELECTED_ASSET',
SET_QUOTE_REQUEST_STATE_PENDING = 'SET_QUOTE_REQUEST_STATE_PENDING',
@@ -31,13 +33,14 @@ export enum ActionTypes {
SET_ERROR = 'SET_ERROR',
HIDE_ERROR = 'HIDE_ERROR',
CLEAR_ERROR = 'CLEAR_ERROR',
+ RESET_AMOUNT = 'RESET_AMOUNT',
}
export const actions = {
updateEthUsdPrice: (price?: BigNumber) => createAction(ActionTypes.UPDATE_ETH_USD_PRICE, price),
- updateSelectedAssetAmount: (amount?: BigNumber) => createAction(ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, amount),
- updateBuyOrderState: (buyState: AsyncProcessState) =>
- createAction(ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, buyState),
+ updateSelectedAssetAmount: (amount?: BigNumberInput) =>
+ createAction(ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, amount),
+ updateBuyOrderState: (orderState: OrderState) => createAction(ActionTypes.UPDATE_BUY_ORDER_STATE, orderState),
updateLatestBuyQuote: (buyQuote?: BuyQuote) => createAction(ActionTypes.UPDATE_LATEST_BUY_QUOTE, buyQuote),
updateSelectedAsset: (assetData?: string) => createAction(ActionTypes.UPDATE_SELECTED_ASSET, assetData),
setQuoteRequestStatePending: () => createAction(ActionTypes.SET_QUOTE_REQUEST_STATE_PENDING),
@@ -45,4 +48,5 @@ export const actions = {
setError: (error?: any) => createAction(ActionTypes.SET_ERROR, error),
hideError: () => createAction(ActionTypes.HIDE_ERROR),
clearError: () => createAction(ActionTypes.CLEAR_ERROR),
+ resetAmount: () => createAction(ActionTypes.RESET_AMOUNT),
};
diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts
index 1538621a5..614ed21ac 100644
--- a/packages/instant/src/redux/reducer.ts
+++ b/packages/instant/src/redux/reducer.ts
@@ -1,11 +1,21 @@
import { AssetBuyer, BuyQuote } from '@0x/asset-buyer';
-import { ObjectMap } from '@0x/types';
+import { AssetProxyId, ObjectMap } from '@0x/types';
import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import { assetMetaDataMap } from '../data/asset_meta_data_map';
-import { Asset, AssetMetaData, AsyncProcessState, DisplayStatus, Network } from '../types';
+import {
+ Asset,
+ AssetMetaData,
+ AsyncProcessState,
+ DisplayStatus,
+ Network,
+ OrderProcessState,
+ OrderState,
+} from '../types';
import { assetUtils } from '../util/asset';
+import { BigNumberInput } from '../util/big_number_input';
import { Action, ActionTypes } from './actions';
@@ -14,8 +24,8 @@ export interface State {
assetBuyer?: AssetBuyer;
assetMetaDataMap: ObjectMap<AssetMetaData>;
selectedAsset?: Asset;
- selectedAssetAmount?: BigNumber;
- buyOrderState: AsyncProcessState;
+ selectedAssetAmount?: BigNumberInput;
+ buyOrderState: OrderState;
ethUsdPrice?: BigNumber;
latestBuyQuote?: BuyQuote;
quoteRequestState: AsyncProcessState;
@@ -27,7 +37,7 @@ export const INITIAL_STATE: State = {
network: Network.Mainnet,
selectedAssetAmount: undefined,
assetMetaDataMap,
- buyOrderState: AsyncProcessState.NONE,
+ buyOrderState: { processState: OrderProcessState.NONE },
ethUsdPrice: undefined,
latestBuyQuote: undefined,
latestError: undefined,
@@ -48,11 +58,19 @@ export const reducer = (state: State = INITIAL_STATE, action: Action): State =>
selectedAssetAmount: action.data,
};
case ActionTypes.UPDATE_LATEST_BUY_QUOTE:
- return {
- ...state,
- latestBuyQuote: action.data,
- quoteRequestState: AsyncProcessState.SUCCESS,
- };
+ const newBuyQuoteIfExists = action.data;
+ const shouldUpdate =
+ _.isUndefined(newBuyQuoteIfExists) || doesBuyQuoteMatchState(newBuyQuoteIfExists, state);
+ if (shouldUpdate) {
+ return {
+ ...state,
+ latestBuyQuote: newBuyQuoteIfExists,
+ quoteRequestState: AsyncProcessState.SUCCESS,
+ };
+ } else {
+ return state;
+ }
+
case ActionTypes.SET_QUOTE_REQUEST_STATE_PENDING:
return {
...state,
@@ -65,7 +83,7 @@ export const reducer = (state: State = INITIAL_STATE, action: Action): State =>
latestBuyQuote: undefined,
quoteRequestState: AsyncProcessState.FAILURE,
};
- case ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE:
+ case ActionTypes.UPDATE_BUY_ORDER_STATE:
return {
...state,
buyOrderState: action.data,
@@ -101,7 +119,41 @@ export const reducer = (state: State = INITIAL_STATE, action: Action): State =>
...state,
selectedAsset: newSelectedAsset,
};
+ case ActionTypes.RESET_AMOUNT:
+ return {
+ ...state,
+ latestBuyQuote: undefined,
+ quoteRequestState: AsyncProcessState.NONE,
+ buyOrderState: { processState: OrderProcessState.NONE },
+ selectedAssetAmount: undefined,
+ };
default:
return state;
}
};
+
+const doesBuyQuoteMatchState = (buyQuote: BuyQuote, state: State): boolean => {
+ const selectedAssetIfExists = state.selectedAsset;
+ const selectedAssetAmountIfExists = state.selectedAssetAmount;
+ // if no selectedAsset or selectedAssetAmount exists on the current state, return false
+ if (_.isUndefined(selectedAssetIfExists) || _.isUndefined(selectedAssetAmountIfExists)) {
+ return false;
+ }
+ // if buyQuote's assetData does not match that of the current selected asset, return false
+ if (selectedAssetIfExists.assetData !== buyQuote.assetData) {
+ return false;
+ }
+ // if ERC20 and buyQuote's assetBuyAmount does not match selectedAssetAmount, return false
+ // if ERC721, return true
+ const selectedAssetMetaData = selectedAssetIfExists.metaData;
+ if (selectedAssetMetaData.assetProxyId === AssetProxyId.ERC20) {
+ const selectedAssetAmountBaseUnits = Web3Wrapper.toBaseUnitAmount(
+ selectedAssetAmountIfExists,
+ selectedAssetMetaData.decimals,
+ );
+ const doesAssetAmountMatch = selectedAssetAmountBaseUnits.eq(buyQuote.assetBuyAmount);
+ return doesAssetAmountMatch;
+ } else {
+ return true;
+ }
+};
diff --git a/packages/instant/src/style/fonts.ts b/packages/instant/src/style/fonts.ts
index 975a30a61..92450502d 100644
--- a/packages/instant/src/style/fonts.ts
+++ b/packages/instant/src/style/fonts.ts
@@ -1,10 +1,10 @@
-import { injectGlobal } from './theme';
-
export const fonts = {
include: () => {
// Inject the inter-ui font into the page
- return injectGlobal`
- @import url('https://rsms.me/inter/inter-ui.css');
- `;
+ const appendTo = document.head || document.getElementsByTagName('head')[0] || document.body;
+ const style = document.createElement('style');
+ style.type = 'text/css';
+ style.appendChild(document.createTextNode(`@import url('https://rsms.me/inter/inter-ui.css')`));
+ appendTo.appendChild(style);
},
};
diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts
index d26c816c1..6575ff9f4 100644
--- a/packages/instant/src/style/theme.ts
+++ b/packages/instant/src/style/theme.ts
@@ -1,6 +1,6 @@
import * as styledComponents from 'styled-components';
-const { default: styled, css, injectGlobal, keyframes, ThemeProvider } = styledComponents;
+const { default: styled, css, keyframes, ThemeProvider } = styledComponents;
export type Theme = { [key in ColorOption]: string };
@@ -28,4 +28,6 @@ export const theme: Theme = {
darkOrange: '#F2994C',
};
-export { styled, css, injectGlobal, keyframes, ThemeProvider };
+export const transparentWhite = 'rgba(255,255,255,0.3)';
+
+export { styled, css, keyframes, ThemeProvider };
diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts
index c340623ad..c63371fb4 100644
--- a/packages/instant/src/types.ts
+++ b/packages/instant/src/types.ts
@@ -7,6 +7,24 @@ export enum AsyncProcessState {
SUCCESS = 'Success',
FAILURE = 'Failure',
}
+
+export enum OrderProcessState {
+ NONE = 'None',
+ AWAITING_SIGNATURE = 'Awaiting Signature',
+ PROCESSING = 'Processing',
+ SUCCESS = 'Success',
+ FAILURE = 'Failure',
+}
+
+interface OrderStatePreTx {
+ processState: OrderProcessState.NONE | OrderProcessState.AWAITING_SIGNATURE;
+}
+interface OrderStatePostTx {
+ processState: OrderProcessState.PROCESSING | OrderProcessState.SUCCESS | OrderProcessState.FAILURE;
+ txHash: string;
+}
+export type OrderState = OrderStatePreTx | OrderStatePostTx;
+
export enum DisplayStatus {
Present,
Hidden,
diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts
index 4e3b2b946..2c5b6325d 100644
--- a/packages/instant/src/util/asset.ts
+++ b/packages/instant/src/util/asset.ts
@@ -2,7 +2,7 @@ import { AssetProxyId, ObjectMap } from '@0x/types';
import * as _ from 'lodash';
import { assetDataNetworkMapping } from '../data/asset_data_network_mapping';
-import { Asset, AssetMetaData, Network, ZeroExInstantError } from '../types';
+import { Asset, AssetMetaData, ERC20Asset, Network, ZeroExInstantError } from '../types';
export const assetUtils = {
createAssetFromAssetData: (
@@ -43,6 +43,16 @@ export const assetUtils = {
return defaultName;
}
},
+ formattedSymbolForAsset: (asset?: ERC20Asset, defaultName: string = '???'): string => {
+ if (_.isUndefined(asset)) {
+ return defaultName;
+ }
+ const symbol = asset.metaData.symbol;
+ if (symbol.length <= 5) {
+ return symbol;
+ }
+ return `${symbol.slice(0, 3)}…`;
+ },
getAssociatedAssetDataIfExists: (assetData: string, network: Network): string | undefined => {
const assetDataGroupIfExists = _.find(assetDataNetworkMapping, value => value[network] === assetData);
if (_.isUndefined(assetDataGroupIfExists)) {
diff --git a/packages/instant/src/util/big_number_input.ts b/packages/instant/src/util/big_number_input.ts
new file mode 100644
index 000000000..d2a9a8dc5
--- /dev/null
+++ b/packages/instant/src/util/big_number_input.ts
@@ -0,0 +1,29 @@
+import { BigNumber } from '@0x/utils';
+import * as _ from 'lodash';
+
+/**
+ * A BigNumber extension that is more flexible about decimal strings.
+ * Such as allowing:
+ * new BigNumberInput('0.') => 0
+ * new BigNumberInput('1.') => 1
+ * new BigNumberInput('1..') => still throws
+ */
+export class BigNumberInput extends BigNumber {
+ private readonly _isEndingWithDecimal: boolean;
+ constructor(bigNumberString: string) {
+ const hasDecimalPeriod = _.endsWith(bigNumberString, '.');
+ let internalString = bigNumberString;
+ if (hasDecimalPeriod) {
+ internalString = bigNumberString.slice(0, -1);
+ }
+ super(internalString);
+ this._isEndingWithDecimal = hasDecimalPeriod;
+ }
+ public toDisplayString(): string {
+ const internalString = super.toString();
+ if (this._isEndingWithDecimal) {
+ return `${internalString}.`;
+ }
+ return internalString;
+ }
+}
diff --git a/packages/instant/src/util/error.ts b/packages/instant/src/util/error.ts
index 40fd24c7e..64c1f4885 100644
--- a/packages/instant/src/util/error.ts
+++ b/packages/instant/src/util/error.ts
@@ -46,6 +46,10 @@ const humanReadableMessageForError = (error: Error, asset?: Asset): string | und
return `${assetName} is currently unavailable`;
}
+ if (error.message === AssetBuyerError.SignatureRequestDenied) {
+ return 'You denied this transaction';
+ }
+
return undefined;
};
diff --git a/packages/instant/src/util/etherscan.ts b/packages/instant/src/util/etherscan.ts
new file mode 100644
index 000000000..cfc2578a3
--- /dev/null
+++ b/packages/instant/src/util/etherscan.ts
@@ -0,0 +1,24 @@
+import * as _ from 'lodash';
+
+import { Network } from '../types';
+
+const etherscanPrefix = (networkId: number): string | undefined => {
+ switch (networkId) {
+ case Network.Kovan:
+ return 'kovan.';
+ case Network.Mainnet:
+ return '';
+ default:
+ return undefined;
+ }
+};
+
+export const etherscanUtil = {
+ getEtherScanTxnAddressIfExists: (txHash: string, networkId: number) => {
+ const prefix = etherscanPrefix(networkId);
+ if (_.isUndefined(prefix)) {
+ return;
+ }
+ return `https://${prefix}etherscan.io/tx/${txHash}`;
+ },
+};
diff --git a/packages/instant/src/util/format.ts b/packages/instant/src/util/format.ts
index 8482b1526..ca7c01359 100644
--- a/packages/instant/src/util/format.ts
+++ b/packages/instant/src/util/format.ts
@@ -24,7 +24,7 @@ export const format = {
if (_.isUndefined(ethUnitAmount)) {
return defaultText;
}
- const roundedAmount = ethUnitAmount.round(decimalPlaces);
+ const roundedAmount = ethUnitAmount.round(decimalPlaces).toDigits(decimalPlaces);
return `${roundedAmount} ETH`;
},
ethBaseAmountInUsd: (
diff --git a/packages/instant/test/util/format.test.ts b/packages/instant/test/util/format.test.ts
index 141df9275..2c9294c78 100644
--- a/packages/instant/test/util/format.test.ts
+++ b/packages/instant/test/util/format.test.ts
@@ -20,8 +20,8 @@ describe('format', () => {
it('converts .432414 ETH in base units to the string `.4324 ETH`', () => {
expect(format.ethBaseAmount(DECIMAL_ETH_IN_BASE_UNITS)).toBe('0.4324 ETH');
});
- it('converts 5.3014059295032 ETH in base units to the string `5.3014 ETH`', () => {
- expect(format.ethBaseAmount(IRRATIONAL_ETH_IN_BASE_UNITS)).toBe('5.3014 ETH');
+ it('converts 5.3014059295032 ETH in base units to the string `5.301 ETH`', () => {
+ expect(format.ethBaseAmount(IRRATIONAL_ETH_IN_BASE_UNITS)).toBe('5.301 ETH');
});
it('returns defaultText param when ethBaseAmount is not defined', () => {
const defaultText = 'defaultText';
@@ -38,8 +38,8 @@ describe('format', () => {
it('converts BigNumer(.432414) to the string `.4324 ETH`', () => {
expect(format.ethUnitAmount(BIG_NUMBER_DECIMAL)).toBe('0.4324 ETH');
});
- it('converts BigNumber(5.3014059295032) to the string `5.3014 ETH`', () => {
- expect(format.ethUnitAmount(BIG_NUMBER_IRRATIONAL)).toBe('5.3014 ETH');
+ it('converts BigNumber(5.3014059295032) to the string `5.301 ETH`', () => {
+ expect(format.ethUnitAmount(BIG_NUMBER_IRRATIONAL)).toBe('5.301 ETH');
});
it('returns defaultText param when ethUnitAmount is not defined', () => {
const defaultText = 'defaultText';
diff --git a/packages/migrations/README.md b/packages/migrations/README.md
index 926654cd8..b90d730eb 100644
--- a/packages/migrations/README.md
+++ b/packages/migrations/README.md
@@ -50,33 +50,10 @@ yarn lint
### Migrate
-#### V2-beta smart contracts
+#### V2 smart contracts
-In order to migrate the V2-beta 0x smart contracts to Kovan using a Ledger Nano S, run:
-
-```bash
-yarn migrate:v2-beta-testnet
-```
-
-**Note:** Ledger settings `contract data` must be `on`, and `browser support` must be set to `off`.
-
-Post-publish steps:
-
-1. Since we don't re-deploy the `WETH9` nor `ZRXToken` contracts, manually copy over the artifacts for them from `2.0.0` into `2.0.0-beta-testnet` and add the Kovan & ganache addresses to both of their `networks` sections.
-2. We now need to copy over the network `50` settings from the `2.0.0` artifacts to the `2.0.0-beta-testnet` artifacts for the newly deployed contracts (e.g `Exchange`, `ERC20Proxy`, `ERC721Proxy` and `AssetProxyOwner`)
-
-#### V2 (under development) smart contracts
-
-In order to migrate the V2 (under development) 0x smart contracts to TestRPC/Ganache running at `http://localhost:8545`, run:
+In order to migrate the V2 0x smart contracts to TestRPC/Ganache running at `http://localhost:8545`, run:
```bash
yarn migrate:v2
```
-
-#### V1 smart contracts
-
-In order to migrate the V1 0x smart contracts to TestRPC/Ganache running at `http://localhost:8545`, run:
-
-```bash
-yarn migrate:v1
-```
diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts
index f8b850604..f175b7a38 100644
--- a/packages/order-utils/test/asset_data_utils_test.ts
+++ b/packages/order-utils/test/asset_data_utils_test.ts
@@ -1,6 +1,7 @@
import * as chai from 'chai';
-import { ERC20AssetData } from '@0x/types';
+import { ERC20AssetData, ERC721AssetData } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import { assetDataUtils } from '../src/asset_data_utils';
@@ -14,18 +15,36 @@ const KNOWN_ENCODINGS = [
address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48',
},
+ {
+ address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
+ tokenId: new BigNumber(1),
+ assetData:
+ '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001',
+ },
];
const ERC20_ASSET_PROXY_ID = '0xf47261b0';
+const ERC721_ASSET_PROXY_ID = '0x02571792';
describe('assetDataUtils', () => {
- it('should encode', () => {
+ it('should encode ERC20', () => {
const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ENCODINGS[0].address);
expect(assetData).to.equal(KNOWN_ENCODINGS[0].assetData);
});
- it('should decode', () => {
+ it('should decode ERC20', () => {
const assetData: ERC20AssetData = assetDataUtils.decodeERC20AssetData(KNOWN_ENCODINGS[0].assetData);
expect(assetData.tokenAddress).to.equal(KNOWN_ENCODINGS[0].address);
expect(assetData.assetProxyId).to.equal(ERC20_ASSET_PROXY_ID);
});
+ it('should encode ERC721', () => {
+ const assetData = assetDataUtils.encodeERC721AssetData(KNOWN_ENCODINGS[1].address, KNOWN_ENCODINGS[1]
+ .tokenId as BigNumber);
+ expect(assetData).to.equal(KNOWN_ENCODINGS[1].assetData);
+ });
+ it('should decode ERC721', () => {
+ const assetData: ERC721AssetData = assetDataUtils.decodeERC721AssetData(KNOWN_ENCODINGS[1].assetData);
+ expect(assetData.tokenAddress).to.equal(KNOWN_ENCODINGS[1].address);
+ expect(assetData.assetProxyId).to.equal(ERC721_ASSET_PROXY_ID);
+ expect(assetData.tokenId).to.be.bignumber.equal(KNOWN_ENCODINGS[1].tokenId);
+ });
});
diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx
index ec73e4199..3d0203573 100644
--- a/packages/website/ts/components/fill_order.tsx
+++ b/packages/website/ts/components/fill_order.tsx
@@ -203,7 +203,10 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
.tokenAddress;
const makerToken = this.props.tokenByAddress[makerTokenAddress];
const makerAssetToken = {
- amount: orderMakerAmount.times(takerAssetToken.amount).div(orderTakerAmount),
+ amount: orderMakerAmount
+ .times(takerAssetToken.amount)
+ .div(orderTakerAmount)
+ .floor(),
symbol: makerToken.symbol,
};
const fillAssetToken = {
diff --git a/python-packages/order_utils/LICENSE b/python-packages/order_utils/LICENSE
new file mode 100644
index 000000000..9096fefaa
--- /dev/null
+++ b/python-packages/order_utils/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2017 ZeroEx Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License. \ No newline at end of file
diff --git a/python-packages/order_utils/README.md b/python-packages/order_utils/README.md
new file mode 100644
index 000000000..a266694db
--- /dev/null
+++ b/python-packages/order_utils/README.md
@@ -0,0 +1,45 @@
+## 0x-order-utils
+
+0x order-related utilities for those developing on top of 0x protocol.
+
+Read the [documentation](https://0x.readthedocs.io/projects/order-utils/en/latest/)
+
+## Installing
+
+```bash
+pip install 0x-order-utils
+```
+
+## Contributing
+
+We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository.
+
+Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
+
+### Install Code and Dependencies
+
+Ensure that you have Python >=3.6 installed, then:
+
+```bash
+pip install -e .[dev]
+```
+
+### Test
+
+`./setup.py test`
+
+### Clean
+
+`./setup.py clean --all`
+
+### Lint
+
+`./setup.py lint`
+
+### Build Documentation
+
+`./setup.py build_sphinx`
+
+### More
+
+See `./setup.py --help-commands` for more info.
diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py
index 1a094cfe1..22a5f4c41 100755
--- a/python-packages/order_utils/setup.py
+++ b/python-packages/order_utils/setup.py
@@ -4,7 +4,7 @@
import subprocess # nosec
from shutil import rmtree
-from os import environ, path, remove, walk
+from os import environ, path
from sys import argv
from distutils.command.clean import clean
@@ -20,13 +20,15 @@ class TestCommandExtension(TestCommand):
"""Invoke pytest."""
import pytest
- pytest.main()
+ exit(pytest.main())
# pylint: disable=too-many-ancestors
class LintCommand(distutils.command.build_py.build_py):
"""Custom setuptools command class for running linters."""
+ description = "Run linters"
+
def run(self):
"""Run linter shell commands."""
lint_commands = [
@@ -73,31 +75,64 @@ class CleanCommandExtension(clean):
def run(self):
"""Run the regular clean, followed by our custom commands."""
super().run()
- rmtree("build", ignore_errors=True)
+ rmtree("dist", ignore_errors=True)
rmtree(".mypy_cache", ignore_errors=True)
rmtree(".tox", ignore_errors=True)
rmtree(".pytest_cache", ignore_errors=True)
rmtree("src/order_utils.egg-info", ignore_errors=True)
- # delete all .pyc files
- for root, _, files in walk("."):
- for file in files:
- (_, extension) = path.splitext(file)
- if extension == ".pyc":
- remove(path.join(root, file))
+
+
+# pylint: disable=too-many-ancestors
+class TestPublishCommand(distutils.command.build_py.build_py):
+ """Custom command to publish to test.pypi.org."""
+
+ description = (
+ "Publish dist/* to test.pypi.org. Run sdist & bdist_wheel first."
+ )
+
+ def run(self):
+ """Run twine to upload to test.pypi.org."""
+ subprocess.check_call( # nosec
+ (
+ "twine upload --repository-url https://test.pypi.org/legacy/"
+ + " --verbose dist/*"
+ ).split()
+ )
+
+
+# pylint: disable=too-many-ancestors
+class PublishCommand(distutils.command.build_py.build_py):
+ """Custom command to publish to pypi.org."""
+
+ description = "Publish dist/* to pypi.org. Run sdist & bdist_wheel first."
+
+ def run(self):
+ """Run twine to upload to pypi.org."""
+ subprocess.check_call("twine upload dist/*".split()) # nosec
+
+
+with open("README.md", "r") as file_handle:
+ README_MD = file_handle.read()
setup(
- name="order_utils",
- version="1.0.0",
+ name="0x-order-utils",
+ version="0.1.0",
description="Order utilities for 0x applications",
+ long_description=README_MD,
+ long_description_content_type="text/markdown",
+ url="https://github.com/0xproject/0x-monorepo/python-packages/order_utils",
author="F. Eugene Aumson",
+ author_email="feuGeneA@users.noreply.github.com",
cmdclass={
"clean": CleanCommandExtension,
"lint": LintCommand,
"test": TestCommandExtension,
+ "test_publish": TestPublishCommand,
+ "publish": PublishCommand,
},
include_package_data=True,
- install_requires=["eth-abi", "web3"],
+ install_requires=["eth-abi", "mypy_extensions", "web3"],
extras_require={
"dev": [
"bandit",
@@ -112,6 +147,7 @@ setup(
"pytest",
"sphinx",
"tox",
+ "twine",
]
},
python_requires=">=3.6, <4",
@@ -121,9 +157,10 @@ setup(
keywords=(
"ethereum cryptocurrency 0x decentralized blockchain dex exchange"
),
- packages=["zero_ex.order_utils"],
+ namespace_packages=["zero_ex"],
+ packages=["zero_ex.order_utils", "zero_ex.dev_utils"],
classifiers=[
- "Development Status :: 1 - Planning",
+ "Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Financial and Insurance Industry",
"License :: OSI Approved :: Apache Software License",
@@ -133,7 +170,10 @@ setup(
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
+ "Topic :: Internet :: WWW/HTTP",
"Topic :: Office/Business :: Financial",
+ "Topic :: Other/Nonlisted Topic",
+ "Topic :: Security :: Cryptography",
"Topic :: Software Development :: Libraries",
"Topic :: Utilities",
],
diff --git a/python-packages/order_utils/src/conf.py b/python-packages/order_utils/src/conf.py
index e74a29d00..606cd3b2a 100644
--- a/python-packages/order_utils/src/conf.py
+++ b/python-packages/order_utils/src/conf.py
@@ -8,11 +8,11 @@ from typing import List
# pylint: disable=invalid-name
# because these variables are not named in upper case, as globals should be.
-project = "order_utils.py"
+project = "0x-order-utils"
# pylint: disable=redefined-builtin
copyright = "2018, ZeroEx, Intl."
author = "F. Eugene Aumson"
-version = "" # The short X.Y version
+version = "0.1.0" # The short X.Y version
release = "" # The full version, including alpha/beta/rc tags
extensions = [
diff --git a/python-packages/order_utils/src/doc_static/.gitkeep b/python-packages/order_utils/src/doc_static/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/python-packages/order_utils/src/doc_static/.gitkeep
diff --git a/python-packages/order_utils/src/doc_templates/.gitkeep b/python-packages/order_utils/src/doc_templates/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/python-packages/order_utils/src/doc_templates/.gitkeep
diff --git a/python-packages/order_utils/src/index.rst b/python-packages/order_utils/src/index.rst
index e09abcca2..22d5b0ef9 100644
--- a/python-packages/order_utils/src/index.rst
+++ b/python-packages/order_utils/src/index.rst
@@ -1,7 +1,7 @@
.. source for the sphinx-generated build/docs/web/index.html
-order_utils.py
-==============
+Python zero_ex.order_utils
+==========================
.. toctree::
:maxdepth: 2
@@ -15,7 +15,9 @@ order_utils.py
.. autoclass:: zero_ex.order_utils.asset_data_utils.ERC20AssetData
-See source for properties. Sphinx does not easily generate class property docs; pull requests welcome.
+.. autoclass:: zero_ex.order_utils.asset_data_utils.ERC721AssetData
+
+See source for class properties. Sphinx does not easily generate class property docs; pull requests welcome.
Indices and tables
==================
diff --git a/python-packages/order_utils/src/zero_ex/__init__.py b/python-packages/order_utils/src/zero_ex/__init__.py
index c3ed1562a..e90d833db 100644
--- a/python-packages/order_utils/src/zero_ex/__init__.py
+++ b/python-packages/order_utils/src/zero_ex/__init__.py
@@ -1 +1,2 @@
"""0x Python API."""
+__import__("pkg_resources").declare_namespace(__name__)
diff --git a/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py b/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py
index 745d014e6..a100da567 100644
--- a/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py
+++ b/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py
@@ -31,3 +31,18 @@ def assert_is_list(value: Any, name: str) -> None:
f"expected variable '{name}', with value {str(value)}, to have"
+ f" type 'list', not '{type(value).__name__}'"
)
+
+
+def assert_is_int(value: Any, name: str) -> None:
+ """If :param value: isn't of type int, raise a TypeError.
+
+ >>> try: assert_is_int('asdf', 'var')
+ ... except TypeError as type_error: print(str(type_error))
+ ...
+ expected variable 'var', with value asdf, to have type 'int', not 'str'
+ """
+ if not isinstance(value, int):
+ raise TypeError(
+ f"expected variable '{name}', with value {str(value)}, to have"
+ + f" type 'int', not '{type(value).__name__}'"
+ )
diff --git a/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py b/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py
index 451de39af..e6f9a07c1 100644
--- a/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py
+++ b/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py
@@ -5,10 +5,11 @@ from mypy_extensions import TypedDict
import eth_abi
from zero_ex.dev_utils import abi_utils
-from zero_ex.dev_utils.type_assertions import assert_is_string
+from zero_ex.dev_utils.type_assertions import assert_is_string, assert_is_int
ERC20_ASSET_DATA_BYTE_LENGTH = 36
+ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH = 53
SELECTOR_LENGTH = 10
@@ -19,6 +20,14 @@ class ERC20AssetData(TypedDict):
token_address: str
+class ERC721AssetData(TypedDict):
+ """Object interface to ERC721 asset data."""
+
+ asset_proxy_id: str
+ token_address: str
+ token_id: int
+
+
def encode_erc20_asset_data(token_address: str) -> str:
"""Encode an ERC20 token address into an asset data string.
@@ -39,7 +48,7 @@ def encode_erc20_asset_data(token_address: str) -> str:
def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData:
# docstring considered all one line by pylint: disable=line-too-long
- """Decode an ERC20 assetData hex string.
+ """Decode an ERC20 asset data hex string.
:param asset_data: String produced by prior call to encode_erc20_asset_data()
@@ -55,7 +64,7 @@ def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData:
+ f" Got {str(len(asset_data))}."
)
- asset_proxy_id: str = asset_data[0:10]
+ asset_proxy_id: str = asset_data[0:SELECTOR_LENGTH]
if asset_proxy_id != abi_utils.method_id("ERC20Token", ["address"]):
raise ValueError(
"Could not decode ERC20 Proxy Data. Expected Asset Proxy Id to be"
@@ -70,3 +79,65 @@ def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData:
)[0]
return {"asset_proxy_id": asset_proxy_id, "token_address": token_address}
+
+
+def encode_erc721_asset_data(token_address: str, token_id: int) -> str:
+ # docstring considered all one line by pylint: disable=line-too-long
+ """Encode an ERC721 asset data hex string.
+
+ :param token_address: the ERC721 token's contract address.
+ :param token_id: the identifier of the asset's instance of the token.
+ :rtype: hex encoded asset data string, usable in the makerAssetData or
+ takerAssetData fields in a 0x order.
+
+ >>> encode_erc721_asset_data('0x1dc4c1cefef38a777b15aa20260a54e584b16c48', 1)
+ '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001'
+ """ # noqa: E501 (line too long)
+ assert_is_string(token_address, "token_address")
+ assert_is_int(token_id, "token_id")
+
+ return (
+ "0x"
+ + abi_utils.simple_encode(
+ "ERC721Token(address,uint256)", token_address, token_id
+ ).hex()
+ )
+
+
+def decode_erc721_asset_data(asset_data: str) -> ERC721AssetData:
+ # docstring considered all one line by pylint: disable=line-too-long
+ """Decode an ERC721 asset data hex string.
+
+ >>> decode_erc721_asset_data('0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001')
+ {'asset_proxy_id': '0x02571792', 'token_address': '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', 'token_id': 1}
+ """ # noqa: E501 (line too long)
+ assert_is_string(asset_data, "asset_data")
+
+ if len(asset_data) < ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH:
+ raise ValueError(
+ "Could not decode ERC721 Asset Data. Expected length of encoded"
+ + f"data to be at least {ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH}. "
+ + f"Got {len(asset_data)}."
+ )
+
+ asset_proxy_id: str = asset_data[0:SELECTOR_LENGTH]
+ # prefer `black` formatting. pylint: disable=C0330
+ if asset_proxy_id != abi_utils.method_id(
+ "ERC721Token", ["address", "uint256"]
+ ):
+ raise ValueError(
+ "Could not decode ERC721 Asset Data. Expected Asset Proxy Id to be"
+ + f" ERC721 ("
+ + f"{abi_utils.method_id('ERC721Token', ['address', 'uint256'])}"
+ + f"), but got {asset_proxy_id}"
+ )
+
+ (token_address, token_id) = eth_abi.decode_abi(
+ ["address", "uint256"], bytes.fromhex(asset_data[SELECTOR_LENGTH:])
+ )
+
+ return {
+ "asset_proxy_id": asset_proxy_id,
+ "token_address": token_address,
+ "token_id": token_id,
+ }
diff --git a/python-packages/order_utils/test/test_asset_data_utils.py b/python-packages/order_utils/test/test_asset_data_utils.py
index eeada5873..079368714 100644
--- a/python-packages/order_utils/test/test_asset_data_utils.py
+++ b/python-packages/order_utils/test/test_asset_data_utils.py
@@ -3,9 +3,12 @@
import pytest
from zero_ex.order_utils.asset_data_utils import (
- encode_erc20_asset_data,
decode_erc20_asset_data,
+ decode_erc721_asset_data,
+ encode_erc20_asset_data,
+ encode_erc721_asset_data,
ERC20_ASSET_DATA_BYTE_LENGTH,
+ ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH,
)
@@ -33,3 +36,37 @@ def test_decode_erc20_asset_data_invalid_proxy_id():
decode_erc20_asset_data(
"0xffffffff" + (" " * ERC20_ASSET_DATA_BYTE_LENGTH)
)
+
+
+def test_encode_erc721_asset_data_type_error_on_token_address():
+ """Test that passing a non-string for token_address raises a TypeError."""
+ with pytest.raises(TypeError):
+ encode_erc721_asset_data(123, 123)
+
+
+def test_encode_erc721_asset_data_type_error_on_token_id():
+ """Test that passing a non-int for token_id raises a TypeError."""
+ with pytest.raises(TypeError):
+ encode_erc721_asset_data("asdf", "asdf")
+
+
+def test_decode_erc721_asset_data_type_error():
+ """Test that passing a non-string for asset_data raises a TypeError."""
+ with pytest.raises(TypeError):
+ decode_erc721_asset_data(123)
+
+
+def test_decode_erc721_asset_data_with_asset_data_too_short():
+ """Test that passing in too short of a string raises a ValueError."""
+ with pytest.raises(ValueError):
+ decode_erc721_asset_data(
+ " " * (ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH - 1)
+ )
+
+
+def test_decode_erc721_asset_data_invalid_proxy_id():
+ """Test that passing in too short of a string raises a ValueError."""
+ with pytest.raises(ValueError):
+ decode_erc721_asset_data(
+ "0xffffffff" + " " * (ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH - 1)
+ )
diff --git a/python-packages/order_utils/tox.ini b/python-packages/order_utils/tox.ini
index 1cce32b5f..ba7d55b56 100644
--- a/python-packages/order_utils/tox.ini
+++ b/python-packages/order_utils/tox.ini
@@ -10,3 +10,16 @@ envlist = py37
commands =
pip install -e .[dev]
python setup.py test
+
+[testenv:run_tests_against_test_deployment]
+commands =
+ # install dependencies from real PyPI
+ pip install eth-abi mypy_extensions web3 pytest
+ # install package-under-test from test PyPI
+ pip install --index-url https://test.pypi.org/legacy/ 0x-order-utils
+ pytest test
+
+[testenv:run_tests_against_deployment]
+commands =
+ pip install 0x-order-utils
+ pytest test
diff --git a/readthedocs.yaml b/readthedocs.yaml
new file mode 100644
index 000000000..7023141da
--- /dev/null
+++ b/readthedocs.yaml
@@ -0,0 +1,7 @@
+build:
+ image: latest
+
+python:
+ version: 3.6
+
+requirements_file: requirements.readthedocs.txt
diff --git a/requirements.readthedocs.txt b/requirements.readthedocs.txt
new file mode 100644
index 000000000..4228f6562
--- /dev/null
+++ b/requirements.readthedocs.txt
@@ -0,0 +1 @@
+./python-packages/order_utils
diff --git a/yarn.lock b/yarn.lock
index f0064bff1..1ea799433 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -484,6 +484,12 @@
dependencies:
"@babel/highlight" "^7.0.0"
+"@babel/helper-annotate-as-pure@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32"
+ dependencies:
+ "@babel/types" "^7.0.0"
+
"@babel/highlight@^7.0.0":
version "7.0.0"
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4"
@@ -504,6 +510,24 @@
dependencies:
regenerator-runtime "^0.12.0"
+"@babel/types@^7.0.0":
+ version "7.1.3"
+ resolved "https://registry.npmjs.org/@babel/types/-/types-7.1.3.tgz#3a767004567060c2f40fca49a304712c525ee37d"
+ dependencies:
+ esutils "^2.0.2"
+ lodash "^4.17.10"
+ to-fast-properties "^2.0.0"
+
+"@emotion/is-prop-valid@^0.6.8":
+ version "0.6.8"
+ resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.6.8.tgz#68ad02831da41213a2089d2cab4e8ac8b30cbd85"
+ dependencies:
+ "@emotion/memoize" "^0.6.6"
+
+"@emotion/memoize@^0.6.6":
+ version "0.6.6"
+ resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz#004b98298d04c7ca3b4f50ca2035d4f60d2eed1b"
+
"@ledgerhq/hw-app-eth@^4.3.0":
version "4.7.3"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-4.7.3.tgz#d352e19658ae296532e522c53c8ec2a1a77b64e5"
@@ -1579,6 +1603,13 @@
"@types/node" "*"
"@types/react" "*"
+"@types/styled-components@^4.0.1":
+ version "4.0.1"
+ resolved "https://registry.npmjs.org/@types/styled-components/-/styled-components-4.0.1.tgz#5eb9a5474dbde3becab2bcc8f04e0b8e8dcf8c06"
+ dependencies:
+ "@types/node" "*"
+ "@types/react" "*"
+
"@types/tmp@^0.0.33":
version "0.0.33"
resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d"
@@ -1869,10 +1900,6 @@ aes-js@^0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-0.2.4.tgz#94b881ab717286d015fa219e08fb66709dda5a3d"
-aes-js@^3.1.1:
- version "3.1.1"
- resolved "https://registry.npmjs.org/aes-js/-/aes-js-3.1.1.tgz#89fd1f94ae51b4c72d62466adc1a7323ff52f072"
-
agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
@@ -2536,6 +2563,13 @@ babel-plugin-jest-hoist@^23.2.0:
version "23.2.0"
resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz#e61fae05a1ca8801aadee57a6d66b8cefaf44167"
+"babel-plugin-styled-components@>= 1":
+ version "1.8.0"
+ resolved "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.8.0.tgz#9dd054c8e86825203449a852a5746f29f2dab857"
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.0.0"
+ lodash "^4.17.10"
+
babel-plugin-syntax-async-functions@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
@@ -3303,7 +3337,7 @@ bs-logger@0.x:
dependencies:
fast-json-stable-stringify "^2.0.0"
-bs58@=4.0.1, bs58@^4.0.0:
+bs58@=4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
dependencies:
@@ -3326,14 +3360,6 @@ bs58check@^1.0.8:
bs58 "^3.1.0"
create-hash "^1.1.0"
-bs58check@^2.1.2:
- version "2.1.2"
- resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc"
- dependencies:
- bs58 "^4.0.0"
- create-hash "^1.1.0"
- safe-buffer "^5.1.2"
-
bser@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
@@ -4593,6 +4619,14 @@ css-to-react-native@^2.0.3:
fbjs "^0.8.5"
postcss-value-parser "^3.3.0"
+css-to-react-native@^2.2.2:
+ version "2.2.2"
+ resolved "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.2.2.tgz#c077d0f7bf3e6c915a539e7325821c9dd01f9965"
+ dependencies:
+ css-color-keywords "^1.0.0"
+ fbjs "^0.8.5"
+ postcss-value-parser "^3.3.0"
+
css-vendor@^0.3.8:
version "0.3.8"
resolved "https://registry.npmjs.org/css-vendor/-/css-vendor-0.3.8.tgz#6421cfd3034ce664fe7673972fd0119fc28941fa"
@@ -5913,19 +5947,6 @@ ethereumjs-wallet@0.6.0:
utf8 "^2.1.1"
uuid "^2.0.1"
-ethereumjs-wallet@~0.6.0:
- version "0.6.2"
- resolved "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-0.6.2.tgz#67244b6af3e8113b53d709124b25477b64aeccda"
- dependencies:
- aes-js "^3.1.1"
- bs58check "^2.1.2"
- ethereumjs-util "^5.2.0"
- hdkey "^1.0.0"
- safe-buffer "^5.1.2"
- scrypt.js "^0.2.0"
- utf8 "^3.0.0"
- uuid "^3.3.2"
-
ethers@~4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65"
@@ -7455,14 +7476,6 @@ hdkey@^0.7.0, hdkey@^0.7.1:
coinstring "^2.0.0"
secp256k1 "^3.0.1"
-hdkey@^1.0.0:
- version "1.1.0"
- resolved "https://registry.npmjs.org/hdkey/-/hdkey-1.1.0.tgz#e74e7b01d2c47f797fa65d1d839adb7a44639f29"
- dependencies:
- coinstring "^2.0.0"
- safe-buffer "^5.1.1"
- secp256k1 "^3.0.1"
-
he@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
@@ -14427,19 +14440,18 @@ styled-components@^3.3.3:
stylis-rule-sheet "^0.0.10"
supports-color "^3.2.3"
-styled-components@^3.4.9:
- version "3.4.10"
- resolved "https://registry.npmjs.org/styled-components/-/styled-components-3.4.10.tgz#9a654c50ea2b516c36ade57ddcfa296bf85c96e1"
+styled-components@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.npmjs.org/styled-components/-/styled-components-4.0.2.tgz#7d4409ada019cdd34c25ba68c4577ea95dbcf0c5"
dependencies:
- buffer "^5.0.3"
- css-to-react-native "^2.0.3"
- fbjs "^0.8.16"
- hoist-non-react-statics "^2.5.0"
+ "@emotion/is-prop-valid" "^0.6.8"
+ babel-plugin-styled-components ">= 1"
+ css-to-react-native "^2.2.2"
+ memoize-one "^4.0.0"
prop-types "^15.5.4"
react-is "^16.3.1"
stylis "^3.5.0"
stylis-rule-sheet "^0.0.10"
- supports-color "^3.2.3"
stylis-rule-sheet@^0.0.10:
version "0.0.10"
@@ -14882,6 +14894,10 @@ to-fast-properties@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+to-fast-properties@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+
to-no-case@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/to-no-case/-/to-no-case-1.0.2.tgz#c722907164ef6b178132c8e69930212d1b4aa16a"
@@ -15546,10 +15562,6 @@ utf8@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.2.tgz#1fa0d9270e9be850d9b05027f63519bf46457d96"
-utf8@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1"
-
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"