aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrandon Millman <brandon.millman@gmail.com>2018-11-08 10:02:24 +0800
committerBrandon Millman <brandon.millman@gmail.com>2018-11-08 15:41:25 +0800
commitd0c009adff53d94414cf51028eff490e0452a3c9 (patch)
tree82a76e2499883d9a8a579b64b6503ef108d7706c
parentf6abc007ffb249e4bbf85b8a7a77309d43e0a147 (diff)
downloaddexon-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.tsx21
-rw-r--r--packages/instant/src/components/buy_order_state_buttons.tsx2
-rw-r--r--packages/instant/src/components/zero_ex_instant_provider.tsx4
-rw-r--r--packages/instant/src/constants.ts14
-rw-r--r--packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts6
-rw-r--r--packages/instant/src/redux/actions.ts8
-rw-r--r--packages/instant/src/redux/async_data.ts21
-rw-r--r--packages/instant/src/redux/reducer.ts29
-rw-r--r--packages/instant/src/util/provider_state_factory.ts8
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)) {