feat(instant): fallback to an empty wallet provider when none is injected
This commit is contained in:
parent
c30dca6961
commit
dfbf10c94b
@ -49,6 +49,7 @@
|
|||||||
"@0x/asset-buyer": "^2.1.0",
|
"@0x/asset-buyer": "^2.1.0",
|
||||||
"@0x/json-schemas": "^2.0.0",
|
"@0x/json-schemas": "^2.0.0",
|
||||||
"@0x/order-utils": "^2.0.0",
|
"@0x/order-utils": "^2.0.0",
|
||||||
|
"@0x/subproviders": "^2.1.0",
|
||||||
"@0x/types": "^1.2.0",
|
"@0x/types": "^1.2.0",
|
||||||
"@0x/typescript-typings": "^3.0.3",
|
"@0x/typescript-typings": "^3.0.3",
|
||||||
"@0x/utils": "^2.0.3",
|
"@0x/utils": "^2.0.3",
|
||||||
|
@ -17,7 +17,7 @@ import { Text } from './ui/text';
|
|||||||
|
|
||||||
export interface BuyButtonProps {
|
export interface BuyButtonProps {
|
||||||
buyQuote?: BuyQuote;
|
buyQuote?: BuyQuote;
|
||||||
assetBuyer?: AssetBuyer;
|
assetBuyer: AssetBuyer;
|
||||||
affiliateInfo?: AffiliateInfo;
|
affiliateInfo?: AffiliateInfo;
|
||||||
onValidationPending: (buyQuote: BuyQuote) => void;
|
onValidationPending: (buyQuote: BuyQuote) => void;
|
||||||
onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void;
|
onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void;
|
||||||
@ -34,7 +34,7 @@ export class BuyButton extends React.Component<BuyButtonProps> {
|
|||||||
onBuyFailure: util.boundNoop,
|
onBuyFailure: util.boundNoop,
|
||||||
};
|
};
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const shouldDisableButton = _.isUndefined(this.props.buyQuote) || _.isUndefined(this.props.assetBuyer);
|
const shouldDisableButton = _.isUndefined(this.props.buyQuote);
|
||||||
return (
|
return (
|
||||||
<Button width="100%" onClick={this._handleClick} isDisabled={shouldDisableButton}>
|
<Button width="100%" onClick={this._handleClick} isDisabled={shouldDisableButton}>
|
||||||
<Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
|
<Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
|
||||||
@ -46,11 +46,13 @@ export class BuyButton extends React.Component<BuyButtonProps> {
|
|||||||
private readonly _handleClick = async () => {
|
private readonly _handleClick = async () => {
|
||||||
// The button is disabled when there is no buy quote anyway.
|
// The button is disabled when there is no buy quote anyway.
|
||||||
const { buyQuote, assetBuyer, affiliateInfo } = this.props;
|
const { buyQuote, assetBuyer, affiliateInfo } = this.props;
|
||||||
if (_.isUndefined(buyQuote) || _.isUndefined(assetBuyer)) {
|
if (_.isUndefined(buyQuote)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onValidationPending(buyQuote);
|
this.props.onValidationPending(buyQuote);
|
||||||
|
|
||||||
|
// TODO(bmillman): move address and balance fetching to the async state
|
||||||
const web3Wrapper = new Web3Wrapper(assetBuyer.provider);
|
const web3Wrapper = new Web3Wrapper(assetBuyer.provider);
|
||||||
const takerAddress = await getBestAddress(web3Wrapper);
|
const takerAddress = await getBestAddress(web3Wrapper);
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import { Text } from './ui/text';
|
|||||||
export interface BuyOrderStateButtonProps {
|
export interface BuyOrderStateButtonProps {
|
||||||
buyQuote?: BuyQuote;
|
buyQuote?: BuyQuote;
|
||||||
buyOrderProcessingState: OrderProcessState;
|
buyOrderProcessingState: OrderProcessState;
|
||||||
assetBuyer?: AssetBuyer;
|
assetBuyer: AssetBuyer;
|
||||||
affiliateInfo?: AffiliateInfo;
|
affiliateInfo?: AffiliateInfo;
|
||||||
onViewTransaction: () => void;
|
onViewTransaction: () => void;
|
||||||
onValidationPending: (buyQuote: BuyQuote) => void;
|
onValidationPending: (buyQuote: BuyQuote) => void;
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
import { AssetBuyer } from '@0x/asset-buyer';
|
import { ObjectMap } from '@0x/types';
|
||||||
import { ObjectMap, SignedOrder } from '@0x/types';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
import { Provider } from 'ethereum-types';
|
import { Provider } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Provider as ReduxProvider } from 'react-redux';
|
import { Provider as ReduxProvider } from 'react-redux';
|
||||||
import { oc } from 'ts-optchain';
|
|
||||||
|
|
||||||
import { SelectedAssetThemeProvider } from '../containers/selected_asset_theme_provider';
|
import { SelectedAssetThemeProvider } from '../containers/selected_asset_theme_provider';
|
||||||
import { asyncData } from '../redux/async_data';
|
import { asyncData } from '../redux/async_data';
|
||||||
import { INITIAL_STATE, State } from '../redux/reducer';
|
import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer';
|
||||||
import { store, Store } from '../redux/store';
|
import { store, Store } from '../redux/store';
|
||||||
import { fonts } from '../style/fonts';
|
import { fonts } from '../style/fonts';
|
||||||
import { AffiliateInfo, AssetMetaData, Network } from '../types';
|
import { AffiliateInfo, AssetMetaData, Network, OrderSource, ProviderState, ProviderType } from '../types';
|
||||||
import { assetUtils } from '../util/asset';
|
import { assetUtils } from '../util/asset';
|
||||||
|
import { assetBuyerFactory } from '../util/asset_buyer_factory';
|
||||||
import { errorFlasher } from '../util/error_flasher';
|
import { errorFlasher } from '../util/error_flasher';
|
||||||
import { gasPriceEstimator } from '../util/gas_price_estimator';
|
import { gasPriceEstimator } from '../util/gas_price_estimator';
|
||||||
import { getInjectedProvider } from '../util/injected_provider';
|
import { providerFactory } from '../util/provider_factory';
|
||||||
|
|
||||||
fonts.include();
|
fonts.include();
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ export type ZeroExInstantProviderProps = ZeroExInstantProviderRequiredProps &
|
|||||||
Partial<ZeroExInstantProviderOptionalProps>;
|
Partial<ZeroExInstantProviderOptionalProps>;
|
||||||
|
|
||||||
export interface ZeroExInstantProviderRequiredProps {
|
export interface ZeroExInstantProviderRequiredProps {
|
||||||
orderSource: string | SignedOrder[];
|
orderSource: OrderSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ZeroExInstantProviderOptionalProps {
|
export interface ZeroExInstantProviderOptionalProps {
|
||||||
@ -41,30 +40,43 @@ export interface ZeroExInstantProviderOptionalProps {
|
|||||||
export class ZeroExInstantProvider extends React.Component<ZeroExInstantProviderProps> {
|
export class ZeroExInstantProvider extends React.Component<ZeroExInstantProviderProps> {
|
||||||
private readonly _store: Store;
|
private readonly _store: Store;
|
||||||
// TODO(fragosti): Write tests for this beast once we inject a provider.
|
// TODO(fragosti): Write tests for this beast once we inject a provider.
|
||||||
private static _mergeInitialStateWithProps(props: ZeroExInstantProviderProps, state: State = INITIAL_STATE): State {
|
private static _mergeDefaultStateWithProps(
|
||||||
const networkId = props.networkId || state.network;
|
props: ZeroExInstantProviderProps,
|
||||||
// TODO: Proper wallet connect flow
|
defaultState: DefaultState = DEFAULT_STATE,
|
||||||
const provider = props.provider || getInjectedProvider();
|
): State {
|
||||||
const assetBuyerOptions = {
|
// use the networkId passed in with the props, otherwise default to that of the default state (1, mainnet)
|
||||||
networkId,
|
const networkId = props.networkId || defaultState.network;
|
||||||
};
|
// construct the ProviderState
|
||||||
let assetBuyer;
|
let provider: Provider;
|
||||||
if (_.isString(props.orderSource)) {
|
let providerType: ProviderType;
|
||||||
assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(
|
if (!_.isUndefined(props.provider)) {
|
||||||
provider,
|
provider = props.provider;
|
||||||
props.orderSource,
|
providerType = ProviderType.Props;
|
||||||
assetBuyerOptions,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
assetBuyer = AssetBuyer.getAssetBuyerForProvidedOrders(provider, props.orderSource, assetBuyerOptions);
|
const injectedProviderIfExists = providerFactory.getInjectedProviderIfExists();
|
||||||
|
if (!_.isUndefined(injectedProviderIfExists)) {
|
||||||
|
provider = injectedProviderIfExists;
|
||||||
|
providerType = ProviderType.Injected;
|
||||||
|
} else {
|
||||||
|
provider = providerFactory.getFallbackNoSigningProvider(networkId);
|
||||||
|
providerType = ProviderType.FallbackEmptyWallet;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
const providerState: ProviderState = {
|
||||||
|
provider,
|
||||||
|
type: providerType,
|
||||||
|
web3Wrapper: new Web3Wrapper(provider),
|
||||||
|
assetBuyer: assetBuyerFactory.getAssetBuyer(provider, props.orderSource, networkId),
|
||||||
|
};
|
||||||
|
// merge the additional additionalAssetMetaDataMap with our default map
|
||||||
const completeAssetMetaDataMap = {
|
const completeAssetMetaDataMap = {
|
||||||
...props.additionalAssetMetaDataMap,
|
...props.additionalAssetMetaDataMap,
|
||||||
...state.assetMetaDataMap,
|
...defaultState.assetMetaDataMap,
|
||||||
};
|
};
|
||||||
|
// construct the final state
|
||||||
const storeStateFromProps: State = {
|
const storeStateFromProps: State = {
|
||||||
...state,
|
...defaultState,
|
||||||
assetBuyer,
|
providerState,
|
||||||
network: networkId,
|
network: networkId,
|
||||||
selectedAsset: _.isUndefined(props.defaultSelectedAssetData)
|
selectedAsset: _.isUndefined(props.defaultSelectedAssetData)
|
||||||
? undefined
|
? undefined
|
||||||
@ -74,7 +86,7 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
|
|||||||
networkId,
|
networkId,
|
||||||
),
|
),
|
||||||
selectedAssetAmount: _.isUndefined(props.defaultAssetBuyAmount)
|
selectedAssetAmount: _.isUndefined(props.defaultAssetBuyAmount)
|
||||||
? state.selectedAssetAmount
|
? undefined
|
||||||
: new BigNumber(props.defaultAssetBuyAmount),
|
: new BigNumber(props.defaultAssetBuyAmount),
|
||||||
availableAssets: _.isUndefined(props.availableAssetDatas)
|
availableAssets: _.isUndefined(props.availableAssetDatas)
|
||||||
? undefined
|
? undefined
|
||||||
@ -86,10 +98,9 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
|
|||||||
}
|
}
|
||||||
constructor(props: ZeroExInstantProviderProps) {
|
constructor(props: ZeroExInstantProviderProps) {
|
||||||
super(props);
|
super(props);
|
||||||
const initialAppState = ZeroExInstantProvider._mergeInitialStateWithProps(this.props, INITIAL_STATE);
|
const initialAppState = ZeroExInstantProvider._mergeDefaultStateWithProps(this.props);
|
||||||
this._store = store.create(initialAppState);
|
this._store = store.create(initialAppState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
const state = this._store.getState();
|
const state = this._store.getState();
|
||||||
// tslint:disable-next-line:no-floating-promises
|
// tslint:disable-next-line:no-floating-promises
|
||||||
@ -108,7 +119,6 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
|
|||||||
// tslint:disable-next-line:no-floating-promises
|
// tslint:disable-next-line:no-floating-promises
|
||||||
this._flashErrorIfWrongNetwork();
|
this._flashErrorIfWrongNetwork();
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<ReduxProvider store={this._store}>
|
<ReduxProvider store={this._store}>
|
||||||
@ -116,19 +126,15 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
|
|||||||
</ReduxProvider>
|
</ReduxProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly _flashErrorIfWrongNetwork = async (): Promise<void> => {
|
private readonly _flashErrorIfWrongNetwork = async (): Promise<void> => {
|
||||||
const msToShowError = 30000; // 30 seconds
|
const msToShowError = 30000; // 30 seconds
|
||||||
const network = this._store.getState().network;
|
const state = this._store.getState();
|
||||||
const assetBuyerIfExists = this._store.getState().assetBuyer;
|
const network = state.network;
|
||||||
const providerIfExists = oc(assetBuyerIfExists).provider();
|
const web3Wrapper = state.providerState.web3Wrapper;
|
||||||
if (!_.isUndefined(providerIfExists)) {
|
|
||||||
const web3Wrapper = new Web3Wrapper(providerIfExists);
|
|
||||||
const networkOfProvider = await web3Wrapper.getNetworkIdAsync();
|
const networkOfProvider = await web3Wrapper.getNetworkIdAsync();
|
||||||
if (network !== networkOfProvider) {
|
if (network !== networkOfProvider) {
|
||||||
const errorMessage = `Wrong network detected. Try switching to ${Network[network]}.`;
|
const errorMessage = `Wrong network detected. Try switching to ${Network[network]}.`;
|
||||||
errorFlasher.flashNewErrorMessage(this._store.dispatch, errorMessage, msToShowError);
|
errorFlasher.flashNewErrorMessage(this._store.dispatch, errorMessage, msToShowError);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
import { Network } from './types';
|
||||||
|
|
||||||
export const BIG_NUMBER_ZERO = new BigNumber(0);
|
export const BIG_NUMBER_ZERO = new BigNumber(0);
|
||||||
export const ETH_DECIMALS = 18;
|
export const ETH_DECIMALS = 18;
|
||||||
export const DEFAULT_ZERO_EX_CONTAINER_SELECTOR = '#zeroExInstantContainer';
|
export const DEFAULT_ZERO_EX_CONTAINER_SELECTOR = '#zeroExInstantContainer';
|
||||||
@ -13,3 +16,8 @@ export const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info';
|
|||||||
export const COINBASE_API_BASE_URL = 'https://api.coinbase.com/v2';
|
export const COINBASE_API_BASE_URL = 'https://api.coinbase.com/v2';
|
||||||
export const PROGRESS_STALL_AT_WIDTH = '95%';
|
export const PROGRESS_STALL_AT_WIDTH = '95%';
|
||||||
export const PROGRESS_FINISH_ANIMATION_TIME_MS = 200;
|
export const PROGRESS_FINISH_ANIMATION_TIME_MS = 200;
|
||||||
|
export const ETHEREUM_NODE_URL_BY_NETWORK = {
|
||||||
|
[Network.Mainnet]: 'https://mainnet.infura.io/',
|
||||||
|
[Network.Kovan]: 'https://kovan.infura.io/',
|
||||||
|
};
|
||||||
|
export const BLOCK_POLLING_INTERVAL_MS = 10000; // 10s
|
||||||
|
@ -14,7 +14,7 @@ import { etherscanUtil } from '../util/etherscan';
|
|||||||
interface ConnectedState {
|
interface ConnectedState {
|
||||||
buyQuote?: BuyQuote;
|
buyQuote?: BuyQuote;
|
||||||
buyOrderProcessingState: OrderProcessState;
|
buyOrderProcessingState: OrderProcessState;
|
||||||
assetBuyer?: AssetBuyer;
|
assetBuyer: AssetBuyer;
|
||||||
affiliateInfo?: AffiliateInfo;
|
affiliateInfo?: AffiliateInfo;
|
||||||
onViewTransaction: () => void;
|
onViewTransaction: () => void;
|
||||||
}
|
}
|
||||||
@ -29,21 +29,22 @@ interface ConnectedDispatch {
|
|||||||
onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void;
|
onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void;
|
||||||
}
|
}
|
||||||
export interface SelectedAssetBuyOrderStateButtons {}
|
export interface SelectedAssetBuyOrderStateButtons {}
|
||||||
const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButtons): ConnectedState => ({
|
const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButtons): ConnectedState => {
|
||||||
|
const assetBuyer = state.providerState.assetBuyer;
|
||||||
|
return {
|
||||||
buyOrderProcessingState: state.buyOrderState.processState,
|
buyOrderProcessingState: state.buyOrderState.processState,
|
||||||
assetBuyer: state.assetBuyer,
|
assetBuyer,
|
||||||
buyQuote: state.latestBuyQuote,
|
buyQuote: state.latestBuyQuote,
|
||||||
affiliateInfo: state.affiliateInfo,
|
affiliateInfo: state.affiliateInfo,
|
||||||
onViewTransaction: () => {
|
onViewTransaction: () => {
|
||||||
if (
|
if (
|
||||||
state.assetBuyer &&
|
state.buyOrderState.processState === OrderProcessState.Processing ||
|
||||||
(state.buyOrderState.processState === OrderProcessState.Processing ||
|
|
||||||
state.buyOrderState.processState === OrderProcessState.Success ||
|
state.buyOrderState.processState === OrderProcessState.Success ||
|
||||||
state.buyOrderState.processState === OrderProcessState.Failure)
|
state.buyOrderState.processState === OrderProcessState.Failure
|
||||||
) {
|
) {
|
||||||
const etherscanUrl = etherscanUtil.getEtherScanTxnAddressIfExists(
|
const etherscanUrl = etherscanUtil.getEtherScanTxnAddressIfExists(
|
||||||
state.buyOrderState.txHash,
|
state.buyOrderState.txHash,
|
||||||
state.assetBuyer.networkId,
|
assetBuyer.networkId,
|
||||||
);
|
);
|
||||||
if (etherscanUrl) {
|
if (etherscanUrl) {
|
||||||
window.open(etherscanUrl, '_blank');
|
window.open(etherscanUrl, '_blank');
|
||||||
@ -51,7 +52,8 @@ const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
const mapDispatchToProps = (
|
||||||
dispatch: Dispatch<Action>,
|
dispatch: Dispatch<Action>,
|
||||||
|
@ -23,7 +23,7 @@ export interface SelectedERC20AssetAmountInputProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ConnectedState {
|
interface ConnectedState {
|
||||||
assetBuyer?: AssetBuyer;
|
assetBuyer: AssetBuyer;
|
||||||
value?: BigNumber;
|
value?: BigNumber;
|
||||||
asset?: ERC20Asset;
|
asset?: ERC20Asset;
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
@ -33,7 +33,7 @@ interface ConnectedState {
|
|||||||
|
|
||||||
interface ConnectedDispatch {
|
interface ConnectedDispatch {
|
||||||
updateBuyQuote: (
|
updateBuyQuote: (
|
||||||
assetBuyer?: AssetBuyer,
|
assetBuyer: AssetBuyer,
|
||||||
value?: BigNumber,
|
value?: BigNumber,
|
||||||
asset?: ERC20Asset,
|
asset?: ERC20Asset,
|
||||||
affiliateInfo?: AffiliateInfo,
|
affiliateInfo?: AffiliateInfo,
|
||||||
@ -59,8 +59,9 @@ const mapStateToProps = (state: State, _ownProps: SelectedERC20AssetAmountInputP
|
|||||||
? (state.selectedAsset as ERC20Asset)
|
? (state.selectedAsset as ERC20Asset)
|
||||||
: undefined;
|
: undefined;
|
||||||
const numberOfAssetsAvailable = _.isUndefined(state.availableAssets) ? undefined : state.availableAssets.length;
|
const numberOfAssetsAvailable = _.isUndefined(state.availableAssets) ? undefined : state.availableAssets.length;
|
||||||
|
const assetBuyer = state.providerState.assetBuyer;
|
||||||
return {
|
return {
|
||||||
assetBuyer: state.assetBuyer,
|
assetBuyer,
|
||||||
value: state.selectedAssetAmount,
|
value: state.selectedAssetAmount,
|
||||||
asset: selectedAsset,
|
asset: selectedAsset,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
@ -128,7 +129,7 @@ const mapDispatchToProps = (
|
|||||||
// reset our buy state
|
// reset our buy state
|
||||||
dispatch(actions.setBuyOrderStateNone());
|
dispatch(actions.setBuyOrderStateNone());
|
||||||
|
|
||||||
if (!_.isUndefined(value) && value.greaterThan(0) && !_.isUndefined(asset) && !_.isUndefined(assetBuyer)) {
|
if (!_.isUndefined(value) && value.greaterThan(0) && !_.isUndefined(asset)) {
|
||||||
// even if it's debounced, give them the illusion it's loading
|
// even if it's debounced, give them the illusion it's loading
|
||||||
dispatch(actions.setQuoteRequestStatePending());
|
dispatch(actions.setQuoteRequestStatePending());
|
||||||
// tslint:disable-next-line:no-floating-promises
|
// tslint:disable-next-line:no-floating-promises
|
||||||
|
@ -20,8 +20,8 @@ export const asyncData = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchAvailableAssetDatasAndDispatchToStore: async (store: Store) => {
|
fetchAvailableAssetDatasAndDispatchToStore: async (store: Store) => {
|
||||||
const { assetBuyer, assetMetaDataMap, network } = store.getState();
|
const { providerState, assetMetaDataMap, network } = store.getState();
|
||||||
if (!_.isUndefined(assetBuyer)) {
|
const assetBuyer = providerState.assetBuyer;
|
||||||
try {
|
try {
|
||||||
const assetDatas = await assetBuyer.getAvailableAssetDatasAsync();
|
const assetDatas = await assetBuyer.getAvailableAssetDatasAsync();
|
||||||
const assets = assetUtils.createAssetsFromAssetDatas(assetDatas, assetMetaDataMap, network);
|
const assets = assetUtils.createAssetsFromAssetDatas(assetDatas, assetMetaDataMap, network);
|
||||||
@ -32,6 +32,5 @@ export const asyncData = {
|
|||||||
// On error, just specify that none are available
|
// On error, just specify that none are available
|
||||||
store.dispatch(actions.setAvailableAssets([]));
|
store.dispatch(actions.setAvailableAssets([]));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AssetBuyer, BuyQuote } from '@0x/asset-buyer';
|
import { BuyQuote } from '@0x/asset-buyer';
|
||||||
import { AssetProxyId, ObjectMap } from '@0x/types';
|
import { AssetProxyId, ObjectMap } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
@ -14,41 +14,48 @@ import {
|
|||||||
Network,
|
Network,
|
||||||
OrderProcessState,
|
OrderProcessState,
|
||||||
OrderState,
|
OrderState,
|
||||||
|
ProviderState,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
import { Action, ActionTypes } from './actions';
|
import { Action, ActionTypes } from './actions';
|
||||||
|
|
||||||
export interface State {
|
// State that is required and we have defaults for, before props are passed in
|
||||||
|
export interface DefaultState {
|
||||||
network: Network;
|
network: Network;
|
||||||
assetBuyer?: AssetBuyer;
|
|
||||||
assetMetaDataMap: ObjectMap<AssetMetaData>;
|
assetMetaDataMap: ObjectMap<AssetMetaData>;
|
||||||
selectedAsset?: Asset;
|
|
||||||
availableAssets?: Asset[];
|
|
||||||
selectedAssetAmount?: BigNumber;
|
|
||||||
buyOrderState: OrderState;
|
buyOrderState: OrderState;
|
||||||
ethUsdPrice?: BigNumber;
|
|
||||||
latestBuyQuote?: BuyQuote;
|
|
||||||
quoteRequestState: AsyncProcessState;
|
|
||||||
latestErrorMessage?: string;
|
|
||||||
latestErrorDisplayStatus: DisplayStatus;
|
latestErrorDisplayStatus: DisplayStatus;
|
||||||
affiliateInfo?: AffiliateInfo;
|
quoteRequestState: AsyncProcessState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const INITIAL_STATE: State = {
|
// State that is required but needs to be derived from the props
|
||||||
|
interface PropsDerivedState {
|
||||||
|
providerState: ProviderState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// State that is optional
|
||||||
|
interface OptionalState {
|
||||||
|
selectedAsset: Asset;
|
||||||
|
availableAssets: Asset[];
|
||||||
|
selectedAssetAmount: BigNumber;
|
||||||
|
ethUsdPrice: BigNumber;
|
||||||
|
latestBuyQuote: BuyQuote;
|
||||||
|
latestErrorMessage: string;
|
||||||
|
affiliateInfo: AffiliateInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type State = DefaultState & PropsDerivedState & Partial<OptionalState>;
|
||||||
|
|
||||||
|
export const DEFAULT_STATE: DefaultState = {
|
||||||
network: Network.Mainnet,
|
network: Network.Mainnet,
|
||||||
selectedAssetAmount: undefined,
|
|
||||||
availableAssets: undefined,
|
|
||||||
assetMetaDataMap,
|
assetMetaDataMap,
|
||||||
buyOrderState: { processState: OrderProcessState.None },
|
buyOrderState: { processState: OrderProcessState.None },
|
||||||
ethUsdPrice: undefined,
|
|
||||||
latestBuyQuote: undefined,
|
|
||||||
latestErrorMessage: undefined,
|
|
||||||
latestErrorDisplayStatus: DisplayStatus.Hidden,
|
latestErrorDisplayStatus: DisplayStatus.Hidden,
|
||||||
quoteRequestState: AsyncProcessState.None,
|
quoteRequestState: AsyncProcessState.None,
|
||||||
affiliateInfo: undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reducer = (state: State = INITIAL_STATE, action: Action): State => {
|
export const createReducer = (initialState: State) => {
|
||||||
|
const reducer = (state: State = initialState, action: Action): State => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionTypes.UPDATE_ETH_USD_PRICE:
|
case ActionTypes.UPDATE_ETH_USD_PRICE:
|
||||||
return {
|
return {
|
||||||
@ -180,6 +187,8 @@ export const reducer = (state: State = INITIAL_STATE, action: Action): State =>
|
|||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
return reducer;
|
||||||
};
|
};
|
||||||
|
|
||||||
const doesBuyQuoteMatchState = (buyQuote: BuyQuote, state: State): boolean => {
|
const doesBuyQuoteMatchState = (buyQuote: BuyQuote, state: State): boolean => {
|
||||||
|
@ -2,12 +2,13 @@ import * as _ from 'lodash';
|
|||||||
import { createStore, Store as ReduxStore } from 'redux';
|
import { createStore, Store as ReduxStore } from 'redux';
|
||||||
import { devToolsEnhancer } from 'redux-devtools-extension/developmentOnly';
|
import { devToolsEnhancer } from 'redux-devtools-extension/developmentOnly';
|
||||||
|
|
||||||
import { reducer, State } from './reducer';
|
import { createReducer, State } from './reducer';
|
||||||
|
|
||||||
export type Store = ReduxStore<State>;
|
export type Store = ReduxStore<State>;
|
||||||
|
|
||||||
export const store = {
|
export const store = {
|
||||||
create: (state: State): Store => {
|
create: (initialState: State): Store => {
|
||||||
return createStore(reducer, state, devToolsEnhancer({}));
|
const reducer = createReducer(initialState);
|
||||||
|
return createStore(reducer, initialState, devToolsEnhancer({}));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { AssetProxyId, ObjectMap } from '@0x/types';
|
import { AssetBuyer } from '@0x/asset-buyer';
|
||||||
|
import { AssetProxyId, ObjectMap, SignedOrder } from '@0x/types';
|
||||||
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
|
import { Provider } from 'ethereum-types';
|
||||||
|
|
||||||
// Reusable
|
// Reusable
|
||||||
export type Maybe<T> = T | undefined;
|
export type Maybe<T> = T | undefined;
|
||||||
@ -89,3 +92,24 @@ export interface AffiliateInfo {
|
|||||||
feeRecipient: string;
|
feeRecipient: string;
|
||||||
feePercentage: number;
|
feePercentage: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our provider may be of 3 different types:
|
||||||
|
* Props: the provider was passed in a prop to the component
|
||||||
|
* Injected: the provider was injected into `window`
|
||||||
|
* FallbackEmptyWallet: the provider has no wallet but responds to data requests using infura
|
||||||
|
*/
|
||||||
|
export enum ProviderType {
|
||||||
|
Props = 'PROPS',
|
||||||
|
Injected = 'INJECTED',
|
||||||
|
FallbackEmptyWallet = 'FALLBACK_EMPTY_WALLET',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProviderState {
|
||||||
|
type: ProviderType;
|
||||||
|
provider: Provider;
|
||||||
|
assetBuyer: AssetBuyer;
|
||||||
|
web3Wrapper: Web3Wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OrderSource = string | SignedOrder[];
|
||||||
|
17
packages/instant/src/util/asset_buyer_factory.ts
Normal file
17
packages/instant/src/util/asset_buyer_factory.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { AssetBuyer, AssetBuyerOpts } from '@0x/asset-buyer';
|
||||||
|
import { Provider } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { Network, OrderSource } from '../types';
|
||||||
|
|
||||||
|
export const assetBuyerFactory = {
|
||||||
|
getAssetBuyer: (provider: Provider, orderSource: OrderSource, network: Network): AssetBuyer => {
|
||||||
|
const assetBuyerOptions: Partial<AssetBuyerOpts> = {
|
||||||
|
networkId: network,
|
||||||
|
};
|
||||||
|
const assetBuyer = _.isString(orderSource)
|
||||||
|
? AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(provider, orderSource, assetBuyerOptions)
|
||||||
|
: AssetBuyer.getAssetBuyerForProvidedOrders(provider, orderSource, assetBuyerOptions);
|
||||||
|
return assetBuyer;
|
||||||
|
},
|
||||||
|
};
|
@ -1,16 +0,0 @@
|
|||||||
import { Provider } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
export const getInjectedProvider = (): Provider => {
|
|
||||||
const injectedProviderIfExists = (window as any).ethereum;
|
|
||||||
if (!_.isUndefined(injectedProviderIfExists)) {
|
|
||||||
// TODO: call enable here when implementing wallet connection flow
|
|
||||||
return injectedProviderIfExists;
|
|
||||||
}
|
|
||||||
const injectedWeb3IfExists = (window as any).web3;
|
|
||||||
if (!_.isUndefined(injectedWeb3IfExists.currentProvider)) {
|
|
||||||
return injectedWeb3IfExists.currentProvider;
|
|
||||||
} else {
|
|
||||||
throw new Error(`No injected web3 found`);
|
|
||||||
}
|
|
||||||
};
|
|
34
packages/instant/src/util/provider_factory.ts
Normal file
34
packages/instant/src/util/provider_factory.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { EmptyWalletSubprovider, RPCSubprovider, Web3ProviderEngine } from '@0x/subproviders';
|
||||||
|
import { Provider } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { BLOCK_POLLING_INTERVAL_MS, ETHEREUM_NODE_URL_BY_NETWORK } from '../constants';
|
||||||
|
import { Maybe, Network } from '../types';
|
||||||
|
|
||||||
|
export const providerFactory = {
|
||||||
|
getInjectedProviderIfExists: (): Maybe<Provider> => {
|
||||||
|
const injectedProviderIfExists = (window as any).ethereum;
|
||||||
|
if (!_.isUndefined(injectedProviderIfExists)) {
|
||||||
|
return injectedProviderIfExists;
|
||||||
|
}
|
||||||
|
const injectedWeb3IfExists = (window as any).web3;
|
||||||
|
if (!_.isUndefined(injectedWeb3IfExists) && !_.isUndefined(injectedWeb3IfExists.currentProvider)) {
|
||||||
|
return injectedWeb3IfExists.currentProvider;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
getFallbackNoSigningProvider: (network: Network): Provider => {
|
||||||
|
const providerEngine = new Web3ProviderEngine({
|
||||||
|
pollingInterval: BLOCK_POLLING_INTERVAL_MS,
|
||||||
|
});
|
||||||
|
// Intercept calls to `eth_accounts` and always return empty
|
||||||
|
providerEngine.addProvider(new EmptyWalletSubprovider());
|
||||||
|
// Construct an RPC subprovider, all data based requests will be sent via the RPCSubprovider
|
||||||
|
// TODO(bmillman): make this more resilient to infura failures
|
||||||
|
const rpcUrl = ETHEREUM_NODE_URL_BY_NETWORK[network];
|
||||||
|
providerEngine.addProvider(new RPCSubprovider(rpcUrl));
|
||||||
|
// // Start the Provider Engine
|
||||||
|
providerEngine.start();
|
||||||
|
return providerEngine;
|
||||||
|
},
|
||||||
|
};
|
41
yarn.lock
41
yarn.lock
@ -1900,10 +1900,6 @@ aes-js@^0.2.3:
|
|||||||
version "0.2.4"
|
version "0.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-0.2.4.tgz#94b881ab717286d015fa219e08fb66709dda5a3d"
|
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:
|
agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0:
|
||||||
version "4.2.1"
|
version "4.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
|
||||||
@ -3341,7 +3337,7 @@ bs-logger@0.x:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fast-json-stable-stringify "^2.0.0"
|
fast-json-stable-stringify "^2.0.0"
|
||||||
|
|
||||||
bs58@=4.0.1, bs58@^4.0.0:
|
bs58@=4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
|
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3364,14 +3360,6 @@ bs58check@^1.0.8:
|
|||||||
bs58 "^3.1.0"
|
bs58 "^3.1.0"
|
||||||
create-hash "^1.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:
|
bser@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
|
resolved "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
|
||||||
@ -5959,19 +5947,6 @@ ethereumjs-wallet@0.6.0:
|
|||||||
utf8 "^2.1.1"
|
utf8 "^2.1.1"
|
||||||
uuid "^2.0.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:
|
ethers@~4.0.4:
|
||||||
version "4.0.4"
|
version "4.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65"
|
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65"
|
||||||
@ -6776,7 +6751,7 @@ ganache-core@0xProject/ganache-core#monorepo-dep:
|
|||||||
ethereumjs-tx "0xProject/ethereumjs-tx#fake-tx-include-signature-by-default"
|
ethereumjs-tx "0xProject/ethereumjs-tx#fake-tx-include-signature-by-default"
|
||||||
ethereumjs-util "^5.2.0"
|
ethereumjs-util "^5.2.0"
|
||||||
ethereumjs-vm "2.3.5"
|
ethereumjs-vm "2.3.5"
|
||||||
ethereumjs-wallet "~0.6.0"
|
ethereumjs-wallet "0.6.0"
|
||||||
fake-merkle-patricia-tree "~1.0.1"
|
fake-merkle-patricia-tree "~1.0.1"
|
||||||
heap "~0.2.6"
|
heap "~0.2.6"
|
||||||
js-scrypt "^0.2.0"
|
js-scrypt "^0.2.0"
|
||||||
@ -7501,14 +7476,6 @@ hdkey@^0.7.0, hdkey@^0.7.1:
|
|||||||
coinstring "^2.0.0"
|
coinstring "^2.0.0"
|
||||||
secp256k1 "^3.0.1"
|
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:
|
he@1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
|
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
|
||||||
@ -15595,10 +15562,6 @@ utf8@^2.1.1:
|
|||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.2.tgz#1fa0d9270e9be850d9b05027f63519bf46457d96"
|
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:
|
util-deprecate@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user