import type { Address } from 'viem' import type { CreateConnectorFn } from '../connectors/createConnector.js' import type { Config, Connection, Connector } from '../createConfig.js' import type { ErrorType } from '../errors/base.js' import type { Compute } from '../types/utils.js' export type ReconnectParameters = { /** Connectors to attempt reconnect with */ connectors?: readonly (CreateConnectorFn | Connector)[] | undefined } export type ReconnectReturnType = Compute[] export type ReconnectErrorType = ErrorType let isReconnecting = false /** https://wagmi.sh/core/api/actions/reconnect */ export async function reconnect( config: Config, parameters: ReconnectParameters = {}, ): Promise { // If already reconnecting, do nothing if (isReconnecting) return [] isReconnecting = true config.setState((x) => ({ ...x, status: x.current ? 'reconnecting' : 'connecting', })) const connectors: Connector[] = [] if (parameters.connectors?.length) { for (const connector_ of parameters.connectors) { let connector: Connector // "Register" connector if not already created if (typeof connector_ === 'function') connector = config._internal.connectors.setup(connector_) else connector = connector_ connectors.push(connector) } } else connectors.push(...config.connectors) // Try recently-used connectors first let recentConnectorId: string | null | undefined try { recentConnectorId = await config.storage?.getItem('recentConnectorId') } catch {} const scores: Record = {} for (const [, connection] of config.state.connections) { scores[connection.connector.id] = 1 } if (recentConnectorId) scores[recentConnectorId] = 0 const sorted = Object.keys(scores).length > 0 ? // .toSorted() [...connectors].sort( (a, b) => (scores[a.id] ?? 10) - (scores[b.id] ?? 10), ) : connectors // Iterate through each connector and try to connect let connected = false const connections: Connection[] = [] const providers: unknown[] = [] for (const connector of sorted) { const provider = await connector.getProvider().catch(() => undefined) if (!provider) continue // If we already have an instance of this connector's provider, // then we have already checked it (ie. injected connectors can // share the same `window.ethereum` instance, so we don't want to // connect to it again). if (providers.some((x) => x === provider)) continue const isAuthorized = await connector.isAuthorized() if (!isAuthorized) continue const data = await connector .connect({ isReconnecting: true }) .catch(() => null) if (!data) continue connector.emitter.off('connect', config._internal.events.connect) connector.emitter.on('change', config._internal.events.change) connector.emitter.on('disconnect', config._internal.events.disconnect) config.setState((x) => { const connections = new Map(connected ? x.connections : new Map()).set( connector.uid, { accounts: data.accounts, chainId: data.chainId, connector }, ) return { ...x, current: connected ? x.current : connector.uid, connections, } }) connections.push({ accounts: data.accounts as readonly [Address, ...Address[]], chainId: data.chainId, connector, }) providers.push(provider) connected = true } // Prevent overwriting connected status from race condition if ( config.state.status === 'reconnecting' || config.state.status === 'connecting' ) { // If connecting didn't succeed, set to disconnected if (!connected) config.setState((x) => ({ ...x, connections: new Map(), current: null, status: 'disconnected', })) else config.setState((x) => ({ ...x, status: 'connected' })) } isReconnecting = false return connections }