import { type Connector, createConnector } from "@wagmi/core"; import { type Address, type Hex, UserRejectedRequestError, getAddress, } from "viem"; import { mainnet } from "viem/chains"; import type { Provider } from "./types/provider.js"; import { YiProvider } from "./yiProvider.js"; export type YiWalletParameters = { projectId: string; }; yiWallet.type = "yiWallet" as const; export function yiWallet(parameters: YiWalletParameters) { type Properties = { connect(parameters?: { chainId?: number | undefined; instantOnboarding?: boolean | undefined; isReconnecting?: boolean | undefined; }): Promise<{ accounts: readonly Address[]; chainId: number; }>; }; let walletProvider: Provider; const projectId = parameters.projectId; let accountsChanged: Connector["onAccountsChanged"] | undefined; let chainChanged: Connector["onChainChanged"] | undefined; let disconnect: Connector["onDisconnect"] | undefined; return createConnector((config) => ({ id: "yiWallet", name: "Yi Wallet", rdns: "com.yi.app", type: "yi", async connect({ chainId } = {}) { try { const provider = await this.getProvider(); const accounts = ( (await provider.request({ method: "eth_requestAccounts", params: [], })) as string[] ).map((x) => getAddress(x)); if (!accountsChanged) { accountsChanged = this.onAccountsChanged.bind(this); provider.on("accountsChanged", accountsChanged); } if (!chainChanged) { chainChanged = this.onChainChanged.bind(this); provider.on("chainChanged", chainChanged); } if (!disconnect) { disconnect = this.onDisconnect.bind(this); provider.on("disconnect", disconnect); } // Switch to chain if provided let currentChainId = await this.getChainId(); if (chainId && currentChainId !== chainId) { const chain = await this.switchChain?.({ chainId }).catch((error) => { if (error.code === UserRejectedRequestError.code) throw error; return { id: currentChainId }; }); currentChainId = chain?.id ?? currentChainId; } return { accounts, chainId: currentChainId }; } catch (error) { if ( /(user closed modal|accounts received is empty|user denied account|request rejected)/i.test( (error as Error).message, ) ) throw new UserRejectedRequestError(error as Error); throw error; } }, async disconnect() { const provider = await this.getProvider(); if (accountsChanged) { provider.removeListener("accountsChanged", accountsChanged); accountsChanged = undefined; } if (chainChanged) { provider.removeListener("chainChanged", chainChanged); chainChanged = undefined; } if (disconnect) { provider.removeListener("disconnect", disconnect); disconnect = undefined; } provider.disconnect(); }, async getAccounts() { const provider = await this.getProvider(); return ( (await provider.request({ method: "eth_accounts", })) as string[] ).map((x) => getAddress(x)); }, async getChainId() { const provider = await this.getProvider(); const chainId = (await provider.request({ method: "eth_chainId", })) as Hex; return Number(chainId); }, async getProvider() { if (!walletProvider) { walletProvider = new YiProvider(projectId); } return walletProvider; }, async isAuthorized() { try { const accounts = await this.getAccounts(); return !!accounts.length; } catch { return false; } }, async switchChain({ chainId: _chainId }: { chainId: number }) { return mainnet; }, onAccountsChanged(accounts) { if (accounts.length === 0) this.onDisconnect(); else config.emitter.emit("change", { accounts: accounts.map((x) => getAddress(x)), }); }, onChainChanged(_chainId) {}, async onDisconnect(_error) { config.emitter.emit("disconnect"); const provider = await this.getProvider(); if (accountsChanged) { provider.removeListener("accountsChanged", accountsChanged); accountsChanged = undefined; } if (chainChanged) { provider.removeListener("chainChanged", chainChanged); chainChanged = undefined; } if (disconnect) { provider.removeListener("disconnect", disconnect); disconnect = undefined; } }, })); }