wagmi-development

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

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "wagmi-development" with this command: npx skills add wevm/wagmi/wevm-wagmi-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<config extends Config = Config> = Compute< ChainIdParameter<config> & viem_MyActionParameters

export type MyActionReturnType = viem_MyActionReturnType

export type MyActionErrorType = viem_MyActionErrorType

/** https://wagmi.sh/core/api/actions/myAction */ export async function myAction<config extends Config>( config: config, parameters: MyActionParameters<config>, ): Promise<MyActionReturnType> { 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<config> 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<ExpectedType>() })

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<typeof abi.erc20, 'balanceOf'> 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<ExactPartial<MyActionParameters<config>> & ScopeKeyParameter> & QueryParameter<MyActionQueryFnData, MyActionErrorType, selectData, MyActionQueryKey<config>>

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

( config: config, options: MyActionOptions<config, selectData> = {}, ): MyActionQueryOptions<config, selectData> { 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<MyActionReturnType> export type MyActionData = MyActionQueryFnData

export function myActionQueryKey<config extends Config>( options: Compute<ExactPartial<MyActionParameters<config>> & ScopeKeyParameter> = {}, ) { return ['myAction', filterQueryOptions(options)] as const }

export type MyActionQueryKey<config extends Config> = ReturnType<typeof myActionQueryKey<config>>

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

= QueryOptions<MyActionQueryFnData, MyActionErrorType, selectData, MyActionQueryKey<config>>

Mutation Structure

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

export type MyActionOptions<config extends Config, context = unknown> = MutationParameter< MyActionData, MyActionErrorType, MyActionVariables<config>, context

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

export type MyActionMutationOptions<config extends Config> = MutationOptions< MyActionData, MyActionErrorType, MyActionVariables<config>

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<MyActionOptions<config, selectData> & ConfigParameter<config>>

export type UseMyActionReturnType<selectData = MyActionData> = UseQueryReturnType<selectData, MyActionErrorType>

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

( parameters: UseMyActionParameters<config, selectData> = {}, ): UseMyActionReturnType<selectData> { 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<config extends Config = Config, context = unknown> = MyActionOptions<config, context> & ConfigParameter<config>

export type UseMyActionReturnType<config extends Config = Config, context = unknown> = UseMutationReturnType< MyActionData, MyActionErrorType, MyActionVariables<config>, context, MyActionMutate<config, context>, MyActionMutateAsync<config, context>

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

( parameters: UseMyActionParameters<config, context> = {}, ): UseMyActionReturnType<config, context> { const config = useConfig(parameters) const options = myActionMutationOptions(config, parameters) const mutation = useMutation(options) type Return = UseMyActionReturnType<config, context> 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<DeepMaybeRef<MyActionOptions<config, selectData> & ConfigParameter<config>>>

export type UseMyActionReturnType<selectData = MyActionData> = UseQueryReturnType<selectData, MyActionErrorType>

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

( parameters: UseMyActionParameters<config, selectData> = {}, ): UseMyActionReturnType<selectData> { 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<ExpectedDataType>() return data }, }, }) expectTypeOf(result.data).toEqualTypeOf<ExpectedDataType>() })

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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Web3

wagmi

No summary provided by upstream source.

Repository SourceNeeds Review
General

incur

No summary provided by upstream source.

Repository SourceNeeds Review
-16
wevm
Web3

wagmi

No summary provided by upstream source.

Repository SourceNeeds Review