feat(mobile): WalletConnect pairing and connect dApp modal - #543
Conversation
Port the browser wallet's WalletConnect v2 integration to React Native so the mobile wallet can pair with external dApps and sign for web apps from the phone. - lib/polyfills.ts: the shims WalletConnect and the Stellar SDK need on Hermes. react-native-compat has to be evaluated first, so the import order is fixed here and this module is imported before anything else in lib/walletConnect.ts. - lib/walletConnectHelpers.ts: pure URI, session and request parsing kept free of native imports so the rules are unit-testable. Pasted or scanned deep links that wrap the pairing URI in a query parameter are unwrapped. - lib/walletConnect.ts: client lifecycle, pairing, session approve/reject/ disconnect and the passkey signing pipeline. Sessions persist to AsyncStorage, hashing goes through expo-crypto, and the passkey assertion comes from an injected signer so this module stays independent of any one passkey implementation. Incoming requests are held in a subscribable queue rather than dispatched as one-shot events, so an approval UI that mounts later still sees them. - lib/walletStore.ts: wallet address and fee-payer secret in the OS keychain via expo-secure-store rather than web storage. - lib/network.ts: network config reading EXPO_PUBLIC_* variables. - hooks/useWalletConnect.ts, components/ConnectDAppModal.tsx: scan with expo-camera or paste a URI, then approve or reject the session proposal. - app/index.tsx: Connect dApp entry point and the list of connected dApps. Adds a jest-expo test harness with unit coverage for the parsing helpers.
|
@noevidence1017 is attempting to deploy a commit to the miracle656's projects Team on Vercel. A member of the Team first needs to authorize it. |
…nect-connect-modal-495 # Conflicts: # frontend/mobile/README.md # frontend/mobile/app/index.tsx # frontend/mobile/package-lock.json # frontend/mobile/package.json
|
@noevidence1017 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
Thanks @noevidence1017 — WalletConnect v2 pairing with the connect-dApp sheet, the polyfill layer Hermes needs, and sessions namespaced per Merged |
Summary
Ports the browser wallet's WalletConnect v2 integration to React Native so the mobile wallet can pair with external dApps and sign for web apps from the phone. Scanning or pasting a
wc:URI establishes a session after an explicit approval.closes #495
What changed
lib/polyfills.ts— React Native shimsWalletConnect and the Stellar SDK both assume browser or Node globals that Hermes does not provide. Import order matters here and is the reason this is its own module:
@walletconnect/react-native-compathas to be evaluated before the WalletConnect core, which readsTextEncoder,URLand async storage at import time. Also installsreact-native-get-random-values,react-native-url-polyfill/autoandBuffer.lib/walletConnect.tsimports it first.lib/walletConnectHelpers.ts— pure logicURI normalisation, session parsing, request parsing, JSON-RPC envelopes and user-rejection detection, kept free of native imports so the rules are unit-testable without booting Expo, WalletConnect or the Stellar SDK.
URI handling is a little more forgiving than the web version, because phones are where the awkward inputs come from: surrounding whitespace from clipboards and scanners is trimmed, and a deep link that wraps the pairing URI in a percent-encoded query parameter is unwrapped.
lib/walletConnect.ts— client, sessions and signingThe signing pipeline is a faithful port — same recording-mode simulation, same
signatureExpirationLedgerhandling, same enforce-mode re-simulation so the footprint covers the wallet contract storage__check_authreads. What differs is the runtime plumbing:AsyncStorageinstead ofsessionStorage, and the fee-payer secret comes from the OS keychain (lib/walletStore.ts,expo-secure-store) rather than web storage.expo-crypto, since Hermes has nocrypto.subtle.registerAuthEntrySigner) rather than a directnavigator.credentialscall, so this module stays independent of any one passkey implementation. Without a registered signer, signing fails with a clear message rather than silently doing nothing.session_requestevents are held in a subscribable queue instead of being dispatched as one-shot DOM events. On mobile an approval surface can easily mount after the request arrives; a fire-and-forget event would drop it.hooks/useWalletConnect.ts— React binding over the store, exposing sessions, the pending proposal, the pending requests, and anerrorfor surfacing a missing project id.components/ConnectDAppModal.tsx— bottom sheet with scan and paste tabs. The browser camera plusjsQRpipeline is replaced byexpo-camera's native QR scanner, with camera-permission handling and a graceful fallback to the paste tab when permission is denied. Paste has a clipboard shortcut. Once a proposal arrives the sheet shows who is asking, what they get and which account is involved, and the session is established only on an explicit approve.app/index.tsx— a Connect dApp entry point and the list of connected dApps, so the flow is reachable and testable.Dependencies
@walletconnect/core,@walletconnect/utilsand@walletconnect/react-native-compatare pinned to2.17.1, matching what@walletconnect/web3wallet@1.16.1itself pins — a floating^2.xpulls in a second copy of@walletconnect/typesand theCoreinstance then fails to typecheck againstICore.Also adds
expo-camera,expo-clipboard,expo-crypto,expo-secure-store,@react-native-async-storage/async-storage,react-native-get-random-values,react-native-url-polyfillandbuffer, plusjest-expoas a dev dependency —frontend/mobilehad no test harness.app.jsongains theexpo-cameraplugin with a camera-usage string and theexpo-secure-storeplugin.Configuration
# frontend/mobile/.env.local EXPO_PUBLIC_WC_PROJECT_ID=your_project_id EXPO_PUBLIC_NETWORK=testnet EXPO_PUBLIC_SOROBAN_RPC_URL=https://soroban-testnet.stellar.orgSessions are namespaced to
stellar:testnetorstellar:pubnetfromEXPO_PUBLIC_NETWORKand advertisestellar_signXDRandstellar_signAndSubmitXDR.Testing
npm test— 16 unit tests over the parsing helpers: URI normalisation (plain, whitespace-padded, uppercase scheme, deep-link-wrapped, invalid), chain id mapping, session parsing with and without namespaced accounts, all seven XDR param shapes dApps send plus the null cases, request flattening, JSON-RPC envelopes and rejection detection.npm run typecheckpasses.Manually verified end to end against a WalletConnect test dApp: scanning the QR code and pasting the URI both produce a session proposal, approving establishes the session, and it appears in the connected list.
Notes for review
tsconfig.jsongains"types": ["jest"]; without it TypeScript did not pick up the jest globals under the Expo base config.package-lock.jsondiff is large because the WalletConnect tree and the jest harness are both new to this package.