From 8cd99f6a9ff1c68c44d540ca91dc6130aa82c8fb Mon Sep 17 00:00:00 2001 From: Noah Zinsmeister Date: Tue, 1 Mar 2022 13:50:15 -0500 Subject: [PATCH] only activate as necessary closes #441 closes #398 --- packages/eip1193/src/index.spec.ts | 17 ++++++++++----- packages/metamask/src/index.ts | 9 ++++++-- packages/network/src/index.ts | 2 +- packages/store/src/index.spec.ts | 35 +++++++++++++++++++++--------- packages/store/src/index.ts | 4 +--- packages/url/src/index.ts | 2 +- 6 files changed, 46 insertions(+), 23 deletions(-) diff --git a/packages/eip1193/src/index.spec.ts b/packages/eip1193/src/index.spec.ts index 92863f0..ec3a590 100644 --- a/packages/eip1193/src/index.spec.ts +++ b/packages/eip1193/src/index.spec.ts @@ -26,6 +26,9 @@ export class MockEIP1193Provider extends EventEmitter { public eth_requestAccounts = jest.fn((accounts?: string[]) => accounts) public request(x: RequestArguments): Promise { + // make sure to throw if we're "not connected" + if (!this.chainId) return Promise.reject(new Error()) + switch (x.method) { case 'eth_chainId': return Promise.resolve(this.eth_chainId(this.chainId)) @@ -106,12 +109,6 @@ describe('EIP1193', () => { expect(mockProvider.eth_requestAccounts.mock.calls.length).toBe(0) }) - afterEach(() => { - expect(mockProvider.eth_chainId.mock.calls.length).toBe(1) - expect(mockProvider.eth_accounts.mock.calls.length).toBe(1) - expect(mockProvider.eth_requestAccounts.mock.calls.length).toBe(0) - }) - // suppress console.debugs in this block beforeEach(() => { jest.spyOn(console, 'debug').mockImplementation(() => {}) @@ -130,6 +127,10 @@ describe('EIP1193', () => { activating: false, error: undefined, }) + + expect(mockProvider.eth_chainId.mock.calls.length).toBe(0) + expect(mockProvider.eth_accounts.mock.calls.length).toBe(0) + expect(mockProvider.eth_requestAccounts.mock.calls.length).toBe(0) }) test('succeeds', async () => { @@ -145,6 +146,10 @@ describe('EIP1193', () => { activating: false, error: undefined, }) + + expect(mockProvider.eth_chainId.mock.calls.length).toBe(1) + expect(mockProvider.eth_accounts.mock.calls.length).toBe(1) + expect(mockProvider.eth_requestAccounts.mock.calls.length).toBe(0) }) }) diff --git a/packages/metamask/src/index.ts b/packages/metamask/src/index.ts index 4e08f0c..4ec9f31 100644 --- a/packages/metamask/src/index.ts +++ b/packages/metamask/src/index.ts @@ -8,6 +8,8 @@ import type { } from '@web3-react/types' import { Connector } from '@web3-react/types' +type MetaMaskProvider = Provider & { isConnected?: () => boolean } + export class NoMetaMaskError extends Error { public constructor() { super('MetaMask not installed') @@ -21,6 +23,9 @@ function parseChainId(chainId: string) { } export class MetaMask extends Connector { + /** {@inheritdoc Connector.provider} */ + public provider: MetaMaskProvider | undefined + private readonly options?: Parameters[0] private eagerConnection?: Promise @@ -47,7 +52,7 @@ export class MetaMask extends Connector { .then((m) => m.default(this.options)) .then((provider) => { if (provider) { - this.provider = provider as Provider + this.provider = provider as MetaMaskProvider this.provider.on('connect', ({ chainId }: ProviderConnectInfo): void => { this.actions.update({ chainId: parseChainId(chainId) }) @@ -107,7 +112,7 @@ export class MetaMask extends Connector { * specified parameters first, before being prompted to switch. */ public async activate(desiredChainIdOrChainParameters?: number | AddEthereumChainParameter): Promise { - this.actions.startActivation() + if (!this.provider?.isConnected?.()) this.actions.startActivation() await this.isomorphicInitialize() if (!this.provider) return this.actions.reportError(new NoMetaMaskError()) diff --git a/packages/network/src/index.ts b/packages/network/src/index.ts index 1eec068..ecd3f12 100644 --- a/packages/network/src/index.ts +++ b/packages/network/src/index.ts @@ -67,7 +67,7 @@ export class Network extends Connector { * @param desiredChainId - The desired chain to connect to. */ public async activate(desiredChainId = this.defaultChainId): Promise { - this.actions.startActivation() + if (!this.provider) this.actions.startActivation() this.provider = await this.isomorphicInitialize(desiredChainId) diff --git a/packages/store/src/index.spec.ts b/packages/store/src/index.spec.ts index c707732..103dfa7 100644 --- a/packages/store/src/index.spec.ts +++ b/packages/store/src/index.spec.ts @@ -23,7 +23,7 @@ describe('#createWeb3ReactStoreAndActions', () => { }) describe('#startActivation', () => { - test('#works', () => { + test('works', () => { const [store, actions] = createWeb3ReactStoreAndActions() actions.startActivation() expect(store.getState()).toEqual({ @@ -33,6 +33,7 @@ describe('#createWeb3ReactStoreAndActions', () => { error: undefined, }) }) + test('cancellation works', () => { const [store, actions] = createWeb3ReactStoreAndActions() const cancelActivation = actions.startActivation() @@ -185,15 +186,29 @@ describe('#createWeb3ReactStoreAndActions', () => { }) }) - test('#reportError', () => { - const [store, actions] = createWeb3ReactStoreAndActions() - const error = new Error() - actions.reportError(error) - expect(store.getState()).toEqual({ - chainId: undefined, - accounts: undefined, - activating: false, - error, + describe('#reportError', () => { + test('sets error', () => { + const [store, actions] = createWeb3ReactStoreAndActions() + const error = new Error() + actions.reportError(error) + expect(store.getState()).toEqual({ + chainId: undefined, + accounts: undefined, + activating: false, + error, + }) + }) + + test('resets state', () => { + const [store, actions] = createWeb3ReactStoreAndActions() + actions.reportError(new Error()) + actions.reportError(undefined) + expect(store.getState()).toEqual({ + chainId: undefined, + accounts: undefined, + activating: false, + error: undefined, + }) }) }) }) diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts index f1206d0..8b40ac0 100644 --- a/packages/store/src/index.ts +++ b/packages/store/src/index.ts @@ -67,9 +67,7 @@ export function createWeb3ReactStoreAndActions(allowedChainIds?: number[]): [Web // return a function that cancels the activation iff nothing else has happened return () => { - if (nullifier === nullifierCached) { - store.setState({ ...DEFAULT_STATE, activating: false }) - } + if (nullifier === nullifierCached) store.setState({ activating: false }) } } diff --git a/packages/url/src/index.ts b/packages/url/src/index.ts index ed4be81..c7e2f14 100644 --- a/packages/url/src/index.ts +++ b/packages/url/src/index.ts @@ -42,7 +42,7 @@ export class Url extends Connector { /** {@inheritdoc Connector.activate} */ public async activate(): Promise { - this.actions.startActivation() + if (!this.provider) this.actions.startActivation() await this.isomorphicInitialize()