---
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.
