Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions packages/sdk-multichain/src/domain/platform/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,36 @@ export function isSecure() {
return isReactNative() || platformType === PlatformType.MobileWeb;
}

export function hasExtension() {
if (typeof window !== 'undefined') {
return window.ethereum?.isMetaMask ?? false;
// Immediately start MetaMask detection when module loads
const detectionPromise: Promise<boolean> = (() => {
if (typeof window === 'undefined') {
return Promise.resolve(false);
}
return false;

return new Promise((resolve) => {
const providers: any[] = [];

const handler = (event: any) => {
if (event?.detail?.info?.rdns) {
providers.push(event.detail);
}
};

window.addEventListener('eip6963:announceProvider', handler);
window.dispatchEvent(new Event('eip6963:requestProvider'));

setTimeout(() => {
window.removeEventListener('eip6963:announceProvider', handler);

const hasMetaMask = providers.some(
(p) => p?.info?.rdns === 'io.metamask',
);

resolve(hasMetaMask);
}, 300); // default timeout
});
})();
Comment thread
chakra-guy marked this conversation as resolved.

export function hasExtension(): Promise<boolean> {
return detectionPromise;
}
8 changes: 5 additions & 3 deletions packages/sdk-multichain/src/multichain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,11 @@ export class MultichainSDK extends MultichainCore {
const { ui } = this.options;
const { preferExtension = true, headless: _headless = false } = ui;
const transportType = await this.storage.getTransport();
const hasExtensionInstalled = await hasExtension();
if (transportType) {
if (transportType === TransportType.Browser) {
//Check if the user still have the extension or not return the transport
if (hasExtension() && preferExtension) {
if (hasExtensionInstalled && preferExtension) {
const apiTransport = new DefaultTransport();
this.__transport = apiTransport;
this.listener = apiTransport.onNotification(this.onTransportNotification.bind(this));
Expand Down Expand Up @@ -396,6 +397,7 @@ export class MultichainSDK extends MultichainCore {
const isWeb = platformType === PlatformType.MetaMaskMobileWebview || platformType === PlatformType.DesktopWeb;
const { preferExtension = true, preferDesktop = false, headless: _headless = false } = ui;
const secure = isSecure();
const hasExtensionInstalled = await hasExtension();

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

Comment thread
chakra-guy marked this conversation as resolved.
if (isWeb && hasExtension() && preferExtension) {
if (isWeb && hasExtensionInstalled && preferExtension) {
//If metamask extension is available, connect to it
const defaultTransport = await this.setupDefaultTransport();
// Web transport has no initial payload
Expand All @@ -420,7 +422,7 @@ export class MultichainSDK extends MultichainCore {
await this.setupMWP();

// Determine preferred option for install modal
const isDesktopPreferred = hasExtension() ? preferDesktop : !preferExtension || preferDesktop;
const isDesktopPreferred = hasExtensionInstalled ? preferDesktop : !preferExtension || preferDesktop;

if (secure && !isDesktopPreferred) {
// Desktop is not preferred option, so we use deeplinks (mobile web)
Expand Down
22 changes: 14 additions & 8 deletions packages/sdk-multichain/src/multichain/transports/mwp/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { type CreateSessionParams, TransportTimeoutError, type TransportRequest, type TransportResponse } from '@metamask/multichain-api-client';
import type { Session, SessionRequest } from '@metamask/mobile-wallet-protocol-core';
import { SessionStore } from '@metamask/mobile-wallet-protocol-core';
import type { DappClient } from '@metamask/mobile-wallet-protocol-dapp-client';

import { createLogger, type ExtendedTransport, type RPCAPI, type Scope, type SessionData, type StoreAdapter } from '../../../domain';
import { type CreateSessionParams, type TransportRequest, type TransportResponse, TransportTimeoutError } from '@metamask/multichain-api-client';
import type { CaipAccountId } from '@metamask/utils';
import { createLogger, type ExtendedTransport, type RPCAPI, type Scope, type SessionData, type StoreAdapter } from '../../../domain';
import { addValidAccounts, getOptionalScopes, getValidAccounts, isSameScopesAndAccounts } from '../../utils';

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

get pendingRequests() {
return this.__pendingRequests;
Expand All @@ -54,7 +54,8 @@ export class MWPTransport implements ExtendedTransport {
) {
this.dappClient.on('message', this.handleMessage.bind(this));
if (typeof window !== 'undefined' && typeof window.addEventListener !== 'undefined') {
window.addEventListener('focus', this.onWindowFocus.bind(this));
this.windowFocusHandler = this.onWindowFocus.bind(this);
window.addEventListener('focus', this.windowFocusHandler);
}
}

Expand Down Expand Up @@ -121,9 +122,9 @@ export class MWPTransport implements ExtendedTransport {
if (walletSession && options) {
const currentScopes = Object.keys(walletSession?.sessionScopes ?? {}) as Scope[];
const proposedScopes = options?.scopes ?? [];
const proposedCaipAccountIds = options?.caipAccountIds ?? [];
const hasSameScopesAndAccounts = isSameScopesAndAccounts(currentScopes, proposedScopes, walletSession, proposedCaipAccountIds);
if (!hasSameScopesAndAccounts) {
const proposedCaipAccountIds = options?.caipAccountIds ?? [];
const hasSameScopesAndAccounts = isSameScopesAndAccounts(currentScopes, proposedScopes, walletSession, proposedCaipAccountIds);
if (!hasSameScopesAndAccounts) {
const optionalScopes = addValidAccounts(getOptionalScopes(options?.scopes ?? []), getValidAccounts(options?.caipAccountIds ?? []));
const sessionRequest: CreateSessionParams<RPCAPI> = { optionalScopes };
const response = await this.request({ method: 'wallet_createSession', params: sessionRequest });
Expand Down Expand Up @@ -169,7 +170,7 @@ export class MWPTransport implements ExtendedTransport {
logger('active session found', activeSession);
session = activeSession;
}
} catch {}
} catch { }

let timeout: NodeJS.Timeout;
const connectionPromise = new Promise<void>((resolve, reject) => {
Expand Down Expand Up @@ -229,6 +230,11 @@ export class MWPTransport implements ExtendedTransport {
* Disconnects from the Mobile Wallet Protocol
*/
async disconnect(): Promise<void> {
// Clean up window focus event listener
if (typeof window !== 'undefined' && typeof window.removeEventListener !== 'undefined' && this.windowFocusHandler) {
window.removeEventListener('focus', this.windowFocusHandler);
this.windowFocusHandler = undefined;
}
return this.dappClient.disconnect();
}

Expand Down
Loading
Loading