Skip to content

Commit 0ac34cd

Browse files
authored
Fix metamask extension detection (#1371)
* fix metamask extension detection * remove console log * pr comments * fix test + remove unused folder
1 parent 07036cd commit 0ac34cd

File tree

7 files changed

+70
-412
lines changed

7 files changed

+70
-412
lines changed

packages/sdk-multichain/src/domain/platform/index.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,36 @@ export function isSecure() {
8080
return isReactNative() || platformType === PlatformType.MobileWeb;
8181
}
8282

83-
export function hasExtension() {
84-
if (typeof window !== 'undefined') {
85-
return window.ethereum?.isMetaMask ?? false;
83+
// Immediately start MetaMask detection when module loads
84+
const detectionPromise: Promise<boolean> = (() => {
85+
if (typeof window === 'undefined') {
86+
return Promise.resolve(false);
8687
}
87-
return false;
88+
89+
return new Promise((resolve) => {
90+
const providers: any[] = [];
91+
92+
const handler = (event: any) => {
93+
if (event?.detail?.info?.rdns) {
94+
providers.push(event.detail);
95+
}
96+
};
97+
98+
window.addEventListener('eip6963:announceProvider', handler);
99+
window.dispatchEvent(new Event('eip6963:requestProvider'));
100+
101+
setTimeout(() => {
102+
window.removeEventListener('eip6963:announceProvider', handler);
103+
104+
const hasMetaMask = providers.some(
105+
(p) => p?.info?.rdns === 'io.metamask',
106+
);
107+
108+
resolve(hasMetaMask);
109+
}, 300); // default timeout
110+
});
111+
})();
112+
113+
export function hasExtension(): Promise<boolean> {
114+
return detectionPromise;
88115
}

packages/sdk-multichain/src/multichain/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,11 @@ export class MultichainSDK extends MultichainCore {
137137
const { ui } = this.options;
138138
const { preferExtension = true, headless: _headless = false } = ui;
139139
const transportType = await this.storage.getTransport();
140+
const hasExtensionInstalled = await hasExtension();
140141
if (transportType) {
141142
if (transportType === TransportType.Browser) {
142143
//Check if the user still have the extension or not return the transport
143-
if (hasExtension() && preferExtension) {
144+
if (hasExtensionInstalled && preferExtension) {
144145
const apiTransport = new DefaultTransport();
145146
this.__transport = apiTransport;
146147
this.listener = apiTransport.onNotification(this.onTransportNotification.bind(this));
@@ -396,6 +397,7 @@ export class MultichainSDK extends MultichainCore {
396397
const isWeb = platformType === PlatformType.MetaMaskMobileWebview || platformType === PlatformType.DesktopWeb;
397398
const { preferExtension = true, preferDesktop = false, headless: _headless = false } = ui;
398399
const secure = isSecure();
400+
const hasExtensionInstalled = await hasExtension();
399401

400402
if (this.__transport?.isConnected() && !secure) {
401403
return this.handleConnection(
@@ -409,7 +411,7 @@ export class MultichainSDK extends MultichainCore {
409411
);
410412
}
411413

412-
if (isWeb && hasExtension() && preferExtension) {
414+
if (isWeb && hasExtensionInstalled && preferExtension) {
413415
//If metamask extension is available, connect to it
414416
const defaultTransport = await this.setupDefaultTransport();
415417
// Web transport has no initial payload
@@ -420,7 +422,7 @@ export class MultichainSDK extends MultichainCore {
420422
await this.setupMWP();
421423

422424
// Determine preferred option for install modal
423-
const isDesktopPreferred = hasExtension() ? preferDesktop : !preferExtension || preferDesktop;
425+
const isDesktopPreferred = hasExtensionInstalled ? preferDesktop : !preferExtension || preferDesktop;
424426

425427
if (secure && !isDesktopPreferred) {
426428
// Desktop is not preferred option, so we use deeplinks (mobile web)

packages/sdk-multichain/src/multichain/transports/mwp/index.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { type CreateSessionParams, TransportTimeoutError, type TransportRequest, type TransportResponse } from '@metamask/multichain-api-client';
21
import type { Session, SessionRequest } from '@metamask/mobile-wallet-protocol-core';
32
import { SessionStore } from '@metamask/mobile-wallet-protocol-core';
43
import type { DappClient } from '@metamask/mobile-wallet-protocol-dapp-client';
5-
6-
import { createLogger, type ExtendedTransport, type RPCAPI, type Scope, type SessionData, type StoreAdapter } from '../../../domain';
4+
import { type CreateSessionParams, type TransportRequest, type TransportResponse, TransportTimeoutError } from '@metamask/multichain-api-client';
75
import type { CaipAccountId } from '@metamask/utils';
6+
import { createLogger, type ExtendedTransport, type RPCAPI, type Scope, type SessionData, type StoreAdapter } from '../../../domain';
87
import { addValidAccounts, getOptionalScopes, getValidAccounts, isSameScopesAndAccounts } from '../../utils';
98

109
const DEFAULT_REQUEST_TIMEOUT = 60 * 1000;
@@ -34,6 +33,7 @@ export class MWPTransport implements ExtendedTransport {
3433
private __pendingRequests = new Map<string, PendingRequests>();
3534
private notificationCallbacks = new Set<(data: unknown) => void>();
3635
private currentSessionRequest: SessionRequest | undefined;
36+
private windowFocusHandler: (() => void) | undefined;
3737

3838
get pendingRequests() {
3939
return this.__pendingRequests;
@@ -54,7 +54,8 @@ export class MWPTransport implements ExtendedTransport {
5454
) {
5555
this.dappClient.on('message', this.handleMessage.bind(this));
5656
if (typeof window !== 'undefined' && typeof window.addEventListener !== 'undefined') {
57-
window.addEventListener('focus', this.onWindowFocus.bind(this));
57+
this.windowFocusHandler = this.onWindowFocus.bind(this);
58+
window.addEventListener('focus', this.windowFocusHandler);
5859
}
5960
}
6061

@@ -121,9 +122,9 @@ export class MWPTransport implements ExtendedTransport {
121122
if (walletSession && options) {
122123
const currentScopes = Object.keys(walletSession?.sessionScopes ?? {}) as Scope[];
123124
const proposedScopes = options?.scopes ?? [];
124-
const proposedCaipAccountIds = options?.caipAccountIds ?? [];
125-
const hasSameScopesAndAccounts = isSameScopesAndAccounts(currentScopes, proposedScopes, walletSession, proposedCaipAccountIds);
126-
if (!hasSameScopesAndAccounts) {
125+
const proposedCaipAccountIds = options?.caipAccountIds ?? [];
126+
const hasSameScopesAndAccounts = isSameScopesAndAccounts(currentScopes, proposedScopes, walletSession, proposedCaipAccountIds);
127+
if (!hasSameScopesAndAccounts) {
127128
const optionalScopes = addValidAccounts(getOptionalScopes(options?.scopes ?? []), getValidAccounts(options?.caipAccountIds ?? []));
128129
const sessionRequest: CreateSessionParams<RPCAPI> = { optionalScopes };
129130
const response = await this.request({ method: 'wallet_createSession', params: sessionRequest });
@@ -169,7 +170,7 @@ export class MWPTransport implements ExtendedTransport {
169170
logger('active session found', activeSession);
170171
session = activeSession;
171172
}
172-
} catch {}
173+
} catch { }
173174

174175
let timeout: NodeJS.Timeout;
175176
const connectionPromise = new Promise<void>((resolve, reject) => {
@@ -229,6 +230,11 @@ export class MWPTransport implements ExtendedTransport {
229230
* Disconnects from the Mobile Wallet Protocol
230231
*/
231232
async disconnect(): Promise<void> {
233+
// Clean up window focus event listener
234+
if (typeof window !== 'undefined' && typeof window.removeEventListener !== 'undefined' && this.windowFocusHandler) {
235+
window.removeEventListener('focus', this.windowFocusHandler);
236+
this.windowFocusHandler = undefined;
237+
}
232238
return this.dappClient.disconnect();
233239
}
234240

0 commit comments

Comments
 (0)