From 7998d7492bc5a5604d2496e3cc8277203970f140 Mon Sep 17 00:00:00 2001 From: Tom Meagher Date: Mon, 11 Sep 2023 12:46:16 -0400 Subject: [PATCH] feat: client actions/hooks --- docs/.vitepress/sidebar.ts | 25 +++ docs/core/api/actions/watchAccount.md | 6 +- docs/core/api/actions/watchChainId.md | 6 +- docs/core/api/actions/watchConnections.md | 2 +- package.json | 2 +- packages/core/src/actions/getClient.test-d.ts | 19 ++ packages/core/src/actions/getClient.test.ts | 8 + packages/core/src/actions/getClient.ts | 30 ++++ .../{viem => }/getPublicClient.test-d.ts | 0 .../{viem => }/getPublicClient.test.ts | 0 .../src/actions/{viem => }/getPublicClient.ts | 7 +- .../{viem => }/getWalletClient.test-d.ts | 0 .../{viem => }/getWalletClient.test.ts | 4 +- .../src/actions/{viem => }/getWalletClient.ts | 7 +- .../core/src/actions/watchAccount.test.ts | 5 +- packages/core/src/actions/watchAccount.ts | 5 +- .../core/src/actions/watchChainId.test.ts | 4 +- packages/core/src/actions/watchChainId.ts | 5 +- .../core/src/actions/watchClient.test-d.ts | 15 ++ packages/core/src/actions/watchClient.test.ts | 23 +++ packages/core/src/actions/watchClient.ts | 34 ++++ .../core/src/actions/watchConnections.test.ts | 4 +- packages/core/src/actions/watchConnections.ts | 5 +- .../src/actions/watchPublicClient.test-d.ts | 15 ++ .../src/actions/watchPublicClient.test.ts | 23 +++ .../core/src/actions/watchPublicClient.ts | 37 ++++ packages/core/src/exports/actions.test.ts | 7 +- packages/core/src/exports/actions.ts | 47 +++-- packages/core/src/exports/index.test.ts | 7 +- packages/core/src/exports/index.ts | 47 +++-- packages/core/src/exports/query.ts | 9 + .../core/src/query/getWalletClient.test.ts | 35 ++++ packages/core/src/query/getWalletClient.ts | 64 +++++++ packages/react/src/exports/actions.test.ts | 3 + packages/react/src/exports/index.test.ts | 3 + packages/react/src/exports/index.ts | 18 ++ packages/react/src/hooks/useBalance.ts | 8 +- packages/react/src/hooks/useBlockNumber.ts | 4 +- packages/react/src/hooks/useClient.test-d.ts | 20 +++ packages/react/src/hooks/useClient.test.ts | 24 +++ packages/react/src/hooks/useClient.ts | 43 +++++ packages/react/src/hooks/useConnect.ts | 9 +- .../src/hooks/useConnectorClient.test.ts | 39 +++++ .../react/src/hooks/useConnectorClient.ts | 4 +- packages/react/src/hooks/useContractRead.ts | 4 +- packages/react/src/hooks/useContractReads.ts | 4 +- .../react/src/hooks/useContractSimulate.ts | 4 +- packages/react/src/hooks/useContractWrite.ts | 9 +- packages/react/src/hooks/useDisconnect.ts | 9 +- packages/react/src/hooks/useEnsAddress.ts | 4 +- packages/react/src/hooks/useEnsAvatar.ts | 4 +- packages/react/src/hooks/useEnsName.ts | 8 +- packages/react/src/hooks/useEnsResolver.ts | 4 +- .../react/src/hooks/useEstimateFeesPerGas.ts | 4 +- packages/react/src/hooks/useEstimateGas.ts | 4 +- .../react/src/hooks/usePublicClient.test-d.ts | 20 +++ .../react/src/hooks/usePublicClient.test.ts | 24 +++ packages/react/src/hooks/usePublicClient.ts | 45 +++++ packages/react/src/hooks/useReconnect.ts | 9 +- .../react/src/hooks/useSendTransaction.ts | 9 +- packages/react/src/hooks/useSignMessage.ts | 9 +- packages/react/src/hooks/useSignTypedData.ts | 9 +- packages/react/src/hooks/useSwitchAccount.ts | 9 +- packages/react/src/hooks/useSwitchChain.ts | 9 +- packages/react/src/hooks/useToken.ts | 4 +- packages/react/src/hooks/useTransaction.ts | 4 +- .../src/hooks/useWaitForTransactionReceipt.ts | 4 +- .../react/src/hooks/useWalletClient.test-d.ts | 12 ++ .../react/src/hooks/useWalletClient.test.ts | 163 ++++++++++++++++++ packages/react/src/hooks/useWalletClient.ts | 92 ++++++++++ packages/react/src/utils/query.ts | 32 ++-- 71 files changed, 1058 insertions(+), 136 deletions(-) create mode 100644 packages/core/src/actions/getClient.test-d.ts create mode 100644 packages/core/src/actions/getClient.test.ts create mode 100644 packages/core/src/actions/getClient.ts rename packages/core/src/actions/{viem => }/getPublicClient.test-d.ts (100%) rename packages/core/src/actions/{viem => }/getPublicClient.test.ts (100%) rename packages/core/src/actions/{viem => }/getPublicClient.ts (84%) rename packages/core/src/actions/{viem => }/getWalletClient.test-d.ts (100%) rename packages/core/src/actions/{viem => }/getWalletClient.test.ts (86%) rename packages/core/src/actions/{viem => }/getWalletClient.ts (89%) create mode 100644 packages/core/src/actions/watchClient.test-d.ts create mode 100644 packages/core/src/actions/watchClient.test.ts create mode 100644 packages/core/src/actions/watchClient.ts create mode 100644 packages/core/src/actions/watchPublicClient.test-d.ts create mode 100644 packages/core/src/actions/watchPublicClient.test.ts create mode 100644 packages/core/src/actions/watchPublicClient.ts create mode 100644 packages/core/src/query/getWalletClient.test.ts create mode 100644 packages/core/src/query/getWalletClient.ts create mode 100644 packages/react/src/hooks/useClient.test-d.ts create mode 100644 packages/react/src/hooks/useClient.test.ts create mode 100644 packages/react/src/hooks/useClient.ts create mode 100644 packages/react/src/hooks/usePublicClient.test-d.ts create mode 100644 packages/react/src/hooks/usePublicClient.test.ts create mode 100644 packages/react/src/hooks/usePublicClient.ts create mode 100644 packages/react/src/hooks/useWalletClient.test-d.ts create mode 100644 packages/react/src/hooks/useWalletClient.test.ts create mode 100644 packages/react/src/hooks/useWalletClient.ts diff --git a/docs/.vitepress/sidebar.ts b/docs/.vitepress/sidebar.ts index a3163fac4e..e69a0ff36b 100644 --- a/docs/.vitepress/sidebar.ts +++ b/docs/.vitepress/sidebar.ts @@ -99,6 +99,7 @@ export function getSidebar() { link: '/react/api/hooks/useBlockNumber', }, { text: 'useChainId', link: '/react/api/hooks/useChainId' }, + { text: 'useClient', link: '/react/api/hooks/useClient' }, { text: 'useConfig', link: '/react/api/hooks/useConfig' }, { text: 'useConnect', link: '/react/api/hooks/useConnect' }, { @@ -137,6 +138,10 @@ export function getSidebar() { text: 'useEnsResolver', link: '/react/api/hooks/useEnsResolver', }, + { + text: 'usePublicClient 🚧', + link: '/react/api/hooks/usePublicClient', + }, { text: 'useEstimateFeesPerGas', link: '/react/api/hooks/useEstimateFeesPerGas', @@ -280,6 +285,10 @@ export function getSidebar() { link: '/core/api/actions/getBlockNumber', }, { text: 'getChainId', link: '/core/api/actions/getChainId' }, + { + text: 'getClient 🚧', + link: '/core/api/actions/getClient', + }, { text: 'getConnections', link: '/core/api/actions/getConnections', @@ -298,11 +307,19 @@ export function getSidebar() { text: 'getEnsResolver', link: '/core/api/actions/getEnsResolver', }, + { + text: 'getPublicClient 🚧', + link: '/core/api/actions/getPublicClient', + }, { text: 'getToken', link: '/core/api/actions/getToken' }, { text: 'getTransaction', link: '/core/api/actions/getTransaction', }, + { + text: 'getWalletClient 🚧', + link: '/core/api/actions/getWalletClient', + }, { text: 'multicall 🚧', link: '/core/api/actions/multicall', @@ -356,10 +373,18 @@ export function getSidebar() { text: 'watchChainId', link: '/core/api/actions/watchChainId', }, + { + text: 'watchClient 🚧', + link: '/core/api/actions/watchClient', + }, { text: 'watchConnections', link: '/core/api/actions/watchConnections', }, + { + text: 'watchPublicClient 🚧', + link: '/core/api/actions/watchPublicClient', + }, { text: 'watchContractEvent 🚧', link: '/core/api/actions/watchContractEvent', diff --git a/docs/core/api/actions/watchAccount.md b/docs/core/api/actions/watchAccount.md index a3e723dda7..a71ca4d161 100644 --- a/docs/core/api/actions/watchAccount.md +++ b/docs/core/api/actions/watchAccount.md @@ -33,7 +33,7 @@ import { type WatchAccountParameters } from '@wagmi/core' ### onChange -`onChange: (data: GetAccountReturnType) => void` +`onChange(account: GetAccountReturnType, prevAccount: GetAccountReturnType): void` Callback function called when account changes. @@ -43,8 +43,8 @@ import { watchAccount } from '@wagmi/core' import { config } from './config' const unwatch = watchAccount(config, { - onChange(data) { // [!code focus:3] - console.log('Account changed!', data) + onChange(account) { // [!code focus:3] + console.log('Account changed!', account) }, }) unwatch() diff --git a/docs/core/api/actions/watchChainId.md b/docs/core/api/actions/watchChainId.md index fcf4915ae8..069e1052de 100644 --- a/docs/core/api/actions/watchChainId.md +++ b/docs/core/api/actions/watchChainId.md @@ -33,7 +33,7 @@ import { type WatchChainIdParameters } from '@wagmi/core' ### onChange -`onChange: (data: GetChainIdReturnType) => void` +`onChange(chainId: GetChainIdReturnType, prevChainId: GetChainIdReturnType): void` Callback function called when chain ID changes. @@ -43,8 +43,8 @@ import { watchChainId } from '@wagmi/core' import { config } from './config' const unwatch = watchChainId(config, { - onChange(data) { // [!code focus:3] - console.log('Chain ID changed!', data) + onChange(chainId) { // [!code focus:3] + console.log('Chain ID changed!', chainId) }, }) unwatch() diff --git a/docs/core/api/actions/watchConnections.md b/docs/core/api/actions/watchConnections.md index f08117c6fb..11cb0a617b 100644 --- a/docs/core/api/actions/watchConnections.md +++ b/docs/core/api/actions/watchConnections.md @@ -33,7 +33,7 @@ import { type WatchConnectionsParameters } from '@wagmi/core' ### onChange -`onChange: (data: GetConnectionsReturnType) => void` +`onChange(connections: GetConnectionsReturnType, prevConnections: GetConnectionsReturnType): void` Callback function called when connections changes. diff --git a/package.json b/package.json index f1affc5adb..9473691c44 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "simple-git-hooks": { "pre-commit": "pnpm format && pnpm lint:fix" }, - "packageManager": "pnpm@8.6.12", + "packageManager": "pnpm@8.7.5", "pnpm": { "overrides": { "@wagmi/connectors": "workspace:*", diff --git a/packages/core/src/actions/getClient.test-d.ts b/packages/core/src/actions/getClient.test-d.ts new file mode 100644 index 0000000000..252c2f2dd9 --- /dev/null +++ b/packages/core/src/actions/getClient.test-d.ts @@ -0,0 +1,19 @@ +import { chain, config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { getClient } from './getClient.js' + +test('default', () => { + const client = getClient(config) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: chainId', () => { + const client = getClient(config, { + chainId: chain.mainnet.id, + }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.chain).not.toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) diff --git a/packages/core/src/actions/getClient.test.ts b/packages/core/src/actions/getClient.test.ts new file mode 100644 index 0000000000..ec732268f5 --- /dev/null +++ b/packages/core/src/actions/getClient.test.ts @@ -0,0 +1,8 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getClient } from './getClient.js' + +test('default', async () => { + expect(getClient(config)).toBeDefined() +}) diff --git a/packages/core/src/actions/getClient.ts b/packages/core/src/actions/getClient.ts new file mode 100644 index 0000000000..0e8dc6d265 --- /dev/null +++ b/packages/core/src/actions/getClient.ts @@ -0,0 +1,30 @@ +import { type Client } from 'viem' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Evaluate } from '../types/utils.js' + +export type GetClientParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], +> = ChainIdParameter + +export type GetClientReturnType< + config extends Config = Config, + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], +> = Evaluate< + Client< + config['_internal']['transports'][chainId], + Extract + > +> + +export function getClient< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: GetClientParameters = {}, +): GetClientReturnType { + return config.getClient(parameters) as GetClientReturnType +} diff --git a/packages/core/src/actions/viem/getPublicClient.test-d.ts b/packages/core/src/actions/getPublicClient.test-d.ts similarity index 100% rename from packages/core/src/actions/viem/getPublicClient.test-d.ts rename to packages/core/src/actions/getPublicClient.test-d.ts diff --git a/packages/core/src/actions/viem/getPublicClient.test.ts b/packages/core/src/actions/getPublicClient.test.ts similarity index 100% rename from packages/core/src/actions/viem/getPublicClient.test.ts rename to packages/core/src/actions/getPublicClient.test.ts diff --git a/packages/core/src/actions/viem/getPublicClient.ts b/packages/core/src/actions/getPublicClient.ts similarity index 84% rename from packages/core/src/actions/viem/getPublicClient.ts rename to packages/core/src/actions/getPublicClient.ts index 93a51add06..0c9e983419 100644 --- a/packages/core/src/actions/viem/getPublicClient.ts +++ b/packages/core/src/actions/getPublicClient.ts @@ -1,7 +1,8 @@ import { type PublicClient, publicActions } from 'viem' -import type { Config } from '../../createConfig.js' -import type { ChainIdParameter } from '../../types/properties.js' -import type { Evaluate } from '../../types/utils.js' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Evaluate } from '../types/utils.js' export type GetPublicClientParameters< config extends Config = Config, diff --git a/packages/core/src/actions/viem/getWalletClient.test-d.ts b/packages/core/src/actions/getWalletClient.test-d.ts similarity index 100% rename from packages/core/src/actions/viem/getWalletClient.test-d.ts rename to packages/core/src/actions/getWalletClient.test-d.ts diff --git a/packages/core/src/actions/viem/getWalletClient.test.ts b/packages/core/src/actions/getWalletClient.test.ts similarity index 86% rename from packages/core/src/actions/viem/getWalletClient.test.ts rename to packages/core/src/actions/getWalletClient.test.ts index df5668a31a..e5d3c4e9e1 100644 --- a/packages/core/src/actions/viem/getWalletClient.test.ts +++ b/packages/core/src/actions/getWalletClient.test.ts @@ -1,8 +1,8 @@ import { config } from '@wagmi/test' import { expect, test } from 'vitest' -import { connect } from '../connect.js' -import { disconnect } from '../disconnect.js' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' import { getWalletClient } from './getWalletClient.js' const connector = config.connectors[0]! diff --git a/packages/core/src/actions/viem/getWalletClient.ts b/packages/core/src/actions/getWalletClient.ts similarity index 89% rename from packages/core/src/actions/viem/getWalletClient.ts rename to packages/core/src/actions/getWalletClient.ts index fb96acf69c..369f2bab0d 100644 --- a/packages/core/src/actions/viem/getWalletClient.ts +++ b/packages/core/src/actions/getWalletClient.ts @@ -1,11 +1,12 @@ import { type Account, type WalletClient, walletActions } from 'viem' -import type { Config } from '../../createConfig.js' -import type { Evaluate } from '../../types/utils.js' + +import type { Config } from '../createConfig.js' +import type { Evaluate } from '../types/utils.js' import { type GetConnectorClientError, type GetConnectorClientParameters, getConnectorClient, -} from '../getConnectorClient.js' +} from './getConnectorClient.js' export type GetWalletClientParameters< config extends Config = Config, diff --git a/packages/core/src/actions/watchAccount.test.ts b/packages/core/src/actions/watchAccount.test.ts index 800a1d8355..c803165888 100644 --- a/packages/core/src/actions/watchAccount.test.ts +++ b/packages/core/src/actions/watchAccount.test.ts @@ -9,8 +9,9 @@ import { watchAccount } from './watchAccount.js' test('default', async () => { const accounts: { address: Address | undefined; status: string }[] = [] const unwatch = watchAccount(config, { - onChange: (data) => - accounts.push({ address: data.address, status: data.status }), + onChange(data) { + accounts.push({ address: data.address, status: data.status }) + }, }) await connect(config, { connector: config.connectors[0]! }) diff --git a/packages/core/src/actions/watchAccount.ts b/packages/core/src/actions/watchAccount.ts index ba55a29561..221f44c288 100644 --- a/packages/core/src/actions/watchAccount.ts +++ b/packages/core/src/actions/watchAccount.ts @@ -3,7 +3,10 @@ import { deepEqual } from '../utils/deepEqual.js' import { type GetAccountReturnType, getAccount } from './getAccount.js' export type WatchAccountParameters = { - onChange(data: GetAccountReturnType, prevData: GetAccountReturnType): void + onChange( + account: GetAccountReturnType, + prevAccount: GetAccountReturnType, + ): void } export type WatchAccountReturnType = () => void diff --git a/packages/core/src/actions/watchChainId.test.ts b/packages/core/src/actions/watchChainId.test.ts index 2758690872..9e27ba7edd 100644 --- a/packages/core/src/actions/watchChainId.test.ts +++ b/packages/core/src/actions/watchChainId.test.ts @@ -6,7 +6,9 @@ import { watchChainId } from './watchChainId.js' test('default', async () => { const chainIds: number[] = [] const unwatch = watchChainId(config, { - onChange: (chainId) => chainIds.push(chainId), + onChange(chainId) { + chainIds.push(chainId) + }, }) config.setState((x) => ({ ...x, chainId: chain.mainnet2.id })) config.setState((x) => ({ ...x, chainId: chain.mainnet.id })) diff --git a/packages/core/src/actions/watchChainId.ts b/packages/core/src/actions/watchChainId.ts index 5bda2e56bc..5035ba3263 100644 --- a/packages/core/src/actions/watchChainId.ts +++ b/packages/core/src/actions/watchChainId.ts @@ -2,7 +2,10 @@ import type { Config } from '../createConfig.js' import type { GetChainIdReturnType } from './getChainId.js' export type WatchChainIdParameters = { - onChange(data: GetChainIdReturnType): void + onChange( + chainId: GetChainIdReturnType, + prevChainId: GetChainIdReturnType, + ): void } export type WatchChainIdReturnType = () => void diff --git a/packages/core/src/actions/watchClient.test-d.ts b/packages/core/src/actions/watchClient.test-d.ts new file mode 100644 index 0000000000..eb0b38f824 --- /dev/null +++ b/packages/core/src/actions/watchClient.test-d.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { watchClient } from './watchClient.js' + +test('default', () => { + watchClient(config, { + onChange(client) { + expectTypeOf(client.chain).toEqualTypeOf< + typeof config['chains'][number] + >() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() + }, + }) +}) diff --git a/packages/core/src/actions/watchClient.test.ts b/packages/core/src/actions/watchClient.test.ts new file mode 100644 index 0000000000..9cb5f447d2 --- /dev/null +++ b/packages/core/src/actions/watchClient.test.ts @@ -0,0 +1,23 @@ +import { config } from '@wagmi/test' +import type { Client } from 'viem' +import { expect, test } from 'vitest' + +import { switchChain } from './switchChain.js' +import { watchClient } from './watchClient.js' + +test('default', async () => { + const clients: Client[] = [] + const unwatch = watchClient(config, { + onChange(client) { + clients.push(client) + }, + }) + + switchChain(config, { chainId: 456 }) + switchChain(config, { chainId: 10 }) + switchChain(config, { chainId: 1 }) + + expect(clients.length).toBe(3) + + unwatch() +}) diff --git a/packages/core/src/actions/watchClient.ts b/packages/core/src/actions/watchClient.ts new file mode 100644 index 0000000000..3ee3bc84dd --- /dev/null +++ b/packages/core/src/actions/watchClient.ts @@ -0,0 +1,34 @@ +import type { Config } from '../createConfig.js' +import { type GetClientReturnType, getClient } from './getClient.js' + +export type WatchClientParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], +> = { + onChange( + publicClient: GetClientReturnType, + prevClient: GetClientReturnType, + ): void +} + +export type WatchClientReturnType = () => void + +/** https://alpha.wagmi.sh/core/api/actions/watchClient */ +export function watchClient< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: WatchClientParameters, +): WatchClientReturnType { + const { onChange } = parameters + return config.subscribe( + () => getClient(config) as GetClientReturnType, + onChange, + { + equalityFn(a, b) { + return a?.uid === b?.uid + }, + }, + ) +} diff --git a/packages/core/src/actions/watchConnections.test.ts b/packages/core/src/actions/watchConnections.test.ts index cdcf6b6dd1..9ddf4444ab 100644 --- a/packages/core/src/actions/watchConnections.test.ts +++ b/packages/core/src/actions/watchConnections.test.ts @@ -9,7 +9,9 @@ import { watchConnections } from './watchConnections.js' test('default', async () => { const connections: Connection[][] = [] const unwatch = watchConnections(config, { - onChange: (connection) => connections.push(connection), + onChange(connection) { + connections.push(connection) + }, }) const connector = config.connectors[0]! diff --git a/packages/core/src/actions/watchConnections.ts b/packages/core/src/actions/watchConnections.ts index 629e5dd84f..bf82c3077e 100644 --- a/packages/core/src/actions/watchConnections.ts +++ b/packages/core/src/actions/watchConnections.ts @@ -6,7 +6,10 @@ import { } from './getConnections.js' export type WatchConnectionsParameters = { - onChange(data: GetConnectionsReturnType): void + onChange( + connections: GetConnectionsReturnType, + prevConnections: GetConnectionsReturnType, + ): void } export type WatchConnectionsReturnType = () => void diff --git a/packages/core/src/actions/watchPublicClient.test-d.ts b/packages/core/src/actions/watchPublicClient.test-d.ts new file mode 100644 index 0000000000..81e6bf83f5 --- /dev/null +++ b/packages/core/src/actions/watchPublicClient.test-d.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { watchPublicClient } from './watchPublicClient.js' + +test('default', () => { + watchPublicClient(config, { + onChange(client) { + expectTypeOf(client.chain).toEqualTypeOf< + typeof config['chains'][number] + >() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() + }, + }) +}) diff --git a/packages/core/src/actions/watchPublicClient.test.ts b/packages/core/src/actions/watchPublicClient.test.ts new file mode 100644 index 0000000000..3d9594002c --- /dev/null +++ b/packages/core/src/actions/watchPublicClient.test.ts @@ -0,0 +1,23 @@ +import { config } from '@wagmi/test' +import type { Client } from 'viem' +import { expect, test } from 'vitest' + +import { switchChain } from './switchChain.js' +import { watchPublicClient } from './watchPublicClient.js' + +test('default', async () => { + const clients: Client[] = [] + const unwatch = watchPublicClient(config, { + onChange(client) { + clients.push(client) + }, + }) + + switchChain(config, { chainId: 456 }) + switchChain(config, { chainId: 10 }) + switchChain(config, { chainId: 1 }) + + expect(clients.length).toBe(3) + + unwatch() +}) diff --git a/packages/core/src/actions/watchPublicClient.ts b/packages/core/src/actions/watchPublicClient.ts new file mode 100644 index 0000000000..778aa3b76f --- /dev/null +++ b/packages/core/src/actions/watchPublicClient.ts @@ -0,0 +1,37 @@ +import type { Config } from '../createConfig.js' +import { + type GetPublicClientReturnType, + getPublicClient, +} from './getPublicClient.js' + +export type WatchPublicClientParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], +> = { + onChange( + publicClient: GetPublicClientReturnType, + prevPublicClient: GetPublicClientReturnType, + ): void +} + +export type WatchPublicClientReturnType = () => void + +/** https://alpha.wagmi.sh/core/api/actions/watchPublicClient */ +export function watchPublicClient< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: WatchPublicClientParameters, +): WatchPublicClientReturnType { + const { onChange } = parameters + return config.subscribe( + () => getPublicClient(config) as GetPublicClientReturnType, + onChange, + { + equalityFn(a, b) { + return a?.uid === b?.uid + }, + }, + ) +} diff --git a/packages/core/src/exports/actions.test.ts b/packages/core/src/exports/actions.test.ts index 0cfc0c7e0a..0342d7efba 100644 --- a/packages/core/src/exports/actions.test.ts +++ b/packages/core/src/exports/actions.test.ts @@ -13,14 +13,17 @@ test('exports', () => { "getBalance", "getBlockNumber", "getChainId", + "getClient", "getConnections", "getConnectorClient", "getEnsAddress", "getEnsAvatar", "getEnsName", "getEnsResolver", + "getPublicClient", "getToken", "getTransaction", + "getWalletClient", "multicall", "readContract", "readContracts", @@ -34,13 +37,13 @@ test('exports', () => { "watchAccount", "watchBlockNumber", "watchChainId", + "watchClient", "watchConnections", "watchContractEvent", "watchPendingTransactions", + "watchPublicClient", "waitForTransactionReceipt", "writeContract", - "getPublicClient", - "getWalletClient", ] `) }) diff --git a/packages/core/src/exports/actions.ts b/packages/core/src/exports/actions.ts index 8b13c605ee..707d199cfc 100644 --- a/packages/core/src/exports/actions.ts +++ b/packages/core/src/exports/actions.ts @@ -53,6 +53,12 @@ export { getChainId, } from '../actions/getChainId.js' +export { + type GetClientParameters, + type GetClientReturnType, + getClient, +} from '../actions/getClient.js' + export { type GetConnectionsReturnType, getConnections, @@ -93,6 +99,12 @@ export { getEnsResolver, } from '../actions/getEnsResolver.js' +export { + type GetPublicClientParameters, + type GetPublicClientReturnType, + getPublicClient, +} from '../actions/getPublicClient.js' + export { type GetTokenError, type GetTokenParameters, @@ -107,6 +119,13 @@ export { getTransaction, } from '../actions/getTransaction.js' +export { + type GetWalletClientError, + type GetWalletClientParameters, + type GetWalletClientReturnType, + getWalletClient, +} from '../actions/getWalletClient.js' + export { type MulticallParameters, type MulticallReturnType, @@ -194,6 +213,12 @@ export { watchChainId, } from '../actions/watchChainId.js' +export { + type WatchClientParameters, + type WatchClientReturnType, + watchClient, +} from '../actions/watchClient.js' + export { type WatchConnectionsParameters, type WatchConnectionsReturnType, @@ -212,6 +237,12 @@ export { watchPendingTransactions, } from '../actions/watchPendingTransactions.js' +export { + type WatchPublicClientParameters, + type WatchPublicClientReturnType, + watchPublicClient, +} from '../actions/watchPublicClient.js' + export { type WaitForTransactionReceiptError, type WaitForTransactionReceiptParameters, @@ -225,19 +256,3 @@ export { type WriteContractReturnType, writeContract, } from '../actions/writeContract.js' - -//////////////////////////////////////////////////////////////////////////////// -// Actions (Viem) - -export { - type GetPublicClientParameters, - type GetPublicClientReturnType, - getPublicClient, -} from '../actions/viem/getPublicClient.js' - -export { - type GetWalletClientError, - type GetWalletClientParameters, - type GetWalletClientReturnType, - getWalletClient, -} from '../actions/viem/getWalletClient.js' diff --git a/packages/core/src/exports/index.test.ts b/packages/core/src/exports/index.test.ts index 977668c20a..e2d6afe4b1 100644 --- a/packages/core/src/exports/index.test.ts +++ b/packages/core/src/exports/index.test.ts @@ -17,14 +17,17 @@ test('exports', () => { "getBalance", "getBlockNumber", "getChainId", + "getClient", "getConnections", "getConnectorClient", "getEnsAddress", "getEnsAvatar", "getEnsName", "getEnsResolver", + "getPublicClient", "getToken", "getTransaction", + "getWalletClient", "multicall", "readContract", "readContracts", @@ -38,13 +41,13 @@ test('exports', () => { "watchAccount", "watchBlockNumber", "watchChainId", + "watchClient", "watchConnections", "watchContractEvent", "watchPendingTransactions", + "watchPublicClient", "waitForTransactionReceipt", "writeContract", - "getPublicClient", - "getWalletClient", "createConfig", "createConnector", "createStorage", diff --git a/packages/core/src/exports/index.ts b/packages/core/src/exports/index.ts index b9deab3f66..423dc32300 100644 --- a/packages/core/src/exports/index.ts +++ b/packages/core/src/exports/index.ts @@ -64,6 +64,12 @@ export { getChainId, } from '../actions/getChainId.js' +export { + type GetClientParameters, + type GetClientReturnType, + getClient, +} from '../actions/getClient.js' + export { type GetConnectionsReturnType, getConnections, @@ -104,6 +110,12 @@ export { getEnsResolver, } from '../actions/getEnsResolver.js' +export { + type GetPublicClientParameters, + type GetPublicClientReturnType, + getPublicClient, +} from '../actions/getPublicClient.js' + export { type GetTokenError, type GetTokenParameters, @@ -118,6 +130,13 @@ export { getTransaction, } from '../actions/getTransaction.js' +export { + type GetWalletClientError, + type GetWalletClientParameters, + type GetWalletClientReturnType, + getWalletClient, +} from '../actions/getWalletClient.js' + export { type MulticallParameters, type MulticallReturnType, @@ -205,6 +224,12 @@ export { watchChainId, } from '../actions/watchChainId.js' +export { + type WatchClientParameters, + type WatchClientReturnType, + watchClient, +} from '../actions/watchClient.js' + export { type WatchConnectionsParameters, type WatchConnectionsReturnType, @@ -223,6 +248,12 @@ export { watchPendingTransactions, } from '../actions/watchPendingTransactions.js' +export { + type WatchPublicClientParameters, + type WatchPublicClientReturnType, + watchPublicClient, +} from '../actions/watchPublicClient.js' + export { type WaitForTransactionReceiptError, type WaitForTransactionReceiptParameters, @@ -237,22 +268,6 @@ export { writeContract, } from '../actions/writeContract.js' -//////////////////////////////////////////////////////////////////////////////// -// Actions (Viem) - -export { - type GetPublicClientParameters, - type GetPublicClientReturnType, - getPublicClient, -} from '../actions/viem/getPublicClient.js' - -export { - type GetWalletClientError, - type GetWalletClientParameters, - type GetWalletClientReturnType, - getWalletClient, -} from '../actions/viem/getWalletClient.js' - //////////////////////////////////////////////////////////////////////////////// // createConfig diff --git a/packages/core/src/exports/query.ts b/packages/core/src/exports/query.ts index 558078c9b6..01ad650a54 100644 --- a/packages/core/src/exports/query.ts +++ b/packages/core/src/exports/query.ts @@ -116,6 +116,15 @@ export { getTransactionQueryOptions, } from '../query/getTransaction.js' +export { + type GetWalletClientData, + type GetWalletClientOptions, + type GetWalletClientQueryFnData, + type GetWalletClientQueryKey, + getWalletClientQueryKey, + getWalletClientQueryOptions, +} from '../query/getWalletClient.js' + export { type ReadContractData, type ReadContractOptions, diff --git a/packages/core/src/query/getWalletClient.test.ts b/packages/core/src/query/getWalletClient.test.ts new file mode 100644 index 0000000000..6ca506a150 --- /dev/null +++ b/packages/core/src/query/getWalletClient.test.ts @@ -0,0 +1,35 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getWalletClientQueryOptions } from './getWalletClient.js' + +test('default', () => { + expect(getWalletClientQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "connectorClient", + { + "connectorUid": undefined, + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getWalletClientQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "connectorClient", + { + "chainId": 1, + "connectorUid": undefined, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getWalletClient.ts b/packages/core/src/query/getWalletClient.ts new file mode 100644 index 0000000000..9ee52e0a45 --- /dev/null +++ b/packages/core/src/query/getWalletClient.ts @@ -0,0 +1,64 @@ +import { type QueryOptions } from '@tanstack/query-core' + +import { + type GetWalletClientError, + type GetWalletClientParameters, + type GetWalletClientReturnType, + getWalletClient, +} from '../actions/getWalletClient.js' +import type { Config } from '../createConfig.js' +import type { Evaluate, ExactPartial } from '../types/utils.js' +import type { ScopeKeyParameter } from './types.js' +import { filterQueryOptions } from './utils.js' + +export type GetWalletClientOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +> = Evaluate< + ExactPartial> & ScopeKeyParameter +> + +export function getWalletClientQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +>(config: config, options: GetWalletClientOptions = {}) { + return { + async queryFn({ queryKey }) { + const { connector } = options + const { connectorUid: _, scopeKey: _s, ...parameters } = queryKey[1] + return getWalletClient(config, { ...parameters, connector }) + }, + queryKey: getWalletClientQueryKey(options), + } as const satisfies QueryOptions< + GetWalletClientQueryFnData, + GetWalletClientError, + GetWalletClientData, + GetWalletClientQueryKey + > +} + +export type GetWalletClientQueryFnData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetWalletClientReturnType + +export type GetWalletClientData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetWalletClientQueryFnData + +export function getWalletClientQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +>(options: GetWalletClientOptions = {}) { + const { connector, ...parameters } = options + return [ + 'walletClient', + { ...filterQueryOptions(parameters), connectorUid: connector?.uid }, + ] as const +} + +export type GetWalletClientQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +> = ReturnType> diff --git a/packages/react/src/exports/actions.test.ts b/packages/react/src/exports/actions.test.ts index e1fea3134c..86a47d50d2 100644 --- a/packages/react/src/exports/actions.test.ts +++ b/packages/react/src/exports/actions.test.ts @@ -13,6 +13,7 @@ test('exports', () => { "getBalance": [Function], "getBlockNumber": [Function], "getChainId": [Function], + "getClient": [Function], "getConnections": [Function], "getConnectorClient": [Function], "getEnsAddress": [Function], @@ -37,9 +38,11 @@ test('exports', () => { "watchAccount": [Function], "watchBlockNumber": [Function], "watchChainId": [Function], + "watchClient": [Function], "watchConnections": [Function], "watchContractEvent": [Function], "watchPendingTransactions": [Function], + "watchPublicClient": [Function], "writeContract": [Function], } `) diff --git a/packages/react/src/exports/index.test.ts b/packages/react/src/exports/index.test.ts index f417981cd5..a1bf3b08be 100644 --- a/packages/react/src/exports/index.test.ts +++ b/packages/react/src/exports/index.test.ts @@ -20,6 +20,7 @@ test('exports', () => { "useBalance", "useBlockNumber", "useChainId", + "useClient", "useConfig", "useConnect", "useConnections", @@ -35,6 +36,7 @@ test('exports', () => { "useEstimateFeesPerGas", "useEstimateGas", "useContractSimulate", + "usePublicClient", "useReconnect", "useSendTransaction", "useSignMessage", @@ -43,6 +45,7 @@ test('exports', () => { "useSwitchChain", "useToken", "useTransaction", + "useWalletClient", "useWaitForTransactionReceipt", "useWatchBlockNumber", "useWatchContractEvent", diff --git a/packages/react/src/exports/index.ts b/packages/react/src/exports/index.ts index 6eeea87c47..ca56c05baf 100644 --- a/packages/react/src/exports/index.ts +++ b/packages/react/src/exports/index.ts @@ -61,6 +61,12 @@ export { useChainId, } from '../hooks/useChainId.js' +export { + type UseClientParameters, + type UseClientReturnType, + useClient, +} from '../hooks/useClient.js' + export { type UseConfigParameters, type UseConfigReturnType, @@ -151,6 +157,12 @@ export { useContractSimulate, } from '../hooks/useContractSimulate.js' +export { + type UsePublicClientParameters, + type UsePublicClientReturnType, + usePublicClient, +} from '../hooks/usePublicClient.js' + export { type UseReconnectParameters, type UseReconnectReturnType, @@ -199,6 +211,12 @@ export { useTransaction, } from '../hooks/useTransaction.js' +export { + type UseWalletClientParameters, + type UseWalletClientReturnType, + useWalletClient, +} from '../hooks/useWalletClient.js' + export { type UseWaitForTransactionReceiptParameters, type UseWaitForTransactionReceiptReturnType, diff --git a/packages/react/src/hooks/useBalance.ts b/packages/react/src/hooks/useBalance.ts index 9b479f6a63..a225e479ed 100644 --- a/packages/react/src/hooks/useBalance.ts +++ b/packages/react/src/hooks/useBalance.ts @@ -17,7 +17,7 @@ import type { GetBalanceQueryFnData } from '@wagmi/core/query' import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -37,10 +37,8 @@ export type UseBalanceParameters< ConfigParameter > -export type UseBalanceReturnType = UseQueryResult< - selectData, - GetBalanceError -> +export type UseBalanceReturnType = + UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useBalance */ export function useBalance< diff --git a/packages/react/src/hooks/useBlockNumber.ts b/packages/react/src/hooks/useBlockNumber.ts index d55860bd05..e62bf44afb 100644 --- a/packages/react/src/hooks/useBlockNumber.ts +++ b/packages/react/src/hooks/useBlockNumber.ts @@ -18,7 +18,7 @@ import { import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -52,7 +52,7 @@ export type UseBlockNumberParameters< > export type UseBlockNumberReturnType = - UseQueryResult + UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useBlockNumber */ export function useBlockNumber< diff --git a/packages/react/src/hooks/useClient.test-d.ts b/packages/react/src/hooks/useClient.test-d.ts new file mode 100644 index 0000000000..3682da09e8 --- /dev/null +++ b/packages/react/src/hooks/useClient.test-d.ts @@ -0,0 +1,20 @@ +import { chain, config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { useClient } from './useClient.js' + +test('default', () => { + const client = useClient({ config }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: chainId', () => { + const client = useClient({ + config, + chainId: chain.mainnet.id, + }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.chain).not.toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) diff --git a/packages/react/src/hooks/useClient.test.ts b/packages/react/src/hooks/useClient.test.ts new file mode 100644 index 0000000000..53d4aa7ce3 --- /dev/null +++ b/packages/react/src/hooks/useClient.test.ts @@ -0,0 +1,24 @@ +import { switchChain } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useClient } from './useClient.js' + +test('default', async () => { + const { result, rerender } = renderHook(() => useClient()) + + expect(result.current.chain.id).toEqual(1) + + await switchChain(config, { chainId: 456 }) + rerender() + + expect(result.current.chain.id).toEqual(456) +}) + +test('parameters: config', () => { + const { result } = renderHook(() => useClient({ config }), { + wrapper: ({ children }) => children, + }) + expect(result.current).toBeDefined() +}) diff --git a/packages/react/src/hooks/useClient.ts b/packages/react/src/hooks/useClient.ts new file mode 100644 index 0000000000..af3e11ed78 --- /dev/null +++ b/packages/react/src/hooks/useClient.ts @@ -0,0 +1,43 @@ +'use client' + +import { + type Config, + type GetClientParameters, + type GetClientReturnType, + type ResolvedRegister, + getClient, + watchClient, +} from '@wagmi/core' +import type { Evaluate } from '@wagmi/core/internal' +import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector.js' + +import type { ConfigParameter } from '../types/properties.js' +import { useConfig } from './useConfig.js' + +export type UseClientParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], +> = Evaluate & ConfigParameter> + +export type UseClientReturnType< + config extends Config = Config, + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], +> = GetClientReturnType + +/** https://alpha.wagmi.sh/react/api/hooks/useClient */ +export function useClient< + config extends Config = ResolvedRegister['config'], + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], +>( + parameters: UseClientParameters = {}, +): UseClientReturnType { + const config = useConfig(parameters) + + return useSyncExternalStoreWithSelector( + (onChange) => watchClient(config, { onChange }), + () => getClient(config, parameters), + () => getClient(config, parameters), + (x) => x, + (a, b) => a.uid === b.uid, + ) as any +} diff --git a/packages/react/src/hooks/useConnect.ts b/packages/react/src/hooks/useConnect.ts index 265be18c83..3e262dba3d 100644 --- a/packages/react/src/hooks/useConnect.ts +++ b/packages/react/src/hooks/useConnect.ts @@ -16,14 +16,17 @@ import { } from '@wagmi/core/query' import type { ConfigParameter } from '../types/properties.js' -import type { UseMutationOptions, UseMutationResult } from '../utils/query.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' import { useConfig } from './useConfig.js' export type UseConnectParameters< config extends Config = Config, context = unknown, > = Evaluate< - UseMutationOptions< + UseMutationParameters< ConnectData, ConnectError, ConnectVariables, @@ -36,7 +39,7 @@ export type UseConnectReturnType< config extends Config = Config, context = unknown, > = Evaluate< - UseMutationResult< + UseMutationReturnType< ConnectData, ConnectError, ConnectVariables, diff --git a/packages/react/src/hooks/useConnectorClient.test.ts b/packages/react/src/hooks/useConnectorClient.test.ts index 3640e45028..bf91511ae5 100644 --- a/packages/react/src/hooks/useConnectorClient.test.ts +++ b/packages/react/src/hooks/useConnectorClient.test.ts @@ -6,6 +6,7 @@ import { expect, test } from 'vitest' import { useConnect } from './useConnect.js' import { useConnectorClient } from './useConnectorClient.js' import { useDisconnect } from './useDisconnect.js' +import { useSwitchChain } from './useSwitchChain.js' const connector = config.connectors[0]! @@ -119,3 +120,41 @@ test('behavior: connect and disconnect', async () => { expect(result.current.useConnectorClient.data).not.toBeDefined(), ) }) + +test('behavior: switch chains', async () => { + const { result } = renderHook(() => ({ + useConnect: useConnect(), + useConnectorClient: useConnectorClient(), + useDisconnect: useDisconnect(), + useSwitchChain: useSwitchChain(), + })) + + expect(result.current.useConnectorClient.data).not.toBeDefined() + + result.current.useConnect.connect({ + connector: result.current.useConnect.connectors[0]!, + }) + + await waitFor(() => + expect(result.current.useConnectorClient.data).toBeDefined(), + ) + + result.current.useSwitchChain.switchChain({ chainId: 456 }) + await waitFor(() => { + expect(result.current.useSwitchChain.isSuccess).toBeTruthy() + result.current.useSwitchChain.reset() + }) + expect(result.current.useConnectorClient.data?.chain.id).toEqual(456) + + result.current.useSwitchChain.switchChain({ chainId: 1 }) + await waitFor(() => + expect(result.current.useSwitchChain.isSuccess).toBeTruthy(), + ) + expect(result.current.useConnectorClient.data?.chain.id).toEqual(1) + + result.current.useDisconnect.disconnect() + + await waitFor(() => + expect(result.current.useConnectorClient.data).not.toBeDefined(), + ) +}) diff --git a/packages/react/src/hooks/useConnectorClient.ts b/packages/react/src/hooks/useConnectorClient.ts index 182f99c690..b504c4db53 100644 --- a/packages/react/src/hooks/useConnectorClient.ts +++ b/packages/react/src/hooks/useConnectorClient.ts @@ -19,7 +19,7 @@ import { useEffect } from 'react' import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useAccount } from './useAccount.js' @@ -45,7 +45,7 @@ export type UseConnectorClientReturnType< config extends Config = Config, chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], selectData = GetConnectorClientData, -> = UseQueryResult +> = UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useConnectorClient */ export function useConnectorClient< diff --git a/packages/react/src/hooks/useContractRead.ts b/packages/react/src/hooks/useContractRead.ts index 4d80c021dd..b82e642ac6 100644 --- a/packages/react/src/hooks/useContractRead.ts +++ b/packages/react/src/hooks/useContractRead.ts @@ -22,7 +22,7 @@ import { import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, structuralSharing, useQuery, } from '../utils/query.js' @@ -65,7 +65,7 @@ export type UseContractReadReturnType< functionName > = ContractFunctionArgs, selectData = ReadContractData, -> = UseQueryResult +> = UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useContractRead */ export function useContractRead< diff --git a/packages/react/src/hooks/useContractReads.ts b/packages/react/src/hooks/useContractReads.ts index 06f69729bc..9202eaadcf 100644 --- a/packages/react/src/hooks/useContractReads.ts +++ b/packages/react/src/hooks/useContractReads.ts @@ -19,7 +19,7 @@ import type { ContractFunctionParameters } from 'viem' import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, structuralSharing, useQuery, } from '../utils/query.js' @@ -46,7 +46,7 @@ export type UseContractReadsReturnType< contracts extends readonly unknown[] = readonly ContractFunctionParameters[], allowFailure extends boolean = true, selectData = ReadContractsData, -> = UseQueryResult +> = UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useContractReads */ export function useContractReads< diff --git a/packages/react/src/hooks/useContractSimulate.ts b/packages/react/src/hooks/useContractSimulate.ts index 6ab307eac0..6d50479d5f 100644 --- a/packages/react/src/hooks/useContractSimulate.ts +++ b/packages/react/src/hooks/useContractSimulate.ts @@ -17,7 +17,7 @@ import type { Abi, ContractFunctionArgs, ContractFunctionName } from 'viem' import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -61,7 +61,7 @@ export type UseContractSimulateReturnType< config extends Config = Config, chainId extends config['chains'][number]['id'] | undefined = undefined, selectData = SimulateContractData, -> = UseQueryResult +> = UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useContractSimulate */ export function useContractSimulate< diff --git a/packages/react/src/hooks/useContractWrite.ts b/packages/react/src/hooks/useContractWrite.ts index 05449b5056..29d7fd9a25 100644 --- a/packages/react/src/hooks/useContractWrite.ts +++ b/packages/react/src/hooks/useContractWrite.ts @@ -12,13 +12,16 @@ import { import type { Abi } from 'viem' import type { ConfigParameter } from '../types/properties.js' -import type { UseMutationOptions, UseMutationResult } from '../utils/query.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' import { useConfig } from './useConfig.js' export type UseContractWriteParameters< config extends Config = Config, context = unknown, -> = UseMutationOptions< +> = UseMutationParameters< WriteContractData, WriteContractError, WriteContractVariables< @@ -35,7 +38,7 @@ export type UseContractWriteParameters< export type UseContractWriteReturnType< config extends Config = Config, context = unknown, -> = UseMutationResult< +> = UseMutationReturnType< WriteContractData, WriteContractError, WriteContractVariables< diff --git a/packages/react/src/hooks/useDisconnect.ts b/packages/react/src/hooks/useDisconnect.ts index 81749266b0..04ce4bef6a 100644 --- a/packages/react/src/hooks/useDisconnect.ts +++ b/packages/react/src/hooks/useDisconnect.ts @@ -12,12 +12,15 @@ import { } from '@wagmi/core/query' import type { ConfigParameter } from '../types/properties.js' -import type { UseMutationOptions, UseMutationResult } from '../utils/query.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' import { useConfig } from './useConfig.js' import { useConnections } from './useConnections.js' export type UseDisconnectParameters = Evaluate< - UseMutationOptions< + UseMutationParameters< DisconnectData, DisconnectError, DisconnectVariables, @@ -27,7 +30,7 @@ export type UseDisconnectParameters = Evaluate< > export type UseDisconnectReturnType = Evaluate< - UseMutationResult< + UseMutationReturnType< DisconnectData, DisconnectError, DisconnectVariables, diff --git a/packages/react/src/hooks/useEnsAddress.ts b/packages/react/src/hooks/useEnsAddress.ts index 7f4b6bfc64..103e1be7db 100644 --- a/packages/react/src/hooks/useEnsAddress.ts +++ b/packages/react/src/hooks/useEnsAddress.ts @@ -13,7 +13,7 @@ import { import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -34,7 +34,7 @@ export type UseEnsAddressParameters< > export type UseEnsAddressReturnType = - UseQueryResult + UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useEnsAddress */ export function useEnsAddress< diff --git a/packages/react/src/hooks/useEnsAvatar.ts b/packages/react/src/hooks/useEnsAvatar.ts index 17bea1dfd6..3f6dbadd05 100644 --- a/packages/react/src/hooks/useEnsAvatar.ts +++ b/packages/react/src/hooks/useEnsAvatar.ts @@ -13,7 +13,7 @@ import { import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -34,7 +34,7 @@ export type UseEnsAvatarParameters< > export type UseEnsAvatarReturnType = - UseQueryResult + UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useEnsAvatar */ export function useEnsAvatar< diff --git a/packages/react/src/hooks/useEnsName.ts b/packages/react/src/hooks/useEnsName.ts index 0413b907bb..fa6738b5bf 100644 --- a/packages/react/src/hooks/useEnsName.ts +++ b/packages/react/src/hooks/useEnsName.ts @@ -13,7 +13,7 @@ import { import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -33,10 +33,8 @@ export type UseEnsNameParameters< ConfigParameter > -export type UseEnsNameReturnType = UseQueryResult< - selectData, - GetEnsNameError -> +export type UseEnsNameReturnType = + UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useEnsName */ export function useEnsName< diff --git a/packages/react/src/hooks/useEnsResolver.ts b/packages/react/src/hooks/useEnsResolver.ts index 4fc1971ce3..35891a7caa 100644 --- a/packages/react/src/hooks/useEnsResolver.ts +++ b/packages/react/src/hooks/useEnsResolver.ts @@ -13,7 +13,7 @@ import { import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -34,7 +34,7 @@ export type UseEnsResolverParameters< > export type UseEnsResolverReturnType = - UseQueryResult + UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useEnsResolver */ export function useEnsResolver< diff --git a/packages/react/src/hooks/useEstimateFeesPerGas.ts b/packages/react/src/hooks/useEstimateFeesPerGas.ts index f94ec1419f..5009ae270e 100644 --- a/packages/react/src/hooks/useEstimateFeesPerGas.ts +++ b/packages/react/src/hooks/useEstimateFeesPerGas.ts @@ -18,7 +18,7 @@ import type { FeeValuesType } from 'viem' import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -42,7 +42,7 @@ export type UseEstimateFeesPerGasParameters< export type UseEstimateFeesPerGasReturnType< type extends FeeValuesType = FeeValuesType, selectData = EstimateFeesPerGasData, -> = UseQueryResult +> = UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useEstimateFeesPerGas */ export function useEstimateFeesPerGas< diff --git a/packages/react/src/hooks/useEstimateGas.ts b/packages/react/src/hooks/useEstimateGas.ts index f7d001afb3..ddf205727a 100644 --- a/packages/react/src/hooks/useEstimateGas.ts +++ b/packages/react/src/hooks/useEstimateGas.ts @@ -12,7 +12,7 @@ import { import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -33,7 +33,7 @@ export type UseEstimateGasParameters< ConfigParameter export type UseEstimateGasReturnType = - UseQueryResult + UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useEstimateGas */ export function useEstimateGas< diff --git a/packages/react/src/hooks/usePublicClient.test-d.ts b/packages/react/src/hooks/usePublicClient.test-d.ts new file mode 100644 index 0000000000..a2cbce7e62 --- /dev/null +++ b/packages/react/src/hooks/usePublicClient.test-d.ts @@ -0,0 +1,20 @@ +import { chain, config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { usePublicClient } from './usePublicClient.js' + +test('default', () => { + const client = usePublicClient({ config }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: chainId', () => { + const client = usePublicClient({ + config, + chainId: chain.mainnet.id, + }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.chain).not.toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) diff --git a/packages/react/src/hooks/usePublicClient.test.ts b/packages/react/src/hooks/usePublicClient.test.ts new file mode 100644 index 0000000000..f8b77ae834 --- /dev/null +++ b/packages/react/src/hooks/usePublicClient.test.ts @@ -0,0 +1,24 @@ +import { switchChain } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { usePublicClient } from './usePublicClient.js' + +test('default', async () => { + const { result, rerender } = renderHook(() => usePublicClient()) + + expect(result.current.chain.id).toEqual(1) + + await switchChain(config, { chainId: 456 }) + rerender() + + expect(result.current.chain.id).toEqual(456) +}) + +test('parameters: config', () => { + const { result } = renderHook(() => usePublicClient({ config }), { + wrapper: ({ children }) => children, + }) + expect(result.current).toBeDefined() +}) diff --git a/packages/react/src/hooks/usePublicClient.ts b/packages/react/src/hooks/usePublicClient.ts new file mode 100644 index 0000000000..f234017496 --- /dev/null +++ b/packages/react/src/hooks/usePublicClient.ts @@ -0,0 +1,45 @@ +'use client' + +import { + type Config, + type GetPublicClientParameters, + type GetPublicClientReturnType, + type ResolvedRegister, + getPublicClient, + watchPublicClient, +} from '@wagmi/core' +import type { Evaluate } from '@wagmi/core/internal' +import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector.js' + +import type { ConfigParameter } from '../types/properties.js' +import { useConfig } from './useConfig.js' + +export type UsePublicClientParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], +> = Evaluate< + GetPublicClientParameters & ConfigParameter +> + +export type UsePublicClientReturnType< + config extends Config = Config, + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], +> = GetPublicClientReturnType + +/** https://alpha.wagmi.sh/react/api/hooks/usePublicClient */ +export function usePublicClient< + config extends Config = ResolvedRegister['config'], + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], +>( + parameters: UsePublicClientParameters = {}, +): UsePublicClientReturnType { + const config = useConfig(parameters) + + return useSyncExternalStoreWithSelector( + (onChange) => watchPublicClient(config, { onChange }), + () => getPublicClient(config, parameters), + () => getPublicClient(config, parameters), + (x) => x, + (a, b) => a.uid === b.uid, + ) as any +} diff --git a/packages/react/src/hooks/useReconnect.ts b/packages/react/src/hooks/useReconnect.ts index 7f53fc5e0a..dfe0bf647c 100644 --- a/packages/react/src/hooks/useReconnect.ts +++ b/packages/react/src/hooks/useReconnect.ts @@ -12,11 +12,14 @@ import { } from '@wagmi/core/query' import type { ConfigParameter } from '../types/properties.js' -import type { UseMutationOptions, UseMutationResult } from '../utils/query.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' import { useConfig } from './useConfig.js' export type UseReconnectParameters = Evaluate< - UseMutationOptions< + UseMutationParameters< ReconnectData, ReconnectError, ReconnectVariables, @@ -26,7 +29,7 @@ export type UseReconnectParameters = Evaluate< > export type UseReconnectReturnType = Evaluate< - UseMutationResult< + UseMutationReturnType< ReconnectData, ReconnectError, ReconnectVariables, diff --git a/packages/react/src/hooks/useSendTransaction.ts b/packages/react/src/hooks/useSendTransaction.ts index e9c301f13d..53a0d68df7 100644 --- a/packages/react/src/hooks/useSendTransaction.ts +++ b/packages/react/src/hooks/useSendTransaction.ts @@ -16,14 +16,17 @@ import { } from '@wagmi/core/query' import type { ConfigParameter } from '../types/properties.js' -import type { UseMutationOptions, UseMutationResult } from '../utils/query.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' import { useConfig } from './useConfig.js' export type UseSendTransactionParameters< config extends Config = Config, context = unknown, > = Evaluate< - UseMutationOptions< + UseMutationParameters< SendTransactionData, SendTransactionError, SendTransactionVariables, @@ -36,7 +39,7 @@ export type UseSendTransactionReturnType< config extends Config = Config, context = unknown, > = Evaluate< - UseMutationResult< + UseMutationReturnType< SendTransactionData, SendTransactionError, SendTransactionVariables, diff --git a/packages/react/src/hooks/useSignMessage.ts b/packages/react/src/hooks/useSignMessage.ts index f055f2c006..a7fa0769f8 100644 --- a/packages/react/src/hooks/useSignMessage.ts +++ b/packages/react/src/hooks/useSignMessage.ts @@ -12,11 +12,14 @@ import { } from '@wagmi/core/query' import type { ConfigParameter } from '../types/properties.js' -import type { UseMutationOptions, UseMutationResult } from '../utils/query.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' import { useConfig } from './useConfig.js' export type UseSignMessageParameters = Evaluate< - UseMutationOptions< + UseMutationParameters< SignMessageData, SignMessageError, SignMessageVariables, @@ -26,7 +29,7 @@ export type UseSignMessageParameters = Evaluate< > export type UseSignMessageReturnType = Evaluate< - UseMutationResult< + UseMutationReturnType< SignMessageData, SignMessageError, SignMessageVariables, diff --git a/packages/react/src/hooks/useSignTypedData.ts b/packages/react/src/hooks/useSignTypedData.ts index a618dcdcf9..0b5723cde9 100644 --- a/packages/react/src/hooks/useSignTypedData.ts +++ b/packages/react/src/hooks/useSignTypedData.ts @@ -12,11 +12,14 @@ import { } from '@wagmi/core/query' import type { ConfigParameter } from '../types/properties.js' -import type { UseMutationOptions, UseMutationResult } from '../utils/query.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' import { useConfig } from './useConfig.js' export type UseSignTypedDataParameters = Evaluate< - UseMutationOptions< + UseMutationParameters< SignTypedDataData, SignTypedDataError, SignTypedDataVariables, @@ -26,7 +29,7 @@ export type UseSignTypedDataParameters = Evaluate< > export type UseSignTypedDataReturnType = Evaluate< - UseMutationResult< + UseMutationReturnType< SignTypedDataData, SignTypedDataError, SignTypedDataVariables, diff --git a/packages/react/src/hooks/useSwitchAccount.ts b/packages/react/src/hooks/useSwitchAccount.ts index 2856139599..cf8d8c8ccd 100644 --- a/packages/react/src/hooks/useSwitchAccount.ts +++ b/packages/react/src/hooks/useSwitchAccount.ts @@ -17,7 +17,10 @@ import { } from '@wagmi/core/query' import type { ConfigParameter } from '../types/properties.js' -import type { UseMutationOptions, UseMutationResult } from '../utils/query.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' import { useConfig } from './useConfig.js' import { useConnections } from './useConnections.js' @@ -25,7 +28,7 @@ export type UseSwitchAccountParameters< config extends Config = Config, context = unknown, > = Evaluate< - UseMutationOptions< + UseMutationParameters< SwitchAccountData, SwitchAccountError, SwitchAccountVariables, @@ -38,7 +41,7 @@ export type UseSwitchAccountReturnType< config extends Config = Config, context = unknown, > = Evaluate< - UseMutationResult< + UseMutationReturnType< SwitchAccountData, SwitchAccountError, SwitchAccountVariables, diff --git a/packages/react/src/hooks/useSwitchChain.ts b/packages/react/src/hooks/useSwitchChain.ts index bd0941ab06..69ff94e4ac 100644 --- a/packages/react/src/hooks/useSwitchChain.ts +++ b/packages/react/src/hooks/useSwitchChain.ts @@ -12,14 +12,17 @@ import { } from '@wagmi/core/query' import type { ConfigParameter } from '../types/properties.js' -import type { UseMutationOptions, UseMutationResult } from '../utils/query.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' import { useConfig } from './useConfig.js' export type UseSwitchChainParameters< config extends Config = Config, context = unknown, > = Evaluate< - UseMutationOptions< + UseMutationParameters< SwitchChainData, SwitchChainError, SwitchChainVariables, @@ -32,7 +35,7 @@ export type UseSwitchChainReturnType< config extends Config = Config, context = unknown, > = Evaluate< - UseMutationResult< + UseMutationReturnType< SwitchChainData, SwitchChainError, SwitchChainVariables, diff --git a/packages/react/src/hooks/useToken.ts b/packages/react/src/hooks/useToken.ts index 0df7e8333a..30bc7c231c 100644 --- a/packages/react/src/hooks/useToken.ts +++ b/packages/react/src/hooks/useToken.ts @@ -13,7 +13,7 @@ import { import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -33,7 +33,7 @@ export type UseTokenParameters< ConfigParameter > -export type UseTokenReturnType = UseQueryResult< +export type UseTokenReturnType = UseQueryReturnType< selectData, GetTokenError > diff --git a/packages/react/src/hooks/useTransaction.ts b/packages/react/src/hooks/useTransaction.ts index ad1cc26668..3bf577094a 100644 --- a/packages/react/src/hooks/useTransaction.ts +++ b/packages/react/src/hooks/useTransaction.ts @@ -13,7 +13,7 @@ import { import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -38,7 +38,7 @@ export type UseTransactionReturnType< config extends Config = Config, chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], selectData = GetTransactionData, -> = UseQueryResult +> = UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useTransaction */ export function useTransaction< diff --git a/packages/react/src/hooks/useWaitForTransactionReceipt.ts b/packages/react/src/hooks/useWaitForTransactionReceipt.ts index 4080d863d0..8f3a51ef32 100644 --- a/packages/react/src/hooks/useWaitForTransactionReceipt.ts +++ b/packages/react/src/hooks/useWaitForTransactionReceipt.ts @@ -17,7 +17,7 @@ import { import type { ConfigParameter } from '../types/properties.js' import { type UseQueryParameters, - type UseQueryResult, + type UseQueryReturnType, useQuery, } from '../utils/query.js' import { useChainId } from './useChainId.js' @@ -42,7 +42,7 @@ export type UseWaitForTransactionReceiptReturnType< config extends Config = Config, chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], selectData = WaitForTransactionReceiptData, -> = UseQueryResult +> = UseQueryReturnType /** https://alpha.wagmi.sh/react/api/hooks/useWaitForTransactionReceipt */ export function useWaitForTransactionReceipt< diff --git a/packages/react/src/hooks/useWalletClient.test-d.ts b/packages/react/src/hooks/useWalletClient.test-d.ts new file mode 100644 index 0000000000..c2ea48bcb1 --- /dev/null +++ b/packages/react/src/hooks/useWalletClient.test-d.ts @@ -0,0 +1,12 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { useWalletClient } from './useWalletClient.js' + +test('parameters: config', async () => { + const client = useWalletClient({ config }) + expectTypeOf(client.data?.chain?.id!).toEqualTypeOf<1 | 456 | 10>() + + const client2 = useWalletClient({ config, chainId: 1 }) + expectTypeOf(client2.data?.chain?.id!).toEqualTypeOf<1>() +}) diff --git a/packages/react/src/hooks/useWalletClient.test.ts b/packages/react/src/hooks/useWalletClient.test.ts new file mode 100644 index 0000000000..a7b54b1afc --- /dev/null +++ b/packages/react/src/hooks/useWalletClient.test.ts @@ -0,0 +1,163 @@ +import { connect, disconnect } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useConnect } from './useConnect.js' +import { useConnectorClient } from './useConnectorClient.js' +import { useDisconnect } from './useDisconnect.js' +import { useSwitchChain } from './useSwitchChain.js' + +// Almost identical implementation to `useConnectorClient` (except for return type) +// Should update both in tandem + +const connector = config.connectors[0]! + +test('default', async () => { + const { result } = renderHook(() => useConnectorClient()) + + await waitFor(() => expect(result.current.isPending).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": false, + "queryKey": [ + "connectorClient", + { + "chainId": 1, + "connectorUid": undefined, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) +}) + +test('behavior: connected on mount', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useConnectorClient()) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, queryKey: _, ...rest } = result.current + expect(data).toMatchObject( + expect.objectContaining({ + account: expect.any(Object), + chain: expect.any(Object), + }), + ) + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": true, + "refetch": [Function], + "status": "success", + } + `) + + await disconnect(config, { connector }) +}) + +test('behavior: connect and disconnect', async () => { + const { result } = renderHook(() => ({ + useConnect: useConnect(), + useConnectorClient: useConnectorClient(), + useDisconnect: useDisconnect(), + })) + + expect(result.current.useConnectorClient.data).not.toBeDefined() + + result.current.useConnect.connect({ + connector: result.current.useConnect.connectors[0]!, + }) + + await waitFor(() => + expect(result.current.useConnectorClient.data).toBeDefined(), + ) + + result.current.useDisconnect.disconnect() + + await waitFor(() => + expect(result.current.useConnectorClient.data).not.toBeDefined(), + ) +}) + +test('behavior: switch chains', async () => { + const { result } = renderHook(() => ({ + useConnect: useConnect(), + useConnectorClient: useConnectorClient(), + useDisconnect: useDisconnect(), + useSwitchChain: useSwitchChain(), + })) + + expect(result.current.useConnectorClient.data).not.toBeDefined() + + result.current.useConnect.connect({ + connector: result.current.useConnect.connectors[0]!, + }) + + await waitFor(() => + expect(result.current.useConnectorClient.data).toBeDefined(), + ) + + result.current.useSwitchChain.switchChain({ chainId: 456 }) + await waitFor(() => { + expect(result.current.useSwitchChain.isSuccess).toBeTruthy() + result.current.useSwitchChain.reset() + }) + expect(result.current.useConnectorClient.data?.chain.id).toEqual(456) + + result.current.useSwitchChain.switchChain({ chainId: 1 }) + await waitFor(() => + expect(result.current.useSwitchChain.isSuccess).toBeTruthy(), + ) + expect(result.current.useConnectorClient.data?.chain.id).toEqual(1) + + result.current.useDisconnect.disconnect() + + await waitFor(() => + expect(result.current.useConnectorClient.data).not.toBeDefined(), + ) +}) diff --git a/packages/react/src/hooks/useWalletClient.ts b/packages/react/src/hooks/useWalletClient.ts new file mode 100644 index 0000000000..3c566f34bd --- /dev/null +++ b/packages/react/src/hooks/useWalletClient.ts @@ -0,0 +1,92 @@ +'use client' + +// Almost identical implementation to `useConnectorClient` (except for return type) +// Should update both in tandem + +import { useQueryClient } from '@tanstack/react-query' +import type { + Config, + GetWalletClientError, + ResolvedRegister, +} from '@wagmi/core' +import { type Evaluate } from '@wagmi/core/internal' +import { + type GetWalletClientData, + type GetWalletClientOptions, + type GetWalletClientQueryFnData, + type GetWalletClientQueryKey, + getWalletClientQueryOptions, +} from '@wagmi/core/query' +import { useEffect } from 'react' + +import type { ConfigParameter } from '../types/properties.js' +import { + type UseQueryParameters, + type UseQueryReturnType, + useQuery, +} from '../utils/query.js' +import { useAccount } from './useAccount.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseWalletClientParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetWalletClientData, +> = Evaluate< + GetWalletClientOptions & + UseQueryParameters< + GetWalletClientQueryFnData, + GetWalletClientError, + selectData, + GetWalletClientQueryKey + > & + ConfigParameter +> + +export type UseWalletClientReturnType< + config extends Config = Config, + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetWalletClientData, +> = UseQueryReturnType + +/** https://alpha.wagmi.sh/react/api/hooks/useWalletClient */ +export function useWalletClient< + config extends Config = ResolvedRegister['config'], + chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetWalletClientData, +>( + parameters: UseWalletClientParameters = {}, +): UseWalletClientReturnType { + const { gcTime = 0, staleTime = Infinity, ...query } = parameters + + const config = useConfig(parameters) + const queryClient = useQueryClient() + const { address, connector, status } = useAccount() + const chainId = useChainId() + + const { queryKey, ...options } = getWalletClientQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + connector: parameters.connector ?? connector, + }) + const enabled = Boolean( + status !== 'disconnected' && (parameters.enabled ?? true), + ) + + // biome-ignore lint/nursery/useExhaustiveDependencies: `queryKey` not required + useEffect(() => { + // invalidate when address changes + if (address) queryClient.invalidateQueries({ queryKey }) + else queryClient.removeQueries({ queryKey }) // remove when account is disconnected + }, [address, queryClient]) + + return useQuery({ + ...options, + ...query, + queryKey, + enabled, + gcTime, + staleTime, + }) +} diff --git a/packages/react/src/utils/query.ts b/packages/react/src/utils/query.ts index e2a0d09b6e..b528eb534a 100644 --- a/packages/react/src/utils/query.ts +++ b/packages/react/src/utils/query.ts @@ -1,10 +1,10 @@ import { type DefaultError, type QueryKey, - type UseMutationOptions as rq_UseMutationOptions, - type UseMutationResult as rq_UseMutationResult, - type UseQueryOptions as rq_UseQueryOptions, - type UseQueryResult as rq_UseQueryResult, + type UseMutationOptions, + type UseMutationResult, + type UseQueryOptions, + type UseQueryResult, replaceEqualDeep, useQuery as tanstack_useQuery, } from '@tanstack/react-query' @@ -16,30 +16,32 @@ import { } from '@wagmi/core/internal' import { hashFn } from '@wagmi/core/query' -export type UseMutationOptions< +export type UseMutationParameters< data = unknown, error = Error, variables = void, context = unknown, > = Evaluate< Omit< - rq_UseMutationOptions, context>, + UseMutationOptions, context>, 'mutationFn' | 'mutationKey' | 'throwOnError' > > -export type UseMutationResult< +export type UseMutationReturnType< data = unknown, error = Error, variables = void, context = unknown, > = Evaluate< Omit< - rq_UseMutationResult, + UseMutationResult, 'mutate' | 'mutateAsync' > > +//////////////////////////////////////////////////////////////////////////////// + export type UseQueryParameters< queryFnData = unknown, error = DefaultError, @@ -48,7 +50,7 @@ export type UseQueryParameters< > = Evaluate< ExactPartial< Omit< - rq_UseQueryOptions, + UseQueryOptions, | 'initialData' | 'queryFn' | 'queryHash' @@ -58,7 +60,7 @@ export type UseQueryParameters< > > & { // Fix `initialData` type - initialData?: rq_UseQueryOptions< + initialData?: UseQueryOptions< queryFnData, error, data, @@ -67,8 +69,8 @@ export type UseQueryParameters< } > -export type UseQueryResult = Evaluate< - rq_UseQueryResult & { +export type UseQueryReturnType = Evaluate< + UseQueryResult & { queryKey: QueryKey } > @@ -80,15 +82,17 @@ export function useQuery( parameters: UseQueryParameters & { queryKey: QueryKey }, -): UseQueryResult { +): UseQueryReturnType { const result = tanstack_useQuery({ ...(parameters as any), queryKeyHashFn: hashFn, // for bigint support - }) as UseQueryResult + }) as UseQueryReturnType result.queryKey = parameters.queryKey return result } +//////////////////////////////////////////////////////////////////////////////// + export function structuralSharing( oldData: data | undefined, newData: data,