import type { Connector as BigmiConnector } from '@bigmi/client' import { useConnect as useBigmiConnect } from '@bigmi/react' import { ChainType } from '@lifi/sdk' import type { Theme } from '@mui/material' import { useMediaQuery } from '@mui/material' import { useWallets } from '@mysten/dapp-kit' import type { WalletWithRequiredFeatures } from '@mysten/wallet-standard' import { WalletReadyState } from '@solana/wallet-adapter-base' import type { Wallet } from '@solana/wallet-adapter-react' import { useWallet } from '@solana/wallet-adapter-react' import { useEffect, useState } from 'react' import type { Connector } from 'wagmi' import { useConnect } from 'wagmi' import { defaultBaseAccountConfig } from '../config/baseAccount.js' import { defaultCoinbaseConfig } from '../config/coinbase.js' import { defaultMetaMaskConfig } from '../config/metaMask.js' import { defaultWalletConnectConfig } from '../config/walletConnect.js' import { createBaseAccountConnector } from '../connectors/baseAccount.js' import { createCoinbaseConnector } from '../connectors/coinbase.js' import { createMetaMaskConnector } from '../connectors/metaMask.js' import { createPortoConnector } from '../connectors/porto.js' import type { CreateConnectorFnExtended } from '../connectors/types.js' import { createWalletConnectConnector } from '../connectors/walletConnect.js' import { useWalletManagementConfig } from '../providers/WalletManagementProvider/WalletManagementContext.js' import type { WalletConnector } from '../types/walletConnector.js' import { getConnectorIcon } from '../utils/getConnectorIcon.js' import { getWalletPriority } from '../utils/getWalletPriority.js' import { isWalletInstalled } from '../utils/isWalletInstalled.js' type CombinedWalletConnector = { connector: WalletConnector chainType: ChainType } export type CombinedWallet = { id: string name: string icon?: string connectors: CombinedWalletConnector[] } type CombinedWallets = { installedWallets: CombinedWallet[] notDetectedWallets: CombinedWallet[] } const normalizeName = (name: string) => name.split(' ')[0].toLowerCase().trim() const combineWalletLists = ( utxoConnectorList: BigmiConnector[], evmConnectorList: (CreateConnectorFnExtended | Connector)[], svmWalletList: Wallet[], suiWalletList: WalletWithRequiredFeatures[], walletEcosystemsOrder?: Record ): CombinedWallet[] => { const walletMap = new Map() utxoConnectorList.forEach((utxo) => { const utxoName = utxo.name const normalizedName = normalizeName(utxoName) const existing = walletMap.get(normalizedName) || { id: utxo.id, name: utxoName, icon: getConnectorIcon(utxo as BigmiConnector), connectors: [] as CombinedWalletConnector[], } existing.connectors.push({ connector: utxo, chainType: ChainType.UTXO }) walletMap.set(normalizedName, existing) }) evmConnectorList.forEach((evm) => { const evmName = (evm as CreateConnectorFnExtended)?.displayName || (evm as Connector)?.name const normalizedName = normalizeName(evmName) const existing = walletMap.get(normalizedName) || { id: evm.id, name: evmName, icon: getConnectorIcon(evm as Connector), connectors: [] as CombinedWalletConnector[], } existing.connectors.push({ connector: evm, chainType: ChainType.EVM }) walletMap.set(normalizedName, existing) }) svmWalletList.forEach((svm) => { const normalizedName = normalizeName(svm.adapter.name) const existing = walletMap.get(normalizedName) || { id: svm.adapter.name, name: svm.adapter.name, icon: svm.adapter.icon, connectors: [] as CombinedWalletConnector[], } existing.connectors.push({ connector: svm.adapter, chainType: ChainType.SVM, }) walletMap.set(normalizedName, existing) }) suiWalletList.forEach((sui) => { const normalizedName = normalizeName(sui.name) const existing = walletMap.get(normalizedName) || { id: sui.name, name: sui.name, icon: sui.icon, connectors: [] as CombinedWalletConnector[], } existing.connectors.push({ connector: sui, chainType: ChainType.MVM }) walletMap.set(normalizedName, existing) }) let combinedWallets = Array.from(walletMap.values()) if (walletEcosystemsOrder) { combinedWallets = combinedWallets.map((wallet) => { const order = walletEcosystemsOrder[wallet.name] if (order) { return { ...wallet, connectors: wallet.connectors.sort((a, b) => walletEcosystemsComparator(a, b, order) ), } } return wallet }) } combinedWallets.sort(walletComparator) return combinedWallets } export const useCombinedWallets = () => { const walletConfig = useWalletManagementConfig() const { connectors: wagmiConnectors } = useConnect() const { connectors: bigmiConnectors } = useBigmiConnect() const { wallets: solanaWallets } = useWallet() const suiWallets = useWallets() const [combinedWallets, setCombinedWallets] = useState( () => { return { installedWallets: [], notDetectedWallets: [], } } ) const isDesktopView = useMediaQuery((theme: Theme) => theme.breakpoints.up('sm') ) useEffect(() => { ;(async () => { let evmConnectors: (CreateConnectorFnExtended | Connector)[] = Array.from( wagmiConnectors // Remove duplicate connectors ).filter( (connector, index, self) => index === self.findIndex((c) => c.id === connector.id) ) // Check if Safe connector exists and can get a provider const safeConnector = evmConnectors.find( (connector) => connector.id === 'safe' ) as Connector | undefined let shouldFilterOutSafeConnector = false if (safeConnector) { try { const provider = await safeConnector.getProvider() // If no provider is available, we should filter out the Safe connector if (!provider) { shouldFilterOutSafeConnector = true } } catch { // If getting provider fails, filter out the Safe connector shouldFilterOutSafeConnector = true } } if (shouldFilterOutSafeConnector) { evmConnectors = evmConnectors.filter( (connector) => connector.id !== 'safe' ) } // Ensure standard connectors are included if ( !evmConnectors.some((connector) => connector.id.toLowerCase().includes('walletconnect') ) ) { evmConnectors.unshift( createWalletConnectConnector( walletConfig?.walletConnect ?? defaultWalletConnectConfig ) ) } if ( !evmConnectors.some((connector) => connector.id.toLowerCase().includes('coinbase') ) ) { evmConnectors.unshift( createCoinbaseConnector( walletConfig?.coinbase ?? defaultCoinbaseConfig ) ) } if ( !evmConnectors.some((connector) => connector.id.toLowerCase().includes('metamask') ) ) { evmConnectors.unshift( createMetaMaskConnector( walletConfig?.metaMask ?? defaultMetaMaskConfig ) ) } if ( !evmConnectors.some((connector) => connector.id.toLowerCase().includes('baseaccount') ) ) { evmConnectors.unshift( createBaseAccountConnector( walletConfig?.baseAccount ?? defaultBaseAccountConfig ) ) } if ( !evmConnectors.some((connector) => connector.id.toLowerCase().includes('porto') ) ) { evmConnectors.unshift(createPortoConnector(walletConfig?.porto)) } const includeEcosystem = (chainType: ChainType) => !walletConfig.enabledChainTypes || walletConfig.enabledChainTypes.includes(chainType) const installedUTXOConnectors = includeEcosystem(ChainType.UTXO) ? bigmiConnectors.filter((connector) => { const isInstalled = isWalletInstalled(connector.id) return isInstalled }) : [] const installedEVMConnectors = includeEcosystem(ChainType.EVM) ? evmConnectors.filter((connector) => { const isInstalled = isWalletInstalled(connector.id) return isInstalled }) : [] const installedSVMWallets = includeEcosystem(ChainType.SVM) ? solanaWallets.filter((wallet) => { const isInstalled = wallet.adapter.readyState === WalletReadyState.Installed || wallet.adapter.readyState === WalletReadyState.Loadable return isInstalled }) : [] const installedSuiWallets = includeEcosystem(ChainType.MVM) ? suiWallets : [] const installedCombinedWallets = combineWalletLists( installedUTXOConnectors, installedEVMConnectors, installedSVMWallets, installedSuiWallets, walletConfig.walletEcosystemsOrder ) const notDetectedUTXOConnectors = bigmiConnectors.filter((connector) => { const isInstalled = isWalletInstalled(connector.id) return !isInstalled && isDesktopView }) const notDetectedEVMConnectors = evmConnectors.filter((connector) => { const isInstalled = isWalletInstalled(connector.id) return !isInstalled && isDesktopView }) const notDetectedSVMWallets = solanaWallets.filter((wallet) => { const isInstalled = wallet.adapter.readyState === WalletReadyState.Installed || wallet.adapter.readyState === WalletReadyState.Loadable return !isInstalled && isDesktopView }) const notDetectedCombinedWallets = combineWalletLists( notDetectedUTXOConnectors, notDetectedEVMConnectors, notDetectedSVMWallets, [] ) installedCombinedWallets.sort(walletComparator) notDetectedCombinedWallets.sort(walletComparator) setCombinedWallets({ installedWallets: installedCombinedWallets, notDetectedWallets: notDetectedCombinedWallets, }) })() }, [ bigmiConnectors, isDesktopView, solanaWallets, suiWallets, wagmiConnectors, walletConfig, ]) return combinedWallets } // Ensure the walletComparator function is updated to handle CombinedWallet const walletComparator = (a: CombinedWallet, b: CombinedWallet) => { const priorityA = getWalletPriority(a.id) const priorityB = getWalletPriority(b.id) if (priorityA !== priorityB) { return priorityA - priorityB } return a.id?.localeCompare(b.id) } const walletEcosystemsComparator = ( a: CombinedWalletConnector, b: CombinedWalletConnector, order: ChainType[] ) => { if (!order.length) { return 0 } const ecosystemA = a.chainType const ecosystemB = b.chainType if (ecosystemA === ecosystemB) { return 0 } const indexA = order.indexOf(ecosystemA) const indexB = order.indexOf(ecosystemB) // If both are in the order list, sort by their position if (indexA !== -1 && indexB !== -1) { return indexA - indexB } // If only one is in the order list, prioritize it if (indexA !== -1) { return -1 } if (indexB !== -1) { return 1 } return 0 }