@wagmi/core
Version:
VanillaJS library for Ethereum
161 lines (149 loc) • 5.24 kB
text/typescript
import type {
Address,
ResourceUnavailableRpcErrorType,
UserRejectedRequestErrorType,
} from 'viem'
import type { CreateConnectorFn } from '../connectors/createConnector.js'
import type { Config, Connector } from '../createConfig.js'
import type { BaseErrorType, ErrorType } from '../errors/base.js'
import {
ConnectorAlreadyConnectedError,
type ConnectorAlreadyConnectedErrorType,
} from '../errors/config.js'
import type { ChainIdParameter } from '../types/properties.js'
import type { Compute } from '../types/utils.js'
export type ConnectParameters<
config extends Config = Config,
connector extends Connector | CreateConnectorFn =
| Connector
| CreateConnectorFn,
withCapabilities extends boolean = false,
///
parameters extends unknown | undefined =
| (connector extends CreateConnectorFn
? Omit<
NonNullable<Parameters<ReturnType<connector>['connect']>[0]>,
'isReconnecting'
>
: never)
| (connector extends Connector
? Omit<
NonNullable<Parameters<connector['connect']>[0]>,
'isReconnecting'
>
: never),
> = Compute<
ChainIdParameter<config> & {
connector: connector | CreateConnectorFn
withCapabilities?: withCapabilities | boolean | undefined
}
> &
parameters
export type ConnectReturnType<
config extends Config = Config,
connector extends Connector | CreateConnectorFn =
| Connector
| CreateConnectorFn,
withCapabilities extends boolean = false,
///
capabilities extends unknown | undefined =
| (connector extends CreateConnectorFn
? Awaited<
ReturnType<ReturnType<connector>['connect']>
>['accounts'] extends
| readonly Address[]
| readonly {
capabilities: infer capabilities
}[]
? capabilities
: Record<string, unknown>
: never)
| (connector extends Connector
? Awaited<ReturnType<connector['connect']>>['accounts'] extends
| readonly Address[]
| readonly {
capabilities: infer capabilities
}[]
? capabilities
: Record<string, unknown>
: never),
> = {
accounts: withCapabilities extends true
? readonly [
{ address: Address; capabilities: capabilities },
...{ address: Address; capabilities: capabilities }[],
]
: readonly [Address, ...Address[]]
chainId:
| config['chains'][number]['id']
| (number extends config['chains'][number]['id'] ? number : number & {})
}
export type ConnectErrorType =
| ConnectorAlreadyConnectedErrorType
// connector.connect()
| UserRejectedRequestErrorType
| ResourceUnavailableRpcErrorType
// base
| BaseErrorType
| ErrorType
/** https://wagmi.sh/core/api/actions/connect */
export async function connect<
config extends Config,
connector extends Connector | CreateConnectorFn,
withCapabilities extends boolean = false,
>(
config: config,
parameters: ConnectParameters<config, connector, withCapabilities>,
): Promise<ConnectReturnType<config, connector, withCapabilities>> {
// "Register" connector if not already created
let connector: Connector
if (typeof parameters.connector === 'function') {
connector = config._internal.connectors.setup(parameters.connector)
} else connector = parameters.connector
// Check if connector is already connected
if (connector.uid === config.state.current)
throw new ConnectorAlreadyConnectedError()
try {
config.setState((x) => ({ ...x, status: 'connecting' }))
connector.emitter.emit('message', { type: 'connecting' })
const { connector: _, ...rest } = parameters
const data = await connector.connect(rest)
connector.emitter.off('connect', config._internal.events.connect)
connector.emitter.on('change', config._internal.events.change)
connector.emitter.on('disconnect', config._internal.events.disconnect)
await config.storage?.setItem('recentConnectorId', connector.id)
config.setState((x) => ({
...x,
connections: new Map(x.connections).set(connector.uid, {
accounts: (rest.withCapabilities
? data.accounts.map((account) =>
typeof account === 'object' ? account.address : account,
)
: data.accounts) as readonly [Address, ...Address[]],
chainId: data.chainId,
connector: connector,
}),
current: connector.uid,
status: 'connected',
}))
return {
// TODO(v3): Remove `withCapabilities: true` default behavior so remove compat marshalling
// Workaround so downstream connectors work with `withCapabilities` without any changes required
accounts: (rest.withCapabilities
? data.accounts.map((address) =>
typeof address === 'object'
? address
: { address, capabilities: {} },
)
: data.accounts) as never,
chainId: data.chainId,
} as never
} catch (error) {
config.setState((x) => ({
...x,
// Keep existing connector connected in case of error
status: x.current ? 'connected' : 'disconnected',
}))
throw error
}
}