aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website/ts
diff options
context:
space:
mode:
authorfragosti <francesco.agosti93@gmail.com>2018-06-26 07:26:34 +0800
committerfragosti <francesco.agosti93@gmail.com>2018-06-26 07:26:34 +0800
commitc4e2dcafa4bd5f4b905680e2c1b80d6ddb6ab6fb (patch)
tree34653ddbd6fa97eb522f827c0b488f91f3038595 /packages/website/ts
parent53e2cda4c8d6698b5c4b69f510ad8764cb089306 (diff)
downloaddexon-0x-contracts-c4e2dcafa4bd5f4b905680e2c1b80d6ddb6ab6fb.tar.gz
dexon-0x-contracts-c4e2dcafa4bd5f4b905680e2c1b80d6ddb6ab6fb.tar.zst
dexon-0x-contracts-c4e2dcafa4bd5f4b905680e2c1b80d6ddb6ab6fb.zip
Have basic network switching working
Diffstat (limited to 'packages/website/ts')
-rw-r--r--packages/website/ts/blockchain.ts224
-rw-r--r--packages/website/ts/blockchain_watcher.ts22
-rw-r--r--packages/website/ts/components/onboarding/portal_onboarding_flow.tsx11
-rw-r--r--packages/website/ts/components/portal/portal.tsx10
-rw-r--r--packages/website/ts/components/top_bar/top_bar.tsx1
-rw-r--r--packages/website/ts/types.ts10
6 files changed, 158 insertions, 120 deletions
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<Provider> {
+ 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<number> {
+ // 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<string>(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<Provider> {
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<void> {
const isConnected = !_.isUndefined(newNetworkId);
if (!isConnected) {
@@ -185,84 +236,14 @@ 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.startEmittingUserBalanceStateAsync();
- this._dispatcher.updateProviderType(ProviderType.Ledger);
+ const useLedgerProvider = true;
+ await this._resetOrInitializeAsync(networkId, shouldPollUserAddress, useLedgerProvider);
}
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.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<void> {
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<void> {
+ 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<void> {
// Ensure we are only ever listening to one set of events
this._stopWatchingExchangeLogFillEvents();
@@ -774,36 +768,37 @@ 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 : 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<void> {
+ 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<void> {
+ // 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<void> {
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<void> {
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<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 > new BigNumber(0) && zrxTokenState.allowance > new BigNumber(0);
+ }
}
return false;
}
@@ -222,6 +224,9 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
return null;
}
const tokenState = this.props.trackedTokenStateByAddress[token.address];
+ if (!tokenState) {
+ return null;
+ }
return (
<AllowanceToggle
token={token}
diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx
index 4166fde53..58acb435e 100644
--- a/packages/website/ts/components/portal/portal.tsx
+++ b/packages/website/ts/components/portal/portal.tsx
@@ -151,11 +151,11 @@ export class Portal extends React.Component<PortalProps, PortalState> {
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;