diff options
author | Steve Klebanoff <steve@0xproject.com> | 2018-10-30 01:12:44 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-30 01:12:44 +0800 |
commit | 4e4291eccdd6c837bbec70603aa6eb64d3aa8d85 (patch) | |
tree | edf8006576143d5cd1c32b052164ac898c57c8b2 /packages | |
parent | ae6202ed3d777605a3fd02cd29141a3ba40f4b34 (diff) | |
parent | 7ed44f7b2f0224b4169f0a8f34bae09e8c6b986f (diff) | |
download | dexon-0x-contracts-4e4291eccdd6c837bbec70603aa6eb64d3aa8d85.tar.gz dexon-0x-contracts-4e4291eccdd6c837bbec70603aa6eb64d3aa8d85.tar.zst dexon-0x-contracts-4e4291eccdd6c837bbec70603aa6eb64d3aa8d85.zip |
Merge pull request #1193 from 0xProject/feature/not-enough-eth
[instant] Not enough ETH error message
Diffstat (limited to 'packages')
-rw-r--r-- | packages/instant/src/components/buy_button.tsx | 29 | ||||
-rw-r--r-- | packages/instant/src/components/buy_order_state_buttons.tsx | 12 | ||||
-rw-r--r-- | packages/instant/src/constants.ts | 2 | ||||
-rw-r--r-- | packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts | 18 | ||||
-rw-r--r-- | packages/instant/src/containers/selected_erc20_asset_amount_input.ts | 3 | ||||
-rw-r--r-- | packages/instant/src/types.ts | 5 | ||||
-rw-r--r-- | packages/instant/src/util/address.ts | 6 | ||||
-rw-r--r-- | packages/instant/src/util/balance.ts | 13 | ||||
-rw-r--r-- | packages/instant/src/util/error.ts | 5 | ||||
-rw-r--r-- | packages/instant/src/util/format.ts | 6 | ||||
-rw-r--r-- | packages/instant/test/util/format.test.ts | 8 |
11 files changed, 77 insertions, 30 deletions
diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx index a70269dde..bcd435250 100644 --- a/packages/instant/src/components/buy_button.tsx +++ b/packages/instant/src/components/buy_button.tsx @@ -4,6 +4,9 @@ import * as React from 'react'; import { WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX } from '../constants'; import { ColorOption } from '../style/theme'; +import { ZeroExInstantError } from '../types'; +import { getBestAddress } from '../util/address'; +import { balanceUtil } from '../util/balance'; import { util } from '../util/util'; import { web3Wrapper } from '../util/web3_wrapper'; @@ -12,7 +15,8 @@ import { Button, Text } from './ui'; export interface BuyButtonProps { buyQuote?: BuyQuote; assetBuyer?: AssetBuyer; - onAwaitingSignature: (buyQuote: BuyQuote) => void; + onValidationPending: (buyQuote: BuyQuote) => void; + onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void; onSignatureDenied: (buyQuote: BuyQuote, preventedError: Error) => void; onBuyProcessing: (buyQuote: BuyQuote, txHash: string) => void; onBuySuccess: (buyQuote: BuyQuote, txHash: string) => void; @@ -42,14 +46,27 @@ export class BuyButton extends React.Component<BuyButtonProps> { return; } + this.props.onValidationPending(buyQuote); + const takerAddress = await getBestAddress(); + + const hasSufficentEth = await balanceUtil.hasSufficentEth(takerAddress, buyQuote, web3Wrapper); + if (!hasSufficentEth) { + this.props.onValidationFail(buyQuote, ZeroExInstantError.InsufficientETH); + return; + } + let txHash: string | undefined; - this.props.onAwaitingSignature(buyQuote); try { - txHash = await assetBuyer.executeBuyQuoteAsync(buyQuote); + txHash = await assetBuyer.executeBuyQuoteAsync(buyQuote, { takerAddress }); } catch (e) { - if (e instanceof Error && e.message === AssetBuyerError.SignatureRequestDenied) { - this.props.onSignatureDenied(buyQuote, e); - return; + if (e instanceof Error) { + if (e.message === AssetBuyerError.SignatureRequestDenied) { + this.props.onSignatureDenied(buyQuote, e); + return; + } else if (e.message === AssetBuyerError.TransactionValueTooLow) { + this.props.onValidationFail(buyQuote, AssetBuyerError.TransactionValueTooLow); + return; + } } throw e; } diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx index b9e92e763..7c06ff31b 100644 --- a/packages/instant/src/components/buy_order_state_buttons.tsx +++ b/packages/instant/src/components/buy_order_state_buttons.tsx @@ -1,4 +1,4 @@ -import { AssetBuyer, BuyQuote } from '@0x/asset-buyer'; +import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer'; import * as React from 'react'; import { BuyButton } from '../components/buy_button'; @@ -7,7 +7,7 @@ import { Flex } from '../components/ui/flex'; import { PlacingOrderButton } from '../components/placing_order_button'; import { ColorOption } from '../style/theme'; -import { OrderProcessState } from '../types'; +import { OrderProcessState, ZeroExInstantError } from '../types'; import { Button } from './ui/button'; import { Text } from './ui/text'; @@ -17,7 +17,8 @@ export interface BuyOrderStateButtonProps { buyOrderProcessingState: OrderProcessState; assetBuyer?: AssetBuyer; onViewTransaction: () => void; - onAwaitingSignature: (buyQuote: BuyQuote) => void; + onValidationPending: (buyQuote: BuyQuote) => void; + onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void; onSignatureDenied: (buyQuote: BuyQuote, error: Error) => void; onBuyProcessing: (buyQuote: BuyQuote, txHash: string) => void; onBuySuccess: (buyQuote: BuyQuote, txHash: string) => void; @@ -45,7 +46,7 @@ export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonP props.buyOrderProcessingState === OrderProcessState.PROCESSING ) { return <SecondaryButton onClick={props.onViewTransaction}>View Transaction</SecondaryButton>; - } else if (props.buyOrderProcessingState === OrderProcessState.AWAITING_SIGNATURE) { + } else if (props.buyOrderProcessingState === OrderProcessState.VALIDATING) { return <PlacingOrderButton />; } @@ -53,7 +54,8 @@ export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonP <BuyButton buyQuote={props.buyQuote} assetBuyer={props.assetBuyer} - onAwaitingSignature={props.onAwaitingSignature} + onValidationPending={props.onValidationPending} + onValidationFail={props.onValidationFail} onSignatureDenied={props.onSignatureDenied} onBuyProcessing={props.onBuyProcessing} onBuySuccess={props.onBuySuccess} diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 48d0d4aa2..e4281d54a 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -1,5 +1,5 @@ import { BigNumber } from '@0x/utils'; export const BIG_NUMBER_ZERO = new BigNumber(0); -export const ethDecimals = 18; +export const ETH_DECIMALS = 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_order_state_buttons.ts b/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts index 8927b8954..241c0192c 100644 --- a/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts +++ b/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts @@ -1,4 +1,4 @@ -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 { connect } from 'react-redux'; @@ -6,10 +6,11 @@ import { Dispatch } from 'redux'; import { Action, actions } from '../redux/actions'; import { State } from '../redux/reducer'; -import { OrderProcessState, OrderState } from '../types'; +import { OrderProcessState, OrderState, ZeroExInstantError } from '../types'; import { etherscanUtil } from '../util/etherscan'; import { BuyOrderStateButtons } from '../components/buy_order_state_buttons'; +import { errorUtil } from '../util/error'; interface ConnectedState { buyQuote?: BuyQuote; @@ -19,12 +20,13 @@ interface ConnectedState { } interface ConnectedDispatch { - onAwaitingSignature: (buyQuote: BuyQuote) => void; + onValidationPending: (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; onRetry: () => void; + onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void; } export interface SelectedAssetBuyOrderStateButtons {} const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButtons): ConnectedState => ({ @@ -54,8 +56,8 @@ const mapDispatchToProps = ( dispatch: Dispatch<Action>, ownProps: SelectedAssetBuyOrderStateButtons, ): ConnectedDispatch => ({ - onAwaitingSignature: (buyQuote: BuyQuote) => { - const newOrderState: OrderState = { processState: OrderProcessState.AWAITING_SIGNATURE }; + onValidationPending: (buyQuote: BuyQuote) => { + const newOrderState: OrderState = { processState: OrderProcessState.VALIDATING }; dispatch(actions.updateBuyOrderState(newOrderState)); }, onBuyProcessing: (buyQuote: BuyQuote, txHash: string) => { @@ -68,7 +70,11 @@ const mapDispatchToProps = ( dispatch(actions.updateBuyOrderState({ processState: OrderProcessState.FAILURE, txHash })), onSignatureDenied: (buyQuote, error) => { dispatch(actions.resetAmount()); - dispatch(actions.setError(error)); + errorUtil.errorFlasher.flashNewError(dispatch, error); + }, + onValidationFail: (buyQuote, error) => { + dispatch(actions.updateBuyOrderState({ processState: OrderProcessState.NONE })); + errorUtil.errorFlasher.flashNewError(dispatch, new Error(error)); }, onRetry: () => { dispatch(actions.resetAmount()); diff --git a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts index 3d741ae97..41ed974a2 100644 --- a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts +++ b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts @@ -7,6 +7,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; +import { ERC20AssetAmountInput } from '../components/erc20_asset_amount_input'; import { Action, actions } from '../redux/actions'; import { State } from '../redux/reducer'; import { ColorOption } from '../style/theme'; @@ -14,8 +15,6 @@ import { ERC20Asset, OrderProcessState } from '../types'; import { BigNumberInput } from '../util/big_number_input'; import { errorUtil } from '../util/error'; -import { ERC20AssetAmountInput } from '../components/erc20_asset_amount_input'; - export interface SelectedERC20AssetAmountInputProps { fontColor?: ColorOption; startingFontSizePx: number; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index c63371fb4..c02b66990 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -10,14 +10,14 @@ export enum AsyncProcessState { export enum OrderProcessState { NONE = 'None', - AWAITING_SIGNATURE = 'Awaiting Signature', + VALIDATING = 'Validating', PROCESSING = 'Processing', SUCCESS = 'Success', FAILURE = 'Failure', } interface OrderStatePreTx { - processState: OrderProcessState.NONE | OrderProcessState.AWAITING_SIGNATURE; + processState: OrderProcessState.NONE | OrderProcessState.VALIDATING; } interface OrderStatePostTx { processState: OrderProcessState.PROCESSING | OrderProcessState.SUCCESS | OrderProcessState.FAILURE; @@ -72,4 +72,5 @@ export enum Network { export enum ZeroExInstantError { AssetMetaDataNotAvailable = 'ASSET_META_DATA_NOT_AVAILABLE', + InsufficientETH = 'INSUFFICIENT_ETH', } diff --git a/packages/instant/src/util/address.ts b/packages/instant/src/util/address.ts new file mode 100644 index 000000000..14d42d8c0 --- /dev/null +++ b/packages/instant/src/util/address.ts @@ -0,0 +1,6 @@ +import { web3Wrapper } from '../util/web3_wrapper'; + +export const getBestAddress = async (): Promise<string | undefined> => { + const addresses = await web3Wrapper.getAvailableAddressesAsync(); + return addresses[0]; +}; diff --git a/packages/instant/src/util/balance.ts b/packages/instant/src/util/balance.ts new file mode 100644 index 000000000..533656858 --- /dev/null +++ b/packages/instant/src/util/balance.ts @@ -0,0 +1,13 @@ +import { BuyQuote } from '@0x/asset-buyer'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as _ from 'lodash'; + +export const balanceUtil = { + hasSufficentEth: async (takerAddress: string | undefined, buyQuote: BuyQuote, web3Wrapper: Web3Wrapper) => { + if (_.isUndefined(takerAddress)) { + return false; + } + const balanceWei = await web3Wrapper.getBalanceInWeiAsync(takerAddress); + return balanceWei >= buyQuote.worstCaseQuoteInfo.totalEthAmount; + }, +}; diff --git a/packages/instant/src/util/error.ts b/packages/instant/src/util/error.ts index 64c1f4885..39c563c75 100644 --- a/packages/instant/src/util/error.ts +++ b/packages/instant/src/util/error.ts @@ -2,7 +2,7 @@ import { AssetBuyerError } from '@0x/asset-buyer'; import { Dispatch } from 'redux'; import { Action, actions } from '../redux/actions'; -import { Asset } from '../types'; +import { Asset, ZeroExInstantError } from '../types'; import { assetUtils } from './asset'; @@ -49,6 +49,9 @@ const humanReadableMessageForError = (error: Error, asset?: Asset): string | und if (error.message === AssetBuyerError.SignatureRequestDenied) { return 'You denied this transaction'; } + if (error.message === ZeroExInstantError.InsufficientETH) { + return "You don't have enough ETH"; + } return undefined; }; diff --git a/packages/instant/src/util/format.ts b/packages/instant/src/util/format.ts index ca7c01359..4a48dec9d 100644 --- a/packages/instant/src/util/format.ts +++ b/packages/instant/src/util/format.ts @@ -2,7 +2,7 @@ import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; -import { ethDecimals } from '../constants'; +import { ETH_DECIMALS } from '../constants'; export const format = { ethBaseAmount: ( @@ -13,7 +13,7 @@ export const format = { if (_.isUndefined(ethBaseAmount)) { return defaultText; } - const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ethDecimals); + const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ETH_DECIMALS); return format.ethUnitAmount(ethUnitAmount, decimalPlaces); }, ethUnitAmount: ( @@ -36,7 +36,7 @@ export const format = { if (_.isUndefined(ethBaseAmount) || _.isUndefined(ethUsdPrice)) { return defaultText; } - const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ethDecimals); + const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ETH_DECIMALS); return format.ethUnitAmountInUsd(ethUnitAmount, ethUsdPrice, decimalPlaces); }, ethUnitAmountInUsd: ( diff --git a/packages/instant/test/util/format.test.ts b/packages/instant/test/util/format.test.ts index 2c9294c78..c346b7604 100644 --- a/packages/instant/test/util/format.test.ts +++ b/packages/instant/test/util/format.test.ts @@ -1,15 +1,15 @@ import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; -import { ethDecimals } from '../../src/constants'; +import { ETH_DECIMALS } from '../../src/constants'; import { format } from '../../src/util/format'; const BIG_NUMBER_ONE = new BigNumber(1); const BIG_NUMBER_DECIMAL = new BigNumber(0.432414); const BIG_NUMBER_IRRATIONAL = new BigNumber(5.3014059295032); -const ONE_ETH_IN_BASE_UNITS = Web3Wrapper.toBaseUnitAmount(BIG_NUMBER_ONE, ethDecimals); -const DECIMAL_ETH_IN_BASE_UNITS = Web3Wrapper.toBaseUnitAmount(BIG_NUMBER_DECIMAL, ethDecimals); -const IRRATIONAL_ETH_IN_BASE_UNITS = Web3Wrapper.toBaseUnitAmount(BIG_NUMBER_IRRATIONAL, ethDecimals); +const ONE_ETH_IN_BASE_UNITS = Web3Wrapper.toBaseUnitAmount(BIG_NUMBER_ONE, ETH_DECIMALS); +const DECIMAL_ETH_IN_BASE_UNITS = Web3Wrapper.toBaseUnitAmount(BIG_NUMBER_DECIMAL, ETH_DECIMALS); +const IRRATIONAL_ETH_IN_BASE_UNITS = Web3Wrapper.toBaseUnitAmount(BIG_NUMBER_IRRATIONAL, ETH_DECIMALS); const BIG_NUMBER_FAKE_ETH_USD_PRICE = new BigNumber(2.534); describe('format', () => { |