Skip to content

Commit

Permalink
feat: useWatchAsset hook (#4128)
Browse files Browse the repository at this point in the history
* feat: `useWatchAsset` hook

* test: add tests

* docs: add docs

* nit: use `getConnectorClient`

* fix: types

* test: fix

* test: fix

* test: fix

* chore: tweaks

* chore: changeset

---------

Co-authored-by: Tom Meagher <tom@meagher.co>
  • Loading branch information
dalechyn and tmm committed Jul 16, 2024
1 parent b7a32aa commit 5581a81
Show file tree
Hide file tree
Showing 27 changed files with 577 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/modern-dots-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wagmi/core": minor
---

Added `watchAsset` action.
5 changes: 5 additions & 0 deletions .changeset/stupid-beds-mate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wagmi": minor
---

Added `useWatchAsset` hook.
23 changes: 23 additions & 0 deletions packages/core/src/actions/watchAsset.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { config } from '@wagmi/test'
import { expect, test } from 'vitest'

import { connect } from './connect.js'
import { disconnect } from './disconnect.js'
import { watchAsset } from './watchAsset.js'

const connector = config.connectors[0]!

test('default', async () => {
await connect(config, { connector })
await expect(
watchAsset(config, {
type: 'ERC20',
options: {
address: '0x0000000000000000000000000000000000000000',
symbol: 'NULL',
decimals: 18,
},
}),
).resolves.toMatchInlineSnapshot('true')
await disconnect(config, { connector })
})
44 changes: 44 additions & 0 deletions packages/core/src/actions/watchAsset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
type WatchAssetErrorType as viem_WatchAssetErrorType,
type WatchAssetParameters as viem_WatchAssetParameters,
type WatchAssetReturnType as viem_WatchAssetReturnType,
watchAsset as viem_watchAsset,
} from 'viem/actions'

import type { Config } from '../createConfig.js'
import type { BaseErrorType, ErrorType } from '../errors/base.js'
import type { ConnectorParameter } from '../types/properties.js'
import type { Compute } from '../types/utils.js'
import { getAction } from '../utils/getAction.js'
import {
type GetConnectorClientErrorType,
getConnectorClient,
} from './getConnectorClient.js'

export type WatchAssetParameters = Compute<
viem_WatchAssetParameters & ConnectorParameter
>

export type WatchAssetReturnType = viem_WatchAssetReturnType

export type WatchAssetErrorType =
// getConnectorClient()
| GetConnectorClientErrorType
// base
| BaseErrorType
| ErrorType
// viem
| viem_WatchAssetErrorType

/** https://wagmi.sh/core/api/actions/watchAsset */
export async function watchAsset(
config: Config,
parameters: WatchAssetParameters,
): Promise<WatchAssetReturnType> {
const { connector, ...rest } = parameters

const client = await getConnectorClient(config, { connector })

const action = getAction(client, viem_watchAsset, 'watchAsset')
return action(rest as viem_WatchAssetParameters)
}
12 changes: 12 additions & 0 deletions packages/core/src/connectors/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type MockParameters = {
signMessageError?: boolean | Error | undefined
signTypedDataError?: boolean | Error | undefined
reconnect?: boolean | undefined
watchAssetError?: boolean | Error | undefined
}
| undefined
}
Expand Down Expand Up @@ -158,6 +159,17 @@ export function mock(parameters: MockParameters) {
return
}

if (method === 'wallet_watchAsset') {
if (features.watchAssetError) {
if (typeof features.watchAssetError === 'boolean')
throw new UserRejectedRequestError(
new Error('Failed to switch chain.'),
)
throw features.watchAssetError
}
return connected
}

