wagmi

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Wagmi Skill

Wagmi 技能

Version: Wagmi 3.x | Official Docs | Requires TypeScript 5.7.3+
Wagmi provides React hooks for Ethereum. This skill ensures correct patterns for provider setup, hooks usage, and React-specific pitfalls.
版本: Wagmi 3.x | 官方文档 | 要求TypeScript 5.7.3+
Wagmi 为Ethereum提供React Hooks。本技能确保Provider配置、Hooks使用以及React特有陷阱的正确处理模式。

Quick Setup (Wagmi v3)

快速上手(Wagmi v3)

1. Config Setup

1. 配置设置

typescript
// config.ts
import { http, createConfig } from 'wagmi'
import { mainnet, polygon, arbitrum } from 'wagmi/chains'
// v3: Install connectors separately: npm i @wagmi/connectors
import { injected, coinbaseWallet, walletConnect } from '@wagmi/connectors'

export const config = createConfig({
  chains: [mainnet, polygon, arbitrum],
  connectors: [
    injected(),
    coinbaseWallet({ appName: 'My App' }),
    walletConnect({ projectId: 'YOUR_PROJECT_ID' }),
  ],
  transports: {
    [mainnet.id]: http('https://eth-mainnet.g.alchemy.com/v2/KEY'),
    [polygon.id]: http('https://polygon-mainnet.g.alchemy.com/v2/KEY'),
    [arbitrum.id]: http('https://arb-mainnet.g.alchemy.com/v2/KEY'),
  },
})
v3 Note: Connectors are now in
@wagmi/connectors
package for better dependency control.
typescript
// config.ts
import { http, createConfig } from 'wagmi'
import { mainnet, polygon, arbitrum } from 'wagmi/chains'
// v3: Install connectors separately: npm i @wagmi/connectors
import { injected, coinbaseWallet, walletConnect } from '@wagmi/connectors'

export const config = createConfig({
  chains: [mainnet, polygon, arbitrum],
  connectors: [
    injected(),
    coinbaseWallet({ appName: 'My App' }),
    walletConnect({ projectId: 'YOUR_PROJECT_ID' }),
  ],
  transports: {
    [mainnet.id]: http('https://eth-mainnet.g.alchemy.com/v2/KEY'),
    [polygon.id]: http('https://polygon-mainnet.g.alchemy.com/v2/KEY'),
    [arbitrum.id]: http('https://arb-mainnet.g.alchemy.com/v2/KEY'),
  },
})
v3 注意事项: Connectors现在位于
@wagmi/connectors
包中,以实现更优的依赖管理。

2. Provider Setup

2. Provider配置

tsx
// providers.tsx
'use client' // Required for Next.js App Router

import { WagmiProvider } from 'wagmi'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { config } from './config'

const queryClient = new QueryClient()

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        {children}
      </QueryClientProvider>
    </WagmiProvider>
  )
}
tsx
// providers.tsx
'use client' // Required for Next.js App Router

import { WagmiProvider } from 'wagmi'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { config } from './config'

const queryClient = new QueryClient()

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        {children}
      </QueryClientProvider>
    </WagmiProvider>
  )
}

Core Hooks

核心Hooks

useAccount

useAccount

tsx
import { useAccount } from 'wagmi'

function Profile() {
  const { address, isConnected, isConnecting, chain } = useAccount()
  
  if (isConnecting) return <div>Connecting...</div>
  if (!isConnected) return <div>Not connected</div>
  
  return <div>Connected: {address} on {chain?.name}</div>
}
tsx
import { useAccount } from 'wagmi'

function Profile() {
  const { address, isConnected, isConnecting, chain } = useAccount()
  
  if (isConnecting) return <div>Connecting...</div>
  if (!isConnected) return <div>Not connected</div>
  
  return <div>Connected: {address} on {chain?.name}</div>
}

useConnect / useDisconnect / useConnectors

useConnect / useDisconnect / useConnectors

