Skip to Content
All articles

Wagmi v2 - How to Generate Instant, Type-Safe Hooks for Your Smart Contracts

 — #Nextjs#wagmi#web3

In the fast-moving world of Web3, developer experience (DX) used to be an afterthought. We spent hours copy-pasting JSON ABIs, manually typing contract addresses, and wrestling with hex-encoded data.

Then came Wagmi.

Wagmi v2, paired with the Wagmi CLI, has fundamentally changed the game. Instead of manual setup, you can now generate type-safe React hooks that "know" your smart contract's functions before you even finish typing them.

Step 1: Installation

First, we need the core Wagmi library, its peer dependency Viem (the lightweight alternative to ethers.js), and TanStack Query for state management.

pnpm install wagmi viem @tanstack/react-query

Next, install the Wagmi CLI as a development dependency. This is the magic tool that will handle your ABIs.

pnpm install --save-dev @wagmi/cli

Step 2: Configure Wagmi

Before we get to the CLI, we need a standard configuration for our app. Create a file named wagmi.ts (or config.ts).

// config/config.ts
import { http, createConfig } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
import { injected, metaMask } from 'wagmi/connectors'

export const config = createConfig({
  chains: [mainnet, sepolia],
  connectors: [
    injected(),
    metaMask(),
  ],
  transports: {
    [mainnet.id]: http(),
    [sepolia.id]: http(),
  },
})

Step 3: Initialize the Wagmi CLI

The CLI needs its own configuration file to know where to fetch your ABIs and where to output the generated code. Run the following command:

npx wagmi init

This creates a wagmi.config.ts file. This is where you tell Wagmi about your smart contracts.

Automating ABI Generation

You have two main ways to get your ABIs:

  1. From a local project (Hardhat/Foundry)
  2. From a block explorer (Etherscan)

Here is a powerhouse configuration that fetches an ABI directly from Etherscan and generates React hooks for it:

import { defineConfig, loadEnv } from "@wagmi/cli";
import { etherscan, react } from "@wagmi/cli/plugins";
import { mainnet } from "wagmi/chains";

export default defineConfig(() => {
  const env = loadEnv({
    envDir: process.cwd(),
  });

  return {
    out: "lib/generated.ts",
    plugins: [
      etherscan({
        apiKey: env.ETHERSCAN_API_KEY!,
        chainId: mainnet.id,
        contracts: [
          {
            name: "MyContract",
            // Your smart contract address
            address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
          },
        ],
      }),
      react(), // This generates the custom hooks!
    ],
  };
});

Step 4: Run the Generator

Once your config is ready, run the generation command:

npx wagmi generate

Wagmi will now:

  • Reach out to Etherscan
  • Grab the ABI for the MyContract
  • Create a file at lib/generated.ts containing custom hooks like useReadEnsRegistryOwner

Step 5: Put it All Together

Create a config/providers.tsx file, and wrap your application with the WagmiProvider and the QueryClientProvider.

// config/providers.tsx
"use client";

import { WagmiProvider } from "wagmi";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { config } from "./config";
import { ReactNode, useState } from "react";

export function Providers({ children }: { children: ReactNode }) {
  const [queryClient] = useState(() => new QueryClient());

  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        {children}
      </QueryClientProvider>
    </WagmiProvider>
  );
}
// app/layout.tsx
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Using Your Generated Hook

Now, instead of writing useReadContract and passing in a messy ABI object, you can do this:

import { useReadEnsRegistryOwner } from './generated'

function MyComponent() {
  const { data, isLoading } = useReadEnsRegistryOwner({
    args: ['0x...'], // Your arguments are now type-checked!
  })

  if (isLoading) return <div>Loading...</div>

  return <div>Owner: {data}</div>
}

Why This Matters

By using the CLI to generate your ABIs and hooks:

  • Type Safety: If a function name changes or you pass the wrong number of arguments, TypeScript will throw an error immediately.
  • Speed: No more manual JSON imports.
  • Maintainability: If you update your contract, just run npx wagmi generate again, and your entire frontend updates its types.

References

  1. Wagmi Documentation

  2. Wagmi CLI Plugin Documentation

  3. Viem Documentation

  4. TanStack Query (React Query)

  5. Etherscan API