2026-06-02 00:28:22 +08:00
|
|
|
import { requestWalletNonce, verifyWalletSignature } from "./api";
|
|
|
|
|
|
2026-06-02 02:58:01 +08:00
|
|
|
export type WalletKind = "tokenPocket" | "metaMask" | "imToken";
|
|
|
|
|
|
2026-06-02 00:28:22 +08:00
|
|
|
export type EthereumProvider = {
|
2026-06-02 02:58:01 +08:00
|
|
|
isMetaMask?: boolean;
|
|
|
|
|
isTokenPocket?: boolean;
|
|
|
|
|
isImToken?: boolean;
|
|
|
|
|
providers?: EthereumProvider[];
|
2026-06-02 00:28:22 +08:00
|
|
|
request: <T = unknown>(args: {
|
|
|
|
|
method: string;
|
|
|
|
|
params?: unknown[];
|
|
|
|
|
}) => Promise<T>;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function getInjectedEthereum(): EthereumProvider | null {
|
|
|
|
|
if (typeof window === "undefined") return null;
|
|
|
|
|
const maybeWindow = window as typeof window & { ethereum?: EthereumProvider };
|
|
|
|
|
return maybeWindow.ethereum ?? null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-02 02:58:01 +08:00
|
|
|
export function getInjectedWallet(kind?: WalletKind): EthereumProvider | null {
|
|
|
|
|
const ethereum = getInjectedEthereum();
|
|
|
|
|
if (!ethereum || !kind) return ethereum;
|
|
|
|
|
const providers = ethereum.providers?.length
|
|
|
|
|
? ethereum.providers
|
|
|
|
|
: [ethereum];
|
|
|
|
|
const match = providers.find((provider) => {
|
|
|
|
|
if (kind === "metaMask") return provider.isMetaMask;
|
|
|
|
|
if (kind === "tokenPocket") return provider.isTokenPocket;
|
|
|
|
|
if (kind === "imToken") return provider.isImToken;
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
return match ?? null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function signInWithInjectedWallet(kind?: WalletKind): Promise<{
|
2026-06-02 00:28:22 +08:00
|
|
|
token: string;
|
|
|
|
|
wallet: string;
|
|
|
|
|
}> {
|
2026-06-02 02:58:01 +08:00
|
|
|
const ethereum = getInjectedWallet(kind);
|
2026-06-02 00:28:22 +08:00
|
|
|
if (!ethereum) throw new Error("No injected wallet found");
|
|
|
|
|
|
2026-06-02 03:43:13 +08:00
|
|
|
// Login is signature-only (EIP-191 personal_sign). The backend verifies the
|
|
|
|
|
// recovered address and never inspects chainId, so we deliberately do NOT
|
|
|
|
|
// switch or add any chain — that only adds a failure-prone wallet popup.
|
2026-06-02 00:28:22 +08:00
|
|
|
const accounts = await ethereum.request<string[]>({
|
|
|
|
|
method: "eth_requestAccounts",
|
|
|
|
|
});
|
|
|
|
|
const address = accounts[0];
|
|
|
|
|
if (!address) throw new Error("No wallet account returned");
|
|
|
|
|
|
|
|
|
|
const nonce = await requestWalletNonce(address);
|
|
|
|
|
const signature = await ethereum.request<string>({
|
|
|
|
|
method: "personal_sign",
|
|
|
|
|
params: [nonce.message, address],
|
|
|
|
|
});
|
|
|
|
|
return verifyWalletSignature({
|
|
|
|
|
address,
|
|
|
|
|
message: nonce.message,
|
|
|
|
|
signature,
|
|
|
|
|
});
|
|
|
|
|
}
|