Skip to content

Commit a3384ca

Browse files
Improve WalletConnect connection and auto-connection flow
1 parent b371bf9 commit a3384ca

File tree

5 files changed

+99
-104
lines changed

5 files changed

+99
-104
lines changed

.changeset/fifty-bars-rest.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Improve walletConnect connection and auto-connection flow

packages/thirdweb/src/react/web/wallets/shared/WalletConnectConnection.tsx

Lines changed: 11 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Chain } from "../../../../chains/types.js";
33
import type { ThirdwebClient } from "../../../../client/client.js";
44
import { wait } from "../../../../utils/promise/wait.js";
55
import { formatWalletConnectUrl } from "../../../../utils/url.js";
6-
import { isAndroid, isIOS, isMobile } from "../../../../utils/web/isMobile.js";
6+
import { isMobile } from "../../../../utils/web/isMobile.js";
77
import { openWindow } from "../../../../utils/web/openWindow.js";
88
import type { WCSupportedWalletIds } from "../../../../wallets/__generated__/wallet-ids.js";
99
import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
@@ -47,35 +47,23 @@ export const WalletConnectConnection: React.FC<{
4747
client: props.client,
4848
walletConnect: {
4949
onDisplayUri(uri) {
50-
const preferNative =
51-
walletInfo.mobile.native || walletInfo.mobile.universal;
5250
try {
5351
if (isMobile()) {
54-
if (isAndroid()) {
55-
if (preferNative) {
56-
openWindow(
57-
formatWalletConnectUrl(preferNative, uri).redirect,
58-
);
59-
}
60-
} else if (isIOS()) {
61-
if (preferNative) {
62-
openWindow(
63-
formatWalletConnectUrl(preferNative, uri).redirect,
64-
);
65-
}
52+
const mobileAppLink =
53+
walletInfo.mobile.native || walletInfo.mobile.universal;
54+
if (mobileAppLink) {
55+
openWindow(
56+
formatWalletConnectUrl(mobileAppLink, uri).redirect,
57+
);
6658
} else {
67-
const preferUniversal =
68-
walletInfo.mobile.universal || walletInfo.mobile.native;
69-
if (preferUniversal) {
70-
openWindow(
71-
formatWalletConnectUrl(preferUniversal, uri).redirect,
72-
);
73-
}
59+
// on android, wc:// links show the app picker
60+
openWindow(uri);
7461
}
7562
} else {
7663
setQrCodeUri(uri);
7764
}
78-
} catch {
65+
} catch (e) {
66+
console.error(e);
7967
setErrorConnecting(true);
8068
}
8169
},
@@ -216,28 +204,7 @@ export const WalletConnectStandaloneConnection: React.FC<{
216204
chain: props.chain,
217205
client: props.client,
218206
onDisplayUri(uri) {
219-
const platformUris = {
220-
android: walletInfo.mobile.universal || "",
221-
ios: walletInfo.mobile.native || "",
222-
other: walletInfo.mobile.universal || "",
223-
};
224-
225207
setQrCodeUri(uri);
226-
if (isMobile()) {
227-
if (isAndroid()) {
228-
openWindow(
229-
`${platformUris.android}wc?uri=${encodeURIComponent(uri)}`,
230-
);
231-
} else if (isIOS()) {
232-
openWindow(
233-
`${platformUris.ios}wc?uri=${encodeURIComponent(uri)}`,
234-
);
235-
} else {
236-
openWindow(
237-
`${platformUris.other}wc?uri=${encodeURIComponent(uri)}`,
238-
);
239-
}
240-
}
241208
},
242209
optionalChains: props.chains,
243210
projectId: props.walletConnect?.projectId,
@@ -253,8 +220,6 @@ export const WalletConnectStandaloneConnection: React.FC<{
253220
}
254221
}, [
255222
props.walletConnect,
256-
walletInfo.mobile.native,
257-
walletInfo.mobile.universal,
258223
wallet,
259224
props.chain,
260225
props.client,

packages/thirdweb/src/utils/web/isMobile.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { detectOS } from "../detect-platform.js";
33
/**
44
* @internal
55
*/
6-
export function isAndroid(): boolean {
6+
function isAndroid(): boolean {
77
// can only detect if useragent is defined
88
if (typeof navigator === "undefined") {
99
return false;
@@ -15,7 +15,7 @@ export function isAndroid(): boolean {
1515
/**
1616
* @internal
1717
*/
18-
export function isIOS(): boolean {
18+
function isIOS(): boolean {
1919
// can only detect if useragent is defined
2020
if (typeof navigator === "undefined") {
2121
return false;

packages/thirdweb/src/wallets/create-wallet.ts

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { trackConnect } from "../analytics/track/connect.js";
22
import type { Chain } from "../chains/types.js";
33
import { getCachedChainIfExists } from "../chains/utils.js";
44
import { webLocalStorage } from "../utils/storage/webStorage.js";
5+
import { formatWalletConnectUrl } from "../utils/url.js";
56
import { isMobile } from "../utils/web/isMobile.js";
67
import { openWindow } from "../utils/web/openWindow.js";
8+
import { getWalletInfo } from "./__generated__/getWalletInfo.js";
79
import type {
810
InjectedSupportedWalletIds,
911
WCSupportedWalletIds,
@@ -313,43 +315,63 @@ export function createWallet<const ID extends WalletId>(
313315
...wcOptions,
314316
walletConnect: {
315317
...wcOptions.walletConnect,
316-
onDisplayUri: wcOptions.walletConnect?.showQrModal
317-
? async (uri) => {
318-
// Check if we're in a browser environment
319-
if (
320-
typeof window !== "undefined" &&
321-
typeof document !== "undefined"
322-
) {
323-
try {
324-
const { createQROverlay } = await import(
325-
"./wallet-connect/qr-overlay.js"
318+
onDisplayUri:
319+
wcOptions.walletConnect?.onDisplayUri ||
320+
(async (uri) => {
321+
// Check if we're in a browser environment
322+
if (
323+
typeof window !== "undefined" &&
324+
typeof document !== "undefined"
325+
) {
326+
// on mobile, open the wallet app via deeplink
327+
if (isMobile()) {
328+
const walletInfo = await getWalletInfo(id);
329+
330+
const mobileAppLink =
331+
walletInfo.mobile.native ||
332+
walletInfo.mobile.universal;
333+
if (mobileAppLink) {
334+
openWindow(
335+
formatWalletConnectUrl(mobileAppLink, uri)
336+
.redirect,
326337
);
338+
} else {
339+
// on android, wc:// links show the app picker
340+
openWindow(uri);
341+
}
342+
return;
343+
}
327344

328-
// Clean up any existing overlay
329-
if (qrOverlay) {
330-
qrOverlay.destroy();
331-
}
345+
try {
346+
// on desktop, create a QR overlay
347+
const { createQROverlay } = await import(
348+
"./wallet-connect/qr-overlay.js"
349+
);
332350

333-
// Create new QR overlay
334-
qrOverlay = createQROverlay(uri, {
335-
theme:
336-
wcOptions.walletConnect?.qrModalOptions
337-
?.themeMode ?? "dark",
338-
qrSize: 280,
339-
showCloseButton: true,
340-
onCancel: () => {
341-
wcOptions.walletConnect?.onCancel?.();
342-
},
343-
});
344-
} catch (error) {
345-
console.error(
346-
"Failed to create QR overlay:",
347-
error,
348-
);
351+
// Clean up any existing overlay
352+
if (qrOverlay) {
353+
qrOverlay.destroy();
349354
}
355+
356+
// Create new QR overlay
357+
qrOverlay = createQROverlay(uri, {
358+
theme:
359+
wcOptions.walletConnect?.qrModalOptions
360+
?.themeMode ?? "dark",
361+
qrSize: 280,
362+
showCloseButton: true,
363+
onCancel: () => {
364+
wcOptions.walletConnect?.onCancel?.();
365+
},
366+
});
367+
} catch (error) {
368+
console.error(
369+
"Failed to create QR overlay:",
370+
error,
371+
);
350372
}
351373
}
352-
: undefined,
374+
}),
353375
},
354376
},
355377
emitter,

packages/thirdweb/src/wallets/wallet-connect/controller.ts

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -123,30 +123,28 @@ export async function connectWC(
123123
optionalChains: optionalChains,
124124
});
125125

126-
if (!provider.session) {
127-
// For UniversalProvider, we need to connect with namespaces
128-
await provider.connect({
129-
...(wcOptions?.pairingTopic
130-
? { pairingTopic: wcOptions?.pairingTopic }
131-
: {}),
132-
namespaces: {
133-
[NAMESPACE]: {
134-
chains: chainsToRequest,
135-
events: ["chainChanged", "accountsChanged"],
136-
methods: [
137-
"eth_sendTransaction",
138-
"eth_signTransaction",
139-
"eth_sign",
140-
"personal_sign",
141-
"eth_signTypedData",
142-
"wallet_switchEthereumChain",
143-
"wallet_addEthereumChain",
144-
],
145-
rpcMap,
146-
},
126+
// For UniversalProvider, we need to connect with namespaces
127+
await provider.connect({
128+
...(wcOptions?.pairingTopic
129+
? { pairingTopic: wcOptions?.pairingTopic }
130+
: {}),
131+
namespaces: {
132+
[NAMESPACE]: {
133+
chains: chainsToRequest,
134+
events: ["chainChanged", "accountsChanged"],
135+
methods: [
136+
"eth_sendTransaction",
137+
"eth_signTransaction",
138+
"eth_sign",
139+
"personal_sign",
140+
"eth_signTypedData",
141+
"wallet_switchEthereumChain",
142+
"wallet_addEthereumChain",
143+
],
144+
rpcMap,
147145
},
148-
});
149-
}
146+
},
147+
});
150148

151149
setRequestedChainsIds(
152150
chainsToRequest.map((x) => Number(x.split(":")[1])),
@@ -223,9 +221,14 @@ export async function autoConnectWC(
223221
sessionHandler,
224222
);
225223

224+
if (!provider.session) {
225+
await provider.disconnect();
226+
throw new Error("No wallet connect session found on provider.");
227+
}
228+
226229
// For UniversalProvider, get accounts from enable() method
227-
const addresses = await provider.enable();
228-
const address = addresses[0];
230+
const namespaceAccounts = provider.session?.namespaces?.[NAMESPACE]?.accounts;
231+
const address = namespaceAccounts?.[0]?.split(":")[2];
229232

230233
if (!address) {
231234
throw new Error("No accounts found on provider.");

0 commit comments

Comments
 (0)