1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
import { AssetBuyer } from '@0x/asset-buyer';
import { ObjectMap, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
import * as React from 'react';
import { Provider as ReduxProvider } from 'react-redux';
import { oc } from 'ts-optchain';
import { SelectedAssetThemeProvider } from '../containers/selected_asset_theme_provider';
import { asyncData } from '../redux/async_data';
import { INITIAL_STATE, State } from '../redux/reducer';
import { store, Store } from '../redux/store';
import { fonts } from '../style/fonts';
import { AffiliateInfo, AssetMetaData, Network } from '../types';
import { assetUtils } from '../util/asset';
import { errorFlasher } from '../util/error_flasher';
import { gasPriceEstimator } from '../util/gas_price_estimator';
import { getInjectedProvider } from '../util/injected_provider';
fonts.include();
export type ZeroExInstantProviderProps = ZeroExInstantProviderRequiredProps &
Partial<ZeroExInstantProviderOptionalProps>;
export interface ZeroExInstantProviderRequiredProps {
orderSource: string | SignedOrder[];
}
export interface ZeroExInstantProviderOptionalProps {
provider: Provider;
availableAssetDatas: string[];
defaultAssetBuyAmount: number;
defaultSelectedAssetData: string;
additionalAssetMetaDataMap: ObjectMap<AssetMetaData>;
networkId: Network;
affiliateInfo: AffiliateInfo;
}
export class ZeroExInstantProvider extends React.Component<ZeroExInstantProviderProps> {
private readonly _store: Store;
// TODO(fragosti): Write tests for this beast once we inject a provider.
private static _mergeInitialStateWithProps(props: ZeroExInstantProviderProps, state: State = INITIAL_STATE): State {
const networkId = props.networkId || state.network;
// TODO: Proper wallet connect flow
const provider = props.provider || getInjectedProvider();
const assetBuyerOptions = {
networkId,
};
let assetBuyer;
if (_.isString(props.orderSource)) {
assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(
provider,
props.orderSource,
assetBuyerOptions,
);
} else {
assetBuyer = AssetBuyer.getAssetBuyerForProvidedOrders(provider, props.orderSource, assetBuyerOptions);
}
const completeAssetMetaDataMap = {
...props.additionalAssetMetaDataMap,
...state.assetMetaDataMap,
};
const storeStateFromProps: State = {
...state,
assetBuyer,
network: networkId,
selectedAsset: _.isUndefined(props.defaultSelectedAssetData)
? undefined
: assetUtils.createAssetFromAssetDataOrThrow(
props.defaultSelectedAssetData,
completeAssetMetaDataMap,
networkId,
),
selectedAssetAmount: _.isUndefined(props.defaultAssetBuyAmount)
? state.selectedAssetAmount
: new BigNumber(props.defaultAssetBuyAmount),
availableAssets: _.isUndefined(props.availableAssetDatas)
? undefined
: assetUtils.createAssetsFromAssetDatas(props.availableAssetDatas, completeAssetMetaDataMap, networkId),
assetMetaDataMap: completeAssetMetaDataMap,
affiliateInfo: props.affiliateInfo,
};
return storeStateFromProps;
}
constructor(props: ZeroExInstantProviderProps) {
super(props);
const initialAppState = ZeroExInstantProvider._mergeInitialStateWithProps(this.props, INITIAL_STATE);
this._store = store.create(initialAppState);
}
public componentDidMount(): void {
const state = this._store.getState();
// tslint:disable-next-line:no-floating-promises
asyncData.fetchEthPriceAndDispatchToStore(this._store);
// fetch available assets if none are specified
if (_.isUndefined(state.availableAssets)) {
// tslint:disable-next-line:no-floating-promises
asyncData.fetchAvailableAssetDatasAndDispatchToStore(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();
}
public render(): React.ReactNode {
return (
<ReduxProvider store={this._store}>
<SelectedAssetThemeProvider>{this.props.children}</SelectedAssetThemeProvider>
</ReduxProvider>
);
}
private readonly _flashErrorIfWrongNetwork = async (): Promise<void> => {
const msToShowError = 30000; // 30 seconds
const network = this._store.getState().network;
const assetBuyerIfExists = this._store.getState().assetBuyer;
const providerIfExists = oc(assetBuyerIfExists).provider();
if (!_.isUndefined(providerIfExists)) {
const web3Wrapper = new Web3Wrapper(providerIfExists);
const networkOfProvider = await web3Wrapper.getNetworkIdAsync();
if (network !== networkOfProvider) {
const errorMessage = `Wrong network detected. Try switching to ${Network[network]}.`;
errorFlasher.flashNewErrorMessage(this._store.dispatch, errorMessage, msToShowError);
}
}
};
}
|