Files
Arkie-Library-Frontend/.unipi/docs/debug/2026-06-02-metamask-wallet-login-debug.md
2026-06-03 00:12:50 +08:00

8.4 KiB
Raw Blame History

title, type, date, severity, status
title type date severity status
MetaMask Wallet Login Does Not Surface Address — Debug Report debug 2026-06-02 high 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, RainbowKits own MetaMask wallet definition intentionally uses Wagmis 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 apps 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.tscompleteLogin(localWalletToken(...), ...) only runs after await connectAsync(...); there is no useAccount effect to complete local login when Wagmi is already/reconnected.
  • File: src/wallet/RainbowWalletProvider.tsxWalletReconnectOnMount 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.tswalletQrUseAnotherDevice 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 MetaMasks 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.
  • 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.