fix(wallet): restore metamask mobile login
This commit is contained in:
103
.unipi/docs/debug/2026-06-02-metamask-wallet-login-debug.md
Normal file
103
.unipi/docs/debug/2026-06-02-metamask-wallet-login-debug.md
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: "MetaMask Wallet Login Does Not Surface Address — Debug Report"
|
||||
type: debug
|
||||
date: 2026-06-02
|
||||
severity: high
|
||||
status: root-caused
|
||||
---
|
||||
|
||||
# MetaMask Wallet Login Does Not Surface Address — Debug Report
|
||||
|
||||
## Summary
|
||||
MetaMask QR login and mobile deeplink login can be approved in MetaMask, but the ARK frontend does not write the approved wallet address into the local wallet session; the mobile QR waiting text is also incorrectly TokenPocket-specific for all wallets.
|
||||
|
||||
## Expected Behavior
|
||||
- Selecting MetaMask and approving the connection should result in the page showing the connected wallet address.
|
||||
- Mobile MetaMask deeplink login should return/reconnect to the page and complete local no-signature login with `local-wallet:<address>`.
|
||||
- QR login copy should be generic or absent; it should not say “waiting in TokenPocket” when the selected wallet is MetaMask or imToken.
|
||||
|
||||
## Actual Behavior
|
||||
- Terry can approve MetaMask QR/deeplink login, but the web page does not show the authorized address.
|
||||
- The QR panel uses TokenPocket-specific copy for any wallet on mobile, e.g. imToken QR login still shows a TokenPocket waiting message.
|
||||
|
||||
## Reproduction Steps
|
||||
1. Open the public frontend and open the wallet login modal.
|
||||
2. Select MetaMask.
|
||||
3. Use either:
|
||||
- QR login: scan the QR using MetaMask and approve, or
|
||||
- Mobile app login: tap “Open wallet app”, approve in MetaMask, then return to the web page.
|
||||
4. Observe that the page does not show the wallet address.
|
||||
5. Select imToken QR login on mobile and observe that the QR panel displays TokenPocket-specific waiting text.
|
||||
|
||||
## Environment
|
||||
- Project: Arkie Library Frontend (`ark-database-web`)
|
||||
- Branch context: `terry-wallet-login`
|
||||
- Stack: React 18, Vite, TypeScript, RainbowKit, Wagmi, WalletConnect/Reown
|
||||
- Wallets involved: MetaMask Mobile, TokenPocket, imToken
|
||||
- Backend auth endpoints intentionally not required for this flow; login is local no-signature wallet session.
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
### Failure Chain
|
||||
1. `WalletLoginModal` calls `wc.start(kind, mode)` for all wallet app/QR flows.
|
||||
2. `useWalletConnectLogin.start()` currently chooses the first generic WalletConnect connector for non-injected flows:
|
||||
- `connectors.find((item) => item.type === "walletConnect") ?? connectors.find((item) => item.id === "walletConnect")`
|
||||
3. On MetaMask mobile, RainbowKit’s own MetaMask wallet definition intentionally uses Wagmi’s `metaMask()` / MetaMask SDK connector, not the generic WalletConnect connector.
|
||||
4. The custom hook bypasses that MetaMask-specific connector on mobile, so MetaMask SDK deeplink/reconnect handling is not used.
|
||||
5. The hook only calls `completeLogin(localWalletToken(address), address)` inside the awaited `connectAsync(...)` result path.
|
||||
6. If MetaMask approval completes while the browser is backgrounded, after a page reload, or through a restored Wagmi connection, there is no `useAccount`/reconnect bridge that converts the Wagmi connected address into the app’s local wallet session.
|
||||
7. `RainbowWalletProvider` calls `useReconnect()`, but this only restores Wagmi connection state; it does not update `WalletProvider` unless `useWalletConnectLogin` observes the restored account and calls `completeLogin`.
|
||||
8. Therefore MetaMask may be approved/connected at the wallet/Wagmi layer but the ARK UI still has no `local-wallet:<address>` token and shows logged-out/no address.
|
||||
|
||||
### Root Cause
|
||||
The MetaMask flow is treated as a generic WalletConnect flow, but RainbowKit/Wagmi have MetaMask-specific mobile behavior. Additionally, local ARK wallet login is tied only to the synchronous `connectAsync` return path instead of being derived from Wagmi account state/reconnect events. This misses MetaMask connections that resolve via mobile app backgrounding, deep link return, QR approval, or page reload.
|
||||
|
||||
### Evidence
|
||||
- File: `src/wallet/useWalletConnectLogin.ts` — connector selection prefers `item.type === "walletConnect"` for all wallets, so mobile MetaMask does not use the RainbowKit/Wagmi MetaMask connector.
|
||||
- File: `src/wallet/useWalletConnectLogin.ts` — `completeLogin(localWalletToken(...), ...)` only runs after `await connectAsync(...)`; there is no `useAccount` effect to complete local login when Wagmi is already/reconnected.
|
||||
- File: `src/wallet/RainbowWalletProvider.tsx` — `WalletReconnectOnMount` calls `useReconnect()`, but no downstream code maps the restored Wagmi account into `WalletProvider`.
|
||||
- File: `node_modules/@rainbow-me/rainbowkit/dist/wallets/walletConnectors/chunk-BQHQU37S.js` — RainbowKit MetaMask wallet uses `metaMask()` connector on mobile and comments that “MetaMask mobile deep linking [is] handled by wagmi”. The custom hook bypasses this by selecting a generic WalletConnect connector.
|
||||
- File: `src/wallet/WalletLoginModal.tsx` — QR text uses `mobileDevice ? t("walletTpWaiting") : t("walletQrUseAnotherDevice")` for every selected wallet, causing TokenPocket-specific copy for MetaMask/imToken.
|
||||
- File: `src/locales/en.ts` and `src/locales/zh-CN.ts` — `walletQrUseAnotherDevice` also explicitly mentions TokenPocket, so even desktop/generic QR copy is wallet-specific.
|
||||
|
||||
## Affected Files
|
||||
- `src/wallet/useWalletConnectLogin.ts` — connector choice, deeplink generation, QR URI generation, local-login completion.
|
||||
- `src/wallet/WalletLoginModal.tsx` — misleading QR panel copy.
|
||||
- `src/wallet/RainbowWalletProvider.tsx` — currently reconnects Wagmi but does not by itself complete local login.
|
||||
- `src/locales/*.ts` — QR copy currently contains TokenPocket-specific text.
|
||||
|
||||
## Suggested Fix
|
||||
Use the wallet-specific connector when a wallet is selected, especially MetaMask, and add a Wagmi account/reconnect bridge that completes the local wallet session whenever Wagmi has an address. Remove or generalize TokenPocket-specific QR waiting copy.
|
||||
|
||||
### Fix Strategy
|
||||
1. In `useWalletConnectLogin.ts`, prefer a connector matching `preferredWallet` before falling back to generic WalletConnect:
|
||||
- MetaMask: `id === "metaMask"` or `type === "metaMask"`
|
||||
- imToken/TokenPocket: matching wallet id when present, otherwise generic WalletConnect
|
||||
2. Add `useAccount()` inside `useWalletConnectLogin` and a `useEffect` that calls `completeLogin(localWalletToken(address), address)` when Wagmi reports `isConnected && address`.
|
||||
3. For MetaMask QR, transform the displayed QR value with the same wallet-specific URI RainbowKit uses: `https://metamask.app.link/wc?uri=<encoded_wc_uri>` instead of always rendering raw `wc:`.
|
||||
4. Remove the QR panel paragraph entirely, or replace it with generic copy such as “Please approve the connection in your wallet app.”
|
||||
5. Re-test MetaMask separately for desktop QR scan, mobile Chrome deeplink return, and MetaMask in-app browser injected login.
|
||||
|
||||
### Risk Assessment
|
||||
- Risk: Selecting MetaMask’s SDK connector may behave differently from WalletConnect for QR mode. Mitigate by falling back to WalletConnect if the MetaMask connector does not emit a `display_uri` in QR mode.
|
||||
- Risk: Auto-completing local login from any Wagmi connected account may log in a stale account after reconnect. Mitigate by only completing when modal/pending/login-in-progress context exists or by clearing stale flags.
|
||||
- Risk: Changing QR value for MetaMask may affect wallets that scan raw WalletConnect URIs. Mitigate by only applying MetaMask app-link QR transformation for MetaMask.
|
||||
|
||||
## Verification Plan
|
||||
1. Run `npx tsc --noEmit`.
|
||||
2. Run `npm run format:check`.
|
||||
3. Run `npm test`.
|
||||
4. Desktop Chrome + MetaMask Mobile QR: Select MetaMask → QR login → scan/approve → confirm address and `local-wallet:<address>`.
|
||||
5. Mobile Chrome + MetaMask app login: Select MetaMask → Open wallet app → approve → return/refresh browser → confirm address appears.
|
||||
6. Regression test TokenPocket and imToken app login.
|
||||
7. Confirm imToken/MetaMask QR login no longer displays TokenPocket-specific text.
|
||||
|
||||
## Related Issues
|
||||
- TokenPocket was previously fixed by handling mobile return/reload behavior.
|
||||
- imToken was previously fixed by adding in-app browser fallback and injected no-signature login.
|
||||
- Existing local-memory context indicates MetaMask QR approval has been observed to not update frontend state.
|
||||
|
||||
## Notes
|
||||
- This report is diagnosis only; no source fix was applied during `/unipi:debug`.
|
||||
- The local no-signature session model means the frontend does not need backend wallet nonce/verify endpoints for this fix.
|
||||
- The current debug UI showing `Wallet debug` may be useful during verification but should be removed or hidden before final production cleanup if no longer needed.
|
||||
Reference in New Issue
Block a user