关于 wagmi
为开发者提供 Wagmi 3.x React hooks 使用指导,面向 Ethereum 应用开发。内容涵盖配置、类型安全的合约交互和常见 React 问题,并提供针对版本的说明。
主要功能
- 01Wagmi v3 配置与 Provider
- 02钱包连接和账户 hooks
- 03合约读取与写入 hooks
- 04ABI 类型安全指导
- 05React hooks 与状态问题
- 06v2 至 v3 迁移说明
适用场景
- 01在 React 应用中配置 Wagmi
- 02连接钱包并读取账户信息
- 03读取或写入 Ethereum 合约
- 04将代码从 Wagmi v2 更新
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ 0xsardius/ onchain-typescript-skills --skill wagmi安装后在 Claude Code 中输入/wagmi确认已加载。
附带文件references/hooks-guide.mdreferences/react-patterns.md
详细介绍
Wagmi 技能
版本: Wagmi 3.x | 官方文档 | 需要 TypeScript 5.7.3+
Wagmi 提供用于 Ethereum 的 React hooks。此技能可确保正确使用 provider 设置、hooks,并规避 React 特有的问题。
快速设置(Wagmi v3)
1. 配置设置
// 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 设置
// 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>
)
}
核心 Hooks
useAccount
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
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(替代 useContractRead)
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(替代 useContractWrite)
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>
)
}
关键模式
ABI 类型安全(重要)
// ✅ 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`
条件调用 Hook(绝不可条件调用)
// ❌ 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 },
})
}
避免过期闭包
// ❌ 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
})
}
}
常见错误
| 错误 | 修正方式 |
|---|---|
| useContractRead(v1) | 使用 useReadContract |
| useContractWrite(v1) | 使用 useWriteContract |
来自 useConnect(v2)的 connectors | 使用 useConnectors() hook(v3) |
来自 useSwitchChain(v2)的 chains | 使用 useChains() hook(v3) |
| 条件调用 hooks | 使用 query: { enabled: bool } |
| 缺少 QueryClientProvider | Wagmi 需要 TanStack Query |
| 未等待 hash | 使用 useWaitForTransactionReceipt |
| 字符串金额 | 使用 BigInt:1000n |
来自 wagmi/connectors 的 Connectors | 使用 @wagmi/connectors 包(v3) |
参考资料
有关详细模式,请参阅:
references/hooks-guide.md- 完整的 hooks 参考references/react-patterns.md- React 特有模式和 SSR- Wagmi 文档 - 官方文档
- Wagmi GitHub - 源码和版本发布
- 从 v2 迁移到 v3 - 重大变更
评论
支持 Markdown