aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrancesco Agosti <francesco.agosti93@gmail.com>2018-06-29 02:36:49 +0800
committerGitHub <noreply@github.com>2018-06-29 02:36:49 +0800
commit2474d1d2f4cf4ae81d5d67c53005f018a0154ef5 (patch)
treeac24b29f975be088a9d3d391f8a73acc7ad14aba
parent0fcbd02d50bd564a9c888f247a4b0a565d928cc6 (diff)
parente4188f5d4c38e53bf6966a364da41a3aa164b567 (diff)
downloaddexon-sol-tools-2474d1d2f4cf4ae81d5d67c53005f018a0154ef5.tar
dexon-sol-tools-2474d1d2f4cf4ae81d5d67c53005f018a0154ef5.tar.gz
dexon-sol-tools-2474d1d2f4cf4ae81d5d67c53005f018a0154ef5.tar.bz2
dexon-sol-tools-2474d1d2f4cf4ae81d5d67c53005f018a0154ef5.tar.lz
dexon-sol-tools-2474d1d2f4cf4ae81d5d67c53005f018a0154ef5.tar.xz
dexon-sol-tools-2474d1d2f4cf4ae81d5d67c53005f018a0154ef5.tar.zst
dexon-sol-tools-2474d1d2f4cf4ae81d5d67c53005f018a0154ef5.zip
Merge pull request #780 from 0xProject/feature/website/support-new-metamask
Refactor Blockchain.ts to allow arbitrary provider state changes
-rw-r--r--packages/website/ts/blockchain.ts259
-rw-r--r--packages/website/ts/blockchain_watcher.ts46
-rw-r--r--packages/website/ts/components/dialogs/blockchain_err_dialog.tsx17
-rw-r--r--packages/website/ts/components/dialogs/ledger_config_dialog.tsx1
-rw-r--r--packages/website/ts/components/legacy_portal/legacy_portal.tsx117
-rw-r--r--packages/website/ts/components/onboarding/portal_onboarding_flow.tsx17
-rw-r--r--packages/website/ts/components/portal/portal.tsx38
-rw-r--r--packages/website/ts/components/top_bar/top_bar.tsx2
-rw-r--r--packages/website/ts/types.ts10
-rw-r--r--packages/website/ts/utils/configs.ts1
-rw-r--r--yarn.lock20
11 files changed, 263 insertions, 265 deletions
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts
index b65debced..d18c34c32 100644
--- a/packages/website/ts/blockchain.ts
+++ b/packages/website/ts/blockchain.ts
@@ -44,6 +44,8 @@ import {
BlockchainErrs,
ContractInstance,
Fill,
+ InjectedProviderObservable,
+ InjectedProviderUpdate,
Order as PortalOrder,
Providers,
ProviderType,
@@ -80,9 +82,9 @@ export class Blockchain {
private _dispatcher: Dispatcher;
private _web3Wrapper?: Web3Wrapper;
private _blockchainWatcher?: BlockchainWatcher;
+ private _injectedProviderObservable?: InjectedProviderObservable;
+ private _injectedProviderUpdateHandler: (update: InjectedProviderUpdate) => Promise<void>;
private _userAddressIfExists: string;
- private _cachedProvider: Provider;
- private _cachedProviderNetworkId: number;
private _ledgerSubprovider: LedgerSubprovider;
private _defaultGasPrice: BigNumber;
private static _getNameGivenProvider(provider: Provider): string {
@@ -93,16 +95,62 @@ export class Blockchain {
}
return providerNameIfExists;
}
- private static async _getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number): Promise<Provider> {
+ private static _getInjectedWeb3(): any {
+ return (window as any).web3;
+ }
+ private static async _getInjectedWeb3ProviderNetworkIdIfExistsAsync(): Promise<number | undefined> {
+ // Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in
+ // order to properly instantiate the web3Wrapper. Since we must use the async call, we cannot
+ // retrieve it from within the web3Wrapper constructor. This is and should remain the only
+ // call to a web3 instance outside of web3Wrapper in the entire dapp.
+ // In addition, if the user has an injectedWeb3 instance that is disconnected from a backing
+ // Ethereum node, this call will throw. We need to handle this case gracefully
+ const injectedWeb3IfExists = Blockchain._getInjectedWeb3();
+ let networkIdIfExists: number;
+ if (!_.isUndefined(injectedWeb3IfExists)) {
+ try {
+ networkIdIfExists = _.parseInt(await promisify<string>(injectedWeb3IfExists.version.getNetwork)());
+ } catch (err) {
+ // Ignore error and proceed with networkId undefined
+ }
+ }
+ return networkIdIfExists;
+ }
+ private static async _getProviderAsync(
+ injectedWeb3: Web3,
+ networkIdIfExists: number,
+ shouldUserLedgerProvider: boolean = false,
+ ): Promise<[Provider, LedgerSubprovider | undefined]> {
const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3);
+ const isNetworkIdAvailable = !_.isUndefined(networkIdIfExists);
const publicNodeUrlsIfExistsForNetworkId = configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists];
const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId);
- let provider;
- if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) {
+ if (shouldUserLedgerProvider && isNetworkIdAvailable) {
+ const isU2FSupported = await utils.isU2FSupportedAsync();
+ if (!isU2FSupported) {
+ throw new Error('Cannot update providerType to LEDGER without U2F support');
+ }
+ const provider = new ProviderEngine();
+ const ledgerWalletConfigs = {
+ networkId: networkIdIfExists,
+ ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync,
+ };
+ const ledgerSubprovider = new LedgerSubprovider(ledgerWalletConfigs);
+ provider.addProvider(ledgerSubprovider);
+ provider.addProvider(new FilterSubprovider());
+ const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists], publicNodeUrl => {
+ return new RpcSubprovider({
+ rpcUrl: publicNodeUrl,
+ });
+ });
+ provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[]));
+ provider.start();
+ return [provider, ledgerSubprovider];
+ } else if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) {
// We catch all requests involving a users account and send it to the injectedWeb3
// instance. All other requests go to the public hosted node.
- provider = new ProviderEngine();
+ const provider = new ProviderEngine();
provider.addProvider(new InjectedWeb3Subprovider(injectedWeb3.currentProvider));
provider.addProvider(new FilterSubprovider());
const rpcSubproviders = _.map(publicNodeUrlsIfExistsForNetworkId, publicNodeUrl => {
@@ -112,16 +160,17 @@ export class Blockchain {
});
provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[]));
provider.start();
+ return [provider, undefined];
} else if (doesInjectedWeb3Exist) {
// Since no public node for this network, all requests go to injectedWeb3 instance
- provider = injectedWeb3.currentProvider;
+ return [injectedWeb3.currentProvider, undefined];
} else {
// If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node
// We do this so that users can still browse the 0x Portal DApp even if they do not have web3
// injected into their browser.
- provider = new ProviderEngine();
+ const provider = new ProviderEngine();
provider.addProvider(new FilterSubprovider());
- const networkId = configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_KOVAN;
+ const networkId = constants.NETWORK_ID_MAINNET;
const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], publicNodeUrl => {
return new RpcSubprovider({
rpcUrl: publicNodeUrl,
@@ -129,14 +178,15 @@ export class Blockchain {
});
provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[]));
provider.start();
+ return [provider, undefined];
}
-
- return provider;
}
constructor(dispatcher: Dispatcher) {
this._dispatcher = dispatcher;
const defaultGasPrice = GWEI_IN_WEI * 30;
this._defaultGasPrice = new BigNumber(defaultGasPrice);
+ // We need a unique reference to this function so we can use it to unsubcribe.
+ this._injectedProviderUpdateHandler = this._handleInjectedProviderUpdateAsync.bind(this);
// tslint:disable-next-line:no-floating-promises
this._updateDefaultGasPriceAsync();
// tslint:disable-next-line:no-floating-promises
@@ -186,84 +236,17 @@ export class Blockchain {
this._ledgerSubprovider.setPath(path);
}
public async updateProviderToLedgerAsync(networkId: number): Promise<void> {
- utils.assert(!_.isUndefined(this._contractWrappers), 'Contract Wrappers must be instantiated.');
-
- const isU2FSupported = await utils.isU2FSupportedAsync();
- if (!isU2FSupported) {
- throw new Error('Cannot update providerType to LEDGER without U2F support');
- }
-
- // Cache injected provider so that we can switch the user back to it easily
- if (_.isUndefined(this._cachedProvider)) {
- this._cachedProvider = this._web3Wrapper.getProvider();
- this._cachedProviderNetworkId = this.networkId;
- }
-
- this._blockchainWatcher.destroy();
-
- delete this._userAddressIfExists;
- this._dispatcher.updateUserAddress(undefined); // Clear old userAddress
-
- const provider = new ProviderEngine();
- const ledgerWalletConfigs = {
- networkId,
- ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync,
- };
- this._ledgerSubprovider = new LedgerSubprovider(ledgerWalletConfigs);
- provider.addProvider(this._ledgerSubprovider);
- provider.addProvider(new FilterSubprovider());
- const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], publicNodeUrl => {
- return new RpcSubprovider({
- rpcUrl: publicNodeUrl,
- });
- });
- provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[]));
- provider.start();
- this.networkId = networkId;
- this._dispatcher.updateNetworkId(this.networkId);
const shouldPollUserAddress = false;
- this._web3Wrapper = new Web3Wrapper(provider);
- this._blockchainWatcher = new BlockchainWatcher(
- this._dispatcher,
- this._web3Wrapper,
- this.networkId,
- shouldPollUserAddress,
- );
- this._contractWrappers.setProvider(provider, this.networkId);
- await this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceStateAsync();
- this._dispatcher.updateProviderType(ProviderType.Ledger);
+ const shouldUserLedgerProvider = true;
+ await this._resetOrInitializeAsync(networkId, shouldPollUserAddress, shouldUserLedgerProvider);
}
public async updateProviderToInjectedAsync(): Promise<void> {
- utils.assert(!_.isUndefined(this._contractWrappers), 'Contract Wrappers must be instantiated.');
-
- if (_.isUndefined(this._cachedProvider)) {
- return; // Going from injected to injected, so we noop
- }
-
- this._blockchainWatcher.destroy();
-
- const provider = this._cachedProvider;
- this.networkId = this._cachedProviderNetworkId;
-
const shouldPollUserAddress = true;
- this._web3Wrapper = new Web3Wrapper(provider);
- this._blockchainWatcher = new BlockchainWatcher(
- this._dispatcher,
- this._web3Wrapper,
- this.networkId,
- shouldPollUserAddress,
- );
-
- const userAddresses = await this._web3Wrapper.getAvailableAddressesAsync();
- this._userAddressIfExists = userAddresses[0];
-
- this._contractWrappers.setProvider(provider, this.networkId);
-
- await this.fetchTokenInformationAsync();
- await this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceStateAsync();
- this._dispatcher.updateProviderType(ProviderType.Injected);
- delete this._ledgerSubprovider;
- delete this._cachedProvider;
+ const shouldUserLedgerProvider = false;
+ this._dispatcher.updateBlockchainIsLoaded(false);
+ // We don't want to be out of sync with the network the injected provider declares.
+ const networkId = await Blockchain._getInjectedWeb3ProviderNetworkIdIfExistsAsync();
+ await this._resetOrInitializeAsync(networkId, shouldPollUserAddress, shouldUserLedgerProvider);
}
public async setProxyAllowanceAsync(token: Token, amountInBaseUnits: BigNumber): Promise<void> {
utils.assert(this.isValidAddress(token.address), BlockchainCallErrs.TokenAddressIsInvalid);
@@ -539,6 +522,7 @@ export class Blockchain {
}
public destroy(): void {
this._blockchainWatcher.destroy();
+ this._injectedProviderObservable.unsubscribe(this._injectedProviderUpdateHandler);
this._stopWatchingExchangeLogFillEvents();
}
public async fetchTokenInformationAsync(): Promise<void> {
@@ -634,6 +618,18 @@ export class Blockchain {
private _doesUserAddressExist(): boolean {
return !_.isUndefined(this._userAddressIfExists);
}
+ private async _handleInjectedProviderUpdateAsync(update: InjectedProviderUpdate): Promise<void> {
+ if (update.networkVersion === 'loading' || !_.isUndefined(this._ledgerSubprovider)) {
+ return;
+ }
+ const updatedNetworkId = _.parseInt(update.networkVersion);
+ if (this.networkId === updatedNetworkId) {
+ return;
+ }
+ const shouldPollUserAddress = true;
+ const shouldUserLedgerProvider = false;
+ await this._resetOrInitializeAsync(updatedNetworkId, shouldPollUserAddress, shouldUserLedgerProvider);
+ }
private async _rehydrateStoreWithContractEventsAsync(): Promise<void> {
// Ensure we are only ever listening to one set of events
this._stopWatchingExchangeLogFillEvents();
@@ -776,49 +772,64 @@ export class Blockchain {
}
private async _onPageLoadInitFireAndForgetAsync(): Promise<void> {
await utils.onPageLoadAsync(); // wait for page to load
-
- // Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in
- // order to properly instantiate the web3Wrapper. Since we must use the async call, we cannot
- // retrieve it from within the web3Wrapper constructor. This is and should remain the only
- // call to a web3 instance outside of web3Wrapper in the entire dapp.
- // In addition, if the user has an injectedWeb3 instance that is disconnected from a backing
- // Ethereum node, this call will throw. We need to handle this case gracefully
- const injectedWeb3 = (window as any).web3;
- let networkIdIfExists: number;
- if (!_.isUndefined(injectedWeb3)) {
- try {
- networkIdIfExists = _.parseInt(await promisify<string>(injectedWeb3.version.getNetwork)());
- } catch (err) {
- // Ignore error and proceed with networkId undefined
+ const networkIdIfExists = await Blockchain._getInjectedWeb3ProviderNetworkIdIfExistsAsync();
+ this.networkId = !_.isUndefined(networkIdIfExists) ? networkIdIfExists : constants.NETWORK_ID_MAINNET;
+ const injectedWeb3IfExists = Blockchain._getInjectedWeb3();
+ if (!_.isUndefined(injectedWeb3IfExists) && !_.isUndefined(injectedWeb3IfExists.currentProvider)) {
+ const injectedProviderObservable = injectedWeb3IfExists.currentProvider.publicConfigStore;
+ if (!_.isUndefined(injectedProviderObservable) && _.isUndefined(this._injectedProviderObservable)) {
+ this._injectedProviderObservable = injectedProviderObservable;
+ this._injectedProviderObservable.subscribe(this._injectedProviderUpdateHandler);
}
}
-
- const provider = await Blockchain._getProviderAsync(injectedWeb3, networkIdIfExists);
- this.networkId = !_.isUndefined(networkIdIfExists)
- ? networkIdIfExists
- : configs.IS_MAINNET_ENABLED
- ? constants.NETWORK_ID_MAINNET
- : constants.NETWORK_ID_KOVAN;
- this._dispatcher.updateNetworkId(this.networkId);
- const zeroExConfigs = {
- networkId: this.networkId,
- };
- this._contractWrappers = new ContractWrappers(provider, zeroExConfigs);
- this._updateProviderName(injectedWeb3);
+ this._updateProviderName(injectedWeb3IfExists);
const shouldPollUserAddress = true;
- this._web3Wrapper = new Web3Wrapper(provider);
- this._blockchainWatcher = new BlockchainWatcher(
- this._dispatcher,
- this._web3Wrapper,
- this.networkId,
- shouldPollUserAddress,
+ const shouldUseLedgerProvider = false;
+ await this._resetOrInitializeAsync(this.networkId, shouldPollUserAddress, shouldUseLedgerProvider);
+ }
+ private async _resetOrInitializeAsync(
+ networkId: number,
+ shouldPollUserAddress: boolean = false,
+ shouldUserLedgerProvider: boolean = false,
+ ): Promise<void> {
+ if (!shouldUserLedgerProvider) {
+ this._dispatcher.updateBlockchainIsLoaded(false);
+ }
+ this._dispatcher.updateUserWeiBalance(undefined);
+ this.networkId = networkId;
+ const injectedWeb3IfExists = Blockchain._getInjectedWeb3();
+ const [provider, ledgerSubproviderIfExists] = await Blockchain._getProviderAsync(
+ injectedWeb3IfExists,
+ networkId,
+ shouldUserLedgerProvider,
);
-
- const userAddresses = await this._web3Wrapper.getAvailableAddressesAsync();
- this._userAddressIfExists = userAddresses[0];
- this._dispatcher.updateUserAddress(this._userAddressIfExists);
- await this.fetchTokenInformationAsync();
- await this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceStateAsync();
+ if (!_.isUndefined(this._contractWrappers)) {
+ this._contractWrappers.setProvider(provider, networkId);
+ } else {
+ this._contractWrappers = new ContractWrappers(provider, { networkId });
+ }
+ if (!_.isUndefined(this._blockchainWatcher)) {
+ this._blockchainWatcher.destroy();
+ }
+ this._web3Wrapper = new Web3Wrapper(provider);
+ this._blockchainWatcher = new BlockchainWatcher(this._dispatcher, this._web3Wrapper, shouldPollUserAddress);
+ if (shouldUserLedgerProvider && !_.isUndefined(ledgerSubproviderIfExists)) {
+ delete this._userAddressIfExists;
+ this._ledgerSubprovider = ledgerSubproviderIfExists;
+ this._dispatcher.updateUserAddress(undefined);
+ this._dispatcher.updateProviderType(ProviderType.Ledger);
+ } else {
+ delete this._ledgerSubprovider;
+ const userAddresses = await this._web3Wrapper.getAvailableAddressesAsync();
+ this._userAddressIfExists = userAddresses[0];
+ this._dispatcher.updateUserAddress(this._userAddressIfExists);
+ if (!_.isUndefined(injectedWeb3IfExists)) {
+ this._dispatcher.updateProviderType(ProviderType.Injected);
+ }
+ await this.fetchTokenInformationAsync();
+ }
+ await this._blockchainWatcher.startEmittingUserBalanceStateAsync();
+ this._dispatcher.updateNetworkId(networkId);
await this._rehydrateStoreWithContractEventsAsync();
}
private _updateProviderName(injectedWeb3: Web3): void {
diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts
index c576db6ac..df5f73fd1 100644
--- a/packages/website/ts/blockchain_watcher.ts
+++ b/packages/website/ts/blockchain_watcher.ts
@@ -6,24 +6,17 @@ import { Dispatcher } from 'ts/redux/dispatcher';
export class BlockchainWatcher {
private _dispatcher: Dispatcher;
private _web3Wrapper: Web3Wrapper;
- private _prevNetworkId: number;
private _shouldPollUserAddress: boolean;
- private _watchNetworkAndBalanceIntervalId: NodeJS.Timer;
+ private _watchBalanceIntervalId: NodeJS.Timer;
private _prevUserEtherBalanceInWei?: BigNumber;
private _prevUserAddressIfExists: string;
- constructor(
- dispatcher: Dispatcher,
- web3Wrapper: Web3Wrapper,
- networkIdIfExists: number,
- shouldPollUserAddress: boolean,
- ) {
+ constructor(dispatcher: Dispatcher, web3Wrapper: Web3Wrapper, shouldPollUserAddress: boolean) {
this._dispatcher = dispatcher;
- this._prevNetworkId = networkIdIfExists;
this._shouldPollUserAddress = shouldPollUserAddress;
this._web3Wrapper = web3Wrapper;
}
public destroy(): void {
- this._stopEmittingNetworkConnectionAndUserBalanceState();
+ this._stopEmittingUserBalanceState();
// HACK: stop() is only available on providerEngine instances
const provider = this._web3Wrapper.getProvider();
if (!_.isUndefined((provider as any).stop)) {
@@ -34,36 +27,23 @@ export class BlockchainWatcher {
public updatePrevUserAddress(userAddress: string): void {
this._prevUserAddressIfExists = userAddress;
}
- public async startEmittingNetworkConnectionAndUserBalanceStateAsync(): Promise<void> {
- if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) {
+ public async startEmittingUserBalanceStateAsync(): Promise<void> {
+ if (!_.isUndefined(this._watchBalanceIntervalId)) {
return; // we are already emitting the state
}
this._prevUserEtherBalanceInWei = undefined;
- this._dispatcher.updateNetworkId(this._prevNetworkId);
- await this._updateNetworkAndBalanceAsync();
- this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval(
- this._updateNetworkAndBalanceAsync.bind(this),
+ await this._updateBalanceAsync();
+ this._watchBalanceIntervalId = intervalUtils.setAsyncExcludingInterval(
+ this._updateBalanceAsync.bind(this),
5000,
(err: Error) => {
logUtils.log(`Watching network and balances failed: ${err.stack}`);
- this._stopEmittingNetworkConnectionAndUserBalanceState();
+ this._stopEmittingUserBalanceState();
},
);
}
- private async _updateNetworkAndBalanceAsync(): Promise<void> {
- // Check for network state changes
+ private async _updateBalanceAsync(): Promise<void> {
let prevNodeVersion: string;
- let currentNetworkId;
- try {
- currentNetworkId = await this._web3Wrapper.getNetworkIdAsync();
- } catch (err) {
- // Noop
- }
- if (currentNetworkId !== this._prevNetworkId) {
- this._prevNetworkId = currentNetworkId;
- this._dispatcher.updateNetworkId(currentNetworkId);
- }
-
// Check for node version changes
const currentNodeVersion = await this._web3Wrapper.getNodeVersionAsync();
if (currentNodeVersion !== prevNodeVersion) {
@@ -99,9 +79,9 @@ export class BlockchainWatcher {
this._dispatcher.updateUserWeiBalance(balanceInWei);
}
}
- private _stopEmittingNetworkConnectionAndUserBalanceState(): void {
- if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) {
- intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId);
+ private _stopEmittingUserBalanceState(): void {
+ if (!_.isUndefined(this._watchBalanceIntervalId)) {
+ intervalUtils.clearAsyncExcludingInterval(this._watchBalanceIntervalId);
}
}
}
diff --git a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx
index b968a3147..c8e10303f 100644
--- a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx
+++ b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx
@@ -4,7 +4,6 @@ import FlatButton from 'material-ui/FlatButton';
import * as React from 'react';
import { Blockchain } from 'ts/blockchain';
import { BlockchainErrs } from 'ts/types';
-import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
interface BlockchainErrDialogProps {
@@ -125,8 +124,7 @@ export class BlockchainErrDialog extends React.Component<BlockchainErrDialogProp
Parity Signer Chrome extension
</a>{' '}
lets you connect to a locally running Parity node. Make sure you have started your local Parity node
- with {configs.IS_MAINNET_ENABLED && '`parity ui` or'} `parity --chain kovan ui` in order to connect
- to {configs.IS_MAINNET_ENABLED ? 'mainnet or Kovan respectively.' : 'Kovan.'}
+ with `parity ui` or `parity --chain kovan ui` in order to connect to mainnet or Kovan respectively.
</div>
<div className="pt2">
<span className="bold">Note:</span> If you have done one of the above steps and are still seeing
@@ -142,10 +140,8 @@ export class BlockchainErrDialog extends React.Component<BlockchainErrDialogProp
<div>
The 0x smart contracts are not deployed on the Ethereum network you are currently connected to
(network Id: {this.props.networkId}). In order to use the 0x portal dApp, please connect to the{' '}
- {Networks.Kovan} testnet (network Id: {constants.NETWORK_ID_KOVAN})
- {configs.IS_MAINNET_ENABLED
- ? ` or ${constants.MAINNET_NAME} (network Id: ${constants.NETWORK_ID_MAINNET}).`
- : `.`}
+ {Networks.Kovan} testnet (network Id: {constants.NETWORK_ID_KOVAN}) or ${constants.MAINNET_NAME}{' '}
+ (network Id: ${constants.NETWORK_ID_MAINNET}).
</div>
<h4>Metamask</h4>
<div>
@@ -159,11 +155,8 @@ export class BlockchainErrDialog extends React.Component<BlockchainErrDialogProp
If using the{' '}
<a href={constants.URL_PARITY_CHROME_STORE} target="_blank">
Parity Signer Chrome extension
- </a>, make sure to start your local Parity node with{' '}
- {configs.IS_MAINNET_ENABLED
- ? '`parity ui` or `parity --chain Kovan ui` in order to connect to mainnet \
- or Kovan respectively.'
- : '`parity --chain kovan ui` in order to connect to Kovan.'}
+ </a>, make sure to start your local Parity node with `parity ui` or `parity --chain Kovan ui` in
+ order to connect to mainnet \ or Kovan respectively.
</div>
</div>
);
diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
index c9727b553..38e4732a4 100644
--- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
+++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
@@ -282,6 +282,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
if (didSucceed) {
this.setState({
stepIndex: LedgerSteps.SELECT_ADDRESS,
+ connectionErrMsg: '',
});
}
return didSucceed;
diff --git a/packages/website/ts/components/legacy_portal/legacy_portal.tsx b/packages/website/ts/components/legacy_portal/legacy_portal.tsx
index 9a54f3474..c85d97207 100644
--- a/packages/website/ts/components/legacy_portal/legacy_portal.tsx
+++ b/packages/website/ts/components/legacy_portal/legacy_portal.tsx
@@ -23,7 +23,6 @@ import { GenerateOrderForm } from 'ts/containers/generate_order_form';
import { localStorage } from 'ts/local_storage/local_storage';
import { Dispatcher } from 'ts/redux/dispatcher';
import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAddress, WebsitePaths } from 'ts/types';
-import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
import { orderParser } from 'ts/utils/order_parser';
import { Translate } from 'ts/utils/translate';
@@ -170,67 +169,53 @@ export class LegacyPortal extends React.Component<LegacyPortalProps, LegacyPorta
/>
<div id="portal" className="mx-auto max-width-4" style={{ width: '100%' }}>
<Paper className="mb3 mt2">
- {!configs.IS_MAINNET_ENABLED && this.props.networkId === constants.NETWORK_ID_MAINNET ? (
- <div className="p3 center">
- <div className="h2 py2">Mainnet unavailable</div>
- <div className="mx-auto pb2 pt2">
- <img src="/images/zrx_token.png" style={{ width: 150 }} />
- </div>
- <div>
- 0x portal is currently unavailable on the Ethereum mainnet.
- <div>To try it out, switch to the Kovan test network (networkId: 42).</div>
- <div className="py2">Check back soon!</div>
- </div>
+ <div className="mx-auto flex">
+ <div className="col col-2 pr2 pt1 sm-hide xs-hide" style={portalMenuContainerStyle}>
+ <LegacyPortalMenu menuItemStyle={{ color: colors.white }} />
</div>
- ) : (
- <div className="mx-auto flex">
- <div className="col col-2 pr2 pt1 sm-hide xs-hide" style={portalMenuContainerStyle}>
- <LegacyPortalMenu menuItemStyle={{ color: colors.white }} />
- </div>
- <div className="col col-12 lg-col-10 md-col-10 sm-col sm-col-12">
- <div className="py2" style={{ backgroundColor: colors.grey50 }}>
- {this.props.blockchainIsLoaded ? (
- <Switch>
- <Route
- path={`${WebsitePaths.Portal}/weth`}
- render={this._renderEthWrapper.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Portal}/fill`}
- render={this._renderFillOrder.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Portal}/balances`}
- render={this._renderTokenBalances.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Portal}/trades`}
- render={this._renderTradeHistory.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Home}`}
- render={this._renderGenerateOrderForm.bind(this)}
- />
- </Switch>
- ) : (
- <div className="pt4 sm-px2 sm-pt2 sm-m1" style={{ height: 500 }}>
- <div
- className="relative sm-px2 sm-pt2 sm-m1"
- style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }}
- >
- <div className="center pb2">
- <CircularProgress size={40} thickness={5} />
- </div>
- <div className="center pt2" style={{ paddingBottom: 11 }}>
- Loading Portal...
- </div>
+ <div className="col col-12 lg-col-10 md-col-10 sm-col sm-col-12">
+ <div className="py2" style={{ backgroundColor: colors.grey50 }}>
+ {this.props.blockchainIsLoaded ? (
+ <Switch>
+ <Route
+ path={`${WebsitePaths.Portal}/weth`}
+ render={this._renderEthWrapper.bind(this)}
+ />
+ <Route
+ path={`${WebsitePaths.Portal}/fill`}
+ render={this._renderFillOrder.bind(this)}
+ />
+ <Route
+ path={`${WebsitePaths.Portal}/balances`}
+ render={this._renderTokenBalances.bind(this)}
+ />
+ <Route
+ path={`${WebsitePaths.Portal}/trades`}
+ render={this._renderTradeHistory.bind(this)}
+ />
+ <Route
+ path={`${WebsitePaths.Home}`}
+ render={this._renderGenerateOrderForm.bind(this)}
+ />
+ </Switch>
+ ) : (
+ <div className="pt4 sm-px2 sm-pt2 sm-m1" style={{ height: 500 }}>
+ <div
+ className="relative sm-px2 sm-pt2 sm-m1"
+ style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }}
+ >
+ <div className="center pb2">
+ <CircularProgress size={40} thickness={5} />
+ </div>
+ <div className="center pt2" style={{ paddingBottom: 11 }}>
+ Loading Portal...
</div>
</div>
- )}
- </div>
+ </div>
+ )}
</div>
</div>
- )}
+ </div>
</Paper>
<BlockchainErrDialog
blockchain={this._blockchain}
@@ -249,16 +234,14 @@ export class LegacyPortal extends React.Component<LegacyPortalProps, LegacyPorta
onToggleDialog={this._onPortalDisclaimerAccepted.bind(this)}
/>
<FlashMessage dispatcher={this.props.dispatcher} flashMessage={this.props.flashMessage} />
- {this.props.blockchainIsLoaded && (
- <LedgerConfigDialog
- providerType={this.props.providerType}
- networkId={this.props.networkId}
- blockchain={this._blockchain}
- dispatcher={this.props.dispatcher}
- toggleDialogFn={this.onToggleLedgerDialog.bind(this)}
- isOpen={this.state.isLedgerDialogOpen}
- />
- )}
+ <LedgerConfigDialog
+ providerType={this.props.providerType}
+ networkId={this.props.networkId}
+ blockchain={this._blockchain}
+ dispatcher={this.props.dispatcher}
+ toggleDialogFn={this.onToggleLedgerDialog.bind(this)}
+ isOpen={this.state.isLedgerDialogOpen}
+ />
</div>
<Footer translate={this.props.translate} dispatcher={this.props.dispatcher} />
</div>
diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
index 10d4af30e..296b410fe 100644
--- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
+++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
@@ -159,9 +159,11 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
const ethToken = utils.getEthToken(this.props.tokenByAddress);
const zrxToken = utils.getZrxToken(this.props.tokenByAddress);
if (ethToken && zrxToken) {
- const ethTokenAllowance = this.props.trackedTokenStateByAddress[ethToken.address].allowance;
- const zrxTokenAllowance = this.props.trackedTokenStateByAddress[zrxToken.address].allowance;
- return ethTokenAllowance > new BigNumber(0) && zrxTokenAllowance > new BigNumber(0);
+ const ethTokenState = this.props.trackedTokenStateByAddress[ethToken.address];
+ const zrxTokenState = this.props.trackedTokenStateByAddress[zrxToken.address];
+ if (ethTokenState && zrxTokenState) {
+ return ethTokenState.allowance.gt(0) && zrxTokenState.allowance.gt(0);
+ }
}
return false;
}
@@ -221,12 +223,15 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
if (!token) {
return null;
}
- const tokenState = this.props.trackedTokenStateByAddress[token.address];
+ const tokenStateIfExists = this.props.trackedTokenStateByAddress[token.address];
+ if (_.isUndefined(tokenStateIfExists)) {
+ return null;
+ }
return (
<AllowanceToggle
token={token}
- tokenState={tokenState}
- isDisabled={!tokenState.isLoaded}
+ tokenState={tokenStateIfExists}
+ isDisabled={!tokenStateIfExists.isLoaded}
blockchain={this.props.blockchain}
// tslint:disable-next-line:jsx-no-lambda
refetchTokenStateAsync={async () => this.props.refetchTokenStateAsync(token.address)}
diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx
index 9498cb388..438c7b52f 100644
--- a/packages/website/ts/components/portal/portal.tsx
+++ b/packages/website/ts/components/portal/portal.tsx
@@ -152,9 +152,8 @@ export class Portal extends React.Component<PortalProps, PortalState> {
}
public componentDidUpdate(prevProps: PortalProps): void {
if (!prevProps.blockchainIsLoaded && this.props.blockchainIsLoaded) {
- const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress);
// tslint:disable-next-line:no-floating-promises
- this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses);
+ this._fetchBalancesAndAllowancesAsync(this._getCurrentTrackedTokensAddresses());
}
}
public componentWillReceiveProps(nextProps: PortalProps): void {
@@ -182,14 +181,14 @@ export class Portal extends React.Component<PortalProps, PortalState> {
prevPathname: nextProps.location.pathname,
});
}
+
+ // If the address changed, but the network did not, we can just refetch the currently tracked tokens.
if (
- nextProps.userAddress !== this.props.userAddress ||
- nextProps.networkId !== this.props.networkId ||
+ (nextProps.userAddress !== this.props.userAddress && nextProps.networkId === this.props.networkId) ||
nextProps.lastForceTokenStateRefetch !== this.props.lastForceTokenStateRefetch
) {
- const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress);
// tslint:disable-next-line:no-floating-promises
- this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses);
+ this._fetchBalancesAndAllowancesAsync(this._getCurrentTrackedTokensAddresses());
}
const nextTrackedTokens = utils.getTrackedTokens(nextProps.tokenByAddress);
@@ -200,7 +199,7 @@ export class Portal extends React.Component<PortalProps, PortalState> {
const newTokenAddresses = _.map(newTokens, token => token.address);
// Add placeholder entry for this token to the state, since fetching the
// balance/allowance is asynchronous
- const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress;
+ const trackedTokenStateByAddress = { ...this.state.trackedTokenStateByAddress };
for (const tokenAddress of newTokenAddresses) {
trackedTokenStateByAddress[tokenAddress] = {
balance: new BigNumber(0),
@@ -265,16 +264,16 @@ export class Portal extends React.Component<PortalProps, PortalState> {
networkId={this.props.networkId}
/>
<FlashMessage dispatcher={this.props.dispatcher} flashMessage={this.props.flashMessage} />
- {this.props.blockchainIsLoaded && (
- <LedgerConfigDialog
- providerType={this.props.providerType}
- networkId={this.props.networkId}
- blockchain={this._blockchain}
- dispatcher={this.props.dispatcher}
- toggleDialogFn={this._onToggleLedgerDialog.bind(this)}
- isOpen={this.state.isLedgerDialogOpen}
- />
- )}
+
+ <LedgerConfigDialog
+ providerType={this.props.providerType}
+ networkId={this.props.networkId}
+ blockchain={this._blockchain}
+ dispatcher={this.props.dispatcher}
+ toggleDialogFn={this._onToggleLedgerDialog.bind(this)}
+ isOpen={this.state.isLedgerDialogOpen}
+ />
+
<AssetPicker
userAddress={this.props.userAddress}
networkId={this.props.networkId}
@@ -608,11 +607,12 @@ export class Portal extends React.Component<PortalProps, PortalState> {
const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm;
return isSmallScreen;
}
-
private _getCurrentTrackedTokens(): Token[] {
return utils.getTrackedTokens(this.props.tokenByAddress);
}
-
+ private _getCurrentTrackedTokensAddresses(): string[] {
+ return _.map(this._getCurrentTrackedTokens(), token => token.address);
+ }
private _getInitialTrackedTokenStateByAddress(trackedTokens: Token[]): TokenStateByAddress {
const trackedTokenStateByAddress: TokenStateByAddress = {};
_.each(trackedTokens, token => {
diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx
index 537edc7bb..fac6c131f 100644
--- a/packages/website/ts/components/top_bar/top_bar.tsx
+++ b/packages/website/ts/components/top_bar/top_bar.tsx
@@ -13,7 +13,6 @@ import { ProviderDisplay } from 'ts/components/top_bar/provider_display';
import { TopBarMenuItem } from 'ts/components/top_bar/top_bar_menu_item';
import { DropDown } from 'ts/components/ui/drop_down';
import { Dispatcher } from 'ts/redux/dispatcher';
-import { zIndex } from 'ts/style/z_index';
import { Deco, Key, ProviderType, WebsiteLegacyPaths, WebsitePaths } from 'ts/types';
import { constants } from 'ts/utils/constants';
import { Translate } from 'ts/utils/translate';
@@ -58,7 +57,6 @@ const styles: Styles = {
width: '100%',
position: 'relative',
top: 0,
- zIndex: zIndex.topBar,
paddingBottom: 1,
},
bottomBar: {
diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts
index 11ae2e0ba..2db947ba3 100644
--- a/packages/website/ts/types.ts
+++ b/packages/website/ts/types.ts
@@ -488,6 +488,16 @@ export enum Providers {
Mist = 'MIST',
}
+export interface InjectedProviderUpdate {
+ selectedAddress: string;
+ networkVersion: string;
+}
+
+export interface InjectedProviderObservable {
+ subscribe(updateHandler: (update: InjectedProviderUpdate) => void): void;
+ unsubscribe(updateHandler: (update: InjectedProviderUpdate) => void): void;
+}
+
export interface TimestampMsRange {
startTimestampMs: number;
endTimestampMs: number;
diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts
index 36c067f84..2f89f8ccb 100644
--- a/packages/website/ts/utils/configs.ts
+++ b/packages/website/ts/utils/configs.ts
@@ -63,7 +63,6 @@ export const configs = {
TKN: '/images/token_icons/tokencard.png',
TRST: '/images/token_icons/trust.png',
} as { [symbol: string]: string },
- IS_MAINNET_ENABLED: true,
GOOGLE_ANALYTICS_ID: 'UA-98720122-1',
LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE: '2017-11-22',
LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE: '2018-6-25',
diff --git a/yarn.lock b/yarn.lock
index 996dd28a9..e2c5ae483 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -44,6 +44,24 @@
ethereumjs-util "5.1.5"
lodash "4.17.10"
+"@0xproject/order-watcher@^0.0.6":
+ version "0.0.6"
+ resolved "https://registry.npmjs.org/@0xproject/order-watcher/-/order-watcher-0.0.6.tgz#85a8fb21e5755bb555f427b12d64d10b89b332e6"
+ dependencies:
+ "@0xproject/assert" "^0.2.12"
+ "@0xproject/base-contract" "^0.3.4"
+ "@0xproject/contract-wrappers" "^0.0.5"
+ "@0xproject/fill-scenarios" "^0.0.4"
+ "@0xproject/json-schemas" "^0.8.1"
+ "@0xproject/order-utils" "^0.0.7"
+ "@0xproject/types" "^0.8.1"
+ "@0xproject/typescript-typings" "^0.4.1"
+ "@0xproject/utils" "^0.7.1"
+ "@0xproject/web3-wrapper" "^0.7.1"
+ bintrees "1.0.2"
+ ethers "3.0.22"
+ lodash "4.17.10"
+
"@0xproject/types@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-0.5.0.tgz#ba3cfbc11a8c6344b57c9680aa7df2ea84b9bf05"
@@ -1708,7 +1726,7 @@ bindings@^1.2.1, bindings@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7"
-bintrees@^1.0.2:
+bintrees@1.0.2, bintrees@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8"