diff --git a/.unipi/docs/debug/2026-06-02-metamask-wallet-login-debug.md b/.unipi/docs/debug/2026-06-02-metamask-wallet-login-debug.md new file mode 100644 index 0000000..a9ef5cd --- /dev/null +++ b/.unipi/docs/debug/2026-06-02-metamask-wallet-login-debug.md @@ -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:
`. +- 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:` 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=- {mobileDevice - ? t("walletTpWaiting") - : t("walletQrUseAnotherDevice")} -