tsx
import { useAccount, useConnect, useDisconnect, useConnectors } from 'wagmi'

function ConnectButton() {
  // v3: Use useConnectors() hook instead of getting from useConnect()
  const connectors = useConnectors()
  const { connect, isPending } = useConnect()
  const { disconnect } = useDisconnect()
  const { isConnected } = useAccount()

  if (isConnected) {
    return <button onClick={() => disconnect()}>Disconnect</button>
  }

  return (
    <div>
      {connectors.map((connector) => (
        <button
          key={connector.id}
          onClick={() => connect({ connector })}
          disabled={isPending}
        >
          {connector.name}
        </button>
      ))}
    </div>
  )
}
tsx
import { useAccount, useConnect, useDisconnect, useConnectors } from 'wagmi'

function ConnectButton() {
  // v3: Use useConnectors() hook instead of getting from useConnect()
  const connectors = useConnectors()
  const { connect, isPending } = useConnect()
  const { disconnect } = useDisconnect()
  const { isConnected } = useAccount()

  if (isConnected) {
    return <button onClick={() => disconnect()}>Disconnect</button>
  }

  return (
    <div>
      {connectors.map((connector) => (
        <button
          key={connector.id}
          onClick={() => connect({ connector })}
          disabled={isPending}
        >
          {connector.name}
        </button>
      ))}
    </div>
  )
}

useReadContract (REPLACES useContractRead)

useReadContract(替代useContractRead)

tsx
import { useReadContract } from 'wagmi'

function Balance() {
  const { data, isLoading, error, refetch } = useReadContract({
    address: '0x...',
    abi, // Use `as const` for type safety
    functionName: 'balanceOf',
    args: ['0x...'],
  })

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>
  
  return <div>Balance: {data?.toString()}</div>
}
tsx
import { useReadContract } from 'wagmi'

function Balance() {
  const { data, isLoading, error, refetch } = useReadContract({
    address: '0x...',
    abi, // Use `as const` for type safety
    functionName: 'balanceOf',
    args: ['0x...'],
  })

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>
  
  return <div>Balance: {data?.toString()}</div>
}

useWriteContract (REPLACES useContractWrite)

useWriteContract(替代useContractWrite)

tsx
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'

function Transfer() {
  const { data: hash, writeContract, isPending, error } = useWriteContract()
  
  const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({
    hash,
  })

  async function handleTransfer() {
    writeContract({
      address: '0x...',
      abi,
      functionName: 'transfer',
      args: ['0x...', 1000n],
    })
  }

  return (
    <div>
      <button onClick={handleTransfer} disabled={isPending}>
        {isPending ? 'Confirming...' : 'Transfer'}
      </button>
      {isConfirming && <div>Waiting for confirmation...</div>}
      {isSuccess && <div>Transaction confirmed!</div>}
      {error && <div>Error: {error.message}</div>}
    </div>
  )
}
tsx
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'

function Transfer() {
  const { data: hash, writeContract, isPending, error } = useWriteContract()
  
  const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({
    hash,
  })

  async function handleTransfer() {
    writeContract({
      address: '0x...',
      abi,
      functionName: 'transfer',
      args: ['0x...', 1000n],
    })
  }

  return (
    <div>
      <button onClick={handleTransfer} disabled={isPending}>
        {isPending ? 'Confirming...' : 'Transfer'}
      </button>
      {isConfirming && <div>Waiting for confirmation...</div>}
      {isSuccess && <div>Transaction confirmed!</div>}
      {error && <div>Error: {error.message}</div>}
    </div>
  )
}

Critical Patterns

关键模式

ABI Type Safety (CRITICAL)

ABI类型安全(至关重要)

typescript
// ✅ CORRECT - as const for full type inference
const abi = [
  {
    name: 'transfer',
    type: 'function',
    stateMutability: 'nonpayable',
    inputs: [
      { name: 'to', type: 'address' },
      { name: 'amount', type: 'uint256' },
    ],
    outputs: [{ name: '', type: 'bool' }],
  },
] as const

