2026-06-04 09:36:20 +08:00
|
|
|
import { useEffect } from "react";
|
feat(wallet): bypass WalletConnect for TP/imToken on mobile to fix China users
The WalletConnect relay (wss://relay.walletconnect.org) is unreliable/blocked
in mainland China. Every wallet flow (desktop QR, mobile deeplink, mobile QR)
depends on it, so Chinese users see the login button hang forever and the
QR code never appears. When RainbowKit's render fails, the whole site goes
white because nothing catches the error.
Changes:
- Add WalletStackErrorBoundary around <RainbowWalletProvider> + modal so
RainbowKit init failures no longer blank the entire app.
- Hoist <WalletProvider> above the boundary; it only depends on the injected
provider, so useWallet keeps working for header / favorites / etc. even
when the WC stack is dead.
- On mobile, the TP/imToken 'Open Wallet App' button now navigates directly
to tpdapp://open / imtokenv2://navigate/DappView with an ?autoLogin=<kind>
query, pulling the site into the wallet's in-app browser without ever
touching the WC relay. MetaMask still uses the WC path (no equivalent
deeplink).
- Add AutoInjectedLogin: when the page loads with ?autoLogin=<kind>, wait
up to 8s for window.ethereum, then connectInjectedWallet + completeLogin.
Strips the param via history.replaceState to avoid re-firing on reload.
- Guard against the in-app-browser disconnect/reconnect case: if
getInjectedWallet(kind) is already truthy, skip the deeplink and let
useWalletConnectLogin's deeplink mode take the injected fast path
(avoids TP trying to open TP recursively).
2026-06-03 20:07:23 +08:00
|
|
|
import {
|
2026-06-04 09:39:14 +08:00
|
|
|
connectInjectedWallet,
|
feat(wallet): bypass WalletConnect for TP/imToken on mobile to fix China users
The WalletConnect relay (wss://relay.walletconnect.org) is unreliable/blocked
in mainland China. Every wallet flow (desktop QR, mobile deeplink, mobile QR)
depends on it, so Chinese users see the login button hang forever and the
QR code never appears. When RainbowKit's render fails, the whole site goes
white because nothing catches the error.
Changes:
- Add WalletStackErrorBoundary around <RainbowWalletProvider> + modal so
RainbowKit init failures no longer blank the entire app.
- Hoist <WalletProvider> above the boundary; it only depends on the injected
provider, so useWallet keeps working for header / favorites / etc. even
when the WC stack is dead.
- On mobile, the TP/imToken 'Open Wallet App' button now navigates directly
to tpdapp://open / imtokenv2://navigate/DappView with an ?autoLogin=<kind>
query, pulling the site into the wallet's in-app browser without ever
touching the WC relay. MetaMask still uses the WC path (no equivalent
deeplink).
- Add AutoInjectedLogin: when the page loads with ?autoLogin=<kind>, wait
up to 8s for window.ethereum, then connectInjectedWallet + completeLogin.
Strips the param via history.replaceState to avoid re-firing on reload.
- Guard against the in-app-browser disconnect/reconnect case: if
getInjectedWallet(kind) is already truthy, skip the deeplink and let
useWalletConnectLogin's deeplink mode take the injected fast path
(avoids TP trying to open TP recursively).
2026-06-03 20:07:23 +08:00
|
|
|
getInjectedWallet,
|
2026-06-04 09:46:24 +08:00
|
|
|
isImTokenBrowser,
|
2026-06-04 07:23:05 +08:00
|
|
|
signInWithInjectedWallet,
|
feat(wallet): bypass WalletConnect for TP/imToken on mobile to fix China users
The WalletConnect relay (wss://relay.walletconnect.org) is unreliable/blocked
in mainland China. Every wallet flow (desktop QR, mobile deeplink, mobile QR)
depends on it, so Chinese users see the login button hang forever and the
QR code never appears. When RainbowKit's render fails, the whole site goes
white because nothing catches the error.
Changes:
- Add WalletStackErrorBoundary around <RainbowWalletProvider> + modal so
RainbowKit init failures no longer blank the entire app.
- Hoist <WalletProvider> above the boundary; it only depends on the injected
provider, so useWallet keeps working for header / favorites / etc. even
when the WC stack is dead.
- On mobile, the TP/imToken 'Open Wallet App' button now navigates directly
to tpdapp://open / imtokenv2://navigate/DappView with an ?autoLogin=<kind>
query, pulling the site into the wallet's in-app browser without ever
touching the WC relay. MetaMask still uses the WC path (no equivalent
deeplink).
- Add AutoInjectedLogin: when the page loads with ?autoLogin=<kind>, wait
up to 8s for window.ethereum, then connectInjectedWallet + completeLogin.
Strips the param via history.replaceState to avoid re-firing on reload.
- Guard against the in-app-browser disconnect/reconnect case: if
getInjectedWallet(kind) is already truthy, skip the deeplink and let
useWalletConnectLogin's deeplink mode take the injected fast path
(avoids TP trying to open TP recursively).
2026-06-03 20:07:23 +08:00
|
|
|
type WalletKind,
|
|
|
|
|
} from "./injected";
|
2026-06-04 09:39:14 +08:00
|
|
|
import { localWalletToken, useWallet } from "./WalletProvider";
|
feat(wallet): bypass WalletConnect for TP/imToken on mobile to fix China users
The WalletConnect relay (wss://relay.walletconnect.org) is unreliable/blocked
in mainland China. Every wallet flow (desktop QR, mobile deeplink, mobile QR)
depends on it, so Chinese users see the login button hang forever and the
QR code never appears. When RainbowKit's render fails, the whole site goes
white because nothing catches the error.
Changes:
- Add WalletStackErrorBoundary around <RainbowWalletProvider> + modal so
RainbowKit init failures no longer blank the entire app.
- Hoist <WalletProvider> above the boundary; it only depends on the injected
provider, so useWallet keeps working for header / favorites / etc. even
when the WC stack is dead.
- On mobile, the TP/imToken 'Open Wallet App' button now navigates directly
to tpdapp://open / imtokenv2://navigate/DappView with an ?autoLogin=<kind>
query, pulling the site into the wallet's in-app browser without ever
touching the WC relay. MetaMask still uses the WC path (no equivalent
deeplink).
- Add AutoInjectedLogin: when the page loads with ?autoLogin=<kind>, wait
up to 8s for window.ethereum, then connectInjectedWallet + completeLogin.
Strips the param via history.replaceState to avoid re-firing on reload.
- Guard against the in-app-browser disconnect/reconnect case: if
getInjectedWallet(kind) is already truthy, skip the deeplink and let
useWalletConnectLogin's deeplink mode take the injected fast path
(avoids TP trying to open TP recursively).
2026-06-03 20:07:23 +08:00
|
|
|
|
|
|
|
|
const AUTO_LOGIN_PARAM = "autoLogin";
|
|
|
|
|
const ETHEREUM_WAIT_MS = 8000;
|
|
|
|
|
const ETHEREUM_POLL_MS = 200;
|
|
|
|
|
|
|
|
|
|
function parseKind(value: string | null): WalletKind | null {
|
|
|
|
|
if (value === "tokenPocket" || value === "metaMask" || value === "imToken") {
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function stripAutoLoginParam(): void {
|
|
|
|
|
const url = new URL(window.location.href);
|
|
|
|
|
url.searchParams.delete(AUTO_LOGIN_PARAM);
|
|
|
|
|
const qs = url.searchParams.toString();
|
|
|
|
|
const next = url.pathname + (qs ? `?${qs}` : "") + url.hash;
|
|
|
|
|
window.history.replaceState({}, "", next);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function waitForInjected(kind: WalletKind): Promise<boolean> {
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
const start = Date.now();
|
|
|
|
|
const tick = () => {
|
|
|
|
|
if (getInjectedWallet(kind)) {
|
|
|
|
|
resolve(true);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (Date.now() - start >= ETHEREUM_WAIT_MS) {
|
|
|
|
|
resolve(false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
window.setTimeout(tick, ETHEREUM_POLL_MS);
|
|
|
|
|
};
|
|
|
|
|
tick();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function AutoInjectedLogin() {
|
|
|
|
|
const { completeLogin, status } = useWallet();
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (typeof window === "undefined") return;
|
|
|
|
|
const params = new URLSearchParams(window.location.search);
|
2026-06-04 09:46:24 +08:00
|
|
|
const explicitKind = parseKind(params.get(AUTO_LOGIN_PARAM));
|
|
|
|
|
const kind = explicitKind ?? (isImTokenBrowser() ? "imToken" : null);
|
feat(wallet): bypass WalletConnect for TP/imToken on mobile to fix China users
The WalletConnect relay (wss://relay.walletconnect.org) is unreliable/blocked
in mainland China. Every wallet flow (desktop QR, mobile deeplink, mobile QR)
depends on it, so Chinese users see the login button hang forever and the
QR code never appears. When RainbowKit's render fails, the whole site goes
white because nothing catches the error.
Changes:
- Add WalletStackErrorBoundary around <RainbowWalletProvider> + modal so
RainbowKit init failures no longer blank the entire app.
- Hoist <WalletProvider> above the boundary; it only depends on the injected
provider, so useWallet keeps working for header / favorites / etc. even
when the WC stack is dead.
- On mobile, the TP/imToken 'Open Wallet App' button now navigates directly
to tpdapp://open / imtokenv2://navigate/DappView with an ?autoLogin=<kind>
query, pulling the site into the wallet's in-app browser without ever
touching the WC relay. MetaMask still uses the WC path (no equivalent
deeplink).
- Add AutoInjectedLogin: when the page loads with ?autoLogin=<kind>, wait
up to 8s for window.ethereum, then connectInjectedWallet + completeLogin.
Strips the param via history.replaceState to avoid re-firing on reload.
- Guard against the in-app-browser disconnect/reconnect case: if
getInjectedWallet(kind) is already truthy, skip the deeplink and let
useWalletConnectLogin's deeplink mode take the injected fast path
(avoids TP trying to open TP recursively).
2026-06-03 20:07:23 +08:00
|
|
|
if (!kind) return;
|
2026-06-04 07:39:17 +08:00
|
|
|
if (status === "loading") return;
|
feat(wallet): bypass WalletConnect for TP/imToken on mobile to fix China users
The WalletConnect relay (wss://relay.walletconnect.org) is unreliable/blocked
in mainland China. Every wallet flow (desktop QR, mobile deeplink, mobile QR)
depends on it, so Chinese users see the login button hang forever and the
QR code never appears. When RainbowKit's render fails, the whole site goes
white because nothing catches the error.
Changes:
- Add WalletStackErrorBoundary around <RainbowWalletProvider> + modal so
RainbowKit init failures no longer blank the entire app.
- Hoist <WalletProvider> above the boundary; it only depends on the injected
provider, so useWallet keeps working for header / favorites / etc. even
when the WC stack is dead.
- On mobile, the TP/imToken 'Open Wallet App' button now navigates directly
to tpdapp://open / imtokenv2://navigate/DappView with an ?autoLogin=<kind>
query, pulling the site into the wallet's in-app browser without ever
touching the WC relay. MetaMask still uses the WC path (no equivalent
deeplink).
- Add AutoInjectedLogin: when the page loads with ?autoLogin=<kind>, wait
up to 8s for window.ethereum, then connectInjectedWallet + completeLogin.
Strips the param via history.replaceState to avoid re-firing on reload.
- Guard against the in-app-browser disconnect/reconnect case: if
getInjectedWallet(kind) is already truthy, skip the deeplink and let
useWalletConnectLogin's deeplink mode take the injected fast path
(avoids TP trying to open TP recursively).
2026-06-03 20:07:23 +08:00
|
|
|
|
2026-06-04 09:46:24 +08:00
|
|
|
if (explicitKind) stripAutoLoginParam();
|
2026-06-04 09:36:20 +08:00
|
|
|
if (status === "loggedIn") return;
|
feat(wallet): bypass WalletConnect for TP/imToken on mobile to fix China users
The WalletConnect relay (wss://relay.walletconnect.org) is unreliable/blocked
in mainland China. Every wallet flow (desktop QR, mobile deeplink, mobile QR)
depends on it, so Chinese users see the login button hang forever and the
QR code never appears. When RainbowKit's render fails, the whole site goes
white because nothing catches the error.
Changes:
- Add WalletStackErrorBoundary around <RainbowWalletProvider> + modal so
RainbowKit init failures no longer blank the entire app.
- Hoist <WalletProvider> above the boundary; it only depends on the injected
provider, so useWallet keeps working for header / favorites / etc. even
when the WC stack is dead.
- On mobile, the TP/imToken 'Open Wallet App' button now navigates directly
to tpdapp://open / imtokenv2://navigate/DappView with an ?autoLogin=<kind>
query, pulling the site into the wallet's in-app browser without ever
touching the WC relay. MetaMask still uses the WC path (no equivalent
deeplink).
- Add AutoInjectedLogin: when the page loads with ?autoLogin=<kind>, wait
up to 8s for window.ethereum, then connectInjectedWallet + completeLogin.
Strips the param via history.replaceState to avoid re-firing on reload.
- Guard against the in-app-browser disconnect/reconnect case: if
getInjectedWallet(kind) is already truthy, skip the deeplink and let
useWalletConnectLogin's deeplink mode take the injected fast path
(avoids TP trying to open TP recursively).
2026-06-03 20:07:23 +08:00
|
|
|
|
|
|
|
|
let cancelled = false;
|
|
|
|
|
void waitForInjected(kind).then(async (ready) => {
|
2026-06-04 09:36:20 +08:00
|
|
|
if (cancelled || !ready) return;
|
|
|
|
|
try {
|
2026-06-04 09:39:14 +08:00
|
|
|
if (kind === "imToken") {
|
|
|
|
|
const address = await connectInjectedWallet(kind);
|
|
|
|
|
if (cancelled) return;
|
|
|
|
|
completeLogin(localWalletToken(address), address);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 09:36:20 +08:00
|
|
|
const res = await signInWithInjectedWallet(kind);
|
|
|
|
|
if (cancelled) return;
|
|
|
|
|
completeLogin(res.token, res.wallet);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.warn("[wallet-autologin] failed", err);
|
feat(wallet): bypass WalletConnect for TP/imToken on mobile to fix China users
The WalletConnect relay (wss://relay.walletconnect.org) is unreliable/blocked
in mainland China. Every wallet flow (desktop QR, mobile deeplink, mobile QR)
depends on it, so Chinese users see the login button hang forever and the
QR code never appears. When RainbowKit's render fails, the whole site goes
white because nothing catches the error.
Changes:
- Add WalletStackErrorBoundary around <RainbowWalletProvider> + modal so
RainbowKit init failures no longer blank the entire app.
- Hoist <WalletProvider> above the boundary; it only depends on the injected
provider, so useWallet keeps working for header / favorites / etc. even
when the WC stack is dead.
- On mobile, the TP/imToken 'Open Wallet App' button now navigates directly
to tpdapp://open / imtokenv2://navigate/DappView with an ?autoLogin=<kind>
query, pulling the site into the wallet's in-app browser without ever
touching the WC relay. MetaMask still uses the WC path (no equivalent
deeplink).
- Add AutoInjectedLogin: when the page loads with ?autoLogin=<kind>, wait
up to 8s for window.ethereum, then connectInjectedWallet + completeLogin.
Strips the param via history.replaceState to avoid re-firing on reload.
- Guard against the in-app-browser disconnect/reconnect case: if
getInjectedWallet(kind) is already truthy, skip the deeplink and let
useWalletConnectLogin's deeplink mode take the injected fast path
(avoids TP trying to open TP recursively).
2026-06-03 20:07:23 +08:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return () => {
|
|
|
|
|
cancelled = true;
|
|
|
|
|
};
|
2026-06-04 09:36:20 +08:00
|
|
|
}, [completeLogin, status]);
|
2026-06-04 07:39:17 +08:00
|
|
|
|
2026-06-04 09:36:20 +08:00
|
|
|
return null;
|
feat(wallet): bypass WalletConnect for TP/imToken on mobile to fix China users
The WalletConnect relay (wss://relay.walletconnect.org) is unreliable/blocked
in mainland China. Every wallet flow (desktop QR, mobile deeplink, mobile QR)
depends on it, so Chinese users see the login button hang forever and the
QR code never appears. When RainbowKit's render fails, the whole site goes
white because nothing catches the error.
Changes:
- Add WalletStackErrorBoundary around <RainbowWalletProvider> + modal so
RainbowKit init failures no longer blank the entire app.
- Hoist <WalletProvider> above the boundary; it only depends on the injected
provider, so useWallet keeps working for header / favorites / etc. even
when the WC stack is dead.
- On mobile, the TP/imToken 'Open Wallet App' button now navigates directly
to tpdapp://open / imtokenv2://navigate/DappView with an ?autoLogin=<kind>
query, pulling the site into the wallet's in-app browser without ever
touching the WC relay. MetaMask still uses the WC path (no equivalent
deeplink).
- Add AutoInjectedLogin: when the page loads with ?autoLogin=<kind>, wait
up to 8s for window.ethereum, then connectInjectedWallet + completeLogin.
Strips the param via history.replaceState to avoid re-firing on reload.
- Guard against the in-app-browser disconnect/reconnect case: if
getInjectedWallet(kind) is already truthy, skip the deeplink and let
useWalletConnectLogin's deeplink mode take the injected fast path
(avoids TP trying to open TP recursively).
2026-06-03 20:07:23 +08:00
|
|
|
}
|