Skip to content

Commit b5f8306

Browse files
Merge pull request #2241 from Web3Auth/fix/metamask-mobile-issue
fixes metamask autoconnect in mobile
2 parents 5a834c8 + 3fd7430 commit b5f8306

File tree

11 files changed

+99
-98
lines changed

11 files changed

+99
-98
lines changed

packages/modal/src/modalManager.ts

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -608,9 +608,8 @@ export class Web3Auth extends Web3AuthNoModal implements IWeb3AuthModal {
608608
}): Promise<void> => {
609609
try {
610610
const connector = this.getConnector(params.connector as WALLET_CONNECTOR_TYPE, params.loginParams?.chainNamespace);
611-
// auto-connect WalletConnect and non-injected MetaMask in background to generate QR code URI without interfering with user's selected connection
612-
const shouldStartConnectionInBackground =
613-
connector.name === WALLET_CONNECTORS.WALLET_CONNECT_V2 || (connector.name === WALLET_CONNECTORS.METAMASK && !connector.isInjected);
611+
// auto-connect WalletConnect in background to generate QR code URI without interfering with user's selected connection
612+
const shouldStartConnectionInBackground = connector.name === WALLET_CONNECTORS.WALLET_CONNECT_V2;
614613
if (shouldStartConnectionInBackground) {
615614
const initialChain = this.getInitialChainIdForConnector(connector);
616615
await connector.connect({ chainId: initialChain.chainId });
@@ -651,32 +650,6 @@ export class Web3Auth extends Web3AuthNoModal implements IWeb3AuthModal {
651650
wcConnector.status = CONNECTOR_STATUS.READY;
652651
}
653652
}
654-
655-
// handle MM session refresh if MM is not injected
656-
const metamaskConnector = this.getConnector(WALLET_CONNECTORS.METAMASK);
657-
if (metamaskConnector && !metamaskConnector.isInjected) {
658-
const status = metamaskConnector?.status;
659-
log.debug("trying refreshing MM session", visibility, status);
660-
if (visibility && (status === CONNECTOR_STATUS.READY || status === CONNECTOR_STATUS.CONNECTING)) {
661-
log.debug("refreshing MM session");
662-
663-
// refreshing session for MM whenever modal is opened.
664-
try {
665-
const initialChain = this.getInitialChainIdForConnector(metamaskConnector);
666-
metamaskConnector.connect({ chainId: initialChain.chainId });
667-
} catch (error) {
668-
log.error(`Error while connecting to MM`, error);
669-
}
670-
}
671-
if (
672-
!visibility &&
673-
this.status === CONNECTOR_STATUS.CONNECTED &&
674-
(status === CONNECTOR_STATUS.READY || status === CONNECTOR_STATUS.CONNECTING)
675-
) {
676-
log.debug("this stops MM connector from trying to reconnect once proposal expires");
677-
metamaskConnector.status = CONNECTOR_STATUS.READY;
678-
}
679-
}
680653
};
681654

682655
private getChainNamespaces = (): ChainNamespaceType[] => {

packages/modal/src/ui/components/Button/ButtonWallet/ButtonWallet.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function ButtonWallet(props: ButtonWalletProps) {
1515
const isDark = useContext(ThemedContext);
1616

1717
const isLink = useMemo(
18-
() => deviceDetails.platform !== "desktop" && button.href && button.hasWalletConnect && !button.hasInjectedWallet,
18+
() => deviceDetails.platform !== "desktop" && button.href && button.hasWalletConnect && !button.isInstalled,
1919
[deviceDetails, button]
2020
);
2121

packages/modal/src/ui/components/ConnectWallet/ConnectWallet.tsx

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,16 @@ function ConnectWallet(props: ConnectWalletProps) {
152152
return walletDiscoverySupported ? defaultButtons.length : installedWalletButtons.length;
153153
}, [walletDiscoverySupported, defaultButtons, installedWalletButtons, isShowAllWallets, totalExternalWalletsCount]);
154154

155+
/**
156+
* Wallet click logic
157+
* - For installed wallets
158+
* - For MetaMask non-injected on desktop, show QR code for connection
159+
* - Ask user to select a chain namespace if it has multiple namespaces
160+
* - Otherwise, use their connectors to connect
161+
* - For wallet-discovery wallets (not installed)
162+
* - On desktop, show QR code for connection if wallet connect v2 is supported, otherwise show install links
163+
* - On mobile, open deeplink with wallet connect uri (won't go into this function as it'll open the deeplink)
164+
*/
155165
const handleWalletClick = (button: ExternalButton) => {
156166
analytics?.track(ANALYTICS_EVENTS.EXTERNAL_WALLET_SELECTED, {
157167
connector: button.isInstalled ? button.name : button.hasWalletConnect ? WALLET_CONNECTORS.WALLET_CONNECT_V2 : "",
@@ -165,41 +175,50 @@ function ConnectWallet(props: ConnectWalletProps) {
165175
total_external_wallets: allUniqueButtons.length,
166176
});
167177

168-
// show chain namespace selector if the button is an injected connector with multiple chain namespaces
169-
const isChainNamespaceSelectorRequired = button.hasInjectedWallet && button.chainNamespaces?.length > 1;
170-
if (isChainNamespaceSelectorRequired) {
171-
setBodyState({
172-
...bodyState,
173-
multiChainSelector: {
174-
show: true,
175-
wallet: button,
176-
},
177-
});
178-
return;
179-
}
178+
// for installed wallets
179+
if (button.isInstalled) {
180+
// for MetaMask non-injected on desktop, show QR code for connection
181+
if (button.name === WALLET_CONNECTORS.METAMASK && !button.hasInjectedWallet && deviceDetails.platform === "desktop") {
182+
handleExternalWalletClick({ connector: button.name });
183+
setSelectedButton(button);
184+
setSelectedWallet(true);
185+
setCurrentPage(CONNECT_WALLET_PAGES.SELECTED_WALLET);
186+
handleWalletDetailsHeight();
187+
return;
188+
}
180189

181-
// connect with connector if injected and single chain namespace or custom connector (except MetaMask)
182-
const isInjectedConnectorAndSingleChainNamespace = button.hasInjectedWallet && button.chainNamespaces?.length === 1;
183-
const isCustomConnector = !button.hasInjectedWallet && button.isInstalled;
184-
if (isInjectedConnectorAndSingleChainNamespace || (isCustomConnector && button.name !== WALLET_CONNECTORS.METAMASK)) {
185-
return handleExternalWalletClick({ connector: button.name });
186-
}
190+
// show chain namespace selector if the button has multiple chain namespaces
191+
if (button.chainNamespaces?.length > 1) {
192+
setBodyState({
193+
...bodyState,
194+
multiChainSelector: {
195+
show: true,
196+
wallet: button,
197+
},
198+
});
199+
return;
200+
}
187201

188-
// show QR code for wallet connect v2 and MM (non-injected)
189-
if (button.hasWalletConnect) {
190-
setSelectedButton(button);
191-
setSelectedWallet(true);
192-
setCurrentPage(CONNECT_WALLET_PAGES.SELECTED_WALLET);
193-
handleWalletDetailsHeight();
202+
// otherwise, use their connectors to connect
203+
handleExternalWalletClick({ connector: button.name });
204+
return;
194205
} else {
195-
// show install links
196-
setBodyState({
197-
...bodyState,
198-
installLinks: {
199-
show: true,
200-
wallet: button,
201-
},
202-
});
206+
// show QR code if wallet connect v2 is supported
207+
if (button.hasWalletConnect) {
208+
setSelectedButton(button);
209+
setSelectedWallet(true);
210+
setCurrentPage(CONNECT_WALLET_PAGES.SELECTED_WALLET);
211+
handleWalletDetailsHeight();
212+
} else {
213+
// otherwise, show install links
214+
setBodyState({
215+
...bodyState,
216+
installLinks: {
217+
show: true,
218+
wallet: button,
219+
},
220+
});
221+
}
203222
}
204223
};
205224

packages/modal/src/ui/components/ConnectWallet/ConnectWalletChainNamespaceSelect/ConnectWalletChainNamespaceSelect.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const ConnectWalletChainNamespaceSelect = (props: ConnectWalletChainNamespaceSel
99
const { isDark, wallet, handleExternalWalletClick } = props;
1010
const [t] = useTranslation(undefined, { i18n });
1111

12-
const chainNamespaces = wallet.chainNamespaces!.map((chainNamespace) => {
12+
const chainNamespaces = wallet.chainNamespaces!.sort().map((chainNamespace) => {
1313
const imageId = chainNamespace === "eip155" ? "evm" : chainNamespace;
1414
const displayName = chainNamespace === "eip155" ? "EVM" : chainNamespace;
1515
return {

packages/modal/src/ui/components/Login/Login.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ function Login(props: LoginProps) {
6060
showInstalledExternalWallets,
6161
logoAlignment = "center",
6262
buttonRadius = "pill",
63+
deviceDetails,
6364
} = props;
6465

6566
const [t] = useTranslation(undefined, { i18n });
@@ -342,6 +343,12 @@ function Login(props: LoginProps) {
342343
}
343344
};
344345

346+
/**
347+
* Installed wallet click logic:
348+
* - For MetaMask: If not injected and on desktop, display QR code for connection.
349+
* - If wallet supports multiple chain namespaces, prompt user to select a chain.
350+
* - Otherwise, connect directly using the wallet connector.
351+
*/
345352
const handleInstalledWalletClick = (wallet: ExternalButton) => {
346353
analytics?.track(ANALYTICS_EVENTS.EXTERNAL_WALLET_SELECTED, {
347354
connector: wallet.name,
@@ -354,8 +361,10 @@ function Login(props: LoginProps) {
354361
has_wallet_registry_item: !!wallet.walletRegistryItem,
355362
total_external_wallets: totalExternalWallets,
356363
});
357-
// for non-injected Metamask, show QR code to connect
358-
if (wallet.name === WALLET_CONNECTORS.METAMASK && !wallet.hasInjectedWallet) {
364+
// for non-injected Metamask on desktop, show QR code to connect
365+
if (wallet.name === WALLET_CONNECTORS.METAMASK && !wallet.hasInjectedWallet && deviceDetails.platform === "desktop") {
366+
handleExternalWalletClick({ connector: wallet.name });
367+
// We should show QR code only if the wallet is not installed.
359368
setBodyState({
360369
...bodyState,
361370
metamaskQrCode: {

packages/modal/src/ui/components/Login/Login.type.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { BUILD_ENV_TYPE, WEB3AUTH_NETWORK_TYPE } from "@web3auth/auth";
22

33
import type {
4+
browser,
45
ButtonRadiusType,
56
ExternalButton,
67
ExternalWalletEventType,
78
LogoAlignmentType,
9+
os,
10+
platform,
811
SocialLoginEventType,
912
SocialLoginsConfig,
1013
} from "../../interfaces";
@@ -31,6 +34,7 @@ export interface LoginProps {
3134
totalExternalWallets: number;
3235
logoAlignment?: LogoAlignmentType;
3336
buttonRadius?: ButtonRadiusType;
37+
deviceDetails: { platform: platform; browser: browser; os: os };
3438
handleExternalWalletBtnClick?: (flag: boolean) => void;
3539
handleSocialLoginClick: (params: SocialLoginEventType) => void;
3640
handleExternalWalletClick: (params: ExternalWalletEventType) => void;

packages/modal/src/ui/components/Root/Root.tsx

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { WALLET_CONNECTORS, type WalletRegistryItem } from "@web3auth/no-modal";
2-
import Bowser from "bowser";
32
import { JSX, useCallback, useContext, useMemo, useState } from "react";
43
import { useTranslation } from "react-i18next";
54

65
import { CONNECT_WALLET_PAGES, DEFAULT_METAMASK_WALLET_REGISTRY_ITEM, PAGES } from "../../constants";
76
import { BodyState, RootContext } from "../../context/RootContext";
87
import { ThemedContext } from "../../context/ThemeContext";
9-
import { browser, ExternalButton, mobileOs, MODAL_STATUS, os, platform, TOAST_TYPE, ToastType } from "../../interfaces";
8+
import { ExternalButton, mobileOs, MODAL_STATUS, TOAST_TYPE, ToastType } from "../../interfaces";
109
import i18n from "../../localeImport";
1110
import { cn, getBrowserExtensionUrl, getBrowserName, getIcons, getMobileInstallLink, getOsName } from "../../utils";
1211
import BottomSheet from "../BottomSheet";
@@ -42,6 +41,7 @@ function Root(props: RootProps) {
4241
isSmsPasswordLessLoginVisible,
4342
preHandleExternalWalletClick,
4443
uiConfig,
44+
deviceDetails,
4545
} = props;
4646

4747
const {
@@ -99,16 +99,6 @@ function Root(props: RootProps) {
9999
};
100100

101101
// Wallet Details
102-
const deviceDetails = useMemo<{ platform: platform; browser: browser; os: mobileOs }>(() => {
103-
if (typeof window === "undefined") return { platform: "mobile", browser: "chrome", os: "ios" };
104-
const browserData = Bowser.getParser(window.navigator.userAgent);
105-
return {
106-
platform: browserData.getPlatformType() as platform,
107-
browser: browserData.getBrowserName().toLowerCase() as browser,
108-
os: browserData.getOSName() as mobileOs,
109-
};
110-
}, []);
111-
112102
const mobileInstallLinks = useMemo<JSX.Element[]>(() => {
113103
if (deviceDetails.platform === "desktop") return [];
114104
const installConfig = bodyState.installLinks?.wallet?.walletRegistryItem?.app || {};
@@ -424,12 +414,8 @@ function Root(props: RootProps) {
424414
);
425415

426416
const isShowLoader = useMemo(() => {
427-
// don't show loader if metamask is connecting and there is a connect uri
428-
if (modalState.detailedLoaderConnector === WALLET_CONNECTORS.METAMASK && modalState.metamaskConnectUri) {
429-
return false;
430-
}
431417
return modalState.status !== MODAL_STATUS.INITIALIZED;
432-
}, [modalState.detailedLoaderConnector, modalState.metamaskConnectUri, modalState.status]);
418+
}, [modalState.status]);
433419

434420
return (
435421
<RootContext.Provider value={contextValue}>
@@ -494,6 +480,7 @@ function Root(props: RootProps) {
494480
totalExternalWallets={totalExternalWallets}
495481
logoAlignment={logoAlignment}
496482
buttonRadius={buttonRadiusType}
483+
deviceDetails={deviceDetails}
497484
handleSocialLoginClick={handleSocialLoginClick}
498485
handleExternalWalletBtnClick={onExternalWalletBtnClick}
499486
handleSocialLoginHeight={handleSocialLoginHeight}
@@ -511,11 +498,7 @@ function Root(props: RootProps) {
511498
allExternalButtons={allButtons}
512499
connectorVisibilityMap={connectorVisibilityMap}
513500
customConnectorButtons={customConnectorButtons}
514-
deviceDetails={{
515-
platform: deviceDetails.platform,
516-
browser: deviceDetails.browser,
517-
os: deviceDetails.os as os,
518-
}}
501+
deviceDetails={deviceDetails}
519502
chainNamespace={chainNamespaces}
520503
buttonRadius={buttonRadiusType}
521504
handleWalletDetailsHeight={handleWalletDetailsHeight}

packages/modal/src/ui/components/Root/Root.type.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ChainNamespaceType, WalletRegistry } from "@web3auth/no-modal";
22

3-
import { ModalState, SocialLoginEventType, SocialLoginsConfig, UIConfig } from "../../interfaces";
3+
import { browser, ModalState, os, platform, SocialLoginEventType, SocialLoginsConfig, UIConfig } from "../../interfaces";
44

55
export interface RootProps {
66
appLogo?: string;
@@ -18,6 +18,7 @@ export interface RootProps {
1818
isEmailPasswordLessLoginVisible: boolean;
1919
isSmsPasswordLessLoginVisible: boolean;
2020
uiConfig: UIConfig;
21+
deviceDetails: { platform: platform; browser: browser; os: os };
2122
handleSocialLoginClick: (params: SocialLoginEventType) => void;
2223
handleExternalWalletBtnClick?: (flag: boolean) => void;
2324
preHandleExternalWalletClick: (params: { connector: string; chainNamespace?: ChainNamespaceType }) => void;

packages/modal/src/ui/components/Widget/Widget.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ function Widget(props: WidgetProps) {
2222
chainNamespaces,
2323
walletRegistry,
2424
uiConfig,
25+
deviceDetails,
2526
} = props;
2627

2728
const { widgetType } = uiConfig;
@@ -183,13 +184,6 @@ function Widget(props: WidgetProps) {
183184
if (wcAvailable && !modalState.walletConnectUri && typeof handleExternalWalletClick === "function") {
184185
handleExternalWalletClick({ connector: WALLET_CONNECTORS.WALLET_CONNECT_V2 });
185186
}
186-
187-
// auto connect to MetaMask if not injected to generate QR code URI for mobile connection
188-
const mmAvailable =
189-
modalState.externalWalletsConfig[WALLET_CONNECTORS.METAMASK] && !modalState.externalWalletsConfig[WALLET_CONNECTORS.METAMASK]?.isInjected;
190-
if (mmAvailable && !modalState.metamaskConnectUri && typeof handleExternalWalletClick === "function") {
191-
handleExternalWalletClick({ connector: WALLET_CONNECTORS.METAMASK });
192-
}
193187
}
194188
}, [modalState, handleExternalWalletClick]);
195189

@@ -226,6 +220,7 @@ function Widget(props: WidgetProps) {
226220
isEmailPasswordLessLoginVisible={isEmailPasswordLessLoginVisible}
227221
isSmsPasswordLessLoginVisible={isSmsPasswordLessLoginVisible}
228222
uiConfig={uiConfig}
223+
deviceDetails={deviceDetails}
229224
/>
230225
)}
231226
</Modal>
@@ -257,6 +252,7 @@ function Widget(props: WidgetProps) {
257252
isEmailPasswordLessLoginVisible={isEmailPasswordLessLoginVisible}
258253
isSmsPasswordLessLoginVisible={isSmsPasswordLessLoginVisible}
259254
uiConfig={uiConfig}
255+
deviceDetails={deviceDetails}
260256
/>
261257
)}
262258
</Embed>
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { SafeEventEmitter } from "@web3auth/auth";
22
import { ChainNamespaceType, WalletRegistry } from "@web3auth/no-modal";
33

4-
import { ExternalWalletEventType, SocialLoginEventType, StateEmitterEvents, UIConfig } from "../../interfaces";
4+
import { browser, ExternalWalletEventType, os, platform, SocialLoginEventType, StateEmitterEvents, UIConfig } from "../../interfaces";
55

66
export interface WidgetProps {
77
stateListener: SafeEventEmitter<StateEmitterEvents>;
88
appLogo?: string;
99
appName?: string;
1010
chainNamespaces: ChainNamespaceType[];
1111
walletRegistry?: WalletRegistry;
12+
uiConfig: UIConfig;
13+
deviceDetails: { platform: platform; browser: browser; os: os };
1214
handleSocialLoginClick: (params: SocialLoginEventType) => void;
1315
handleExternalWalletClick: (params: ExternalWalletEventType) => void;
1416
handleShowExternalWallets: (externalWalletsInitialized: boolean) => void;
1517
closeModal: () => void;
16-
uiConfig: UIConfig;
1718
}

0 commit comments

Comments
 (0)