// ❌ WRONG - no type inference
const abi = [{ ... }] // Missing `as const`
typescript
// ✅ CORRECT - as const for full type inference
const abi = [
  {
    name: 'transfer',
    type: 'function',
    stateMutability: 'nonpayable',
    inputs: [
      { name: 'to', type: 'address' },
      { name: 'amount', type: 'uint256' },
    ],
    outputs: [{ name: '', type: 'bool' }],
  },
] as const

// ❌ WRONG - no type inference
const abi = [{ ... }] // Missing `as const`

Conditional Hook Calls (NEVER conditional)

条件式Hook调用(绝对禁止)

tsx
// ❌ WRONG - Violates Rules of Hooks
function BadComponent({ shouldFetch }) {
  if (shouldFetch) {
    const { data } = useReadContract({ ... })
  }
}

// ✅ CORRECT - Use enabled option
function GoodComponent({ shouldFetch }) {
  const { data } = useReadContract({
    ...params,
    query: { enabled: shouldFetch },
  })
}
tsx
// ❌ WRONG - Violates Rules of Hooks
function BadComponent({ shouldFetch }) {
  if (shouldFetch) {
    const { data } = useReadContract({ ... })
  }
}

// ✅ CORRECT - Use enabled option
function GoodComponent({ shouldFetch }) {
  const { data } = useReadContract({
    ...params,
    query: { enabled: shouldFetch },
  })
}

Stale Closure Prevention

避免陈旧闭包

tsx
// ❌ WRONG - Captures stale values
function BadComponent() {
  const [amount, setAmount] = useState(0n)
  
  const { writeContract } = useWriteContract()
  
  // This captures `amount` at render time!
  const handleClick = () => {
    writeContract({
      ...params,
      args: [amount], // May be stale!
    })
  }
}

// ✅ CORRECT - Pass fresh values
function GoodComponent() {
  const [amount, setAmount] = useState(0n)
  const { writeContract } = useWriteContract()

  const handleClick = () => {
    writeContract({
      ...params,
      args: [amount], // Fresh from closure
    })
  }
}
tsx
// ❌ WRONG - Captures stale values
function BadComponent() {
  const [amount, setAmount] = useState(0n)
  
  const { writeContract } = useWriteContract()
  
  // This captures `amount` at render time!
  const handleClick = () => {
    writeContract({
      ...params,
      args: [amount], // May be stale!
    })
  }
}

// ✅ CORRECT - Pass fresh values
function GoodComponent() {
  const [amount, setAmount] = useState(0n)
  const { writeContract } = useWriteContract()

  const handleClick = () => {
    writeContract({
      ...params,
      args: [amount], // Fresh from closure
    })
  }
}

Common Mistakes

常见错误

MistakeFix
useContractRead (v1)Use
useReadContract
useContractWrite (v1)Use
useWriteContract
connectors
from useConnect (v2)
Use
useConnectors()
hook (v3)
chains
from useSwitchChain (v2)
Use
useChains()
hook (v3)
Conditional hooksUse
query: { enabled: bool }
Missing QueryClientProviderWagmi requires TanStack Query
Not awaiting hashUse
useWaitForTransactionReceipt
String amountsUse BigInt:
1000n
Connectors from
wagmi/connectors
Use
@wagmi/connectors
package (v3)
错误修复方案
useContractRead (v1)使用
useReadContract
useContractWrite (v1)使用
useWriteContract
从useConnect获取connectors (v2)使用
useConnectors()
Hook (v3)
从useSwitchChain获取chains (v2)使用
useChains()
Hook (v3)
条件式调用Hooks使用
query: { enabled: bool }
缺少QueryClientProviderWagmi依赖TanStack Query
未等待交易哈希确认使用
useWaitForTransactionReceipt
使用字符串表示金额使用BigInt:
1000n
wagmi/connectors
导入Connectors
使用
@wagmi/connectors
包 (v3)

References

参考资料

For detailed patterns, see:
如需详细模式说明,请参阅: