diff options
author | Brandon Millman <brandon.millman@gmail.com> | 2018-11-08 10:02:24 +0800 |
---|---|---|
committer | Brandon Millman <brandon.millman@gmail.com> | 2018-11-08 15:41:25 +0800 |
commit | d0c009adff53d94414cf51028eff490e0452a3c9 (patch) | |
tree | 82a76e2499883d9a8a579b64b6503ef108d7706c | |
parent | f6abc007ffb249e4bbf85b8a7a77309d43e0a147 (diff) | |
download | dexon-0x-contracts-d0c009adff53d94414cf51028eff490e0452a3c9.tar.gz dexon-0x-contracts-d0c009adff53d94414cf51028eff490e0452a3c9.tar.zst dexon-0x-contracts-d0c009adff53d94414cf51028eff490e0452a3c9.zip |
feat(instant): fetch account address at startup and drive account state changes
-rw-r--r-- | packages/instant/src/components/buy_button.tsx | 21 | ||||
-rw-r--r-- | packages/instant/src/components/buy_order_state_buttons.tsx | 2 | ||||
-rw-r--r-- | packages/instant/src/components/zero_ex_instant_provider.tsx | 4 | ||||
-rw-r--r-- | packages/instant/src/constants.ts | 14 | ||||
-rw-r--r-- | packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts | 6 | ||||
-rw-r--r-- | packages/instant/src/redux/actions.ts | 8 | ||||
-rw-r--r-- | packages/instant/src/redux/async_data.ts | 21 | ||||
-rw-r--r-- | packages/instant/src/redux/reducer.ts | 29 | ||||
-rw-r--r-- | packages/instant/src/util/provider_state_factory.ts | 8 |
9 files changed, 88 insertions, 25 deletions
diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx index 515cd18e9..f24bb57ee 100644 --- a/packages/instant/src/components/buy_button.tsx +++ b/packages/instant/src/components/buy_button.tsx @@ -16,6 +16,7 @@ import { Button } from './ui/button'; import { Text } from './ui/text'; export interface BuyButtonProps { + accountAddress?: string; buyQuote?: BuyQuote; assetBuyer: AssetBuyer; affiliateInfo?: AffiliateInfo; @@ -34,7 +35,8 @@ export class BuyButton extends React.Component<BuyButtonProps> { onBuyFailure: util.boundNoop, }; public render(): React.ReactNode { - const shouldDisableButton = _.isUndefined(this.props.buyQuote); + const { buyQuote, accountAddress } = this.props; + const shouldDisableButton = _.isUndefined(buyQuote) || _.isUndefined(accountAddress); return ( <Button width="100%" onClick={this._handleClick} isDisabled={shouldDisableButton}> <Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px"> @@ -45,30 +47,25 @@ export class BuyButton extends React.Component<BuyButtonProps> { } private readonly _handleClick = async () => { // The button is disabled when there is no buy quote anyway. - const { buyQuote, assetBuyer, affiliateInfo } = this.props; - if (_.isUndefined(buyQuote)) { + const { buyQuote, assetBuyer, affiliateInfo, accountAddress } = this.props; + if (_.isUndefined(buyQuote) || _.isUndefined(accountAddress)) { return; } - this.props.onValidationPending(buyQuote); - - // TODO(bmillman): move address and balance fetching to the async state + // TODO(bmillman): move balance fetching to the async state and get rid of web3 wrapper here const web3Wrapper = new Web3Wrapper(assetBuyer.provider); - const takerAddress = await getBestAddress(web3Wrapper); - - const hasSufficientEth = await balanceUtil.hasSufficientEth(takerAddress, buyQuote, web3Wrapper); + const hasSufficientEth = await balanceUtil.hasSufficientEth(accountAddress, buyQuote, web3Wrapper); if (!hasSufficientEth) { this.props.onValidationFail(buyQuote, ZeroExInstantError.InsufficientETH); return; } - let txHash: string | undefined; const gasInfo = await gasPriceEstimator.getGasInfoAsync(); const feeRecipient = oc(affiliateInfo).feeRecipient(); try { txHash = await assetBuyer.executeBuyQuoteAsync(buyQuote, { feeRecipient, - takerAddress, + takerAddress: accountAddress, gasPrice: gasInfo.gasPriceInWei, }); } catch (e) { @@ -83,7 +80,6 @@ export class BuyButton extends React.Component<BuyButtonProps> { } throw e; } - const startTimeUnix = new Date().getTime(); const expectedEndTimeUnix = startTimeUnix + gasInfo.estimatedTimeMs; this.props.onBuyProcessing(buyQuote, txHash, startTimeUnix, expectedEndTimeUnix); @@ -96,7 +92,6 @@ export class BuyButton extends React.Component<BuyButtonProps> { } throw e; } - this.props.onBuySuccess(buyQuote, txHash); }; } diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx index 3d0ede7b1..b15415e71 100644 --- a/packages/instant/src/components/buy_order_state_buttons.tsx +++ b/packages/instant/src/components/buy_order_state_buttons.tsx @@ -13,6 +13,7 @@ import { Flex } from './ui/flex'; import { Text } from './ui/text'; export interface BuyOrderStateButtonProps { + accountAddress?: string; buyQuote?: BuyQuote; buyOrderProcessingState: OrderProcessState; assetBuyer: AssetBuyer; @@ -52,6 +53,7 @@ export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonP return ( <BuyButton + accountAddress={props.accountAddress} buyQuote={props.buyQuote} assetBuyer={props.assetBuyer} affiliateInfo={props.affiliateInfo} diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 1fb5cf64f..fa0588b71 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -92,12 +92,12 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider // tslint:disable-next-line:no-floating-promises asyncData.fetchAvailableAssetDatasAndDispatchToStore(this._store); } - + // tslint:disable-next-line:no-floating-promises + asyncData.fetchAccountInfoAndDispatchToStore(this._store); // warm up the gas price estimator cache just in case we can't // grab the gas price estimate when submitting the transaction // tslint:disable-next-line:no-floating-promises gasPriceEstimator.getGasInfoAsync(); - // tslint:disable-next-line:no-floating-promises this._flashErrorIfWrongNetwork(); } diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 00521b56e..705313ce7 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -1,6 +1,6 @@ import { BigNumber } from '@0x/utils'; -import { Network } from './types'; +import { AccountNotReady, AccountState, Network } from './types'; export const BIG_NUMBER_ZERO = new BigNumber(0); export const ETH_DECIMALS = 18; @@ -21,3 +21,15 @@ export const ETHEREUM_NODE_URL_BY_NETWORK = { [Network.Kovan]: 'https://kovan.infura.io/', }; export const BLOCK_POLLING_INTERVAL_MS = 10000; // 10s +export const NO_ACCOUNT: AccountNotReady = { + state: AccountState.None, +}; +export const LOADING_ACCOUNT: AccountNotReady = { + state: AccountState.Loading, +}; +export const LOCKED_ACCOUNT: AccountNotReady = { + state: AccountState.Locked, +}; +export const ERROR_ACCOUNT: AccountNotReady = { + state: AccountState.Error, +}; 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 c3a5e88b9..2678d4a36 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 @@ -7,11 +7,12 @@ import { Dispatch } from 'redux'; import { BuyOrderStateButtons } from '../components/buy_order_state_buttons'; import { Action, actions } from '../redux/actions'; import { State } from '../redux/reducer'; -import { AffiliateInfo, OrderProcessState, ZeroExInstantError } from '../types'; +import { AccountState, AffiliateInfo, OrderProcessState, ZeroExInstantError } from '../types'; import { errorFlasher } from '../util/error_flasher'; import { etherscanUtil } from '../util/etherscan'; interface ConnectedState { + accountAddress?: string; buyQuote?: BuyQuote; buyOrderProcessingState: OrderProcessState; assetBuyer: AssetBuyer; @@ -31,7 +32,10 @@ interface ConnectedDispatch { export interface SelectedAssetBuyOrderStateButtons {} const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButtons): ConnectedState => { const assetBuyer = state.providerState.assetBuyer; + const account = state.providerState.account; + const accountAddress = account.state === AccountState.Ready ? account.address : undefined; return { + accountAddress, buyOrderProcessingState: state.buyOrderState.processState, assetBuyer, buyQuote: state.latestBuyQuote, diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts index c41c5054b..084360629 100644 --- a/packages/instant/src/redux/actions.ts +++ b/packages/instant/src/redux/actions.ts @@ -21,6 +21,10 @@ function createAction<T extends string, P>(type: T, data?: P): PlainAction<T> | } export enum ActionTypes { + SET_ACCOUNT_STATE_LOADING = 'SET_ACCOUNT_STATE_LOADING', + 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_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', @@ -40,6 +44,10 @@ export enum ActionTypes { } export const actions = { + setAccountStateLoading: () => createAction(ActionTypes.SET_ACCOUNT_STATE_LOADING), + setAccountStateLocked: () => createAction(ActionTypes.SET_ACCOUNT_STATE_LOCKED), + setAccountStateError: () => createAction(ActionTypes.SET_ACCOUNT_STATE_ERROR), + setAccountStateReady: (address: string) => createAction(ActionTypes.SET_ACCOUNT_STATE_READY, address), 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 c3d190af2..e27ec1dc3 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -1,6 +1,7 @@ import * as _ from 'lodash'; import { BIG_NUMBER_ZERO } from '../constants'; +import { AccountState } from '../types'; import { assetUtils } from '../util/asset'; import { coinbaseApi } from '../util/coinbase_api'; import { errorFlasher } from '../util/error_flasher'; @@ -33,4 +34,24 @@ export const asyncData = { store.dispatch(actions.setAvailableAssets([])); } }, + fetchAccountInfoAndDispatchToStore: async (store: Store) => { + const { providerState } = store.getState(); + const web3Wrapper = providerState.web3Wrapper; + if (providerState.account.state !== AccountState.Loading) { + store.dispatch(actions.setAccountStateLoading()); + } + let availableAddresses: string[]; + try { + availableAddresses = await web3Wrapper.getAvailableAddressesAsync(); + } catch (e) { + store.dispatch(actions.setAccountStateError()); + return; + } + if (!_.isEmpty(availableAddresses)) { + const activeAddress = availableAddresses[0]; + store.dispatch(actions.setAccountStateReady(activeAddress)); + } else { + store.dispatch(actions.setAccountStateLocked()); + } + }, }; diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index 4a939839a..9bbd114a2 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -4,8 +4,12 @@ import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; +import { ERROR_ACCOUNT, LOADING_ACCOUNT, LOCKED_ACCOUNT } from '../constants'; import { assetMetaDataMap } from '../data/asset_meta_data_map'; import { + Account, + AccountReady, + AccountState, AffiliateInfo, Asset, AssetMetaData, @@ -57,6 +61,18 @@ export const DEFAULT_STATE: DefaultState = { export const createReducer = (initialState: State) => { const reducer = (state: State = initialState, action: Action): State => { switch (action.type) { + case ActionTypes.SET_ACCOUNT_STATE_LOADING: + return reduceStateWithAccount(state, LOADING_ACCOUNT); + case ActionTypes.SET_ACCOUNT_STATE_LOCKED: + return reduceStateWithAccount(state, LOCKED_ACCOUNT); + case ActionTypes.SET_ACCOUNT_STATE_ERROR: + return reduceStateWithAccount(state, ERROR_ACCOUNT); + case ActionTypes.SET_ACCOUNT_STATE_READY: + const account: AccountReady = { + state: AccountState.Ready, + address: action.data, + }; + return reduceStateWithAccount(state, account); case ActionTypes.UPDATE_ETH_USD_PRICE: return { ...state, @@ -80,7 +96,6 @@ export const createReducer = (initialState: State) => { } else { return state; } - case ActionTypes.SET_QUOTE_REQUEST_STATE_PENDING: return { ...state, @@ -191,6 +206,18 @@ export const createReducer = (initialState: State) => { return reducer; }; +const reduceStateWithAccount = (state: State, account: Account) => { + const oldProviderState = state.providerState; + const newProviderState: ProviderState = { + ...oldProviderState, + account, + }; + return { + ...state, + providerState: newProviderState, + }; +}; + const doesBuyQuoteMatchState = (buyQuote: BuyQuote, state: State): boolean => { const selectedAssetIfExists = state.selectedAsset; const selectedAssetAmountIfExists = state.selectedAssetAmount; diff --git a/packages/instant/src/util/provider_state_factory.ts b/packages/instant/src/util/provider_state_factory.ts index 18b188d89..3006c5e14 100644 --- a/packages/instant/src/util/provider_state_factory.ts +++ b/packages/instant/src/util/provider_state_factory.ts @@ -2,18 +2,12 @@ import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; +import { LOADING_ACCOUNT, NO_ACCOUNT } from '../constants'; import { AccountNotReady, AccountState, Maybe, Network, OrderSource, ProviderState } from '../types'; import { assetBuyerFactory } from './asset_buyer_factory'; import { providerFactory } from './provider_factory'; -const LOADING_ACCOUNT: AccountNotReady = { - state: AccountState.Loading, -}; -const NO_ACCOUNT: AccountNotReady = { - state: AccountState.None, -}; - export const providerStateFactory = { getInitialProviderState: (orderSource: OrderSource, network: Network, provider?: Provider): ProviderState => { if (!_.isUndefined(provider)) { |