From cde192df0d48bb45f57c319179dbd9f64abd13e9 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Wed, 7 Nov 2018 23:36:00 -0800 Subject: feat(instant): fetch balance at startup --- packages/instant/src/components/buy_button.tsx | 13 +++++++------ .../instant/src/components/buy_order_state_buttons.tsx | 6 ++++++ .../containers/selected_asset_buy_order_state_buttons.ts | 8 ++++++++ packages/instant/src/redux/actions.ts | 2 ++ packages/instant/src/redux/async_data.ts | 16 ++++++++++++++++ packages/instant/src/redux/reducer.ts | 15 ++++++++++++++- packages/instant/src/util/balance.ts | 13 ------------- 7 files changed, 53 insertions(+), 20 deletions(-) delete mode 100644 packages/instant/src/util/balance.ts diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx index f24bb57ee..9a6e22ea9 100644 --- a/packages/instant/src/components/buy_button.tsx +++ b/packages/instant/src/components/buy_button.tsx @@ -1,4 +1,5 @@ import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer'; +import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; import * as React from 'react'; @@ -7,8 +8,6 @@ import { oc } from 'ts-optchain'; import { WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX } from '../constants'; import { ColorOption } from '../style/theme'; import { AffiliateInfo, ZeroExInstantError } from '../types'; -import { getBestAddress } from '../util/address'; -import { balanceUtil } from '../util/balance'; import { gasPriceEstimator } from '../util/gas_price_estimator'; import { util } from '../util/util'; @@ -17,8 +16,10 @@ import { Text } from './ui/text'; export interface BuyButtonProps { accountAddress?: string; + accountEthBalanceInWei?: BigNumber; buyQuote?: BuyQuote; assetBuyer: AssetBuyer; + web3Wrapper: Web3Wrapper; affiliateInfo?: AffiliateInfo; onValidationPending: (buyQuote: BuyQuote) => void; onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void; @@ -47,14 +48,14 @@ export class BuyButton extends React.Component { } private readonly _handleClick = async () => { // The button is disabled when there is no buy quote anyway. - const { buyQuote, assetBuyer, affiliateInfo, accountAddress } = this.props; + const { buyQuote, assetBuyer, affiliateInfo, accountAddress, accountEthBalanceInWei, web3Wrapper } = this.props; if (_.isUndefined(buyQuote) || _.isUndefined(accountAddress)) { return; } this.props.onValidationPending(buyQuote); - // TODO(bmillman): move balance fetching to the async state and get rid of web3 wrapper here - const web3Wrapper = new Web3Wrapper(assetBuyer.provider); - const hasSufficientEth = await balanceUtil.hasSufficientEth(accountAddress, buyQuote, web3Wrapper); + const ethNeededForBuy = buyQuote.worstCaseQuoteInfo.totalEthAmount; + // if we don't have a balance for the user, let the transaction through, it will be handled by the wallet + const hasSufficientEth = _.isUndefined(accountEthBalanceInWei) || accountEthBalanceInWei.gte(ethNeededForBuy); if (!hasSufficientEth) { this.props.onValidationFail(buyQuote, ZeroExInstantError.InsufficientETH); return; diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx index b15415e71..b46ff7714 100644 --- a/packages/instant/src/components/buy_order_state_buttons.tsx +++ b/packages/instant/src/components/buy_order_state_buttons.tsx @@ -1,4 +1,6 @@ import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import * as React from 'react'; import { ColorOption } from '../style/theme'; @@ -14,9 +16,11 @@ import { Text } from './ui/text'; export interface BuyOrderStateButtonProps { accountAddress?: string; + accountEthBalanceInWei?: BigNumber; buyQuote?: BuyQuote; buyOrderProcessingState: OrderProcessState; assetBuyer: AssetBuyer; + web3Wrapper: Web3Wrapper; affiliateInfo?: AffiliateInfo; onViewTransaction: () => void; onValidationPending: (buyQuote: BuyQuote) => void; @@ -54,8 +58,10 @@ export const BuyOrderStateButtons: React.StatelessComponent void; } @@ -32,12 +36,16 @@ interface ConnectedDispatch { export interface SelectedAssetBuyOrderStateButtons {} const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButtons): ConnectedState => { const assetBuyer = state.providerState.assetBuyer; + const web3Wrapper = state.providerState.web3Wrapper; const account = state.providerState.account; const accountAddress = account.state === AccountState.Ready ? account.address : undefined; + const accountEthBalanceInWei = account.state === AccountState.Ready ? account.ethBalanceInWei : undefined; return { accountAddress, + accountEthBalanceInWei, buyOrderProcessingState: state.buyOrderState.processState, assetBuyer, + web3Wrapper, buyQuote: state.latestBuyQuote, affiliateInfo: state.affiliateInfo, onViewTransaction: () => { diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts index 084360629..9b0d80152 100644 --- a/packages/instant/src/redux/actions.ts +++ b/packages/instant/src/redux/actions.ts @@ -25,6 +25,7 @@ export enum ActionTypes { SET_ACCOUNT_STATE_LOCKED = 'SET_ACCOUNT_STATE_LOCKED', SET_ACCOUNT_STATE_ERROR = 'SET_ACCOUNT_STATE_ERROR', SET_ACCOUNT_STATE_READY = 'SET_ACCOUNT_STATE_READY', + UPDATE_ACCOUNT_ETH_BALANCE = 'UPDATE_ACCOUNT_ETH_BALANCE', UPDATE_ETH_USD_PRICE = 'UPDATE_ETH_USD_PRICE', UPDATE_SELECTED_ASSET_AMOUNT = 'UPDATE_SELECTED_ASSET_AMOUNT', SET_BUY_ORDER_STATE_NONE = 'SET_BUY_ORDER_STATE_NONE', @@ -48,6 +49,7 @@ export const actions = { setAccountStateLocked: () => createAction(ActionTypes.SET_ACCOUNT_STATE_LOCKED), setAccountStateError: () => createAction(ActionTypes.SET_ACCOUNT_STATE_ERROR), setAccountStateReady: (address: string) => createAction(ActionTypes.SET_ACCOUNT_STATE_READY, address), + updateAccountEthBalance: (balance: BigNumber) => createAction(ActionTypes.UPDATE_ACCOUNT_ETH_BALANCE, balance), updateEthUsdPrice: (price?: BigNumber) => createAction(ActionTypes.UPDATE_ETH_USD_PRICE, price), updateSelectedAssetAmount: (amount?: BigNumber) => createAction(ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, amount), setBuyOrderStateNone: () => createAction(ActionTypes.SET_BUY_ORDER_STATE_NONE), diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index e27ec1dc3..862f60e78 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -50,8 +50,24 @@ export const asyncData = { if (!_.isEmpty(availableAddresses)) { const activeAddress = availableAddresses[0]; store.dispatch(actions.setAccountStateReady(activeAddress)); + await asyncData.fetchAccountBalanceAndDispatchToStore(store); } else { store.dispatch(actions.setAccountStateLocked()); } }, + fetchAccountBalanceAndDispatchToStore: async (store: Store) => { + const { providerState } = store.getState(); + const web3Wrapper = providerState.web3Wrapper; + const account = providerState.account; + if (account.state !== AccountState.Ready) { + return; + } + try { + const ethBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(account.address); + store.dispatch(actions.updateAccountEthBalance(ethBalanceInWei)); + } catch (e) { + // leave balance as is + return; + } + }, }; diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index 9bbd114a2..961e29619 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -67,12 +67,25 @@ export const createReducer = (initialState: State) => { return reduceStateWithAccount(state, LOCKED_ACCOUNT); case ActionTypes.SET_ACCOUNT_STATE_ERROR: return reduceStateWithAccount(state, ERROR_ACCOUNT); - case ActionTypes.SET_ACCOUNT_STATE_READY: + case ActionTypes.SET_ACCOUNT_STATE_READY: { const account: AccountReady = { state: AccountState.Ready, address: action.data, }; return reduceStateWithAccount(state, account); + } + case ActionTypes.UPDATE_ACCOUNT_ETH_BALANCE: { + const account = state.providerState.account; + if (account.state !== AccountState.Ready) { + return state; + } else { + const newAccount: AccountReady = { + ...account, + ethBalanceInWei: action.data, + }; + return reduceStateWithAccount(state, newAccount); + } + } case ActionTypes.UPDATE_ETH_USD_PRICE: return { ...state, diff --git a/packages/instant/src/util/balance.ts b/packages/instant/src/util/balance.ts deleted file mode 100644 index f2271495b..000000000 --- a/packages/instant/src/util/balance.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { BuyQuote } from '@0x/asset-buyer'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as _ from 'lodash'; - -export const balanceUtil = { - hasSufficientEth: async (takerAddress: string | undefined, buyQuote: BuyQuote, web3Wrapper: Web3Wrapper) => { - if (_.isUndefined(takerAddress)) { - return false; - } - const balanceWei = await web3Wrapper.getBalanceInWeiAsync(takerAddress); - return balanceWei.gte(buyQuote.worstCaseQuoteInfo.totalEthAmount); - }, -}; -- cgit v1.2.3