617d3c910e
payment-host.ts 新增 polyweather.top。site-url.ts/wallet.ts/usePaymentFlow/AccountCenter 硬编码域名替换。PRODUCTION_SITE_URL 改为 https://polyweather.top。
238 lines
6.9 KiB
TypeScript
238 lines
6.9 KiB
TypeScript
import type { Eip6963ProviderDetail, EvmProvider, InjectedProviderOption } from "./types";
|
|
import { WALLETCONNECT_POLYGON_RPC_URL, WALLETCONNECT_PROJECT_ID } from "./constants";
|
|
|
|
let walletConnectProviderCache: EvmProvider | null = null;
|
|
let walletConnectProviderChainId: number | null = null;
|
|
export const eip6963Providers = new Map<string, Eip6963ProviderDetail>();
|
|
|
|
export function isWalletConnectResetError(error: unknown): boolean {
|
|
const source = error as any;
|
|
const message = String(
|
|
source?.shortMessage ||
|
|
source?.message ||
|
|
source?.reason ||
|
|
source?.data?.message ||
|
|
source?.cause?.message ||
|
|
source?.error?.message ||
|
|
(error instanceof Error ? error.message : "") ||
|
|
(typeof error === "string" ? error : ""),
|
|
).toLowerCase();
|
|
return (
|
|
message.includes("connection request reset") ||
|
|
message.includes("pairing aborted") ||
|
|
message.includes("pairing attempt") ||
|
|
message.includes("unable to connect")
|
|
);
|
|
}
|
|
|
|
export async function resetWalletConnectProvider(): Promise<void> {
|
|
if (walletConnectProviderCache?.disconnect) {
|
|
try {
|
|
await walletConnectProviderCache.disconnect();
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|
|
walletConnectProviderCache = null;
|
|
walletConnectProviderChainId = null;
|
|
}
|
|
|
|
export function getEvmProvider(): EvmProvider | null {
|
|
return listInjectedProviders()[0]?.provider || null;
|
|
}
|
|
|
|
export function getEip6963Providers(): Eip6963ProviderDetail[] {
|
|
return Array.from(eip6963Providers.values());
|
|
}
|
|
|
|
export function detectWalletLabel(
|
|
provider: EvmProvider | null,
|
|
detail?: Eip6963ProviderDetail,
|
|
): string {
|
|
if (!provider && !detail) return "EVM 钱包";
|
|
const announcedName = String(detail?.info?.name || "").trim();
|
|
const announcedRdns = String(detail?.info?.rdns || "").toLowerCase();
|
|
|
|
if (
|
|
provider?.isOkxWallet ||
|
|
announcedName.toLowerCase().includes("okx") ||
|
|
announcedRdns.includes("okx")
|
|
) {
|
|
return "OKX Wallet";
|
|
}
|
|
if (
|
|
provider?.isRabby ||
|
|
announcedName.toLowerCase().includes("rabby") ||
|
|
announcedRdns.includes("rabby")
|
|
) {
|
|
return "Rabby";
|
|
}
|
|
if (
|
|
provider?.isBitKeep ||
|
|
announcedName.toLowerCase().includes("bitget") ||
|
|
announcedRdns.includes("bitkeep") ||
|
|
announcedRdns.includes("bitget")
|
|
) {
|
|
return "Bitget Wallet";
|
|
}
|
|
if (
|
|
(provider as any)?.isBinance ||
|
|
(provider as any)?.bnbSign ||
|
|
announcedName.toLowerCase().includes("binance") ||
|
|
announcedRdns.includes("binance")
|
|
) {
|
|
return "Binance Web3 Wallet";
|
|
}
|
|
if (
|
|
provider?.isMetaMask ||
|
|
announcedName.toLowerCase().includes("metamask") ||
|
|
announcedRdns.includes("metamask")
|
|
) {
|
|
return "MetaMask";
|
|
}
|
|
if (announcedName) return announcedName;
|
|
return "EVM 钱包";
|
|
}
|
|
|
|
export function collectInjectedProviders(): EvmProvider[] {
|
|
if (typeof window === "undefined") return [];
|
|
const out: EvmProvider[] = [];
|
|
const seen = new Set<EvmProvider>();
|
|
|
|
const push = (provider: unknown) => {
|
|
if (!provider || typeof provider !== "object") return;
|
|
const candidate = provider as EvmProvider;
|
|
if (typeof candidate.request !== "function") return;
|
|
if (seen.has(candidate)) return;
|
|
seen.add(candidate);
|
|
out.push(candidate);
|
|
};
|
|
|
|
const root = window.ethereum;
|
|
if (Array.isArray(root?.providers)) {
|
|
root.providers.forEach(push);
|
|
}
|
|
push(root);
|
|
push(window.okxwallet?.ethereum);
|
|
push(window.okexchain);
|
|
push(window.rabby);
|
|
push(window.bitkeep?.ethereum);
|
|
|
|
return out;
|
|
}
|
|
|
|
export function getInjectedProviderStableId(
|
|
provider: EvmProvider,
|
|
index: number,
|
|
detail?: Eip6963ProviderDetail,
|
|
): string {
|
|
const rdns = String(detail?.info?.rdns || "").toLowerCase();
|
|
const announcedName = String(detail?.info?.name || "")
|
|
.toLowerCase()
|
|
.trim();
|
|
if (rdns) return `rdns:${rdns}`;
|
|
if (announcedName) return `name:${announcedName}`;
|
|
if (provider.isOkxWallet || rdns.includes("okx")) return `okx:${index}`;
|
|
if (provider.isMetaMask || rdns.includes("metamask"))
|
|
return `metamask:${index}`;
|
|
if (provider.isRabby || rdns.includes("rabby")) return `rabby:${index}`;
|
|
if (
|
|
provider.isBitKeep ||
|
|
rdns.includes("bitkeep") ||
|
|
rdns.includes("bitget")
|
|
) {
|
|
return `bitget:${index}`;
|
|
}
|
|
return `evm:${index}`;
|
|
}
|
|
|
|
export function listInjectedProviders(): InjectedProviderOption[] {
|
|
const detailByProvider = new Map<EvmProvider, Eip6963ProviderDetail>();
|
|
getEip6963Providers().forEach((detail) => {
|
|
if (detail?.provider && typeof detail.provider.request === "function") {
|
|
detailByProvider.set(detail.provider, detail);
|
|
}
|
|
});
|
|
const candidates = collectInjectedProviders();
|
|
detailByProvider.forEach((_detail, provider) => {
|
|
if (!candidates.includes(provider)) {
|
|
candidates.push(provider);
|
|
}
|
|
});
|
|
const seen = new Set<string>();
|
|
const seenLabels = new Set<string>();
|
|
const out: InjectedProviderOption[] = [];
|
|
candidates.forEach((provider, index) => {
|
|
const detail = detailByProvider.get(provider);
|
|
const label = detectWalletLabel(provider, detail);
|
|
const key = getInjectedProviderStableId(provider, index, detail);
|
|
if (seen.has(key)) return;
|
|
const normalizedLabel = label.trim().toLowerCase();
|
|
if (normalizedLabel && seenLabels.has(normalizedLabel)) return;
|
|
seen.add(key);
|
|
if (normalizedLabel) seenLabels.add(normalizedLabel);
|
|
out.push({
|
|
key,
|
|
provider,
|
|
label,
|
|
mode: "auto",
|
|
});
|
|
});
|
|
return out;
|
|
}
|
|
|
|
export function getEvmWalletLabel(provider: EvmProvider | null): string {
|
|
return detectWalletLabel(provider);
|
|
}
|
|
|
|
export async function getWalletConnectProvider(
|
|
chainId: number,
|
|
rpcUrl: string,
|
|
): Promise<EvmProvider> {
|
|
if (!WALLETCONNECT_PROJECT_ID) {
|
|
throw new Error(
|
|
"WalletConnect 未配置:缺少 NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID。",
|
|
);
|
|
}
|
|
if (walletConnectProviderCache && walletConnectProviderChainId === chainId) {
|
|
return walletConnectProviderCache;
|
|
}
|
|
const { EthereumProvider } = await import("@walletconnect/ethereum-provider");
|
|
const rpcMap: Record<number, string> = {
|
|
[chainId]: rpcUrl || WALLETCONNECT_POLYGON_RPC_URL,
|
|
};
|
|
const origin =
|
|
typeof window !== "undefined"
|
|
? window.location.origin
|
|
: "https://polyweather.top";
|
|
const provider = (await EthereumProvider.init({
|
|
projectId: WALLETCONNECT_PROJECT_ID,
|
|
chains: [chainId],
|
|
optionalChains: [chainId],
|
|
showQrModal: true,
|
|
methods: [
|
|
"eth_sendTransaction",
|
|
"personal_sign",
|
|
"eth_signTypedData",
|
|
"eth_signTypedData_v4",
|
|
"eth_sign",
|
|
"eth_call",
|
|
"eth_chainId",
|
|
"eth_accounts",
|
|
"eth_requestAccounts",
|
|
],
|
|
events: ["accountsChanged", "chainChanged", "disconnect"],
|
|
rpcMap,
|
|
metadata: {
|
|
name: "PolyWeather",
|
|
description: "PolyWeather Pro checkout",
|
|
url: origin,
|
|
icons: [`${origin}/favicon.ico`],
|
|
},
|
|
})) as unknown as EvmProvider;
|
|
walletConnectProviderCache = provider;
|
|
walletConnectProviderChainId = chainId;
|
|
return provider;
|
|
}
|
|
|