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";
|
|
|
|
|
|
|
|
|
|
const bnbChainIdHex = "0x38";
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function ensureBnbChain(ethereum: EthereumProvider): Promise<void> {
|
|
|
|
|
try {
|
|
|
|
|
await ethereum.request({
|
|
|
|
|
method: "wallet_switchEthereumChain",
|
|
|
|
|
params: [{ chainId: bnbChainIdHex }],
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const code = (error as { code?: number | string }).code;
|
|
|
|
|
if (code !== 4902 && code !== "4902") throw error;
|
|
|
|
|
await ethereum.request({
|
|
|
|
|
method: "wallet_addEthereumChain",
|
|
|
|
|
params: [
|
|
|
|
|
{
|
|
|
|
|
blockExplorerUrls: ["https://bscscan.com"],
|
|
|
|
|
chainId: bnbChainIdHex,
|
|
|
|
|
chainName: "BNB Smart Chain",
|
|
|
|
|
nativeCurrency: { decimals: 18, name: "BNB", symbol: "BNB" },
|
|
|
|
|
rpcUrls: ["https://bsc-dataseed.binance.org"],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 02:58:01 +08:00
|
|
|
await ensureBnbChain(ethereum);
|
|
|
|
|
|
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,
|
|
|
|
|
});
|
|
|
|
|
}
|