Files
Arkie-Library-Frontend/src/wallet/injected.ts

100 lines
3.2 KiB
TypeScript
Raw Normal View History

2026-06-02 00:28:22 +08:00
import { requestWalletNonce, verifyWalletSignature } from "./api";
export type WalletKind = "tokenPocket" | "metaMask" | "imToken";
2026-06-02 00:28:22 +08:00
export type EthereumProvider = {
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;
}
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;
}
/** Diagnostic: log what injected providers the browser exposes. */
export function logWalletProviders(): void {
const ethereum = getInjectedEthereum();
const list = (
ethereum?.providers?.length
? ethereum.providers
: ethereum
? [ethereum]
: []
).map((p) => ({
isMetaMask: Boolean(p.isMetaMask),
isTokenPocket: Boolean(p.isTokenPocket),
isImToken: Boolean(p.isImToken),
}));
// eslint-disable-next-line no-console
console.info("[wallet-login] providers", {
hasEthereum: Boolean(ethereum),
count: list.length,
list,
});
}
export async function signInWithInjectedWallet(kind?: WalletKind): Promise<{
2026-06-02 00:28:22 +08:00
token: string;
wallet: string;
}> {
/* eslint-disable no-console */
console.info("[wallet-login] start injected", { kind });
logWalletProviders();
const ethereum = getInjectedWallet(kind);
if (!ethereum) {
console.warn("[wallet-login] no injected provider found");
throw new Error("No injected wallet found");
}
2026-06-02 00:28:22 +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.
console.info("[wallet-login] requesting accounts (eth_requestAccounts)…");
2026-06-02 00:28:22 +08:00
const accounts = await ethereum.request<string[]>({
method: "eth_requestAccounts",
});
console.info("[wallet-login] accounts", accounts);
2026-06-02 00:28:22 +08:00
const address = accounts[0];
if (!address) throw new Error("No wallet account returned");
console.info("[wallet-login] requesting nonce for", address);
2026-06-02 00:28:22 +08:00
const nonce = await requestWalletNonce(address);
console.info("[wallet-login] got nonce, requesting personal_sign…");
2026-06-02 00:28:22 +08:00
const signature = await ethereum.request<string>({
method: "personal_sign",
params: [nonce.message, address],
});
console.info("[wallet-login] signed, verifying with backend…");
const result = await verifyWalletSignature({
2026-06-02 00:28:22 +08:00
address,
message: nonce.message,
signature,
});
console.info("[wallet-login] verified, wallet =", result.wallet);
return result;
/* eslint-enable no-console */
2026-06-02 00:28:22 +08:00
}