From 53e2cda4c8d6698b5c4b69f510ad8764cb089306 Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 22 Jun 2018 15:37:42 -0700 Subject: Remove existing networkId watching logic from BlockchainWatcher --- packages/website/ts/blockchain.ts | 6 ++--- packages/website/ts/blockchain_watcher.ts | 37 ++++++++++--------------------- 2 files changed, 15 insertions(+), 28 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 46a4d6629..11202eb64 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -229,7 +229,7 @@ export class Blockchain { shouldPollUserAddress, ); this._contractWrappers.setProvider(provider, this.networkId); - await this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceStateAsync(); + await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); this._dispatcher.updateProviderType(ProviderType.Ledger); } public async updateProviderToInjectedAsync(): Promise { @@ -259,7 +259,7 @@ export class Blockchain { this._contractWrappers.setProvider(provider, this.networkId); await this.fetchTokenInformationAsync(); - await this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceStateAsync(); + await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); this._dispatcher.updateProviderType(ProviderType.Injected); delete this._ledgerSubprovider; delete this._cachedProvider; @@ -816,7 +816,7 @@ export class Blockchain { this._userAddressIfExists = userAddresses[0]; this._dispatcher.updateUserAddress(this._userAddressIfExists); await this.fetchTokenInformationAsync(); - await this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceStateAsync(); + await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); 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..8ae5e7797 100644 --- a/packages/website/ts/blockchain_watcher.ts +++ b/packages/website/ts/blockchain_watcher.ts @@ -8,7 +8,7 @@ export class BlockchainWatcher { private _web3Wrapper: Web3Wrapper; private _prevNetworkId: number; private _shouldPollUserAddress: boolean; - private _watchNetworkAndBalanceIntervalId: NodeJS.Timer; + private _watchBalanceIntervalId: NodeJS.Timer; private _prevUserEtherBalanceInWei?: BigNumber; private _prevUserAddressIfExists: string; constructor( @@ -23,7 +23,7 @@ export class BlockchainWatcher { 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 +34,23 @@ export class BlockchainWatcher { public updatePrevUserAddress(userAddress: string): void { this._prevUserAddressIfExists = userAddress; } - public async startEmittingNetworkConnectionAndUserBalanceStateAsync(): Promise { - if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) { + public async startEmittingUserBalanceStateAsync(): Promise { + 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 { - // Check for network state changes + private async _updateBalanceAsync(): Promise { 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 +86,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); } } } -- cgit v1.2.3 From c4e2dcafa4bd5f4b905680e2c1b80d6ddb6ab6fb Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 25 Jun 2018 16:26:34 -0700 Subject: Have basic network switching working --- packages/website/ts/blockchain.ts | 224 +++++++++++---------- packages/website/ts/blockchain_watcher.ts | 22 +- .../onboarding/portal_onboarding_flow.tsx | 11 +- packages/website/ts/components/portal/portal.tsx | 10 +- packages/website/ts/components/top_bar/top_bar.tsx | 1 - packages/website/ts/types.ts | 10 + 6 files changed, 158 insertions(+), 120 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 11202eb64..16ed8d03a 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -50,6 +50,8 @@ import { SideToAssetToken, Token, TokenByAddress, + InjectedProviderObservable, + InjectedProviderUpdate, } from 'ts/types'; import { backendClient } from 'ts/utils/backend_client'; import { configs } from 'ts/utils/configs'; @@ -79,9 +81,8 @@ export class Blockchain { private _dispatcher: Dispatcher; private _web3Wrapper?: Web3Wrapper; private _blockchainWatcher?: BlockchainWatcher; + private _injectedProviderObservable?: InjectedProviderObservable; private _userAddressIfExists: string; - private _cachedProvider: Provider; - private _cachedProviderNetworkId: number; private _ledgerSubprovider: LedgerSubprovider; private _defaultGasPrice: BigNumber; private static _getNameGivenProvider(provider: Provider): string { @@ -92,13 +93,62 @@ export class Blockchain { } return providerNameIfExists; } - private static async _getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number): Promise { + private static _getInjectedWeb3(): any { + return (window as any).web3; + } + private static _getFallbackNetworkId(): number { + return configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_KOVAN; + } + private static async _getInjectedWeb3ProviderNetworkIdIfExistsAsync(): Promise { + // 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 = Blockchain._getInjectedWeb3(); + let networkIdIfExists: number; + if (!_.isUndefined(injectedWeb3)) { + try { + networkIdIfExists = _.parseInt(await promisify(injectedWeb3.version.getNetwork)()); + } catch (err) { + // Ignore error and proceed with networkId undefined + } + } + return networkIdIfExists; + } + private static async _getProviderAsync( + injectedWeb3: Web3, + networkIdIfExists: number, + userLedgerProvider: boolean = false, + ): Promise { const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); + const isNetworkIdDefined = !_.isUndefined(networkIdIfExists); const publicNodeUrlsIfExistsForNetworkId = configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); let provider; - if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { + if (userLedgerProvider && isNetworkIdDefined) { + const isU2FSupported = await utils.isU2FSupportedAsync(); + if (!isU2FSupported) { + throw new Error('Cannot update providerType to LEDGER without U2F support'); + } + 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(); + } 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(); @@ -120,7 +170,7 @@ export class Blockchain { // injected into their browser. provider = new ProviderEngine(); provider.addProvider(new FilterSubprovider()); - const networkId = configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_KOVAN; + const networkId = Blockchain._getFallbackNetworkId(); const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], publicNodeUrl => { return new RpcSubprovider({ rpcUrl: publicNodeUrl, @@ -141,6 +191,7 @@ export class Blockchain { // tslint:disable-next-line:no-floating-promises this._onPageLoadInitFireAndForgetAsync(); } + // TODO: Investigate if we need this. public async networkIdUpdatedFireAndForgetAsync(newNetworkId: number): Promise { const isConnected = !_.isUndefined(newNetworkId); if (!isConnected) { @@ -185,84 +236,14 @@ export class Blockchain { this._ledgerSubprovider.setPath(path); } public async updateProviderToLedgerAsync(networkId: number): Promise { - 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.startEmittingUserBalanceStateAsync(); - this._dispatcher.updateProviderType(ProviderType.Ledger); + const useLedgerProvider = true; + await this._resetOrInitializeAsync(networkId, shouldPollUserAddress, useLedgerProvider); } public async updateProviderToInjectedAsync(): Promise { - 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.startEmittingUserBalanceStateAsync(); - this._dispatcher.updateProviderType(ProviderType.Injected); - delete this._ledgerSubprovider; - delete this._cachedProvider; + const userLedgerProvider = false; + await this._resetOrInitializeAsync(this.networkId, shouldPollUserAddress, userLedgerProvider); } public async setProxyAllowanceAsync(token: Token, amountInBaseUnits: BigNumber): Promise { utils.assert(this.isValidAddress(token.address), BlockchainCallErrs.TokenAddressIsInvalid); @@ -632,6 +613,19 @@ export class Blockchain { private _doesUserAddressExist(): boolean { return !_.isUndefined(this._userAddressIfExists); } + private async _handleInjectedProviderUpdateAsync(update: InjectedProviderUpdate): Promise { + if (update.networkVersion === 'loading') { + // Who comes up with this stuff. + return; + } + const updatedNetworkId = _.parseInt(update.networkVersion); + if (this.networkId === updatedNetworkId) { + return; + } + const shouldPollUserAddress = true; + const useLedgerProvider = false; + await this._resetOrInitializeAsync(updatedNetworkId, shouldPollUserAddress, useLedgerProvider); + } private async _rehydrateStoreWithContractEventsAsync(): Promise { // Ensure we are only ever listening to one set of events this._stopWatchingExchangeLogFillEvents(); @@ -774,36 +768,37 @@ export class Blockchain { } private async _onPageLoadInitFireAndForgetAsync(): Promise { 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(injectedWeb3.version.getNetwork)()); - } catch (err) { - // Ignore error and proceed with networkId undefined + const networkIdIfExists = await Blockchain._getInjectedWeb3ProviderNetworkIdIfExistsAsync(); + this.networkId = !_.isUndefined(networkIdIfExists) ? networkIdIfExists : Blockchain._getFallbackNetworkId(); + const injectedWeb3 = Blockchain._getInjectedWeb3(); + if (injectedWeb3) { + const injectedProviderObservable = injectedWeb3.currentProvider.publicConfigStore; + if (injectedProviderObservable && !this._injectedProviderObservable) { + this._injectedProviderObservable = injectedProviderObservable; + this._injectedProviderObservable.subscribe(this._handleInjectedProviderUpdateAsync.bind(this)); } } - - 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); const shouldPollUserAddress = true; + const shouldUseLedger = false; + await this._resetOrInitializeAsync(this.networkId, shouldPollUserAddress, shouldUseLedger); + } + private async _resetOrInitializeAsync( + networkId: number, + shouldPollUserAddress: boolean = false, + useLedgerProvider: boolean = false, + ): Promise { + this.networkId = networkId; + this._dispatcher.updateNetworkId(networkId); + const injectedWeb3 = Blockchain._getInjectedWeb3(); + const provider = await Blockchain._getProviderAsync(injectedWeb3, networkId, useLedgerProvider); + // 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, @@ -811,10 +806,19 @@ export class Blockchain { this.networkId, shouldPollUserAddress, ); - - const userAddresses = await this._web3Wrapper.getAvailableAddressesAsync(); - this._userAddressIfExists = userAddresses[0]; - this._dispatcher.updateUserAddress(this._userAddressIfExists); + if (useLedgerProvider) { + // TODO: why? + delete this._userAddressIfExists; + 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); + this._updateProviderName(injectedWeb3); + } + // TOOD: should not call this in ledger case? await this.fetchTokenInformationAsync(); await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); await this._rehydrateStoreWithContractEventsAsync(); diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts index 8ae5e7797..5d029d4f1 100644 --- a/packages/website/ts/blockchain_watcher.ts +++ b/packages/website/ts/blockchain_watcher.ts @@ -7,6 +7,7 @@ export class BlockchainWatcher { private _dispatcher: Dispatcher; private _web3Wrapper: Web3Wrapper; private _prevNetworkId: number; + private _isWatchingNetworkId: boolean = false; private _shouldPollUserAddress: boolean; private _watchBalanceIntervalId: NodeJS.Timer; private _prevUserEtherBalanceInWei?: BigNumber; @@ -18,8 +19,8 @@ export class BlockchainWatcher { shouldPollUserAddress: boolean, ) { this._dispatcher = dispatcher; - this._prevNetworkId = networkIdIfExists; this._shouldPollUserAddress = shouldPollUserAddress; + this._prevNetworkId = networkIdIfExists; this._web3Wrapper = web3Wrapper; } public destroy(): void { @@ -34,6 +35,16 @@ export class BlockchainWatcher { public updatePrevUserAddress(userAddress: string): void { this._prevUserAddressIfExists = userAddress; } + // public async startEmittingInjectedProviderNetworkIdAsync(injectedProvider: any): Promise { + // if (this._isWatchingNetworkId) { + // return; // we are already watching the network id + // } + // const observable = injectedProvider.publicConfigStore; + // if (observable && observable.subscribe) { + // observable.subscribe(this._handleInjectedProviderUpdate.bind(this)); + // this._isWatchingNetworkId = true; + // } + // } public async startEmittingUserBalanceStateAsync(): Promise { if (!_.isUndefined(this._watchBalanceIntervalId)) { return; // we are already emitting the state @@ -49,6 +60,15 @@ export class BlockchainWatcher { }, ); } + // private _handleInjectedProviderUpdate(update: InjectedProviderUpdate): void { + // const updatedNetworkId = _.parseInt(update.networkVersion); + // if (this._prevNetworkId === updatedNetworkId) { + // return; + // } + // this._prevNetworkId = updatedNetworkId; + // this._dispatcher.updateNetworkId(updatedNetworkId); + // this._updateBalanceAsync(); + // } private async _updateBalanceAsync(): Promise { let prevNodeVersion: string; // Check for node version changes diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index 10d4af30e..35b51140d 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 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 > new BigNumber(0) && zrxTokenState.allowance > new BigNumber(0); + } } return false; } @@ -222,6 +224,9 @@ class PlainPortalOnboardingFlow extends React.Component { this.props.dispatcher.resetState(); } 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); - } + // if (!prevProps.blockchainIsLoaded && this.props.blockchainIsLoaded) { + // const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); + // // tslint:disable-next-line:no-floating-promises + // this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); + // } } public componentWillReceiveProps(nextProps: PortalProps): void { if (nextProps.networkId !== this.state.prevNetworkId) { diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index 537edc7bb..85578b116 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -58,7 +58,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 d00154652..411fc2233 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; -- cgit v1.2.3 From e52d0a351265616d58d9126404db7a359a6648be Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 25 Jun 2018 16:14:04 -0700 Subject: Change metamask install link based on browser --- packages/website/package.json | 1 + .../components/wallet/wallet_disconnected_item.tsx | 47 +++++++++++++++------- packages/website/ts/types.ts | 7 ++++ packages/website/ts/utils/constants.ts | 3 ++ packages/website/ts/utils/utils.ts | 13 ++++++ 5 files changed, 57 insertions(+), 14 deletions(-) (limited to 'packages/website') diff --git a/packages/website/package.json b/packages/website/package.json index a2ac617eb..c7b689356 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -29,6 +29,7 @@ "accounting": "^0.4.1", "basscss": "^8.0.3", "blockies": "^0.0.2", + "bowser": "^1.9.3", "deep-equal": "^1.0.1", "ethereumjs-util": "^5.1.1", "find-versions": "^2.0.0", diff --git a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx index 1015dce29..024b28544 100644 --- a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx +++ b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx @@ -3,7 +3,7 @@ import FlatButton from 'material-ui/FlatButton'; import * as React from 'react'; import { colors } from 'ts/style/colors'; -import { ProviderType } from 'ts/types'; +import { BrowserType, ProviderType } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; @@ -66,16 +66,35 @@ interface ProviderButtonProps { isExternallyInjectedProvider: boolean; } -const ProviderButton: React.StatelessComponent = (props: ProviderButtonProps) => ( - } - style={props.isExternallyInjectedProvider ? styles.button : { ...styles.button, ...styles.hrefAdjustment }} - href={props.isExternallyInjectedProvider ? undefined : constants.URL_METAMASK_CHROME_STORE} - target={props.isExternallyInjectedProvider ? undefined : '_blank'} - disabled={props.isExternallyInjectedProvider} - /> -); +const ProviderButton: React.StatelessComponent = (props: ProviderButtonProps) => { + const browserType = utils.getBrowserType(); + let extensionLink; + if (!props.isExternallyInjectedProvider) { + switch (browserType) { + case BrowserType.Chrome: + extensionLink = constants.URL_METAMASK_CHROME_STORE; + break; + case BrowserType.Firefox: + extensionLink = constants.URL_METAMASK_FIREFOX_STORE; + break; + case BrowserType.Opera: + extensionLink = constants.URL_METAMASK_OPERA_STORE; + break; + default: + extensionLink = constants.URL_METAMASK_HOMEPAGE; + } + } + return ( + } + style={props.isExternallyInjectedProvider ? styles.button : { ...styles.button, ...styles.hrefAdjustment }} + href={extensionLink} + target={props.isExternallyInjectedProvider ? undefined : '_blank'} + disabled={props.isExternallyInjectedProvider} + /> + ); +}; diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index d00154652..8a88c31b2 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -546,4 +546,11 @@ export interface WebsiteBackendJobInfo { office: string; url: string; } + +export enum BrowserType { + Chrome = 'Chrome', + Firefox = 'Firefox', + Opera = 'Opera', + Other = 'Other', +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index 25670ef27..a3f8eacb0 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -70,6 +70,9 @@ export const constants = { URL_GITHUB_ORG: 'https://github.com/0xProject', URL_GITHUB_WIKI: 'https://github.com/0xProject/wiki', URL_METAMASK_CHROME_STORE: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn', + URL_METAMASK_FIREFOX_STORE: 'https://addons.mozilla.org/en-US/firefox/addon/ether-metamask/', + URL_METAMASK_HOMEPAGE: 'https://metamask.io/', + URL_METAMASK_OPERA_STORE: 'https://addons.opera.com/en/extensions/details/metamask/', URL_MIST_DOWNLOAD: 'https://github.com/ethereum/mist/releases', URL_PARITY_CHROME_STORE: 'https://chrome.google.com/webstore/detail/parity-ethereum-integrati/himekenlppkgeaoeddcliojfddemadig', diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index 0cb965f05..ff06d923d 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -4,11 +4,13 @@ import { constants as sharedConstants, Networks } from '@0xproject/react-shared' import { ECSignature, Provider } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as bowser from 'bowser'; import deepEqual = require('deep-equal'); import * as _ from 'lodash'; import * as moment from 'moment'; import { BlockchainCallErrs, + BrowserType, Environments, Order, Providers, @@ -368,4 +370,15 @@ export const utils = { isMobile(screenWidth: ScreenWidths): boolean { return screenWidth === ScreenWidths.Sm; }, + getBrowserType(): BrowserType { + if (bowser.chrome) { + return BrowserType.Chrome; + } else if (bowser.firefox) { + return BrowserType.Firefox; + } else if (bowser.opera) { + return BrowserType.Opera; + } else { + return BrowserType.Other; + } + }, }; -- cgit v1.2.3 From 3a808fe7fde04585b8a9c728c6266ab5f0f20c33 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 26 Jun 2018 10:40:51 -0700 Subject: Update relayer grid fallback icon --- packages/website/public/images/relayer_fallback.png | Bin 0 -> 4707 bytes .../ts/components/relayer_index/relayer_grid_tile.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 packages/website/public/images/relayer_fallback.png (limited to 'packages/website') diff --git a/packages/website/public/images/relayer_fallback.png b/packages/website/public/images/relayer_fallback.png new file mode 100644 index 000000000..587fc9ef7 Binary files /dev/null and b/packages/website/public/images/relayer_fallback.png differ diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index 18b069ae2..b26bf512b 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -54,7 +54,7 @@ const styles: Styles = { }, }; -const FALLBACK_IMG_SRC = '/images/landing/hero_chip_image.png'; +const FALLBACK_IMG_SRC = '/images/relayer_fallback.png'; const FALLBACK_PRIMARY_COLOR = colors.grey200; const NO_CONTENT_MESSAGE = '--'; const RELAYER_ICON_HEIGHT = '110px'; -- cgit v1.2.3 From f70c1ff009385266d694269c2c4dd0e9fb38dc46 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 26 Jun 2018 11:01:50 -0700 Subject: Ledger to metamask to ledger working --- packages/website/ts/blockchain.ts | 51 ++++++++++++++---------- packages/website/ts/blockchain_watcher.ts | 19 --------- packages/website/ts/components/portal/portal.tsx | 7 ++-- 3 files changed, 35 insertions(+), 42 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 16ed8d03a..d55cb6771 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -121,19 +121,18 @@ export class Blockchain { injectedWeb3: Web3, networkIdIfExists: number, userLedgerProvider: boolean = false, - ): Promise { + ): Promise<[Provider, LedgerSubprovider]> { const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); const isNetworkIdDefined = !_.isUndefined(networkIdIfExists); const publicNodeUrlsIfExistsForNetworkId = configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); - let provider; if (userLedgerProvider && isNetworkIdDefined) { const isU2FSupported = await utils.isU2FSupportedAsync(); if (!isU2FSupported) { throw new Error('Cannot update providerType to LEDGER without U2F support'); } - provider = new ProviderEngine(); + const provider = new ProviderEngine(); const ledgerWalletConfigs = { networkId: networkIdIfExists, ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync, @@ -148,10 +147,11 @@ export class Blockchain { }); 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 => { @@ -161,14 +161,15 @@ 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 = Blockchain._getFallbackNetworkId(); const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], publicNodeUrl => { @@ -178,9 +179,8 @@ export class Blockchain { }); provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[])); provider.start(); + return [provider, undefined]; } - - return provider; } constructor(dispatcher: Dispatcher) { this._dispatcher = dispatcher; @@ -243,6 +243,9 @@ export class Blockchain { public async updateProviderToInjectedAsync(): Promise { const shouldPollUserAddress = true; const userLedgerProvider = false; + this._dispatcher.updateBlockchainIsLoaded(false); + // We don't want to be out of sync with the network metamask declares. + const networkId = await Blockchain._getInjectedWeb3ProviderNetworkIdIfExistsAsync(); await this._resetOrInitializeAsync(this.networkId, shouldPollUserAddress, userLedgerProvider); } public async setProxyAllowanceAsync(token: Token, amountInBaseUnits: BigNumber): Promise { @@ -614,8 +617,7 @@ export class Blockchain { return !_.isUndefined(this._userAddressIfExists); } private async _handleInjectedProviderUpdateAsync(update: InjectedProviderUpdate): Promise { - if (update.networkVersion === 'loading') { - // Who comes up with this stuff. + if (update.networkVersion === 'loading' || !_.isUndefined(this._ledgerSubprovider)) { return; } const updatedNetworkId = _.parseInt(update.networkVersion); @@ -778,6 +780,7 @@ export class Blockchain { this._injectedProviderObservable.subscribe(this._handleInjectedProviderUpdateAsync.bind(this)); } } + this._updateProviderName(injectedWeb3); const shouldPollUserAddress = true; const shouldUseLedger = false; await this._resetOrInitializeAsync(this.networkId, shouldPollUserAddress, shouldUseLedger); @@ -787,15 +790,20 @@ export class Blockchain { shouldPollUserAddress: boolean = false, useLedgerProvider: boolean = false, ): Promise { + this._dispatcher.updateBlockchainIsLoaded(false); + this._dispatcher.updateUserWeiBalance(undefined); this.networkId = networkId; - this._dispatcher.updateNetworkId(networkId); const injectedWeb3 = Blockchain._getInjectedWeb3(); - const provider = await Blockchain._getProviderAsync(injectedWeb3, networkId, useLedgerProvider); - // if (!_.isUndefined(this._contractWrappers)) { - // this._contractWrappers.setProvider(provider, networkId); - // } else { - // } - this._contractWrappers = new ContractWrappers(provider, { networkId }); + const [provider, ledgerSubproviderIfExists] = await Blockchain._getProviderAsync( + injectedWeb3, + networkId, + useLedgerProvider, + ); + if (!_.isUndefined(this._contractWrappers)) { + this._contractWrappers.setProvider(provider, networkId); + } else { + this._contractWrappers = new ContractWrappers(provider, { networkId }); + } if (!_.isUndefined(this._blockchainWatcher)) { this._blockchainWatcher.destroy(); } @@ -806,9 +814,10 @@ export class Blockchain { this.networkId, shouldPollUserAddress, ); - if (useLedgerProvider) { + if (useLedgerProvider && !_.isUndefined(ledgerSubproviderIfExists)) { // TODO: why? delete this._userAddressIfExists; + this._ledgerSubprovider = ledgerSubproviderIfExists; this._dispatcher.updateUserAddress(undefined); this._dispatcher.updateProviderType(ProviderType.Ledger); } else { @@ -816,11 +825,13 @@ export class Blockchain { const userAddresses = await this._web3Wrapper.getAvailableAddressesAsync(); this._userAddressIfExists = userAddresses[0]; this._dispatcher.updateUserAddress(this._userAddressIfExists); - this._updateProviderName(injectedWeb3); + if (!_.isUndefined(injectedWeb3)) { + this._dispatcher.updateProviderType(ProviderType.Injected); + } } - // TOOD: should not call this in ledger case? 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 5d029d4f1..4112c8a15 100644 --- a/packages/website/ts/blockchain_watcher.ts +++ b/packages/website/ts/blockchain_watcher.ts @@ -35,16 +35,6 @@ export class BlockchainWatcher { public updatePrevUserAddress(userAddress: string): void { this._prevUserAddressIfExists = userAddress; } - // public async startEmittingInjectedProviderNetworkIdAsync(injectedProvider: any): Promise { - // if (this._isWatchingNetworkId) { - // return; // we are already watching the network id - // } - // const observable = injectedProvider.publicConfigStore; - // if (observable && observable.subscribe) { - // observable.subscribe(this._handleInjectedProviderUpdate.bind(this)); - // this._isWatchingNetworkId = true; - // } - // } public async startEmittingUserBalanceStateAsync(): Promise { if (!_.isUndefined(this._watchBalanceIntervalId)) { return; // we are already emitting the state @@ -60,15 +50,6 @@ export class BlockchainWatcher { }, ); } - // private _handleInjectedProviderUpdate(update: InjectedProviderUpdate): void { - // const updatedNetworkId = _.parseInt(update.networkVersion); - // if (this._prevNetworkId === updatedNetworkId) { - // return; - // } - // this._prevNetworkId = updatedNetworkId; - // this._dispatcher.updateNetworkId(updatedNetworkId); - // this._updateBalanceAsync(); - // } private async _updateBalanceAsync(): Promise { let prevNodeVersion: string; // Check for node version changes diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 58acb435e..bb88e3824 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -182,9 +182,10 @@ export class Portal extends React.Component { 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); @@ -200,7 +201,7 @@ export class Portal extends React.Component { 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), -- cgit v1.2.3 From 744e6e60c51b2bfd081920b58a73e566113fee0e Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 26 Jun 2018 11:17:00 -0700 Subject: Polish asset picker layout --- packages/website/ts/components/generate_order/asset_picker.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/generate_order/asset_picker.tsx b/packages/website/ts/components/generate_order/asset_picker.tsx index b0dcf5678..0f569e1ad 100644 --- a/packages/website/ts/components/generate_order/asset_picker.tsx +++ b/packages/website/ts/components/generate_order/asset_picker.tsx @@ -117,7 +117,7 @@ export class AssetPicker extends React.Component -
{token.name}
+
{token.symbol}
); }); @@ -181,7 +181,7 @@ export class AssetPicker extends React.Component Date: Tue, 26 Jun 2018 11:59:23 -0700 Subject: Fix subtle issues with ledger config dialog --- packages/website/ts/blockchain.ts | 2 +- .../ts/components/dialogs/ledger_config_dialog.tsx | 2 ++ packages/website/ts/components/portal/portal.tsx | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index d55cb6771..d2040abb5 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -828,8 +828,8 @@ export class Blockchain { if (!_.isUndefined(injectedWeb3)) { this._dispatcher.updateProviderType(ProviderType.Injected); } + await this.fetchTokenInformationAsync(); } - await this.fetchTokenInformationAsync(); await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); this._dispatcher.updateNetworkId(networkId); await this._rehydrateStoreWithContractEventsAsync(); diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index c9727b553..69144bdd3 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -283,6 +283,8 @@ export class LedgerConfigDialog extends React.Component { networkId={this.props.networkId} /> - {this.props.blockchainIsLoaded && ( - - )} + + + Date: Tue, 26 Jun 2018 12:09:20 -0700 Subject: Get things working with legacy portal --- .../ts/components/legacy_portal/legacy_portal.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/legacy_portal/legacy_portal.tsx b/packages/website/ts/components/legacy_portal/legacy_portal.tsx index b4a174a03..a52c79e26 100644 --- a/packages/website/ts/components/legacy_portal/legacy_portal.tsx +++ b/packages/website/ts/components/legacy_portal/legacy_portal.tsx @@ -249,16 +249,14 @@ export class LegacyPortal extends React.Component - {this.props.blockchainIsLoaded && ( - - )} +