WA

wagmi-development

Streamlines full-stack Wagmi development for web3 applications.

Install

mkdir -p .claude/skills/wagmi-development && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/4453" && unzip -o skill.zip -d .claude/skills/wagmi-development && rm skill.zip

Installs to .claude/skills/wagmi-development

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Creates Wagmi features across all layers - core actions, query options, framework bindings. Use when adding new actions, hooks, or working across packages/core, packages/react, packages/vue.
190 chars✓ has a “when” trigger
Advanced

Key capabilities

  • Create core actions wrapping Viem
  • Implement TanStack Query options
  • Develop React and Vue framework bindings
  • Generate type tests and benchmarks
  • Configure wallet action testing

How it works

The skill provides patterns for wrapping Viem actions into Wagmi core actions, query options, and framework-specific hooks. It enforces strict type inference and testing standards across core, React, and Vue packages.

Inputs & outputs

You give it
Action parameters and configuration
You get back
Framework-specific hooks or query options

When to use wagmi-development

  • Create new blockchain interaction hooks
  • Implement TanStack Query options for web3
  • Build framework-specific actions for Wagmi

About this skill

Wagmi Development

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

Layer Overview

  1. Core Action (packages/core/src/actions/) - Base functionality wrapping Viem
  2. Query Options (packages/core/src/query/) - TanStack Query integration
  3. 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 })
})

2. 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(`...`)
})

3. 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 Con

---

*Content truncated.*

When not to use it

  • Implementing Wagmi config actions
  • Non-Viem based blockchain actions

Prerequisites

vitest@wagmi/test

Limitations

  • Covers Viem-based actions only
  • Requires manual setup for complex type inference

How it compares

Unlike manual implementation, this provides standardized patterns for type inference, client access, and framework-specific reactivity.

Compared to similar skills

wagmi-development side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
wagmi-development (this skill)16moReviewAdvanced
ai-model-web12moReviewIntermediate
convex-realtime16moNo flagsIntermediate
query-layer12moNo flagsAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry