fix(headless): use Universal Link for custom-deeplink wallets on mobile#5675
fix(headless): use Universal Link for custom-deeplink wallets on mobile#5675svenvoskamp wants to merge 2 commits into
Conversation
Solflare's mobile_link is solflare://, so the headless connect() was building solflare://wc?uri=... which Solflare's app doesn't handle. Route Phantom/Solflare/Coinbase/Binance through MobileWalletUtil.handleMobileDeeplinkRedirect (their /ul/v1/browse/... Universal Link) before falling through to the WC-URI deeplink. Matches the headful selectWalletConnector behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 4c9ec0a The changes in this PR will be included in the next version bump. This PR includes changesets to release 26 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
9 Skipped Deployments
|
Visual Regression Test Results ✅ PassedChromatic Build: https://www.chromatic.com/build?appId=6493191bf4b10fed8ca7353f&number=924 👉 Please review the visual changes in Chromatic and accept or reject them. |
|
📦 Bundle Size Check✅ All bundles are within size limits 📊 View detailed bundle sizes> @reown/appkit-monorepo@1.7.1 size /home/runner/work/appkit/appkit > size-limit |
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||
Solflare's in-app browser exposes both window.solflare and window.ethereum, so the .../ul/v1/browse/<url> Universal Link is the right entry point for EVM dApps too. Previously isCustomDeeplinkWallet and the redirect itself both gated Solflare on namespace === SOLANA, so EVM-only consumers (no Solana adapter registered) fell through to solflare://wc?uri=..., which Solflare's app does not handle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
In headless mode (
useAppKitWallets().connect), tapping Solflare on mobile producedsolflare://wc?uri=…, which Solflare's app does not handle — nothing opened. Phantom worked only by accident because itsmobile_linkhappens to be a valid Universal Link base.Two fixes:
0daaed468— headlessconnect()now mirrors the headfulselectWalletConnectorbehavior: for any wallet flagged byMobileWalletUtil.isCustomDeeplinkWallet, fire the Universal Link viahandleMobileDeeplinkRedirectinstead of building<mobile_link>wc?uri=….4c9ec0a2e—isCustomDeeplinkWalletand the matching branch inhandleMobileDeeplinkRedirectwere Solana-only for Solflare. They now also covereip155. Solflare's in-app browser exposes bothwindow.solflare(Solana) andwindow.ethereum(EVM), so the samehttps://solflare.com/ul/v1/browse/<dapp_url>URL is the right entry point either way.How the bug happens (before fix)
flowchart TD A[user taps Solflare on mobile] --> B{isInjected && connector?} B -- yes --> X1[connectExternal] B -- no --> C{fallback connector?} C -- yes --> X2[connectExternal fallback] C -- no --> D{isMobile?} D -- yes --> E{wcWallet.mobile_link set?} E -- yes --> F["onConnectMobile<br/>builds solflare://wc?uri=..."] E -- no --> G[handleMobileDeeplinkRedirect<br/>Universal Link] F --> H["⛔ Solflare ignores<br/>solflare://wc?uri=..."] G --> I[✅ Solflare opens] style F fill:#ffd5d5,stroke:#c00 style H fill:#ffd5d5,stroke:#c00Solflare's WC-registry
mobile_linkissolflare://, so the headless flow always took the left branch and producedsolflare://wc?uri=…. Solflare's app does not register a handler for that URL, so the redirect was a no-op.Phantom's
mobile_linkishttps://phantom.app/ul/v1/, so<mobile_link>wc?uri=…was coincidentally a valid Phantom Universal Link. That's why Phantom worked while Solflare didn't.Headful vs. headless before any fix
The headful path always called
handleMobileDeeplinkRedirectfirst (Universal Link). The headless path skipped that whenevermobile_linkwas set.Fix #1 — mirror the headful behavior in headless
connect()flowchart TD A[user taps Solflare on mobile] --> B{isInjected && connector?} B -- yes --> X1[connectExternal] B -- no --> C{fallback connector?} C -- yes --> X2[connectExternal fallback] C -- no --> D{isMobile?} D -- yes --> NEW{{"isCustomDeeplinkWallet(id, ns)?<br/>NEW CHECK"}} NEW -- yes --> G[handleMobileDeeplinkRedirect<br/>Universal Link] NEW -- no --> E{wcWallet.mobile_link?} E -- yes --> F[onConnectMobile<br/>WC URI deeplink] E -- no --> G style NEW fill:#d5e6ff,stroke:#06c style G fill:#d5f5d5,stroke:#0a0Custom-deeplink wallets (Phantom, Solflare, Coinbase, Binance) now skip the WC URI deeplink entirely and go straight to the Universal Link, matching headful.
Fix #2 — Solflare on EVM also needs the Universal Link
Before Fix #2, the allow-list inside
MobileWalletUtil.isCustomDeeplinkWalletwas:solanaeip155bip122solanasolanaeip155bip122When BX has both the wagmi adapter and the Solana adapter, and the user taps Solflare while the active chain is
eip155:flowchart TD A[Solflare clicked<br/>activeChain = eip155] --> B{"isCustomDeeplinkWallet(SOLFLARE_ID, 'eip155')?"} B -- "false (before Fix #2)" --> C["onConnectMobile<br/>solflare://wc?uri=..."] C --> D[⛔ silently fails] B -- "true (after Fix #2)" --> E["handleMobileDeeplinkRedirect('eip155')"] E --> F[Universal Link<br/>https://solflare.com/ul/v1/browse/...] F --> G[✅ Solflare opens dApp<br/>in in-app browser] style C fill:#ffd5d5,stroke:#c00 style D fill:#ffd5d5,stroke:#c00 style E fill:#d5e6ff,stroke:#06c style F fill:#d5f5d5,stroke:#0a0 style G fill:#d5f5d5,stroke:#0a0The same Solana-only gate existed inside
handleMobileDeeplinkRedirectitself, so it was double-locked. Both checks now allowsolana | eip155.Final allow-list
solanaeip155bip122solanaeip155← changedsolanaeip155bip122End-to-end fixed flow
Test plan
pnpm --filter @reown/appkit-controllers test— full suite passing, including new cases for Solflare/Phantom on mobile acrosssolana,eip155, andbip122namespaces intests/hooks/react.test.tsandtests/utils/MobileWallet.test.ts.pnpm --filter @reown/appkit-controllers typecheck— clean.1.8.21-solflare-headless-deeplink-evm.0in WalletConnect Pay buyer-experience — Solflare now opens on EVM-active and Solana-active flows; Phantom still opens; regular WC wallets still use the WC URI deeplink.🤖 Generated with Claude Code