if (method === 'wallet_getCapabilities')
return {
'0x2105': {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/exports/actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ test('exports', () => {
"verifyMessage",
"verifyTypedData",
"watchAccount",
"watchAsset",
"watchBlocks",
"watchBlockNumber",
"watchChainId",
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/exports/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,12 @@ export {
watchAccount,
} from '../actions/watchAccount.js'

export {
type WatchAssetParameters,
type WatchAssetReturnType,
watchAsset,
} from '../actions/watchAsset.js'

export {
type WatchBlocksParameters,
type WatchBlocksReturnType,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/exports/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ test('exports', () => {
"verifyMessage",
"verifyTypedData",
"watchAccount",
"watchAsset",
"watchBlocks",
"watchBlockNumber",
"watchChainId",
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/exports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,13 @@ export {
watchAccount,
} from '../actions/watchAccount.js'

export {
type WatchAssetParameters,
type WatchAssetErrorType,
type WatchAssetReturnType,
watchAsset,
} from '../actions/watchAsset.js'

export {
type WatchBlocksParameters,
type WatchBlocksReturnType,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/exports/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ test('exports', () => {
"verifyTypedDataQueryOptions",
"waitForTransactionReceiptQueryKey",
"waitForTransactionReceiptQueryOptions",
"watchAssetMutationOptions",
"writeContractMutationOptions",
"hashFn",
"structuralSharing",
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/exports/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,14 @@ export {
waitForTransactionReceiptQueryOptions,
} from '../query/waitForTransactionReceipt.js'

export {
type WatchAssetData,
type WatchAssetVariables,
type WatchAssetMutate,
type WatchAssetMutateAsync,
watchAssetMutationOptions,
} from '../query/watchAsset.js'

export {
type WriteContractData,
type WriteContractVariables,
Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/query/watchAsset.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { config } from '@wagmi/test'
import { expect, test } from 'vitest'

import { watchAssetMutationOptions } from './watchAsset.js'

test('default', () => {
expect(watchAssetMutationOptions(config)).toMatchInlineSnapshot(`
{
"mutationFn": [Function],
"mutationKey": [
"watchAsset",
],
}
`)
})
42 changes: 42 additions & 0 deletions packages/core/src/query/watchAsset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { MutationOptions } from '@tanstack/query-core'

import {
type WatchAssetErrorType,
type WatchAssetParameters,
type WatchAssetReturnType,
watchAsset,
} from '../actions/watchAsset.js'
import type { Config } from '../createConfig.js'
import type { Compute } from '../types/utils.js'
import type { Mutate, MutateAsync } from './types.js'

export function watchAssetMutationOptions(config: Config) {
return {
mutationFn(variables) {
return watchAsset(config, variables)
},
mutationKey: ['watchAsset'],
} as const satisfies MutationOptions<
WatchAssetData,
WatchAssetErrorType,
WatchAssetVariables
>
}

export type WatchAssetData = WatchAssetReturnType

export type WatchAssetVariables = Compute<WatchAssetParameters>

export type WatchAssetMutate<context = unknown> = Mutate<
WatchAssetData,
WatchAssetErrorType,
WatchAssetVariables,
context
>

export type WatchAssetMutateAsync<context = unknown> = MutateAsync<
WatchAssetData,
WatchAssetErrorType,
WatchAssetVariables,
context
>
1 change: 1 addition & 0 deletions packages/react/src/exports/actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ test('exports', () => {
"verifyMessage",
"verifyTypedData",
"watchAccount",
"watchAsset",
"watchBlocks",
"watchBlockNumber",
"watchChainId",
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/exports/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ test('exports', () => {
"useVerifyTypedData",
"useWalletClient",
"useWaitForTransactionReceipt",
"useWatchAsset",
"useWatchBlocks",
"useWatchBlockNumber",
"useWatchContractEvent",
Expand Down
6 changes: 6 additions & 0 deletions packages/react/src/exports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,12 @@ export {
useWaitForTransactionReceipt,
} from '../hooks/useWaitForTransactionReceipt.js'

export {
type UseWatchAssetParameters,
type UseWatchAssetReturnType,
useWatchAsset,
} from '../hooks/useWatchAsset.js'

export {
type UseWatchBlocksParameters,
type UseWatchBlocksReturnType,
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/exports/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ test('exports', () => {
"verifyTypedDataQueryOptions",
"waitForTransactionReceiptQueryKey",
"waitForTransactionReceiptQueryOptions",
"watchAssetMutationOptions",
"writeContractMutationOptions",
"hashFn",
"structuralSharing",
Expand Down
66 changes: 66 additions & 0 deletions packages/react/src/hooks/useWatchAsset.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { WatchAssetErrorType } from '@wagmi/core'
import type { WatchAssetVariables } from '@wagmi/core/query'
import { expectTypeOf, test } from 'vitest'

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

const tokenInfo = {
address: '0x0000000000000000000000000000000000000000',
symbol: 'NULL',
decimals: 18,
}
const contextValue = { foo: 'bar' } as const

test('context', () => {
const { context, data, error, watchAsset, variables } = useWatchAsset({
mutation: {
onMutate(variables) {
expectTypeOf(variables).toEqualTypeOf<WatchAssetVariables>()
return contextValue
},
onError(error, variables, context) {
expectTypeOf(variables).toEqualTypeOf<WatchAssetVariables>()
expectTypeOf(error).toEqualTypeOf<WatchAssetErrorType>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue | undefined>()
},
onSuccess(data, variables, context) {
expectTypeOf(variables).toEqualTypeOf<WatchAssetVariables>()
expectTypeOf(data).toEqualTypeOf<boolean>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue>()
},
onSettled(data, error, variables, context) {
expectTypeOf(data).toEqualTypeOf<boolean | undefined>()
expectTypeOf(error).toEqualTypeOf<WatchAssetErrorType | null>()
expectTypeOf(variables).toEqualTypeOf<WatchAssetVariables>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue | undefined>()
},
},
})

expectTypeOf(data).toEqualTypeOf<boolean | undefined>()
expectTypeOf(error).toEqualTypeOf<WatchAssetErrorType | null>()
expectTypeOf(variables).toEqualTypeOf<WatchAssetVariables | undefined>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue | undefined>()

watchAsset(
{ type: 'ERC20', options: tokenInfo },
{
onError(error, variables, context) {
expectTypeOf(variables).toEqualTypeOf<WatchAssetVariables>()
expectTypeOf(error).toEqualTypeOf<WatchAssetErrorType>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue | undefined>()
},
onSuccess(data, variables, context) {
expectTypeOf(variables).toEqualTypeOf<WatchAssetVariables>()
expectTypeOf(data).toEqualTypeOf<boolean>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue>()
},
onSettled(data, error, variables, context) {
expectTypeOf(data).toEqualTypeOf<boolean | undefined>()
expectTypeOf(error).toEqualTypeOf<WatchAssetErrorType | null>()
expectTypeOf(variables).toEqualTypeOf<WatchAssetVariables>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue | undefined>()
},
},
)
})
27 changes: 27 additions & 0 deletions packages/react/src/hooks/useWatchAsset.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { connect, disconnect } from '@wagmi/core'
import { config } from '@wagmi/test'
import { renderHook, waitFor } from '@wagmi/test/react'
import { expect, test } from 'vitest'

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

const connector = config.connectors[0]!

const tokenInfo = {
address: '0x0000000000000000000000000000000000000000',
symbol: 'NULL',
decimals: 18,
}

test('default', async () => {
await connect(config, { connector })

const { result } = renderHook(() => useWatchAsset())

result.current.watchAsset({ type: 'ERC20', options: tokenInfo })
await waitFor(() => expect(result.current.isSuccess).toBeTruthy())

expect(result.current.data).toEqual(true)

await disconnect(config, { connector })
})
Loading

0 comments on commit 5581a81

Please sign in to comment.