Skip to content

Commit

Permalink
feat(core,react,vue): connect parameters (#4453)
Browse files Browse the repository at this point in the history
* wip: make config connectors generic

* feat: tweaks

* chore: changeset

* refactor: types
  • Loading branch information
tmm authored Dec 10, 2024
1 parent 4e749d4 commit 070e484
Show file tree
Hide file tree
Showing 28 changed files with 367 additions and 116 deletions.
6 changes: 6 additions & 0 deletions .changeset/spotty-kangaroos-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"wagmi": minor
"@wagmi/vue": minor
---

Added support to `useConnect` for custom `connector.connect` parameters.
7 changes: 7 additions & 0 deletions .changeset/thirty-camels-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@wagmi/connectors": minor
"@wagmi/core": minor
---

Added narrowing to `config.connectors`.

17 changes: 16 additions & 1 deletion packages/connectors/src/walletConnect.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { config, walletConnectProjectId } from '@wagmi/test'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { afterAll, afterEach, beforeAll, expect, test, vi } from 'vitest'
import {
afterAll,
afterEach,
beforeAll,
expect,
expectTypeOf,
test,
vi,
} from 'vitest'

import { walletConnect } from './walletConnect.js'

Expand Down Expand Up @@ -49,4 +57,11 @@ test('setup', () => {
const connectorFn = walletConnect({ projectId: walletConnectProjectId })
const connector = config._internal.connectors.setup(connectorFn)
expect(connector.name).toEqual('WalletConnect')

type ConnectFnParameters = NonNullable<
Parameters<(typeof connector)['connect']>[0]
>
expectTypeOf<ConnectFnParameters['pairingTopic']>().toMatchTypeOf<
string | undefined
>()
})
6 changes: 5 additions & 1 deletion packages/connectors/src/walletConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ export function walletConnect(parameters: WalletConnectParameters) {

type Provider = Awaited<ReturnType<(typeof EthereumProvider)['init']>>
type Properties = {
connect(parameters?: { chainId?: number; pairingTopic?: string }): Promise<{
connect(parameters?: {
chainId?: number | undefined
isReconnecting?: boolean | undefined
pairingTopic?: string | undefined
}): Promise<{
accounts: readonly Address[]
chainId: number
}>
Expand Down
48 changes: 48 additions & 0 deletions packages/core/src/actions/connect.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { accounts } from '@wagmi/test'
import { http } from 'viem'
import { mainnet } from 'viem/chains'
import { expectTypeOf, test } from 'vitest'

import type { CreateConnectorFn } from '../connectors/createConnector.js'
import { mock } from '../connectors/mock.js'
import { type Connector, createConfig } from '../createConfig.js'
import { connect } from './connect.js'

const config = createConfig({
chains: [mainnet],
transports: { [mainnet.id]: http() },
})

test('parameters: connector (ConnectorFn)', () => {
const connectorFn = mock({ accounts })

connect(config, {
connector: connectorFn,
foo: 'bar',
})
expectTypeOf<
typeof connectorFn extends CreateConnectorFn ? true : false
>().toEqualTypeOf<true>()

type Result = NonNullable<
Parameters<typeof connect<typeof config, typeof connectorFn>>[1]
>
expectTypeOf<Result['foo']>().toEqualTypeOf<string | undefined>()
})

test('parameters: connector (Connector)', () => {
const connector = config._internal.connectors.setup(mock({ accounts }))

connect(config, {
connector,
foo: 'bar',
})
expectTypeOf<
typeof connector extends Connector ? true : false
>().toEqualTypeOf<true>()

type Result = NonNullable<
Parameters<typeof connect<typeof config, typeof connector>>[1]
>
expectTypeOf<Result['foo']>().toEqualTypeOf<string | undefined>()
})
33 changes: 28 additions & 5 deletions packages/core/src/actions/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,31 @@ import {
import type { ChainIdParameter } from '../types/properties.js'
import type { Compute } from '../types/utils.js'

export type ConnectParameters<config extends Config = Config> = Compute<
export type ConnectParameters<
config extends Config = Config,
connector extends Connector | CreateConnectorFn =
| Connector
| CreateConnectorFn,
///
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
connector: connector | CreateConnectorFn
}
>
> &
parameters

export type ConnectReturnType<config extends Config = Config> = {
accounts: readonly [Address, ...Address[]]
Expand All @@ -37,9 +57,12 @@ export type ConnectErrorType =
| ErrorType

/** https://wagmi.sh/core/api/actions/connect */
export async function connect<config extends Config>(
export async function connect<
config extends Config,
connector extends Connector | CreateConnectorFn,
>(
config: config,
parameters: ConnectParameters<config>,
parameters: ConnectParameters<config, connector>,
): Promise<ConnectReturnType<config>> {
// "Register" connector if not already created
let connector: Connector
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/actions/getConnectorClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,20 @@ test('behavior: account does not exist on connector', async () => {

test('behavior: reconnecting', async () => {
config.setState((state) => ({ ...state, status: 'reconnecting' }))
const { id, name, type, uuid } = connector
const { id, name, type, uid } = connector
await expect(
getConnectorClient(config, {
connector: {
id,
name,
type,
uuid,
uid,
} as unknown as Connector,
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
[ConnectorUnavailableReconnectingError: Connector "Mock Connector" unavailable while reconnecting.
Details: During the reconnection step, the only connector methods guaranteed to be available are: \`id\`, \`name\`, \`type\`, \`uuid\`. All other methods are not guaranteed to be available until reconnection completes and connectors are fully restored. This error commonly occurs for connectors that asynchronously inject after reconnection has already started.
Details: During the reconnection step, the only connector methods guaranteed to be available are: \`id\`, \`name\`, \`type\`, \`uid\`. All other methods are not guaranteed to be available until reconnection completes and connectors are fully restored. This error commonly occurs for connectors that asynchronously inject after reconnection has already started.
Version: @wagmi/[email protected]]
`)
config.setState((state) => ({ ...state, status: 'disconnected' }))
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/actions/getConnectors.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import type { Config, Connector } from '../createConfig.js'
import { deepEqual } from '../utils/deepEqual.js'

export type GetConnectorsReturnType = readonly Connector[]
export type GetConnectorsReturnType<config extends Config = Config> =
config['connectors']

let previousConnectors: readonly Connector[] = []

/** https://wagmi.sh/core/api/actions/getConnectors */
export function getConnectors(config: Config): GetConnectorsReturnType {
export function getConnectors<config extends Config>(
config: config,
): GetConnectorsReturnType<config> {
const connectors = config.connectors
if (deepEqual(previousConnectors, connectors)) return previousConnectors
previousConnectors = connectors
Expand Down
12 changes: 6 additions & 6 deletions packages/core/src/actions/watchConnectors.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import type { Config } from '../createConfig.js'
import type { GetConnectorsReturnType } from './getConnectors.js'

export type WatchConnectorsParameters = {
export type WatchConnectorsParameters<config extends Config = Config> = {
onChange(
connections: GetConnectorsReturnType,
prevConnectors: GetConnectorsReturnType,
connections: GetConnectorsReturnType<config>,
prevConnectors: GetConnectorsReturnType<config>,
): void
}

export type WatchConnectorsReturnType = () => void

/** https://wagmi.sh/core/api/actions/watchConnectors */
export function watchConnectors(
config: Config,
parameters: WatchConnectorsParameters,
export function watchConnectors<config extends Config>(
config: config,
parameters: WatchConnectorsParameters<config>,
): WatchConnectorsReturnType {
const { onChange } = parameters
return config._internal.connectors.subscribe((connectors, prevConnectors) => {
Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/connectors/createConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ export function createConnector<
provider,
properties extends Record<string, unknown> = Record<string, unknown>,
storageItem extends Record<string, unknown> = Record<string, unknown>,
>(createConnectorFn: CreateConnectorFn<provider, properties, storageItem>) {
///
createConnectorFn extends CreateConnectorFn<
provider,
properties,
storageItem
> = CreateConnectorFn<provider, properties, storageItem>,
>(createConnectorFn: createConnectorFn) {
return createConnectorFn
}
16 changes: 15 additions & 1 deletion packages/core/src/connectors/mock.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { accounts, config } from '@wagmi/test'
import { expect, test } from 'vitest'
import { expect, expectTypeOf, test } from 'vitest'

import type { Connector } from '../createConfig.js'
import type { CreateConnectorFn } from './createConnector.js'
import { mock } from './mock.js'

test('setup', () => {
const connectorFn = mock({ accounts })
const connector = config._internal.connectors.setup(connectorFn)
expect(connector.name).toEqual('Mock Connector')

expectTypeOf<
typeof connectorFn extends CreateConnectorFn ? true : false
>().toEqualTypeOf<true>()
expectTypeOf<
typeof connector extends Connector ? true : false
>().toEqualTypeOf<true>()

type ConnectFnParameters = NonNullable<
Parameters<(typeof connector)['connect']>[0]
>
expectTypeOf<ConnectFnParameters['foo']>().toMatchTypeOf<string | undefined>()
})

test('behavior: features.connectError', () => {
Expand Down
12 changes: 11 additions & 1 deletion packages/core/src/connectors/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,20 @@ export function mock(parameters: MockParameters) {
type Provider = ReturnType<
Transport<'custom', unknown, EIP1193RequestFn<WalletRpcSchema>>
>
type Properties = {
connect(parameters?: {
chainId?: number | undefined
isReconnecting?: boolean | undefined
foo?: string | undefined
}): Promise<{
accounts: readonly Address[]
chainId: number
}>
}
let connected = features.defaultConnected
let connectedChainId: number

return createConnector<Provider>((config) => ({
return createConnector<Provider, Properties>((config) => ({
id: 'mock',
name: 'Mock Connector',
type: mock.type,
Expand Down
22 changes: 22 additions & 0 deletions packages/core/src/createConfig.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,25 @@ test('behavior: parameters should not include certain client config properties',
'transport' extends Result ? true : false
>().toEqualTypeOf<false>()
})

test('infer connectors', () => {
const connectorFn = mock({ accounts })
const config = createConfig({
chains: [mainnet, sepolia],
connectors: [connectorFn],
transports: {
[mainnet.id]: webSocket(),
[sepolia.id]: http(),
},
})

const connector = config.connectors[0]!
expectTypeOf(connector).toEqualTypeOf(
config._internal.connectors.setup(connectorFn),
)

type ConnectFnParameters = NonNullable<
Parameters<(typeof connector)['connect']>[0]
>
expectTypeOf<ConnectFnParameters['foo']>().toMatchTypeOf<string | undefined>()
})
Loading

0 comments on commit 070e484

Please sign in to comment.