From 2cddc24e20d95228b60b11c77f587507cc9f306b Mon Sep 17 00:00:00 2001 From: VGLoic Date: Sun, 4 Dec 2022 17:18:22 +0100 Subject: [PATCH 1/2] feat: select MetaMask provider when conflicts with CoinbaseWallet --- src/index.ts | 21 ++++++++++++++++--- test/spec.js | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 2141c3e..a232c45 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ interface MetaMaskEthereumProvider { } interface Window { - ethereum?: MetaMaskEthereumProvider; + ethereum?: MetaMaskEthereumProvider & { providers?: MetaMaskEthereumProvider[] }; } export = detectEthereumProvider; @@ -67,9 +67,9 @@ function detectEthereumProvider({ window.removeEventListener('ethereum#initialized', handleEthereum); - const { ethereum } = window as Window; + const ethereum = getEthereum(mustBeMetaMask); - if (ethereum && (!mustBeMetaMask || ethereum.isMetaMask)) { + if (ethereum) { resolve(ethereum as unknown as T); } else { @@ -95,3 +95,18 @@ function detectEthereumProvider({ } } } + +function getEthereum(mustBeMetaMask: boolean) { + const { ethereum } = window as Window; + if (!ethereum) return undefined; + // The `providers` field is populated when CoinBase Wallet extension is also installed + // The expected object is an array of providers, the MetaMask provider is inside + // See https://docs.cloud.coinbase.com/wallet-sdk/docs/injected-provider-guidance for more information + if (Array.isArray(ethereum.providers)) { + if (mustBeMetaMask) return ethereum.providers.find(p => p.isMetaMask); + return ethereum.providers[0]; + } + if (!mustBeMetaMask) return ethereum; + if (!ethereum.isMetaMask) return undefined; + return ethereum; +} diff --git a/test/spec.js b/test/spec.js index 7489cf9..e502191 100644 --- a/test/spec.js +++ b/test/spec.js @@ -24,6 +24,16 @@ const providerWithMetaMask = { const providerNoMetaMask = {} const noProvider = null +const manyProvidersWithMetaMask = { + providers: [{ isMetaMask: true }], +} +const manyProvidersWithoutMetaMask = { + providers: [{}], +} +const manyProvidersNoProvider = { + providers: [], +} + test('detectProvider: defaults with ethereum already set', async function (t) { mockGlobalProps(providerNoMetaMask) @@ -36,6 +46,18 @@ test('detectProvider: defaults with ethereum already set', async function (t) { t.end() }) +test('detectProvider: defaults with ethereum already set in `providers` array field', async function (t) { + + mockGlobalProps(manyProvidersWithoutMetaMask) + + const provider = await detectProvider() + + t.deepEquals({}, provider, 'resolve with expected provider') + t.ok(window.addEventListener.notCalled, 'addEventListener should not have been called') + t.ok(window.removeEventListener.calledOnce, 'removeEventListener called once') + t.end() +}) + test('detectProvider: mustBeMetamask with ethereum already set', async function (t) { mockGlobalProps(providerWithMetaMask) @@ -48,6 +70,18 @@ test('detectProvider: mustBeMetamask with ethereum already set', async function t.end() }) +test('detectProvider: mustBeMetamask with ethereum already set in `providers` array field', async function (t) { + + mockGlobalProps(manyProvidersWithMetaMask) + + const provider = await detectProvider() + + t.ok(provider.isMetaMask, 'should have resolved expected provider object') + t.ok(window.addEventListener.notCalled, 'addEventListener should not have been called') + t.ok(window.removeEventListener.calledOnce, 'removeEventListener called once') + t.end() +}) + test('detectProvider: mustBeMetamask with non-MetaMask ethereum already set', async function (t) { mockGlobalProps(providerNoMetaMask) @@ -59,6 +93,17 @@ test('detectProvider: mustBeMetamask with non-MetaMask ethereum already set', as t.end() }) +test('detectProvider: mustBeMetamask with non-MetaMask ethereum already set in `providers` array field', async function (t) { + + mockGlobalProps(manyProvidersWithoutMetaMask) + + const result = await detectProvider({ timeout: 1, mustBeMetaMask: true }) + t.equal(result, null, 'promise should have resolved null') + t.ok(window.addEventListener.notCalled, 'addEventListener should not have been called') + t.ok(window.removeEventListener.calledOnce, 'removeEventListener called once') + t.end() +}) + test('detectProvider: ethereum set on ethereum#initialized', async function (t) { mockGlobalProps(noProvider) @@ -119,6 +164,18 @@ test('detectProvider: ethereum never set', async function (t) { t.end() }) +test('detectProvider: ethereum never set with `providers` array field', async function (t) { + + mockGlobalProps(manyProvidersNoProvider) + + const result = await detectProvider({ timeout: 1 }) + t.equal(result, null, 'promise should have resolved null') + t.ok(window.addEventListener.notCalled, 'addEventListener should have been called once') + t.ok(window.removeEventListener.calledOnce, 'removeEventListener should have been called once') + t.ok(console.error.calledOnce, 'console.error should have been called once') + t.end() +}) + test('detectProvider: ethereum never set (silent mode)', async function (t) { mockGlobalProps(noProvider) From cb31691218c426915eae64f9cd6589346e07b0b7 Mon Sep 17 00:00:00 2001 From: VGLoic Date: Sun, 4 Dec 2022 17:19:29 +0100 Subject: [PATCH 2/2] feat: apply linter --- src/index.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index a232c45..a821ee0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -98,15 +98,23 @@ function detectEthereumProvider({ function getEthereum(mustBeMetaMask: boolean) { const { ethereum } = window as Window; - if (!ethereum) return undefined; + if (!ethereum) { + return undefined; + } // The `providers` field is populated when CoinBase Wallet extension is also installed // The expected object is an array of providers, the MetaMask provider is inside // See https://docs.cloud.coinbase.com/wallet-sdk/docs/injected-provider-guidance for more information if (Array.isArray(ethereum.providers)) { - if (mustBeMetaMask) return ethereum.providers.find(p => p.isMetaMask); + if (mustBeMetaMask) { + return ethereum.providers.find((p) => p.isMetaMask); + } return ethereum.providers[0]; } - if (!mustBeMetaMask) return ethereum; - if (!ethereum.isMetaMask) return undefined; + if (!mustBeMetaMask) { + return ethereum; + } + if (!ethereum.isMetaMask) { + return undefined; + } return ethereum; }