UNPKG

@particle-network/connectkit

Version:
4 lines 130 kB
{ "version": 3, "sources": ["../../src/utils/theme.ts", "../../src/utils/deepEqual.ts", "../../src/actions/getAccount.ts", "../../src/actions/watchAccount.ts", "../../src/hooks/useSyncExternalStoreWithTracked.ts", "../../src/hooks/useAccount.ts", "../../src/hooks/useLocales.ts", "../../src/hooks/useModalError.ts", "../../src/hooks/useModalState.ts", "../../src/pages/index.ts", "../../src/hooks/useRouteState.ts", "../../src/hooks/useRouter.ts", "../../src/components/connectModal/index.tsx", "../../src/utils/index.ts", "../../src/styles/themes/dark.ts", "../../src/styles/themes/light.ts", "../../src/styles/themes/index.ts", "../../src/styles/index.ts", "../../src/hooks/useAAPlugin.ts", "../../src/actions/getConnections.ts", "../../src/actions/watchConnections.ts", "../../src/hooks/useConnections.ts", "../../src/config/wallet.tsx", "../../src/actions/filterConnectors.ts", "../../src/actions/reconnect.ts", "../../src/utils/hydrate.ts", "../../src/context/modalProvider/index.tsx", "../../src/actions/getSolanaWalletClient.ts", "../../src/actions/getConnectorClient.ts", "../../src/actions/getWalletClient.ts", "../../src/actions/mapWalletClient.ts", "../../src/hooks/useMutation.ts", "../../src/context/web3Provider/index.tsx", "../../src/context/index.tsx", "../../src/hooks/useAppearance.ts", "../../src/hooks/useIconColor.ts", "../../src/assets/wallets/Trust.tsx"], "sourcesContent": ["export function getSystemMode() {\n if (typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {\n return 'dark';\n } else {\n return 'light';\n }\n}\n", "/** Forked from https://github.com/epoberezkin/fast-deep-equal */\n\nexport function deepEqual(a: any, b: any) {\n if (a === b) return true;\n\n if (a && b && typeof a === 'object' && typeof b === 'object') {\n if (a.constructor !== b.constructor) return false;\n\n let length: number;\n let i: number;\n\n if (Array.isArray(a) && Array.isArray(b)) {\n length = a.length;\n if (length !== b.length) return false;\n for (i = length; i-- !== 0; ) if (!deepEqual(a[i], b[i])) return false;\n return true;\n }\n\n if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();\n if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();\n\n const keys = Object.keys(a);\n length = keys.length;\n if (length !== Object.keys(b).length) return false;\n\n for (i = length; i-- !== 0; ) if (!Object.prototype.hasOwnProperty.call(b, keys[i]!)) return false;\n\n for (i = length; i-- !== 0; ) {\n const key = keys[i];\n\n if (key && !deepEqual(a[key], b[key])) return false;\n }\n\n return true;\n }\n\n // true if both NaN, false otherwise\n // biome-ignore lint/suspicious/noSelfCompare: <explanation>\n return a !== a && b !== b;\n}\n", "import type { Connector, State } from '@particle-network/connector-core';\nimport { checksumAddress, isAddress, type Address, type Chain } from 'viem';\nimport type { Config } from '../createConfig';\nimport { deepEqual } from '../utils/deepEqual';\n\nexport type GetAccountReturnType =\n | {\n address: string;\n addresses: readonly [string, ...string[]];\n chain: Chain | undefined;\n chainId: number;\n connector: Connector;\n isConnected: true;\n isConnecting: false;\n isDisconnected: false;\n isReconnecting: false;\n status: 'connected';\n }\n | {\n address: string | undefined;\n addresses: readonly string[] | undefined;\n chain: Chain | undefined;\n chainId: number | undefined;\n connector: Connector | undefined;\n isConnected: boolean;\n isConnecting: false;\n isDisconnected: false;\n isReconnecting: true;\n status: 'reconnecting';\n }\n | {\n address: string | undefined;\n addresses: readonly string[] | undefined;\n chain: Chain | undefined;\n chainId: number | undefined;\n connector: Connector | undefined;\n isConnected: false;\n isReconnecting: false;\n isConnecting: true;\n isDisconnected: false;\n status: 'connecting';\n }\n | {\n address: undefined;\n addresses: undefined;\n chain: undefined;\n chainId: undefined;\n connector: undefined;\n isConnected: false;\n isReconnecting: false;\n isConnecting: false;\n isDisconnected: true;\n status: 'disconnected';\n };\n\nlet previousAccount: GetAccountReturnType;\nlet previousState: State;\nexport function getAccount(config: Config): GetAccountReturnType {\n const state = config.state;\n if (deepEqual(previousState, state)) return previousAccount;\n\n const uid = config.state.current!;\n const connection = config.state.connections.get(uid);\n let addresses = connection?.accounts;\n if (addresses?.length && addresses.every((account) => isAddress(account, { strict: false }))) {\n addresses = addresses.map((item) => checksumAddress(item as Address)) as unknown as readonly [string, ...string[]];\n }\n const address = addresses?.[0];\n const chain = config.chains.find((chain) => chain.id === connection?.chainId) as GetAccountReturnType['chain'];\n const status = config.state.status;\n\n let result;\n\n switch (status) {\n case 'connected':\n result = {\n address: address!,\n addresses: addresses!,\n chain,\n // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain\n chainId: connection?.chainId!,\n // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain\n connector: connection?.connector!,\n isConnected: true,\n isConnecting: false,\n isDisconnected: false,\n isReconnecting: false,\n status,\n };\n break;\n case 'reconnecting':\n result = {\n address,\n addresses,\n chain,\n chainId: connection?.chainId,\n connector: connection?.connector,\n isConnected: !!address,\n isConnecting: false,\n isDisconnected: false,\n isReconnecting: true,\n status,\n };\n break;\n case 'connecting':\n result = {\n address,\n addresses,\n chain,\n chainId: connection?.chainId,\n connector: connection?.connector,\n isConnected: false,\n isConnecting: true,\n isDisconnected: false,\n isReconnecting: false,\n status,\n };\n break;\n case 'disconnected':\n result = {\n address: undefined,\n addresses: undefined,\n chain: undefined,\n chainId: undefined,\n connector: undefined,\n isConnected: false,\n isConnecting: false,\n isDisconnected: true,\n isReconnecting: false,\n status,\n };\n break;\n }\n previousAccount = result;\n return result;\n}\n", "import { type Config } from '../createConfig';\nimport { deepEqual } from '../utils/deepEqual';\nimport { getAccount, type GetAccountReturnType } from './getAccount';\n\nexport type WatchAccountParameters = {\n onChange(account: GetAccountReturnType, prevAccount: GetAccountReturnType): void;\n};\n\nexport type WatchAccountReturnType = () => void;\n\nexport function watchAccount(config: Config, parameters: WatchAccountParameters): WatchAccountReturnType {\n const { onChange } = parameters;\n\n return config.subscribe(() => getAccount(config), onChange, {\n equalityFn(a, b) {\n const { connector: aConnector, ...aRest } = a;\n const { connector: bConnector, ...bRest } = b;\n return (\n deepEqual(aRest, bRest) &&\n // check connector separately\n aConnector?.id === bConnector?.id &&\n aConnector?.uid === bConnector?.uid\n );\n },\n });\n}\n", "import { useRef } from 'react';\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector.js';\nimport { deepEqual } from '../utils/deepEqual';\n\nconst isPlainObject = (obj: unknown) => typeof obj === 'object' && !Array.isArray(obj);\n\nexport function useSyncExternalStoreWithTracked<snapshot extends selection, selection = snapshot>(\n subscribe: (onStoreChange: () => void) => () => void,\n getSnapshot: () => snapshot,\n getServerSnapshot: undefined | null | (() => snapshot) = getSnapshot,\n isEqual: (a: selection, b: selection) => boolean = deepEqual\n) {\n const trackedKeys = useRef<string[]>([]);\n const result = useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getServerSnapshot,\n (x) => x,\n (a, b) => {\n if (isPlainObject(a) && isPlainObject(b) && trackedKeys.current.length) {\n for (const key of trackedKeys.current) {\n const equal = isEqual((a as { [_a: string]: any })[key], (b as { [_b: string]: any })[key]);\n if (!equal) return false;\n }\n return true;\n }\n return isEqual(a, b);\n }\n );\n\n if (isPlainObject(result)) {\n const trackedResult = { ...result };\n let properties = {};\n for (const [key, value] of Object.entries(trackedResult as { [key: string]: any })) {\n properties = {\n ...properties,\n [key]: {\n configurable: false,\n enumerable: true,\n get: () => {\n if (!trackedKeys.current.includes(key)) {\n trackedKeys.current.push(key);\n }\n return value;\n },\n },\n };\n }\n Object.defineProperties(trackedResult, properties);\n return trackedResult;\n }\n\n return result;\n}\n", "import { getAccount, type GetAccountReturnType } from '../actions/getAccount';\nimport { watchAccount } from '../actions/watchAccount';\nimport { useContext } from '../context';\nimport { useSyncExternalStoreWithTracked } from './useSyncExternalStoreWithTracked';\n\nexport function useAccount(): GetAccountReturnType {\n const { config } = useContext();\n\n return useSyncExternalStoreWithTracked(\n (onChange) => watchAccount(config, { onChange }),\n () => getAccount(config)\n );\n}\n", "import { useMemo } from 'react';\nimport { getLocale, type TransactionType } from '../locales';\nimport { useAppearance } from './useAppearance';\n\nexport default function useLocales() {\n const { appearance } = useAppearance();\n\n const language = useMemo(() => {\n return appearance?.language ?? 'en-US';\n }, [appearance.language]);\n\n const overrideLocales = useMemo(() => {\n return appearance.overrideLocales;\n }, [appearance.overrideLocales]);\n\n const translations: TransactionType = useMemo(() => {\n const locales = getLocale(language);\n const overrideLanguage = overrideLocales?.[language];\n return { ...locales, ...overrideLanguage };\n }, [language, overrideLocales]);\n\n if (!translations) {\n throw new Error(`Missing translations for ${language}`);\n }\n\n return translations;\n}\n", "import { create } from 'zustand';\n\ntype State = {\n error?: string;\n};\n\ntype Actions = {\n showError: (error: string) => void;\n hideError: () => void;\n};\n\nexport const useModalError = create<State & Actions>((set) => ({\n showError: (value) => {\n set({ error: value });\n },\n hideError: () => {\n set({ error: undefined });\n },\n}));\n", "import { create } from 'zustand';\n\ntype State = {\n open: boolean;\n};\n\ntype Actions = {\n setOpen: (value: boolean) => void;\n};\n\nexport const useModalState = create<State & Actions>((set) => ({\n open: false,\n setOpen: (value) => set({ open: value }),\n}));\n", "import type { FC } from 'react';\nimport { lazy } from 'react';\nimport type { TransactionKey } from '../locales';\n\nconst Captcha = lazy(() => import('./captcha'));\nconst Connecting = lazy(() => import('./connecting'));\nconst Connectors = lazy(() => import('./connectors'));\nconst DownloadApp = lazy(() => import('./downloadApp'));\nconst LoginPasskey = lazy(() => import('./loginPasskey'));\nconst PasskeySetup = lazy(() => import('./passkeySetup'));\nconst Receive = lazy(() => import('./receive'));\nconst SelectChain = lazy(() => import('./selectChain'));\nconst Signup = lazy(() => import('./signup'));\nconst WalletInfo = lazy(() => import('./walletInfo'));\nconst WalletInfoTitle = lazy(() => import('./walletInfo/title'));\nconst Walletconnect = lazy(() => import('./walletconnect'));\n\nexport enum RoutePath {\n Signup = 'signup',\n Connectors = 'connectors',\n Captcha = 'captcha',\n Connecting = 'connecting',\n DownloadApp = 'download-app',\n SelectChain = 'select-chain',\n Walletconnect = 'walletconnect',\n WalletInfo = 'wallet-info',\n Receive = 'receive',\n PasskeySetup = 'passkey-setup',\n LoginPasskey = 'login-passkey',\n}\n\nexport const safeRoutes = {\n connected: [RoutePath.WalletInfo, RoutePath.Receive],\n disconnected: [\n RoutePath.Signup,\n RoutePath.Connectors,\n RoutePath.Captcha,\n RoutePath.Connecting,\n RoutePath.DownloadApp,\n RoutePath.SelectChain,\n RoutePath.Walletconnect,\n RoutePath.PasskeySetup,\n RoutePath.LoginPasskey,\n ],\n};\n\nexport const allRoutes = [...safeRoutes.connected, ...safeRoutes.disconnected] as const;\n\nexport interface IPage {\n title?: TransactionKey | FC;\n path: RoutePath;\n element: FC<any>;\n}\n\nexport const pages: IPage[] = [\n {\n title: 'signupTitle',\n path: RoutePath.Signup,\n element: Signup,\n },\n {\n title: 'connectorsTitle',\n path: RoutePath.Connectors,\n element: Connectors,\n },\n {\n title: 'captchaTitle',\n path: RoutePath.Captcha,\n element: Captcha,\n },\n {\n path: RoutePath.Connecting,\n element: Connecting,\n },\n {\n path: RoutePath.DownloadApp,\n element: DownloadApp,\n },\n {\n title: 'scanWithPhone',\n path: RoutePath.Walletconnect,\n element: Walletconnect,\n },\n {\n title: 'selectChainTitle',\n path: RoutePath.SelectChain,\n element: SelectChain,\n },\n {\n title: WalletInfoTitle,\n path: RoutePath.WalletInfo,\n element: WalletInfo,\n },\n {\n title: 'receive',\n path: RoutePath.Receive,\n element: Receive,\n },\n {\n title: 'setupPasskey',\n path: RoutePath.PasskeySetup,\n element: PasskeySetup,\n },\n {\n title: 'usePasskeyToLogin',\n path: RoutePath.LoginPasskey,\n element: LoginPasskey,\n },\n] as const;\n\nexport const getPage = (path: RoutePath): IPage => {\n const page = pages.find((item) => item.path === path);\n if (!page) {\n throw new Error('route path undefined');\n }\n\n return page;\n};\n", "import { create } from 'zustand';\nimport type { Route } from '../context/types';\nimport { RoutePath } from '../pages';\n\ntype State = {\n route: Route;\n};\n\ntype Actions = {\n setRoute: (value: Route | RoutePath) => void;\n};\n\nconst initialState = { path: RoutePath.Signup };\n\nexport const useRouteState = create<State & Actions>((set) => ({\n route: initialState,\n setRoute: (value) => {\n if (typeof value === 'string') {\n set({\n route: {\n path: value,\n },\n });\n } else {\n set({ route: value });\n }\n },\n}));\n", "import { useCallback } from 'react';\nimport { RoutePath, allRoutes, safeRoutes } from '../pages';\nimport { useAccount } from './useAccount';\nimport { useModalState } from './useModalState';\nimport { useRouteState } from './useRouteState';\n\nexport const useRouter = () => {\n const { setOpen, open } = useModalState();\n const setRoute = useRouteState((state) => state.setRoute);\n const { isConnected } = useAccount();\n\n const navigate = (route: RoutePath, state?: object) => {\n let validRoute = route;\n if (!allRoutes.includes(route)) {\n validRoute = isConnected ? RoutePath.WalletInfo : RoutePath.Signup;\n console.log(`Route ${route} is not a valid route, navigating to ${validRoute} instead.`);\n }\n\n if (isConnected) {\n if (!safeRoutes.connected.includes(route)) {\n validRoute = RoutePath.WalletInfo;\n console.log(`Route ${route} is not a valid route when connected, navigating to ${validRoute} instead.`);\n }\n } else {\n if (!safeRoutes.disconnected.includes(route)) {\n validRoute = RoutePath.Signup;\n console.log(`Route ${route} is not a valid route when disconnected, navigating to ${validRoute} instead.`);\n }\n }\n if (!open) {\n setOpen(true);\n }\n setRoute({ path: validRoute, state });\n };\n\n const close = useCallback(() => {\n setOpen(false);\n }, [setOpen]);\n\n return {\n navigate,\n close,\n isOpen: open,\n };\n};\n", "import { lazy, useCallback, useEffect, useMemo } from 'react';\nimport { useAccount } from '../../hooks/useAccount';\nimport useLocales from '../../hooks/useLocales';\nimport { useModalError } from '../../hooks/useModalError';\nimport { useModalState } from '../../hooks/useModalState';\nimport { useRouteState } from '../../hooks/useRouteState';\nimport { useRouter } from '../../hooks/useRouter';\nimport { RoutePath, getPage } from '../../pages';\n\nconst Modal = lazy(() => import('../modal'));\n\nconst ConnectModal = () => {\n const { setOpen, open } = useModalState();\n const { route } = useRouteState();\n const { navigate } = useRouter();\n const locales = useLocales();\n const { hideError } = useModalError();\n\n const { isConnected } = useAccount();\n\n useEffect(() => {\n setOpen(false);\n }, [isConnected, setOpen]);\n\n useEffect(() => {\n hideError();\n }, [route, hideError]);\n\n const {\n element: PageElement,\n title,\n state,\n } = useMemo(() => {\n const page = getPage(route.path);\n return { ...page, state: route.state };\n }, [route]);\n\n const onClose = useCallback(() => {\n setOpen(false);\n }, [setOpen]);\n\n const onBack = useCallback(() => {\n navigate(RoutePath.Signup);\n }, [navigate]);\n\n const isCanBack = useMemo(() => route.path !== RoutePath.Signup && route.path !== RoutePath.WalletInfo, [route.path]);\n\n return (\n <Modal\n open={open}\n onClose={onClose}\n title={typeof title === 'string' ? locales[title] : title}\n onBack={isCanBack ? onBack : undefined}\n >\n <PageElement {...state} />\n </Modal>\n );\n};\n\nexport default ConnectModal;\n", "import { detect } from 'detect-browser';\nimport type { RefObject } from 'react';\nimport { formatUnits } from 'viem/utils';\nimport type { WalletProps } from '../hooks/useWalletsProps';\n\nconst truncateRegex = /^(0x[a-zA-Z0-9]{4})[a-zA-Z0-9]+([a-zA-Z0-9]{4})$/;\n\nexport const defaultTokenIcon = 'https://static.particle.network/token-list/defaultToken/default.png';\n\ninterface ElementDimensions {\n width: number;\n height: number;\n topDistance: number;\n bottomDistance: number;\n}\n\nconst truncateEthAddress = (address?: string, separator = '\u2022\u2022\u2022\u2022') => {\n if (!address) return '';\n const match = address.match(truncateRegex);\n if (!match) return address;\n return `${match[1]}${separator}${match[2]}`;\n};\n\nconst detectBrowser = () => {\n const browser = detect();\n return browser?.name ?? '';\n};\nconst detectOS = () => {\n const browser = detect();\n return browser?.os ?? '';\n};\n\nconst isIOS = () => {\n const os = detectOS();\n return os.toLowerCase().includes('ios');\n};\nconst isAndroid = () => {\n const os = detectOS();\n return os.toLowerCase().includes('android');\n};\nconst isMobile = () => {\n return isAndroid() || isIOS();\n};\n\nexport const isWalletConnectConnector = (connectorId?: string) => connectorId === 'walletConnect';\n\nexport { detectBrowser, detectOS, isAndroid, isIOS, isMobile, truncateEthAddress };\n\nexport const hexToRgba = (hex, alpha) => {\n let r = 0,\n g = 0,\n b = 0;\n if (hex.length === 4) {\n r = parseInt(hex[1] + hex[1], 16);\n g = parseInt(hex[2] + hex[2], 16);\n b = parseInt(hex[3] + hex[3], 16);\n } else if (hex.length === 7) {\n r = parseInt(hex[1] + hex[2], 16);\n g = parseInt(hex[3] + hex[4], 16);\n b = parseInt(hex[5] + hex[6], 16);\n }\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n};\n\nexport const shortAddress = (address: string | undefined | null) => {\n if (!address) {\n return '';\n }\n if (address.length <= 10) {\n return address;\n }\n return `${address.slice(0, 6)}...${address.slice(-4)}`;\n};\n\n/**\n * Copy text to clipboard\n * @param {string} text\n * @returns {Promise<void>}\n */\nexport async function copyToClipboard(text: string) {\n const clipboardCopy = async () => {\n if (navigator.clipboard) {\n return navigator.clipboard.writeText(text);\n } else {\n const textarea = document.createElement('textarea');\n textarea.value = text;\n textarea.setAttribute('readonly', '');\n textarea.style.position = 'absolute';\n textarea.style.left = '-9999px';\n document.body.appendChild(textarea);\n\n textarea.select();\n const result = document.execCommand('copy');\n\n document.body.removeChild(textarea);\n\n if (!result) {\n throw new Error('Copy to clipboard failed');\n }\n }\n };\n\n return new Promise((resolve, reject) => {\n clipboardCopy().then(resolve).catch(reject);\n });\n}\n\nexport const openMobileUrl = (url: string) => {\n if (url.startsWith('http')) {\n const link = document.createElement('a');\n link.href = url;\n link.target = '_blank';\n link.rel = 'noreferrer noopener';\n link.click();\n } else {\n window.location.href = url;\n }\n};\n\nexport const hasPCDownloadUrl = (walletProps: WalletProps) => {\n return Boolean(\n walletProps.downloadUrls?.chrome ||\n walletProps.downloadUrls?.brave ||\n walletProps.downloadUrls?.edge ||\n walletProps.downloadUrls?.firefox ||\n walletProps.downloadUrls?.safari\n );\n};\n\n/**\n * \u4EE3\u5E01\u89C4\u5219\uFF1A\n * \u6700\u591A\u4FDD\u7559\u5C0F\u6570\u70B9\u540E9\u4F4D\uFF0C\u53EA\u5C55\u793A\u5230\u6700\u540E\u4E00\u4E2A\u975E\u96F6\u6570\u5B57\u4F4D\u3002\u5F53\u6574\u6570\u4F4D\u8D85\u8FC75\u4F4D\uFF0C\u5C0F\u6570\u540E\u4F4D\u6570\u9010\u6E10\u51CF\u5C11\u3002\u6574\u6570\u4E3A\u8D85\u8FC715\u4F4D\u65F6\uFF0C\u4F7F\u7528\u79D1\u5B66\u8BA1\u6570\u6CD5\uFF0C\u5C0F\u6570\u70B9\u540E\u4E24\u4F4D\n * @param bn\n * @param decimal\n * @param decimalPlaces\n */\nexport const formatTokenAmount = (value: bigint | number | `0x${string}`, decimal: number, decimalPlaces = 9) => {\n if (!value) return '0';\n if (typeof value === 'string' || typeof value === 'number') {\n value = BigInt(value);\n }\n let result = formatUnits(value, decimal);\n\n const [integerPart, decimalPart] = result.split('.');\n if (decimalPlaces >= 5 && integerPart === '0' && decimalPart?.startsWith(''.padEnd(decimalPlaces, '0'))) {\n return '<0.'.padEnd(decimalPlaces + 3 - 1, '0') + '1';\n }\n result = formatStringNumber(result, decimalPlaces);\n return result;\n};\n\nfunction formatStringNumber(num: string, n: number): string {\n let [integerPart, decimalPart] = num.split('.');\n\n // \u5982\u679C\u6CA1\u6709\u5C0F\u6570\u90E8\u5206\uFF0C\u76F4\u63A5\u8FD4\u56DE\u6574\u6570\u90E8\u5206\n if (!decimalPart) {\n return integerPart as string;\n }\n\n // \u622A\u53D6\u5C0F\u6570\u90E8\u5206\u7684\u524Dn\u4F4D\n decimalPart = decimalPart.slice(0, n);\n\n // \u53BB\u6389\u5C0F\u6570\u90E8\u5206\u672B\u5C3E\u7684\u96F6\n decimalPart = decimalPart.replace(/0+$/, '');\n\n // \u5982\u679C\u5C0F\u6570\u90E8\u5206\u5168\u4E3A\u96F6\uFF0C\u8FD4\u56DE\u6574\u6570\u90E8\u5206\n if (decimalPart === '') {\n return integerPart as string;\n }\n\n return `${integerPart}.${decimalPart}`;\n}\n\nexport function getElementDimensionsRelativeToParent(ref: RefObject<HTMLElement | null>): ElementDimensions | null {\n if (!ref.current) {\n return null;\n }\n\n const element = ref.current;\n const parent = element.parentElement;\n\n if (!parent) {\n return null;\n }\n\n const elementRect = element.getBoundingClientRect();\n const parentRect = parent.getBoundingClientRect();\n\n const topDistance = elementRect.top - parentRect.top;\n const bottomDistance = parentRect.bottom - elementRect.bottom;\n\n const dimensions = {\n width: elementRect.width,\n height: elementRect.height,\n topDistance: topDistance,\n bottomDistance: bottomDistance,\n };\n\n return dimensions;\n}\n\nexport function getBuyUrl(\n chainId?: number,\n address?: string,\n currency = 'USD',\n theme = 'light',\n language = 'en-US'\n): string {\n const RAMP_WEB_URL = 'https://ramp.particle.network';\n\n const supportedNetworks: { [key: string]: string[] } = {\n '1': ['Ethereum', 'ETH'],\n '10': ['Optimism', 'ETH'],\n '56': ['Binance Smart Chain', 'BNB'],\n '101': ['Solana', 'SOL'],\n '137': ['Polygon', 'MATIC'],\n '204': ['opBNB', 'BNB'],\n '324': ['Zksync', 'ETH'],\n '5000': ['Mantle', 'MNT'],\n '8453': ['Base', 'ETH'],\n '42161': ['Arbitrum One', 'ETH'],\n '42220': ['Celo', 'CELO'],\n '43114': ['Avalanche', 'AVAX'],\n '728126428': ['Tron', 'TRX'],\n // sui TODO\n };\n\n if (!chainId || !address || !supportedNetworks[chainId]) {\n return '';\n }\n\n const chainName = supportedNetworks[chainId]?.[0] || '';\n\n const cryptoCoin = supportedNetworks[chainId]?.[1] || 'ETH';\n\n const buyParams = {\n walletAddress: address,\n fiatCoin: currency,\n cryptoCoin: cryptoCoin,\n chainName: chainName,\n showBackButton: false,\n theme: theme,\n language: language,\n };\n\n const queryString = new URLSearchParams(buyParams as any).toString();\n return `${RAMP_WEB_URL}/?${queryString}`;\n}\n\nexport function popupWindow(url: string, title: string, w: number, h: number): Window | null {\n const left = screen.width / 2 - w / 2;\n const top = screen.height / 2 - h / 2;\n return window.open(\n url,\n title,\n 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=' +\n w +\n ', height=' +\n h +\n ', top=' +\n top +\n ', left=' +\n left\n );\n}\n\nexport const roundToEven = (value: number) => {\n return Math.floor(value / 2) * 2;\n};\n\nexport function hexToRgbaColor(hex, alpha) {\n if (!hex) {\n return '';\n }\n hex = hex.replace(/^#/, '');\n\n if (hex.length === 3) {\n hex = hex\n .split('')\n .map((char) => char + char)\n .join('');\n }\n\n if (!/^[0-9A-Fa-f]{6}$/.test(hex)) {\n throw new Error('Invalid hex color format');\n }\n\n if (typeof alpha !== 'number' || alpha < 0 || alpha > 1) {\n throw new Error('Alpha value must be a number between 0 and 1');\n }\n\n const r = parseInt(hex.slice(0, 2), 16);\n const g = parseInt(hex.slice(2, 4), 16);\n const b = parseInt(hex.slice(4, 6), 16);\n\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n\n/**\n * Judge whether the user is registering a passkey for the first time\n * @returns {boolean} True if it's the first time, false otherwise\n */\nexport const isFirstRegisterPasskey = () => {\n return localStorage.getItem('passkeyRegistrationStatus') !== 'completed';\n};\n\n/**\n * Set that the user has completed passkey registration\n */\nexport const setFirstRegisterPasskey = () => {\n localStorage.setItem('passkeyRegistrationStatus', 'completed');\n};\n", "import type { CustomTheme } from '../../types';\n\nconst darkTheme: CustomTheme = {\n '--pcm-overlay-background': 'rgba(0,0,0,0.4)',\n '--pcm-modal-box-shadow': '0px 6px 20px 6px rgba(0, 0, 0, 0.1)',\n '--pcm-body-background': '#1C1D22',\n '--pcm-body-background-secondary': '#41424A',\n '--pcm-body-background-tertiary': '#232529',\n\n '--pcm-body-color': '#ffffff',\n '--pcm-body-color-secondary': '#8B8EA1',\n '--pcm-body-color-tertiary': '#42444B',\n\n '--pcm-body-action-color': '#808080',\n '--pcm-button-border-color': '#292B36',\n '--pcm-accent-color': '#A257FA',\n\n // button\n '--pcm-button-font-weight': '500',\n '--pcm-button-hover-shadow': '0px 2px 4px rgba(255, 255, 255, 0.05)',\n\n //primary button\n '--pcm-primary-button-color': '#000000',\n '--pcm-primary-button-bankground': '#FFFFFF',\n '--pcm-primary-button-hover-background': '#f5f5f5',\n\n // secondary button\n '--pcm-secondary-button-color': '#FFFFFF',\n '--pcm-secondary-button-bankground': '#41424A',\n '--pcm-secondary-button-hover-background': '#5A5B66',\n};\n\nexport default darkTheme;\n", "import type { CustomTheme } from '../../types';\n\nconst lightTheme: CustomTheme = {\n '--pcm-overlay-background': 'rgba(71, 88, 107, 0.24)',\n '--pcm-modal-box-shadow': '0px 2px 4px rgba(0, 0, 0, 0.1)',\n '--pcm-body-background': '#ffffff',\n '--pcm-body-background-secondary': '#EFF0F2',\n '--pcm-body-background-tertiary': '#F9F9FA',\n\n '--pcm-body-color': '#181B1E',\n '--pcm-body-color-secondary': '#8B8EA1',\n '--pcm-body-color-tertiary': '#DCDFE6',\n\n '--pcm-body-action-color': '#999999',\n '--pcm-button-border-color': '#EAECF0',\n '--pcm-accent-color': '#A257FA',\n\n // button\n '--pcm-button-font-weight': '500',\n '--pcm-button-hover-shadow': '0px 2px 4px rgba(0, 0, 0, 0.05)',\n\n // primary button\n '--pcm-primary-button-color': '#ffffff',\n '--pcm-primary-button-bankground': '#181B1E',\n '--pcm-primary-button-hover-background': '#353738',\n\n // secondary button\n '--pcm-secondary-button-color': '#181B1E',\n '--pcm-secondary-button-bankground': '#EFF0F2',\n '--pcm-secondary-button-hover-background': '#D9DBDD',\n};\n\nexport default lightTheme;\n", "import dark from './dark';\nimport light from './light';\n\nconst base = {\n dark,\n light,\n};\n\nexport default { base };\n", "import { motion } from 'framer-motion';\nimport { css, styled } from 'styled-components';\nimport type { Appearance } from '../context/types';\nimport type { CustomTheme } from '../types';\nimport { hexToRgba } from '../utils';\nimport { getSystemMode } from '../utils/theme';\nimport predefinedThemes from './themes';\nimport dark from './themes/dark';\nimport light from './themes/light';\n\nexport const getWalletOption = (appearance?: Appearance) => {\n let theme = appearance?.mode ?? getSystemMode();\n if (theme === 'auto') {\n theme = getSystemMode();\n }\n\n let themeConfig: any = theme === 'light' ? light : dark;\n themeConfig = mergeThemes({ ...themeGlobals.default, ...themeConfig }, appearance?.theme || {});\n\n const walletTheme: any = {};\n // \u5F3A\u8C03\u8272\n walletTheme.colorAccent = themeConfig['--pcm-accent-color'];\n\n // \u4E3B\u8981\u80CC\u666F\u8272\n walletTheme.colorPrimary = themeConfig['--pcm-body-background'];\n\n // \u4E3B\u8981\u6587\u5B57\u8272\uFF08\u4F7F\u7528\u4E3B\u6309\u94AE\u6587\u5B57\u8272\uFF09\n walletTheme.colorOnPrimary = themeConfig['--pcm-primary-button-color'];\n\n // \u4E3B\u8981\u6309\u94AE\u80CC\u666F\u8272\uFF08\u4F7F\u7528\u666E\u901A\u72B6\u6001\u548C\u60AC\u505C\u72B6\u6001\u989C\u8272\uFF09\n walletTheme.primaryButtonBackgroundColors = [\n themeConfig['--pcm-primary-button-bankground'],\n themeConfig['--pcm-primary-button-hover-background'],\n ];\n\n // \u56FE\u6807\u6309\u94AE\u80CC\u666F\u8272\uFF08\u4F7F\u7528\u6B21\u8981\u6309\u94AE\u7684\u666E\u901A\u72B6\u6001\u548C\u60AC\u505C\u72B6\u6001\u989C\u8272\uFF09\n walletTheme.primaryIconButtonBackgroundColors = [\n themeConfig['--pcm-accent-color'],\n themeConfig['--pcm-accent-color'],\n ];\n\n // \u56FE\u6807\u6309\u94AE\u6587\u5B57\u8272\n walletTheme.primaryIconTextColor = themeConfig['--pcm-secondary-button-color'];\n\n // \u4E3B\u8981\u6309\u94AE\u6587\u5B57\u8272\n walletTheme.primaryButtonTextColor = themeConfig['--pcm-primary-button-color'];\n\n // \u53D6\u6D88\u6309\u94AE\u80CC\u666F\u8272\uFF08\u4F7F\u7528\u6B21\u8981\u6309\u94AE\u80CC\u666F\u8272\uFF09\n walletTheme.cancelButtonBackgroundColor = themeConfig['--pcm-secondary-button-bankground'];\n\n // \u80CC\u666F\u8272\u6570\u7EC4\n walletTheme.backgroundColors = [\n themeConfig['--pcm-body-background-secondary'],\n [\n [themeConfig['--pcm-body-background'], themeConfig['--pcm-body-background']],\n [themeConfig['--pcm-body-background'], themeConfig['--pcm-body-background']],\n ],\n ];\n\n // \u6D88\u606F\u989C\u8272\uFF08\u4F7F\u7528\u6210\u529F\u548C\u9519\u8BEF\u989C\u8272\uFF09\n walletTheme.messageColors = [themeConfig['--pcm-success-color'], themeConfig['--pcm-error-color']];\n\n // \u8FB9\u6846\u53D1\u5149\u989C\u8272\n walletTheme.borderGlowColors = [themeConfig['--pcm-accent-color'], themeConfig['--pcm-body-color']];\n\n // \u6A21\u6001\u906E\u7F69\u80CC\u666F\u8272\n walletTheme.modalMaskBackgroundColor = themeConfig['--pcm-overlay-background'];\n\n // \u5361\u7247\u8FB9\u6846\u5706\u89D2\uFF08\u4F7F\u7528\u4E2D\u7B49\u5706\u89D2\u503C\u5E76\u8F6C\u6362\u4E3A\u6570\u5B57\uFF09\n walletTheme.cardBorderRadius = parseInt(themeConfig['--pcm-rounded-xl']);\n\n const options = {\n themeType: theme,\n customStyle: {\n [theme]: walletTheme,\n },\n language: appearance?.language,\n };\n return options;\n};\n\nconst mergeThemes = (base: any, override: any) => {\n const result = { ...base };\n Object.keys(override).forEach((key) => {\n if (Object.prototype.hasOwnProperty.call(override, key) && override[key] !== '') {\n result[key] = override[key];\n }\n });\n return result;\n};\n\nexport const getAuthCoreTheme = (appearance?: Appearance) => {\n let theme = appearance?.mode ?? getSystemMode();\n if (theme === 'auto') {\n theme = getSystemMode();\n }\n\n let themeConfig: any = theme === 'light' ? light : dark;\n themeConfig = mergeThemes({ ...themeGlobals.default, ...themeConfig }, appearance?.theme || {});\n\n const authTheme = {\n primaryBtnColor: themeConfig['--pcm-primary-button-color'],\n primaryBtnBackgroundColor: themeConfig['--pcm-primary-button-bankground'],\n // secondaryBtnColor: themeConfig['--pcm-secondary-button-color'],\n // secondaryBtnBackgroundColor: themeConfig['--pcm-secondary-button-bankground'],\n textColor: themeConfig['--pcm-body-color'],\n secondaryTextColor: themeConfig['--pcm-body-color-secondary'],\n themeBackgroundColor: themeConfig['--pcm-body-background'],\n iconBorderColor: themeConfig['--pcm-button-border-color'],\n accentColor: themeConfig['--pcm-accent-color'],\n inputBackgroundColor: themeConfig['--pcm-body-background-secondary'],\n inputBorderColor: themeConfig['--pcm-button-border-color'],\n inputPlaceholderColor: themeConfig['--pcm-body-color-tertiary'],\n cardBorderColor: themeConfig['--pcm-button-border-color'],\n cardUnclickableBackgroundColor: themeConfig['--pcm-body-background-tertiary'],\n cardUnclickableBorderColor: themeConfig['--pcm-button-border-color'],\n cardDividerColor: themeConfig['--pcm-button-border-color'],\n tagBackgroundColor: themeConfig['--pcm-body-background-secondary'],\n modalBackgroundColor: themeConfig['--pcm-body-background'],\n tipsBackgroundColor: themeConfig['--pcm-body-background-secondary'],\n primaryBtnBorderRadius: themeConfig['--pcm-rounded-lg'],\n cardBorderRadius: themeConfig['--pcm-rounded-md'],\n modalBorderRadius: themeConfig['--pcm-rounded-lg'],\n };\n return authTheme;\n};\n\n/**\n * Theme variables for the modal\n */\nexport const themeGlobals = {\n default: {\n '--pcm-font-family': `-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica,\n 'Apple Color Emoji', Arial, sans-serif, 'Segoe UI Emoji',\n 'Segoe UI Symbol'`,\n '--pcm-overlay-backdrop-filter': 'blur(6px)',\n '--pcm-rounded-sm': '4px',\n '--pcm-rounded-md': '8px',\n '--pcm-rounded-lg': '11px',\n '--pcm-rounded-xl': '22px',\n '--pcm-rounded-full': '9999px',\n '--pcm-error-color': '#EA4335',\n '--pcm-error-background': hexToRgba('#EA4335', 0.2),\n '--pcm-success-color': '#58C08F',\n '--pcm-success-background': hexToRgba('#58C08F', 0.2),\n '--pcm-warning-color': '#F59E0A',\n '--pcm-warning-background': hexToRgba('#F59E0A', 0.2),\n '--pcm-wallet-lable-color': '#33C759',\n '--pcm-wallet-lable-background': hexToRgba('#33C759', 0.2),\n },\n};\n\nconst themeColors = {\n light: predefinedThemes.base.light,\n dark: predefinedThemes.base.dark,\n};\n\nconst createCssVars = (\n scheme: {\n [key: string]: string;\n },\n override?: boolean\n) => {\n const important = override ? ' !important' : '';\n return css`\n ${Object.keys(scheme).map((key) => {\n const value = scheme[key];\n return value && `${key}:${value}${important};`;\n })}\n `;\n};\n\nconst themes = {\n default: createCssVars(themeGlobals.default),\n light: createCssVars(themeColors.light),\n dark: createCssVars(themeColors.dark),\n};\n\nlet mode = 'auto';\nexport const ThemeContainer = styled(motion.div)<{\n $useMode?: string;\n $useTheme?: CustomTheme;\n}>`\n ${themes.default}\n\n ${(props) => {\n if (props.$useMode === 'light') {\n mode = 'light';\n return themes.light;\n } else if (props.$useMode === 'dark') {\n mode = 'dark';\n return themes.dark;\n } else {\n return css`\n @media (prefers-color-scheme: light) {\n ${themes.light}\n }\n @media (prefers-color-scheme: dark) {\n ${themes.dark}\n }\n `;\n }\n }}\n \n\n ${(props) => {\n if (props.$useTheme) {\n return createCssVars(props.$useTheme, true);\n }\n return undefined;\n }}\n\n\n ${(props) => {\n if (props.$useTheme) {\n const merge = {};\n if (props.$useTheme['--pcm-success-color']) {\n merge['--pcm-success-background'] = hexToRgba(props.$useTheme['--pcm-success-color'], 0.2);\n }\n\n if (props.$useTheme['--pcm-warning-color']) {\n merge['--pcm-warning-background'] = hexToRgba(props.$useTheme['--pcm-warning-color'], 0.2);\n }\n\n if (props.$useTheme['--pcm-error-color']) {\n merge['--pcm-error-background'] = hexToRgba(props.$useTheme['--pcm-error-color'], 0.2);\n }\n\n if (props.$useTheme['--pcm-wallet-lable-color']) {\n merge['--pcm-wallet-lable-background'] = hexToRgba(props.$useTheme['--pcm-wallet-lable-color'], 0.2);\n }\n\n return createCssVars(merge, true);\n }\n return undefined;\n }}\n\n\n all: initial;\n text-align: left;\n text-direction: ltr;\n text-rendering: optimizeLegibility;\n -webkit-font-smoothing: auto;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n -webkit-text-stroke: 0.001px transparent;\n text-size-adjust: none;\n font-size: 16px;\n color: var(--pcm-body-color);\n\n &,\n * {\n font-family: var(--pcm-font-family);\n box-sizing: border-box;\n\n &:after,\n &:before {\n box-sizing: border-box;\n border: 0 solid #e5e7eb;\n }\n }\n\n img,\n svg {\n max-width: 100%;\n overflow: visible;\n }\n strong {\n font-weight: 600;\n }\n input:focus-visible,\n a:focus-visible,\n button:focus-visible {\n outline: 1px solid var(--pcm-focus-color, var(--pcm-accent-color));\n }\n\n input {\n caret-color: var(--pcm-focus-color, var(--pcm-accent-color));\n }\n\n @keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n }\n`;\n", "import type { SmartAccount } from '@particle-network/aa-plugin';\nimport { create } from 'zustand';\n\ntype State = {\n smartAccount: SmartAccount | null;\n};\n\ntype Actions = {\n setSmartAccount: (smartAccount: SmartAccount | null) => void;\n};\n\nexport const useAAPlugin = create<State & Actions>((set) => ({\n smartAccount: null,\n setSmartAccount: (value) => {\n set({ smartAccount: value });\n },\n}));\n", "import type { Connection, Evaluate } from '@particle-network/connector-core';\nimport type { Config } from '../createConfig';\nimport { deepEqual } from '../utils/deepEqual';\n\nexport type GetConnectionsReturnType = Evaluate<Connection>[];\n\nlet previousConnections: Connection[] = [];\n\nexport function getConnections(config: Config): GetConnectionsReturnType {\n if (config.state.status === 'reconnecting') return previousConnections;\n\n const connections = [...config.state.connections.values()];\n const current = config.state.current;\n connections.sort((a, b) => {\n if (a.connector.uid === current) {\n return -1;\n }\n\n if (b.connector.uid === current) {\n return 1;\n }\n return 0;\n });\n if (deepEqual(previousConnections, connections)) return previousConnections;\n\n previousConnections = connections;\n return connections;\n}\n", "import type { Config } from '../createConfig';\nimport { deepEqual } from '../utils/deepEqual';\nimport { getConnections, type GetConnectionsReturnType } from './getConnections';\n\nexport type WatchConnectionsParameters = {\n onChange(connections: GetConnectionsReturnType, prevConnections: GetConnectionsReturnType): void;\n};\n\nexport type WatchConnectionsReturnType = () => void;\n\nexport function watchConnections(config: Config, parameters: WatchConnectionsParameters): WatchConnectionsReturnType {\n const { onChange } = parameters;\n return config.subscribe(() => getConnections(config), onChange, {\n equalityFn: deepEqual,\n });\n}\n", "import { useSyncExternalStore } from 'react';\nimport { getConnections } from '../actions/getConnections';\nimport { watchConnections } from '../actions/watchConnections';\nimport { useContext } from '../context';\n\nexport function useConnections() {\n const { config } = useContext();\n\n return useSyncExternalStore(\n (onChange) => watchConnections(config, { onChange }),\n () => getConnections(config),\n () => getConnections(config)\n );\n}\n", "import PasskeyIcon from '../assets/icons/PasskeyIcon';\nimport BitKeep from '../assets/wallets/BitKeep';\nimport Brave from '../assets/wallets/Brave';\nimport Bybit from '../assets/wallets/Bybit';\nimport Coin98 from '../assets/wallets/Coin98';\nimport Coinbase from '../assets/wallets/Coinbase';\nimport Frontier from '../assets/wallets/Frontier';\nimport ImToken from '../assets/wallets/ImToken';\nimport InfinityWallet from '../assets/wallets/InfinityWallet';\nimport Injected from '../assets/wallets/Injected';\nimport Ledger from '../assets/wallets/Ledger';\nimport MetaMask from '../assets/wallets/MetaMask';\nimport OKX from '../assets/wallets/OKX';\nimport ONTO from '../assets/wallets/ONTO';\nimport Onekey from '../assets/wallets/Onekey';\nimport Phantom from '../assets/wallets/Phantom';\nimport Rabby from '../assets/wallets/Rabby';\nimport Rainbow from '../assets/wallets/Rainbow';\nimport SafePal from '../assets/wallets/SafePal';\nimport Slope from '../assets/wallets/Slope';\nimport Talisman from '../assets/wallets/Talisman';\nimport TokenPocket from '../assets/wallets/TokenPocket';\nimport Trust from '../assets/wallets/Trust';\nimport WalletConnect from '../assets/wallets/WalletConnect';\nimport Zerion from '../assets/wallets/Zerion';\n\nimport React from 'react';\nimport { isAndroid, isIOS } from '../utils';\n\nexport type WalletConfigProps = {\n // Wallets name\n name?: string;\n // Wallets short name. Defaults to `name`\n shortName?: string;\n // Icon to display in the modal\n icon?: React.ReactNode;\n // Defaults to `'circle'`, but some icons look better as squircle (e.g. if they have a background)\n iconShape?: 'squircle' | 'circle' | 'square';\n // Defaults to `false`, but some icons don't have a background and look better if they shrink to fit the container\n iconShouldShrink?: boolean;\n // Links to download the wallet\n downloadUrls?: {\n // wallet's website\n website?: string;\n\n // browser extensions\n chrome?: string;\n firefox?: string;\n brave?: string;\n edge?: string;\n safari?: string;\n\n // app downloads\n android?: string;\n ios?: string;\n };\n // Create URI for QR code, where uri is encoded data from WalletConnect\n getWalletConnectDeeplink?: (uri: string) => string;\n\n // open url in Wallet's browser\n getDAppBrowserDeeplink?: (url: string) => string;\n};\n\n// Organised in alphabetical order by key\nexport const walletConfigs: {\n [rdns: string]: WalletConfigProps; // for multiple cases seperate rdns by comma\n} = {\n 'particleEVM, particleSolana, network.particle': {\n name: 'Particle Auth',\n iconShape: 'circle',\n },\n 'coinbaseWallet, coinbaseWalletSDK, com.coinbase.wallet': {\n name: 'Coinbase Wallet',\n shortName: 'Coinbase',\n icon: <Coinbase />,\n iconShape: 'squircle',\n downloadUrls: {\n website: 'https://www.coinbase.com/wallet/getting-started-extension',\n chrome: 'https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad',\n brave: 'https://chromewebstore.google.com/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad',\n android: 'https://play.google.com/store/apps/details?id=org.toshi',\n ios: 'https://apps.apple.com/app/coinbase-wallet-store-crypto/id1278383455',\n },\n getWalletConnectDeeplink: (uri: string) => {\n return uri;\n },\n getDAppBrowserDeeplink: (uri: string) => {\n return `https://go.cb-w.com/dapp?cb_url=${encodeURIComponent(uri)}`;\n },\n },\n 'metaMask, io.metamask, io.metamask.mobile': {\n name: 'MetaMask',\n icon: <MetaMask />,\n iconShouldShrink: true,\n downloadUrls: {\n website: 'https://metamask.io/download/',\n chrome: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn',\n firefox: 'https://addons.mozilla.org/firefox/addon/ether-metamask/',\n brave: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn',\n edge: 'https://microsoftedge.microsoft.com/addons/detail/metamask/ejbalbakoplchlghecdalmeeeajnimhm',\n android: 'https://play.google.com/store/apps/details?id=io.metamask',\n ios: 'https://apps.apple.com/app/metamask/id1438144202',\n },\n getWalletConnectDeeplink: (uri: string) => {\n return isAndroid() || isIOS()\n ? `metamask://wc?uri=${encodeURIComponent(uri)}`\n : `https://metamask.app.link/wc?uri=${encodeURIComponent(uri)}`;\n },\n },\n 'phantom, app.phantom': {\n name: 'Phantom',\n iconShape: 'squircle',\n icon: <Phantom />,\n downloadUrls: {\n website: 'https://phantom.app/download',\n chrome: 'https://chromewebstore.google.com/detail/phantom/bfnaelmomeimhlpmgjnjophhpkkoljpa',\n brave: 'https://chromewebstore.google.com/detail/phantom/bfnaelmomeimhlpmgjnjophhpkkoljpa',\n firefox: 'https://addons.mozilla.org/en-US/firefox/addon/phantom-app/',\n android: 'https://play.google.com/store/apps/details?id=app.phantom',\n ios: 'https://apps.apple.com/us/app/phantom-crypto-wallet/id1598432977',\n },\n getDAppBrowserDeeplink: (uri: string) => {\n return `https://phantom.app/ul/browse/${encodeURIComponent(uri)}?ref=${encodeURIComponent(window.location.origin)}`;\n },\n },\n 'okxWallet, oKExWallet, com.okex.wallet': {\n name: 'OKX Wallet',\n shortName: 'OKX',\n iconShape: 'squircle',\n icon: <OKX />,\n downloadUrls: {\n website: 'https://www.okx.com/download',\n chrome:\n 'https://chromewebstore.google.com/detail/%E6%AC%A7%E6%98%93-web3-%E9%92%B1%E5%8C%85/mcohilncbfahbmgdjkbpemcciiolgcge',\n edge: 'https://microsoftedge.microsoft.com/addons/detail/okx-wallet/pbpjkcldjiffchgbbndmhojiacbgflha',\n brave:\n 'https://chromewebstore.google.com/detail/%E6%AC%A7%E6%98%93-web3-%E9%92%B1%E5%8C%85/mcohilncbfahbmgdjkbpemcciiolgcge',\n android: 'https://play.google.com/store/apps/details?id=com.okinc.okex.gp',\n ios: 'https://apps.apple.com/us/app/okx-buy-bitcoin-btc-crypto/id1327268470',\n },\n getWalletConnectDeeplink: (uri: string) => {\n return `okex://main/wc?uri=${encodeURIComponent(uri)}`;\n },\n getDAppBrowserDeeplink: (uri: string) => {\n const deepLink = `okx://wallet/dapp/url?dappUrl=${encodeURIComponent(uri)}`;\n return `https://www.okx.com/download?deeplink=${encodeURIComponent(deepLink)}`;\n },\n },\n 'bitKeep, bitget, com.bitget.web3': {\n name: 'Bitget Wallet',\n shortName: 'Bitget',\n iconShape: 'circle',\n icon: <BitKeep />,\n downloadUrls: {\n website: 'https://web3.bitget.com/wallet-download',\n chrome: 'https://chromewebstore.google.com/detail/bitget-wallet-formerly-bi/jiidiaalihmmhddjgbnbgdfflelocpak',\n android: 'https://play.google.com/store/apps/details?id=com.bitkeep.wallet',\n ios: 'https://apps.apple.com/us/app/bitget-wallet-ex-bitkeep/id1395301115',\n },\n getWalletConnectDeeplink: (uri: string) => {\n return `bitkeep://wc?uri=${encodeURIComponent(uri)}`;\n },\n getDAppBrowserDeeplink: (uri: string) => {\n return `https://bkcode.vip?action=dapp&url=${encodeURIComponent(uri)}`;\n },\n },\n 'rabby, io.rabby': {\n name: 'Rabby Wallet',\n shortName: 'Rabby',\n icon: <Rabby />,\n downloadUrls: {\n website: 'https://rabby.io',\n chrome: 'https://chrome.google.com/webstore/detail/rabby-wallet/acmacodkjbdgmoleebolmdjonilkdbch',\n },\n },\n 'rainbow, me.rainbow': {\n name: 'Rainbow Wallet',\n shortName: 'Rainbow',\n icon: <Rainbow />,\n iconShape: 'squircle',\n downloadUrls: {\n website: 'https://rainbow.me/?utm_source=connectkit',\n chrome: 'https://chrome.google.com/webstore/detail/opfgelmcmbiajamepnmloijbpoleiama',\n edge: 'https://microsoftedge.microsoft.com/addons/detail/cpojfbodiccabbabgimdeohkkpjfpbnf',\n firefox: 'https://addons.mozilla.org/firefox/addon/rainbow-extension',\n brave: 'https://rainbow.me/extension?utm_source=connectkit',\n android: 'https://play.google.com/store/apps/details?id=me.rainbow',\n ios: 'https://apps.apple.com/app/id1457119021',\n },\n getWalle