wagmi-development

仓库: wevm/wagmi
安装量: 50
排名: #14691

安装

npx skills add https://github.com/wevm/wagmi --skill wagmi-development

Wagmi Development

Full-stack patterns for adding Wagmi features. This skill covers Viem-based actions only (not Wagmi config actions).

Layer Overview Core Action (packages/core/src/actions/) - Base functionality wrapping Viem Query Options (packages/core/src/query/) - TanStack Query integration Framework Bindings - React (packages/react/src/hooks/), Vue (packages/vue/src/composables/) 1. Core Action Structure import { type MyActionErrorType as viem_MyActionErrorType, type MyActionParameters as viem_MyActionParameters, type MyActionReturnType as viem_MyActionReturnType, myAction as viem_myAction, } from 'viem/actions'

import type { Config } from '../createConfig.js' import type { ChainIdParameter, ConnectorParameter } from '../types/properties.js' import type { Compute } from '../types/utils.js' import { getAction } from '../utils/getAction.js'

export type MyActionParameters = Compute< ChainIdParameter & viem_MyActionParameters

export type MyActionReturnType = viem_MyActionReturnType

export type MyActionErrorType = viem_MyActionErrorType

/* https://wagmi.sh/core/api/actions/myAction / export async function myAction( config: config, parameters: MyActionParameters, ): Promise { const { chainId, ...rest } = parameters const client = config.getClient({ chainId }) const action = getAction(client, viem_myAction, 'myAction') return action(rest) }

Key Rules Viem imports: Prefix with viem_ (e.g., viem_getBalance) Client access: Read-only: config.getClient({ chainId }) Wallet: await getConnectorClient(config, { chainId, connector, account }) Mixed: Use getConnectorClient for account, getClient for action (see estimateGas.ts) Parameters: Add ChainIdParameter always. Add ConnectorParameter for wallet actions. Type params: Mirror Viem's type params for inference. Use const modifier for literal inference (abi, args). Spread: Omit wagmi-specific props (chainId, connector) when calling Viem action. Testing

Runtime tests (action.test.ts):

import { abi, address, config } from '@wagmi/test' import { expect, test } from 'vitest' import { myAction } from './myAction.js'

test('default', async () => { await expect(myAction(config, { / required params / })).resolves.toMatchInlineSnapshot(...) })

test('parameters: chainId', async () => { / test chainId param / })

test('behavior: error case', async () => { / test error handling / })

Type tests (action.test-d.ts) - only if action has type inference:

import { config } from '@wagmi/test' import { expectTypeOf, test } from 'vitest' import { myAction } from './myAction.js'

test('default', async () => { const result = await myAction(config, { / params / }) expectTypeOf(result).toEqualTypeOf() })

Type benchmarks (action.bench-d.ts) - only if action has type inference:

import { attest } from '@ark/attest' import { test } from 'vitest' import type { MyActionParameters } from './myAction.js'

test('default', () => { type Result = MyActionParameters const res = {} as Result attest.instantiations([12345, 'instantiations']) attest(res.args).type.toString.snap(readonly [account: \0x\${string}`]`) })

Wallet action tests: Connect before, disconnect after:

test('default', async () => { await connect(config, { connector }) await expect(myAction(config, { / params / })).resolves.toMatchInlineSnapshot(...) await disconnect(config, { connector }) })

  1. Query Options

Query (read-only) or Mutation (wallet) options for TanStack Query.

Query Structure import { type MyActionErrorType, type MyActionParameters, type MyActionReturnType, myAction, } from '../actions/myAction.js' import type { Config } from '../createConfig.js' import type { ScopeKeyParameter } from '../types/properties.js' import type { QueryOptions, QueryParameter } from '../types/query.js' import type { Compute, ExactPartial } from '../types/utils.js' import { filterQueryOptions, structuralSharing } from './utils.js'

export type MyActionOptions< config extends Config, selectData = MyActionData,

= Compute> & ScopeKeyParameter> & QueryParameter>

export function myActionQueryOptions< config extends Config, selectData = MyActionData,

( config: config, options: MyActionOptions = {}, ): MyActionQueryOptions { return { ...options.query, enabled: Boolean(options.requiredParam && (options.query?.enabled ?? true)), queryFn: async (context) => { const [, { scopeKey: _, ...parameters }] = context.queryKey if (!parameters.requiredParam) throw new Error('requiredParam is required') const result = await myAction(config, { ...(parameters as MyActionParameters), requiredParam: parameters.requiredParam, }) return result ?? null }, queryKey: myActionQueryKey(options), structuralSharing, // include when returning complex objects/arrays } }

export type MyActionQueryFnData = Compute export type MyActionData = MyActionQueryFnData

export function myActionQueryKey( options: Compute> & ScopeKeyParameter> = {}, ) { return ['myAction', filterQueryOptions(options)] as const }

export type MyActionQueryKey = ReturnType>

export type MyActionQueryOptions< config extends Config, selectData = MyActionData,

= QueryOptions>

Mutation Structure import type { MutationOptions, MutationParameter } from '../types/query.js'

export type MyActionOptions = MutationParameter< MyActionData, MyActionErrorType, MyActionVariables, context

export function myActionMutationOptions( config: config, options: MyActionOptions = {}, ): MyActionMutationOptions { return { ...options.mutation, mutationFn: async (variables) => { return myAction(config, variables) }, mutationKey: ['myAction'], } }

export type MyActionMutationOptions = MutationOptions< MyActionData, MyActionErrorType, MyActionVariables

Key Rules ExactPartial vs UnionExactPartial: Use ExactPartial for simple types, UnionExactPartial for complex unions (contract actions) enabled: Based on required params being truthy structuralSharing: Include when action returns objects/arrays filterQueryOptions: Filters common non-serializable props. Skip props like onReplaced manually in query key. Query key: Always ['actionName', filterQueryOptions(options)] Testing import { config } from '@wagmi/test' import { expect, test } from 'vitest' import { myActionQueryOptions } from './myAction.js'

test('default', () => { expect(myActionQueryOptions(config, {})).toMatchInlineSnapshot({ "enabled": false, "queryFn": [Function], "queryKey": ["myAction", {}], }) })

test('enabled', () => { expect(myActionQueryOptions(config, { requiredParam: 'value' }).enabled).toBe(true) })

test('queryFn: calls query fn', async () => { const options = myActionQueryOptions(config, { requiredParam: 'value' }) const result = await options.queryFn({ queryKey: options.queryKey } as any) expect(result).toMatchInlineSnapshot(...) })

  1. Framework Bindings React Query Hook 'use client' import type { Config, MyActionErrorType, ResolvedRegister } from '@wagmi/core' import type { Compute } from '@wagmi/core/internal' import { type MyActionData, type MyActionOptions, myActionQueryOptions, } from '@wagmi/core/query' import type { ConfigParameter } from '../types/properties.js' import { type UseQueryReturnType, useQuery } from '../utils/query.js' import { useChainId } from './useChainId.js' import { useConfig } from './useConfig.js'

export type UseMyActionParameters< config extends Config = Config, selectData = MyActionData,

= Compute & ConfigParameter\>

export type UseMyActionReturnType = UseQueryReturnType

/* https://wagmi.sh/react/api/hooks/useMyAction / export function useMyAction< config extends Config = ResolvedRegister['config'], selectData = MyActionData,

( parameters: UseMyActionParameters = {}, ): UseMyActionReturnType { const config = useConfig(parameters) const chainId = useChainId({ config }) const options = myActionQueryOptions(config, { ...parameters, chainId: parameters.chainId ?? chainId, query: parameters.query, }) return useQuery(options) }

React Mutation Hook 'use client' import { useMutation } from '@tanstack/react-query' import type { Config, ResolvedRegister, MyActionErrorType } from '@wagmi/core' import { type MyActionData, type MyActionMutate, type MyActionMutateAsync, type MyActionOptions, type MyActionVariables, myActionMutationOptions, } from '@wagmi/core/query' import type { ConfigParameter } from '../types/properties.js' import type { UseMutationReturnType } from '../utils/query.js' import { useConfig } from './useConfig.js'

export type UseMyActionParameters = MyActionOptions & ConfigParameter

export type UseMyActionReturnType = UseMutationReturnType< MyActionData, MyActionErrorType, MyActionVariables, context, MyActionMutate, MyActionMutateAsync

/* https://wagmi.sh/react/api/hooks/useMyAction / export function useMyAction< config extends Config = ResolvedRegister['config'], context = unknown,

( parameters: UseMyActionParameters = {}, ): UseMyActionReturnType { const config = useConfig(parameters) const options = myActionMutationOptions(config, parameters) const mutation = useMutation(options) type Return = UseMyActionReturnType return { ...mutation, mutate: mutation.mutate as Return['mutate'], mutateAsync: mutation.mutateAsync as Return['mutateAsync'], } }

Vue Composable (Query) import type { Config, MyActionErrorType, ResolvedRegister } from '@wagmi/core' import type { Compute } from '@wagmi/core/internal' import { type MyActionData, type MyActionOptions, myActionQueryOptions, } from '@wagmi/core/query' import { computed } from 'vue' import type { ConfigParameter } from '../types/properties.js' import type { DeepMaybeRef } from '../types/ref.js' import { deepUnref } from '../utils/cloneDeep.js' import { type UseQueryReturnType, useQuery } from '../utils/query.js' import { useChainId } from './useChainId.js' import { useConfig } from './useConfig.js'

export type UseMyActionParameters< config extends Config = Config, selectData = MyActionData,

= Compute & ConfigParameter\>>

export type UseMyActionReturnType = UseQueryReturnType

/* https://wagmi.sh/vue/api/composables/useMyAction / export function useMyAction< config extends Config = ResolvedRegister['config'], selectData = MyActionData,

( parameters: UseMyActionParameters = {}, ): UseMyActionReturnType { const params = computed(() => deepUnref(parameters)) const config = useConfig(params) const chainId = useChainId({ config }) const options = computed(() => myActionQueryOptions(config as any, { ...params.value, chainId: params.value.chainId ?? chainId.value, query: params.value.query, }), ) return useQuery(options as any) as any }

Framework Rules Rule React Vue Top directive 'use client' None Parameters wrapper Compute<...> Compute<DeepMaybeRef<...>> Reactivity Direct computed() + deepUnref() Doc URL wagmi.sh/react/api/hooks/ wagmi.sh/vue/api/composables/

Shared rules:

ResolvedRegister['config']: Use in function signature only, not type defs No enabled/structuralSharing in hooks: Handled by queryOptions Testing

Query hook test-d.ts:

import { abi } from '@wagmi/test' import { expectTypeOf, test } from 'vitest' import { useMyAction } from './useMyAction.js'

test('select data', () => { const result = useMyAction({ / params / query: { select(data) { expectTypeOf(data).toEqualTypeOf() return data }, }, }) expectTypeOf(result.data).toEqualTypeOf() })

Mutation hook test-d.ts:

import { expectTypeOf, test } from 'vitest' import { useMyAction } from './useMyAction.js'

test('context', () => { const { mutate } = useMyAction({ mutation: { onMutate(variables) { expectTypeOf(variables).toMatchTypeOf<{ / expected shape / }>() return { foo: 'bar' } }, onError(error, variables, context) { / test types / }, onSuccess(data, variables, context) { / test types / }, onSettled(data, error, variables, context) { / test types / }, }, })

mutate({ / params / }, { onSuccess(data, variables, context) { / test inference / }, }) })

Exports

Add to exports/index.ts in respective package:

// packages/core/src/exports/index.ts export { type MyActionParameters, type MyActionReturnType, type MyActionErrorType, myAction, } from '../actions/myAction.js'

// packages/core/src/exports/query.ts export { type MyActionData, type MyActionOptions, type MyActionQueryFnData, type MyActionQueryKey, type MyActionQueryOptions, myActionQueryKey, myActionQueryOptions, } from '../query/myAction.js'

// packages/react/src/exports/index.ts export { type UseMyActionParameters, type UseMyActionReturnType, useMyAction, } from '../hooks/useMyAction.js'

Verification

Format

pnpm format

Type check (all or filtered)

pnpm check:types pnpm --filter @wagmi/core check:types pnpm --filter wagmi check:types

Test (all or filtered)

pnpm test pnpm test --project core pnpm test --project react

Update test snapshots

pnpm vitest -u

Type benchmarks

pnpm bench:types

Viem version mismatch in test snapshots

pnpm version:update:viem

Build (all or filtered)

pnpm run clean && pnpm build pnpm --filter @wagmi/core build

返回排行榜