@wagmi/core
Version:
VanillaJS library for Ethereum
128 lines (110 loc) • 4.01 kB
text/typescript
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<Connection>[]
export type ReconnectErrorType = ErrorType
let isReconnecting = false
/** https://wagmi.sh/core/api/actions/reconnect */
export async function reconnect(
config: Config,
parameters: ReconnectParameters = {},
): Promise<ReconnectReturnType> {
// 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<string, number> = {}
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
}