From dd501cc5a8b694cda9c8a686c90103731501f2d9 Mon Sep 17 00:00:00 2001 From: bassem-abdelazim-trilitech Date: Mon, 14 Sep 2026 14:16:23 +0100 Subject: [PATCH 1/3] [SDK] Fix: refresh stale io.rabby wallet registry entry The generated entry for io.rabby recorded no iOS app, no Android app and no deep link, while the WalletConnect registry publishes all three. That left Rabby with no mobile hand-off and excluded it from WCSupportedWalletIds, so it fell through to the generic external-connect path on desktop instead of the in-modal WalletConnect QR screen. Co-Authored-By: Claude Opus 5 (1M context) --- .changeset/rabby-mobile-registry.md | 7 ++++++ .../src/wallets/__generated__/wallet-ids.ts | 3 ++- .../src/wallets/__generated__/wallet-infos.ts | 2 +- .../__generated__/wallet/io.rabby/index.ts | 25 ++++++++++--------- 4 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 .changeset/rabby-mobile-registry.md diff --git a/.changeset/rabby-mobile-registry.md b/.changeset/rabby-mobile-registry.md new file mode 100644 index 00000000000..d6eb85d8832 --- /dev/null +++ b/.changeset/rabby-mobile-registry.md @@ -0,0 +1,7 @@ +--- +"thirdweb": patch +--- + +Fix Rabby connections on mobile and in-modal QR on desktop by refreshing its stale wallet registry entry. + +The generated entry for `io.rabby` recorded no iOS app, no Android app and no deep link, even though the WalletConnect registry has published all three for some time. As a result Rabby had no mobile hand-off available and was excluded from `WCSupportedWalletIds`, so mobile users could not reach the Rabby app and desktop users were routed onto the generic external-connect fallback instead of the in-modal WalletConnect QR screen. diff --git a/packages/thirdweb/src/wallets/__generated__/wallet-ids.ts b/packages/thirdweb/src/wallets/__generated__/wallet-ids.ts index e54b5ace2df..05e810c8103 100644 --- a/packages/thirdweb/src/wallets/__generated__/wallet-ids.ts +++ b/packages/thirdweb/src/wallets/__generated__/wallet-ids.ts @@ -2,7 +2,7 @@ // This file is auto-generated by the `scripts/wallets/generate.ts` script. // Do not modify this file manually. -// 476 wallets +// 477 wallets export type WCSupportedWalletIds = | "io.1inch.wallet" | "com.binance.wallet" @@ -32,6 +32,7 @@ export type WCSupportedWalletIds = | "com.roninchain.wallet" | "com.okex.wallet" | "com.wemixplay" + | "io.rabby" | "com.tangem" | "com.exodus" | "com.hashpack.wallet" diff --git a/packages/thirdweb/src/wallets/__generated__/wallet-infos.ts b/packages/thirdweb/src/wallets/__generated__/wallet-infos.ts index 4ab85132870..ecddffb1d18 100644 --- a/packages/thirdweb/src/wallets/__generated__/wallet-infos.ts +++ b/packages/thirdweb/src/wallets/__generated__/wallet-infos.ts @@ -2357,7 +2357,7 @@ const ALL_MINIMAL_WALLET_INFOS = [ { id: "io.rabby", name: "Rabby", - hasMobileSupport: false, + hasMobileSupport: true, }, { id: "com.brave.wallet", diff --git a/packages/thirdweb/src/wallets/__generated__/wallet/io.rabby/index.ts b/packages/thirdweb/src/wallets/__generated__/wallet/io.rabby/index.ts index ffcad3849f3..a8baed10a5a 100644 --- a/packages/thirdweb/src/wallets/__generated__/wallet/io.rabby/index.ts +++ b/packages/thirdweb/src/wallets/__generated__/wallet/io.rabby/index.ts @@ -9,25 +9,26 @@ export const wallet = { app: { browser: "https://chrome.google.com/webstore/detail/rabby/acmacodkjbdgmoleebolmdjonilkdbch", - ios: null, - android: null, - mac: null, - windows: null, - linux: null, + ios: "https://apps.apple.com/us/app/rabby-wallet-crypto-evm/id6474381673", + android: + "https://play.google.com/store/apps/details?id=com.debank.rabbymobile", + mac: "", + windows: "", + linux: "", chrome: "https://chrome.google.com/webstore/detail/rabby-wallet/acmacodkjbdgmoleebolmdjonilkdbch", - firefox: null, - safari: null, - edge: null, - opera: null, + firefox: "", + safari: "", + edge: "", + opera: "", }, rdns: "io.rabby", mobile: { - native: null, - universal: null, + native: "rabby://", + universal: "", }, desktop: { - native: null, + native: "", universal: "https://chrome.google.com/webstore/detail/rabby/acmacodkjbdgmoleebolmdjonilkdbch", }, From f1ccf78a46dc7b5184bcd1deaf74f9d35d19ab50 Mon Sep 17 00:00:00 2001 From: bassem-abdelazim-trilitech Date: Mon, 14 Sep 2026 14:19:05 +0100 Subject: [PATCH 2/3] [SDK] Fix: keep the WalletConnect QR overlay clickable and unstacked createQROverlay is appended to document.body and declared no pointer-events, so it inherited `pointer-events: none` from modal libraries that disable the body while a dialog is open - leaving the overlay visible but click-dead. It now sets pointer-events explicitly. Each connect attempt also only tracked its own overlay, so abandoned attempts and expired pairings stacked overlays. Overlays are now tagged and stale ones removed before a new one is appended. Co-Authored-By: Claude Opus 5 (1M context) --- .changeset/qr-overlay-pointer-events.md | 9 ++++ .../wallets/wallet-connect/qr-overlay.test.ts | 44 +++++++++++++++++++ .../src/wallets/wallet-connect/qr-overlay.ts | 18 ++++++++ 3 files changed, 71 insertions(+) create mode 100644 .changeset/qr-overlay-pointer-events.md create mode 100644 packages/thirdweb/src/wallets/wallet-connect/qr-overlay.test.ts diff --git a/.changeset/qr-overlay-pointer-events.md b/.changeset/qr-overlay-pointer-events.md new file mode 100644 index 00000000000..b390df76c1f --- /dev/null +++ b/.changeset/qr-overlay-pointer-events.md @@ -0,0 +1,9 @@ +--- +"thirdweb": patch +--- + +Keep the WalletConnect QR overlay usable when it is opened from inside another modal, and stop overlays stacking. + +The vanilla `createQROverlay` did not declare `pointer-events` on its root. Because the overlay is appended to `document.body`, and modal libraries commonly set `pointer-events: none` there while a dialog is open, the overlay inherited that value and became unclickable — visible at `z-index: 9999`, but with clicks passing through to the dialog behind it. It now sets `pointer-events: auto` explicitly. + +Separately, each connect attempt only tracked the overlay it created, so an abandoned attempt — or a pairing that expired and re-emitted its URI — left its overlay in the DOM and the next attempt stacked another on top. Overlays are now tagged and any stale one is removed before a new one is appended. diff --git a/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.test.ts b/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.test.ts new file mode 100644 index 00000000000..f4b5f1ee675 --- /dev/null +++ b/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.test.ts @@ -0,0 +1,44 @@ +// @vitest-environment happy-dom +import { beforeEach, describe, expect, it } from "vitest"; +import { createQROverlay } from "./qr-overlay.js"; + +const URI = "wc:1234@2?relay-protocol=irn&symKey=abcd"; + +/** + * The overlay is identified by its own styling rather than by any attribute it + * sets, so these tests describe observable behaviour rather than implementation. + */ +function overlayElements(): HTMLElement[] { + return Array.from(document.body.children).filter( + (el): el is HTMLElement => + el instanceof HTMLElement && el.style.zIndex === "9999", + ); +} + +describe("createQROverlay", () => { + beforeEach(() => { + document.body.innerHTML = ""; + }); + + it("keeps the overlay clickable when the host page disables pointer events on body", () => { + // Modal libraries commonly do this while a dialog is open. + document.body.style.pointerEvents = "none"; + + const overlay = createQROverlay(URI); + + const [root] = overlayElements(); + expect(root).toBeDefined(); + // Without an explicit value the overlay inherits `none` from body and + // becomes unclickable despite being visible. + expect(root?.style.pointerEvents).toBe("auto"); + + overlay.destroy(); + }); + + it("replaces an overlay left behind by a previous attempt instead of stacking", () => { + createQROverlay(URI); + createQROverlay(URI); + + expect(overlayElements()).toHaveLength(1); + }); +}); diff --git a/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts b/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts index 24a665fe6d0..f3b7afe788e 100644 --- a/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts +++ b/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts @@ -53,6 +53,12 @@ export interface QROverlay { show: () => void; } +/** + * Marks overlays created by this module so stale ones can be cleaned up. + * @internal + */ +const QR_OVERLAY_ATTRIBUTE = "data-tw-qr-overlay"; + /** * Creates a QR code overlay for the given WalletConnect URI */ @@ -69,14 +75,26 @@ export function createQROverlay( onCancel, } = options; + // Remove overlays left behind by earlier connect attempts. Each call only + // tracks the overlay it created, so an abandoned attempt - or a pairing that + // expired and re-emitted its URI - would otherwise stack another one on top. + for (const stale of document.querySelectorAll(`[${QR_OVERLAY_ATTRIBUTE}]`)) { + stale.remove(); + } + // Create overlay backdrop const overlay = document.createElement("div"); + overlay.setAttribute(QR_OVERLAY_ATTRIBUTE, ""); + // pointer-events is set explicitly because the overlay is appended to + // document.body, and modal libraries commonly disable pointer events there + // while a dialog is open. Inheriting that would render the overlay unclickable. overlay.style.cssText = ` position: fixed; inset: 0; background-color: ${theme === "dark" ? "rgba(0, 0, 0, 0.8)" : "rgba(0, 0, 0, 0.5)"}; backdrop-filter: blur(10px); z-index: 9999; + pointer-events: auto; display: flex; align-items: center; justify-content: center; From 5391bd1d6773206297f71403b34be62597f9ec36 Mon Sep 17 00:00:00 2001 From: bassem-abdelazim-trilitech Date: Mon, 14 Sep 2026 16:45:58 +0100 Subject: [PATCH 3/3] [SDK] Fix: iterate the overlay NodeList without DOM.Iterable tsconfig.base.json declares lib ["ES2022", "DOM"] without DOM.Iterable, so `for...of` over querySelectorAll fails to typecheck with TS2488. Use NodeListOf.forEach, which lib.dom declares directly. Co-Authored-By: Claude Opus 5 (1M context) --- packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts b/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts index f3b7afe788e..8cdf0b0cc58 100644 --- a/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts +++ b/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts @@ -78,9 +78,9 @@ export function createQROverlay( // Remove overlays left behind by earlier connect attempts. Each call only // tracks the overlay it created, so an abandoned attempt - or a pairing that // expired and re-emitted its URI - would otherwise stack another one on top. - for (const stale of document.querySelectorAll(`[${QR_OVERLAY_ATTRIBUTE}]`)) { + document.querySelectorAll(`[${QR_OVERLAY_ATTRIBUTE}]`).forEach((stale) => { stale.remove(); - } + }); // Create overlay backdrop const overlay = document.createElement("div");