Wagmi
版本过时
本文档基于 Wagmi 2.x 的 API 与用法,不适用于 Wagmi 3.x。 升级到 3.x 请参考官方迁移指南,并按新 API 调整。
Wagmi(We Are All Gonna Make It)2.x 是一个强大的 React 库,专为 Web3 开发设计,提供了简洁的 API 和多链支持。它可以轻松处理钱包连接、交易签名、智能合约交互等任务,并支持多种钱包(如 MetaMask、Ledger Live)以及 Sign-in with Ethereum 功能。Wagmi 还具有缓存、请求去重和持久化的特性,适合开发去中心化应用(DApp)、NFT 平台、DeFi 平台和 Web3 社交应用等。
安装
自动安装
对于新项目,建议使用 create-wagmi 命令行界面 (CLI) 设置您的 Wagmi 应用程序。这将使用 TypeScript 创建一个新的 Wagmi 项目并安装所需的依赖项。
npm create wagmi@latestyarn create wagmi手动安装
要开始使用 Wagmi,需要安装相关依赖。以下是安装步骤:
npm install wagmi viem@2.x @tanstack/react-queryyarn add wagmi viem@2.x @tanstack/react-query<script type="module">
import React from 'https://esm.sh/react@18.2.0'
import {QueryClient} from 'https://esm.sh/@tanstack/react-query'
import {createClient} from 'https://esm.sh/viem@2.x'
import {createConfig} from 'https://esm.sh/wagmi'
</script>wagmi:核心库,提供与以太坊交互的 React Hooks。@tanstack/react-query:Wagmi 依赖的库,用于处理数据查询和缓存。
TypeScript
类型当前需要使用 TypeScript >=5.0.4。为确保一切正常工作,请确保您的 tsconfig.json 将严格模式设置为 true。
创建配置
import {createConfig, http} from 'wagmi'
import {mainnet, sepolia} from 'wagmi/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})import {defineChain} from 'viem'
import {createConfig, http} from 'wagmi'
import {mainnet, sepolia} from 'wagmi/chains'
import {injected} from 'wagmi/connectors'
const monadTestnet = defineChain({
id: 10143, // ✅ Monad Testnet Chain ID
name: "Monad Testnet",
nativeCurrency: {
name: "Monad",
symbol: "MON",
decimals: 18,
},
rpcUrls: {
default: {
http: ["https://testnet-rpc.monad.xyz"],
webSocket: ["wss://testnet-rpc.monad.xyz"],
},
public: {
http: ["https://testnet-rpc.monad.xyz"],
webSocket: ["wss://testnet-rpc.monad.xyz"],
},
},
blockExplorers: {
default: {
name: "Monad Explorer",
url: "https://explorer.testnet.monad.xyz",
},
},
});
export const config = createConfig({
chains: [mainnet, sepolia, monadTestnet],
connectors: [injected()],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
[monadTestnet.id]: http("https://testnet-rpc.monad.xyz"),
},
});WagmiProvider
将您的应用程序包装在 WagmiProvider React Context Provider 中,并将您之前创建的配置传递给 config 属性。
import {WagmiProvider} from 'wagmi'
import {config} from './config'
function App() {
return (
<WagmiProvider config={config}>
{/** ... */}
</WagmiProvider>
)
}设置 TanStack 查询
在 WagmiProvider 中,将您的应用程序包装在 TanStack Query React Context Provider(例如 QueryClientProvider)中,并将新的 QueryClient 实例传递给客户端属性。
import {QueryClient, QueryClientProvider} from '@tanstack/react-query'
import {WagmiProvider} from 'wagmi'
import {config} from './config'
const queryClient = new QueryClient()
function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
{/** ... */}
</QueryClientProvider>
</WagmiProvider>
)
}使用 Wagmi
现在一切都已设置完毕,Wagmi 和 TanStack 查询提供程序内的每个组件都可以使用 Wagmi React Hooks。
import {useAccount, useEnsName} from 'wagmi'
export function Profile() {
const {address} = useAccount()
const {data, error, status} = useEnsName({address})
if (status === 'pending') return <div>Loading ENS name</div>
if (status === 'error')
return <div>Error fetching ENS name: {error.message}</div>
return <div>ENS name: {data}</div>
}import {QueryClient, QueryClientProvider} from '@tanstack/react-query'
import {WagmiProvider} from 'wagmi'
import {config} from './config'
import {Profile} from './profile'
const queryClient = new QueryClient()
function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<Profile/>
</QueryClientProvider>
</WagmiProvider>
)
}import {createConfig, http} from 'wagmi'
import {mainnet, sepolia} from 'wagmi/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})连接钱包
用户连接钱包的能力是任何 Dapp 的核心功能。它允许用户执行诸如:写入合约、签署消息或发送交易等任务。
手动构建钱包
- 配置
Wagmi
import {http, createConfig} from 'wagmi'
import {base, mainnet, optimism} from 'wagmi/chains'
import {injected, metaMask, safe, walletConnect} from 'wagmi/connectors'
const projectId = '<WALLETCONNECT_PROJECT_ID>'
export const config = createConfig({
chains: [mainnet, base],
connectors: [
injected(),
walletConnect({projectId}),
metaMask(),
safe(),
],
transports: {
[mainnet.id]: http(),
[base.id]: http(),
},
})WalletConnect
如果您想使用 WalletConnect,请务必将 projectId 替换为您自己的 WalletConnect 项目 ID! 去获取项目 ID
WagmiProvider导入配置文件config。
<WagmiProvider config={config}>- 显示钱包选项
import * as React from 'react'
import {Connector, useConnect} from 'wagmi'
export function WalletOptions() {
const {connectors, connect} = useConnect()
return connectors.map((connector) => (
<WalletOption
key={connector.uid}
connector={connector}
onClick={() => connect({connector})}
/>
))
}
function WalletOption({connector, onClick}: {
connector: Connector
onClick: () => void
}) {
const [ready, setReady] = React.useState(false)
React.useEffect(() => {
(async () => {
const provider = await connector.getProvider()
setReady(!!provider)
})()
}, [connector])
return (
<button disabled={!ready} onClick={onClick}>
{connector.name}
</button>
)
}第三方钱包
| 库名称 | 中文名 | 简介 | 特点 |
|---|---|---|---|
| ConnectKit | ConnectKit(连接组件工具) | Wagmi 团队维护的官方 UI 组件 | 极简、风格统一、完全兼容 wagmi |
| AppKit | AppKit(应用工具组件) | 基于 ConnectKit 的进一步封装,强调全流程用户引导 | 引导 + 连接 + 状态管理一体化 |
| RainbowKit | 彩虹套件 | 由 Rainbow 团队出品的钱包连接 UI 工具 | 视觉体验最优秀之一,适合主流 DApp |
| Dynamic | Dynamic(动态钱包 SDK) | 允许你支持多种社交登录+钱包连接(Web2→Web3) | 登录即钱包,适合 Web2 用户过渡 |
| Privy | 枢密院(Privy) | 支持嵌入式钱包、邮箱/Google 登录等隐私钱包 | 内嵌钱包 + 身份管理,适合非加密用户 |
ConnectKit
Wagmi 团队维护的官方 UI 组件
npm install connectkitimport {WagmiProvider, createConfig, http} from "wagmi";
import {mainnet} from "wagmi/chains";
import {ConnectKitProvider, getDefaultConfig} from "connectkit";
const config = createConfig(
getDefaultConfig({
// Your dApps chains
chains: [mainnet],
transports: {
// RPC URL for each chain
[mainnet.id]: http(
`https://eth-mainnet.g.alchemy.com/v2/${process.env.NEXT_PUBLIC_ALCHEMY_ID}`,
),
},
// Required API Keys
walletConnectProjectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID,
// Required App Info
appName: "Your App Name",
// Optional App Info
appDescription: "Your App Description",
appUrl: "https://family.co", // your app's url
appIcon: "https://family.co/logo.png", // your app's icon, no bigger than 1024x1024px (max. 1MB)
}),
);
export default configimport React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './styles/globals.css'
import {WagmiProvider} from "wagmi";
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
import {ConnectKitProvider, ConnectKitButton} from "connectkit";
import config from "@/lib/config.ts";
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<ConnectKitProvider mode="light"> {/*钱包容器*/}
<ConnectKitButton/> {/*集成式钱包组件*/}
</ConnectKitProvider>
</QueryClientProvider>
</WagmiProvider>
</React.StrictMode>,
)AppKit
AppKit 提供与多个区块链生态系统的无缝集成。.它支持以太坊上的 Wagmi 和 Ethers v6, Solana 以及比特币和其他网络上的 @solana/web3.js。具有通用提供程序库的 AppKit 核心,可实现跨任何区块链协议的兼容性。

安装依赖
npm install @reown/appkit @reown/appkit-adapter-wagmi wagmi viem @tanstack/react-queryWagmi 配置示例:
import {createAppKit} from '@reown/appkit/react'
import {mainnet} from '@reown/appkit/networks'
import {WagmiAdapter} from '@reown/appkit-adapter-wagmi'
// 1. 从 https://dashboard.reown.com 获取你的 projectId
const projectId = import.meta.env.VITE_WALLETCONNECT_PROJECT_ID;
// 2. 创建一个可选的 metadata 对象,用于描述你的应用
const metadata = {
name: 'AppKit', // 应用名称
description: 'AppKit Example', // 应用描述
url: 'https://example.com', // 应用网址,必须和你的域名和子域名匹配
icons: ['https://avatars.githubusercontent.com/u/179229932'] // 应用图标列表
}
// 3. 设置支持的网络,这里是 Ethereum 主网 (mainnet)
// 使用类型断言确保 networks 是一个至少包含一个元素的元组类型
const networks = [mainnet] as [typeof mainnet, ...typeof mainnet[]];
// 4. 创建一个 Wagmi 适配器,负责连接钱包等功能
const wagmiAdapter = new WagmiAdapter({
networks, // 支持的网络列表
projectId, // WalletConnect 的项目 ID
ssr: true, // 是否支持服务端渲染 (Server-Side Rendering)
})
// 5. 创建 AppKit 模态框,传入适配器、网络、主题模式、项目 ID、元数据和功能配置
createAppKit({
adapters: [wagmiAdapter], // 传入适配器数组
networks, // 支持的网络列表
themeMode: "light", // 主题模式,这里是浅色模式
projectId, // WalletConnect 项目 ID
metadata, // 应用元数据
features: {
analytics: true // 是否启用分析功能,默认为你在云端的配置
}
})
// 导出 wagmiAdapter 的配置,用于后续在应用中使用
export default wagmiAdapter.wagmiConfig// 以Monad测试网为示例
import {defineChain} from "viem";
const customNetwork = defineChain({
id: 10143,
name: "Monad Testnet",
nativeCurrency: {
name: "Monad",
symbol: "MON",
decimals: 18,
},
rpcUrls: {
default: {
http: ["https://testnet-rpc.monad.xyz"],
webSocket: ["wss://testnet-rpc.monad.xyz"],
},
public: {
http: ["https://testnet-rpc.monad.xyz"],
webSocket: ["wss://testnet-rpc.monad.xyz"],
},
},
blockExplorers: {
default: {
name: "Monad Explorer",
url: "https://explorer.testnet.monad.xyz",
},
},
})
const networks = [mainnet, customNetwork] as [typeof mainnet, typeof customNetwork]import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './styles/globals.css'
import {WagmiProvider} from "wagmi";
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
import config from "@/lib/config.ts";
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<WagmiProvider config={config}
>
<QueryClientProvider client={queryClient}>
<App/>
</QueryClientProvider>
</WagmiProvider>
</React.StrictMode>,
)// Web 组件是不需要导入的全局 html 元素。
export default function ConnectButton() {
return <appkit-button/>
}import * as React from 'react'
declare global {
namespace JSX {
interface IntrinsicElements {
'appkit-button': React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
// Add other custom elements here if needed
}
}
}import {useAppKit} from "@reown/appkit/react";
export default function ConnectButton() {
const {open} = useAppKit();
return (
<>
<button onClick={() => open()}>Open Connect Modal</button>
<button onClick={() => open({view: "Networks"})}>
Open Network Modal
</button>
</>
);
}RainbowKit
RainbowKit 是一个 React 库,可以轻松地将钱包连接添加到您的 dapp。它直观、响应迅速且可自定义。它直观、响应迅速且可自定义。

安装RainbowKit及其对等依赖项, wagmi, viem,和 @tanstack/react-query。
npm install @rainbow-me/rainbowkit wagmi viem@2.x @tanstack/react-queryimport React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './styles/globals.css'
import {WagmiProvider} from "wagmi";
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
import config from "@/lib/config.ts";
import {darkTheme, RainbowKitProvider} from "@rainbow-me/rainbowkit";
const queryClient = new QueryClient();
import '@rainbow-me/rainbowkit/styles.css'; // 样式引入
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<WagmiProvider config={config}
>
<QueryClientProvider client={queryClient}>
<RainbowKitProvider theme={darkTheme()}> {/*lightTheme(默认)、darkTheme、midnightTheme*/}
<App/>
</RainbowKitProvider>
</QueryClientProvider>
</WagmiProvider>
</React.StrictMode>,
)import {getDefaultConfig} from '@rainbow-me/rainbowkit';
import {http} from 'wagmi';
import {mainnet, sepolia} from 'wagmi/chains';
const config = getDefaultConfig({
appName: 'My RainbowKit App',
projectId: 'YOUR_PROJECT_ID',
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http('https://eth-mainnet.g.alchemy.com/v2/...'),
[sepolia.id]: http('https://eth-sepolia.g.alchemy.com/v2/...'),
},
});import {getDefaultConfig} from '@rainbow-me/rainbowkit';
import {http} from 'wagmi';
import {mainnet, sepolia} from 'wagmi/chains';
import {defineChain} from "viem";
const projectId = import.meta.env.VITE_WALLETCONNECT_PROJECT_ID;
const customNetwork = defineChain({
id: 10143,
name: "Monad Testnet",
nativeCurrency: {
name: "Monad",
symbol: "MON",
decimals: 18,
},
rpcUrls: {
default: {
http: ["https://testnet-rpc.monad.xyz"],
webSocket: ["wss://testnet-rpc.monad.xyz"],
},
public: {
http: ["https://testnet-rpc.monad.xyz"],
webSocket: ["wss://testnet-rpc.monad.xyz"],
},
},
blockExplorers: {
default: {
name: "Monad Explorer",
url: "https://explorer.testnet.monad.xyz",
},
},
})
const config = getDefaultConfig({
appName: 'My RainbowKit App',
projectId,
chains: [mainnet, customNetwork],
transports: {
[mainnet.id]: http('https://eth-mainnet.g.alchemy.com/v2/...'),
[customNetwork.id]: http('https://testnet-rpc.monad.xyz'),
},
});
export default config;import {ConnectButton} from '@rainbow-me/rainbowkit';
export const App = () => {
return <ConnectButton/>;
};钱包连接器
用于连接常见钱包和区块链协议的适配器。
// 基本Base链 SDK 的连接器。
import {createConfig, http} from 'wagmi'
import {mainnet, sepolia} from 'wagmi/chains'
import {baseAccount} from 'wagmi/connectors'
export const config = createConfig({
chains: [mainnet, sepolia],
connectors: [baseAccount()],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})// 浏览器环境注入的以太坊 provider
import {createConfig, http} from 'wagmi'
import {mainnet, sepolia} from 'wagmi/chains'
import {injected} from 'wagmi/connectors'
export const config = createConfig({
chains: [mainnet, sepolia],
connectors: [injected()],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})// MetaMask SDK 的连接器。
import {createConfig, http} from 'wagmi'
import {mainnet, sepolia} from 'wagmi/chains'
import {metaMask} from 'wagmi/connectors'
export const config = createConfig({
chains: [mainnet, sepolia],
connectors: [metaMask()],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})// 用于连接 Safe 应用 SDK 的适配器。
import {createConfig, http} from 'wagmi'
import {mainnet, sepolia} from 'wagmi/chains'
import {safe} from 'wagmi/connectors'
export const config = createConfig({
chains: [mainnet, sepolia],
connectors: [safe()],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})// WalletConnect 的连接器。https://reown.com/
import {createConfig, http} from 'wagmi'
import {mainnet, sepolia} from 'wagmi/chains'
import {walletConnect} from 'wagmi/connectors'
export const config = createConfig({
chains: [mainnet, sepolia],
connectors: [
walletConnect({
projectId: '3fcc6bba6f1de962d911bb5b5c3dba68',
}),
],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})// 用于模拟 Wagmi 功能的连接器。
import {createConfig, http} from 'wagmi'
import {mainnet, sepolia} from 'wagmi/chains'
import {mock} from 'wagmi/connectors'
export const config = createConfig({
chains: [mainnet, sepolia],
connectors: [
mock({
accounts: [
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
'0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
'0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
],
}),
],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})Transports
createConfig 可以为每条链配置一组 传输层(Transports)。传输层是负责将发出的 JSON-RPC 请求发送到 RPC 提供者(比如 Alchemy、Infura 等)的中间层。
换句话说,传输层充当客户端和区块链节点之间的桥梁,确保请求正确发送并获得响应。
custom
自定义传输(Custom Transport)通过自定义方式连接到 JSON-RPC API,封装了 Viem 库中的自定义传输功能。
import {
createConfig,
custom
} from 'wagmi'
import {mainnet} from 'wagmi/chains'
import {customRpc} from './rpc'
export const config = createConfig({
chains: [mainnet],
connectors: [injected()],
transports: {
[mainnet.id]: custom({
async request({method, params}) {
const response = await customRpc.request(method, params)
return response
}
})
},
})fallback
后备传输(fallback Transport) 会依次使用多个传输(Transports)。当某个传输请求失败时,它会自动切换到列表中的下一个传输继续请求。该功能封装了 Viem 库中的后备传输机制。
import {
createConfig,
fallback,
http,
} from 'wagmi'
import {mainnet} from 'wagmi/chains'
export const config = createConfig({
chains: [mainnet],
connectors: [injected()],
transports: {
[mainnet.id]: fallback([
http('https://foo-bar-baz.quiknode.pro/...'),
http('https://mainnet.infura.io/v3/...'),
])
},
})http
http 传输(http Transport) 通过 HTTP 连接到 JSON-RPC API,封装了 Viem 库中的 http 传输功能。
import {
createConfig,
http
} from 'wagmi'
import {mainnet, sepolia} from 'wagmi/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
connectors: [injected()],
transports: {
[mainnet.id]: http('https://foo-bar-baz.quiknode.pro/...'),
[sepolia.id]: http('https://foo-bar-sep.quiknode.pro/...'),
},
})webSocket
WebSocket 传输(webSocket Transport) 通过 WebSocket 连接到 JSON-RPC API,封装了 Viem 库中的 webSocket 传输功能。
import {
createConfig,
webSocket
} from 'wagmi'
import {mainnet, sepolia} from 'wagmi/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
connectors: [injected()],
transports: {
[mainnet.id]: webSocket('wss://foo-bar-baz.quiknode.pro/...'),
[sepolia.id]: webSocket('wss://foo-bar-sep.quicknode.pro/...'),
},
})WagmiProvider
Wagmi 的 React Context Provider 是一个 React 组件,用来在应用中全局提供 Wagmi 的配置和状态。
import {WagmiProvider} from 'wagmi'
import {config} from './config'
function App() {
return (
<WagmiProvider config={config}>
{/** ... */}
</WagmiProvider>
)
}import {type WagmiProviderProps} from 'wagmi'| Props 参数 | 说明 |
|---|---|
config | Config 对象以注入上下文。 |
initialState | 初始状态,以水合到 Wagmi 配置中。对 SSR 很有用。 |
reconnectOnMount | 控制组件挂载时是否自动尝试重新连接之前已连接的钱包。 |
常用 Hook
账户与连接管理
用于管理钱包连接与账户生命周期状态,获取已连接地址、连接进度与断开行为,常用于导航栏或全局状态展示。
import {useAccount} from 'wagmi'
function Profile() {
const {address, isConnected, status} = useAccount()
return <div>{isConnected ? address : status}</div>
}import {useConnect, useDisconnect} from 'wagmi'
function WalletButtons() {
const {connect, connectors} = useConnect()
const {disconnect} = useDisconnect()
return (
<>
{connectors.map((c) => (
<button key={c.uid} onClick={() => connect({connector: c})}>
{c.name}
</button>
))}
<button onClick={() => disconnect()}>Disconnect</button>
</>
)
}链上数据交互
用于读取余额与执行合约读写操作,内置缓存与请求状态管理,适合数据展示与交互按钮。
import {useBalance} from 'wagmi'
function Balance({address}: {address: `0x${string}`}) {
const {data} = useBalance({address})
return <div>{data?.formatted} {data?.symbol}</div>
}import {useReadContract, useWriteContract} from 'wagmi'
import {abi} from './abi'
function Token({address, user}: {address: `0x${string}`; user: `0x${string}`}) {
const {data} = useReadContract({
address,
abi,
functionName: 'balanceOf',
args: [user],
})
const write = useWriteContract()
const transfer = () =>
write.mutate({
address,
abi,
functionName: 'transfer',
args: [user, 100n],
})
return (
<>
<div>{data?.toString()}</div>
<button onClick={transfer}>Transfer</button>
</>
)
}事件监听与轮询
用于监听合约事件与新区块变化;支持 WebSocket 订阅与基于 HTTP 的轮询。
import {useWatchContractEvent} from 'wagmi'
import {abi} from './abi'
function EventWatcher({address}: {address: `0x${string}`}) {
useWatchContractEvent({
address,
abi,
eventName: 'Transfer',
onLogs(logs) {
console.log('Transfer logs', logs)
},
})
return null
}import {useBlockNumber} from 'wagmi'
function BlockPolling() {
const {data} = useBlockNumber({
watch: {pollingInterval: 4000},
})
return <div>Block: {data?.toString()}</div>
}常用 Actions
账户与配置
基于配置的命令式调用,获取当前账户与基础链信息,适合非 React 环境或通用工具函数。
import {getAccount} from '@wagmi/core'
import {config} from './config'
const account = getAccount(config)合约交互
以命令式方式进行合约读写,适合服务端、脚本与大规模前端项目;支持按需引入以优化打包体积。
import {readContract, writeContract} from 'wagmi/actions'
import {config} from './config'
import {abi} from './abi'
const balance = await readContract(config, {
address: '0xToken',
abi,
functionName: 'balanceOf',
args: ['0xUser'],
})
const hash = await writeContract(config, {
address: '0xToken',
abi,
functionName: 'transfer',
args: ['0xUser', 100n],
})事件监听与轮询
以命令式方式监听事件或区块变化,可配置轮询间隔与订阅行为,适合脚本与服务端环境。
import {watchContractEvent} from '@wagmi/core'
import {config} from './config'
import {abi} from './abi'
const unwatchLogs = watchContractEvent(config, {
address: '0xContract',
abi,
eventName: 'Transfer',
onLogs(logs) {
console.log('logs', logs)
},
})
// 调用 unwatchLogs() 以停止监听import {watchBlockNumber} from '@wagmi/core'
import {config} from './config'
const unwatchBlock = watchBlockNumber(config, {
pollingInterval: 1000,
onBlockNumber(n) {
console.log('block', n)
},
})
// 调用 unwatchBlock() 以停止监听进阶
multicall
Multicall 就是为了解决这个问题:把多个 eth_call 打包成一个合约调用,链上 Multicall 合约帮你执行,最后一次性返回结果。
import {multicall} from '@wagmi/core'
import {config} from './config'
const wagmigotchiContract = {
address: '0xecb504d39723b0be0e3a9aa33d646642d1051ee1',
abi: wagmigotchiABI,
} as const
const mlootContract = {
address: '0x1dfe7ca09e99d10835bf73044a23b73fc20623df',
abi: mlootABI,
} as const
const result = await multicall(config, {
contracts: [
{
...wagmigotchiContract,
functionName: 'getAlive',
},
{
...wagmigotchiContract,
functionName: 'getBoredom',
},
{
...mlootContract,
functionName: 'getChest',
args: [69],
},
{
...mlootContract,
functionName: 'getWaist',
args: [69],
},
],
})环境说明
- React 前端:通过
wagmi使用 React Hooks 与状态;底层传输由viem提供 - Node.js/SSR:使用
@wagmi/core或wagmi/actions搭配viem,不依赖 React - 稳定性:在
createConfig.transports中使用fallback([...])增强容错 - 体积优化:按需导入 hooks/actions,避免不必要的打包体积
// 例如Monad Test目前没有部署multicall合约,就需要自己亲自部署,在Etherscan有源码
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
/// @title Multicall3
/// @notice Aggregate results from multiple function calls
/// @dev Multicall & Multicall2 backwards-compatible
/// @dev Aggregate methods are marked `payable` to save 24 gas per call
/// @author Michael Elliot <mike@makerdao.com>
/// @author Joshua Levine <joshua@makerdao.com>
/// @author Nick Johnson <arachnid@notdot.net>
/// @author Andreas Bigger <andreas@nascent.xyz>
/// @author Matt Solomon <matt@mattsolomon.dev>
contract Multicall3 {
struct Call {
address target;
bytes callData;
}
struct Call3 {
address target;
bool allowFailure;
bytes callData;
}
struct Call3Value {
address target;
bool allowFailure;
uint256 value;
bytes callData;
}
struct Result {
bool success;
bytes returnData;
}
/// @notice Backwards-compatible call aggregation with Multicall
/// @param calls An array of Call structs
/// @return blockNumber The block number where the calls were executed
/// @return returnData An array of bytes containing the responses
function aggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes[] memory returnData) {
blockNumber = block.number;
uint256 length = calls.length;
returnData = new bytes[](length);
Call calldata call;
for (uint256 i = 0; i < length;) {
bool success;
call = calls[i];
(success, returnData[i]) = call.target.call(call.callData);
require(success, "Multicall3: call failed");
unchecked {++i;}
}
}
/// @notice Backwards-compatible with Multicall2
/// @notice Aggregate calls without requiring success
/// @param requireSuccess If true, require all calls to succeed
/// @param calls An array of Call structs
/// @return returnData An array of Result structs
function tryAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (Result[] memory returnData) {
uint256 length = calls.length;
returnData = new Result[](length);
Call calldata call;
for (uint256 i = 0; i < length;) {
Result memory result = returnData[i];
call = calls[i];
(result.success, result.returnData) = call.target.call(call.callData);
if (requireSuccess) require(result.success, "Multicall3: call failed");
unchecked {++i;}
}
}
/// @notice Backwards-compatible with Multicall2
/// @notice Aggregate calls and allow failures using tryAggregate
/// @param calls An array of Call structs
/// @return blockNumber The block number where the calls were executed
/// @return blockHash The hash of the block where the calls were executed
/// @return returnData An array of Result structs
function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) {
blockNumber = block.number;
blockHash = blockhash(block.number);
returnData = tryAggregate(requireSuccess, calls);
}
/// @notice Backwards-compatible with Multicall2
/// @notice Aggregate calls and allow failures using tryAggregate
/// @param calls An array of Call structs
/// @return blockNumber The block number where the calls were executed
/// @return blockHash The hash of the block where the calls were executed
/// @return returnData An array of Result structs
function blockAndAggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) {
(blockNumber, blockHash, returnData) = tryBlockAndAggregate(true, calls);
}
/// @notice Aggregate calls, ensuring each returns success if required
/// @param calls An array of Call3 structs
/// @return returnData An array of Result structs
function aggregate3(Call3[] calldata calls) public payable returns (Result[] memory returnData) {
uint256 length = calls.length;
returnData = new Result[](length);
Call3 calldata calli;
for (uint256 i = 0; i < length;) {
Result memory result = returnData[i];
calli = calls[i];
(result.success, result.returnData) = calli.target.call(calli.callData);
assembly {
// Revert if the call fails and failure is not allowed
// `allowFailure := calldataload(add(calli, 0x20))` and `success := mload(result)`
if iszero(or(calldataload(add(calli, 0x20)), mload(result))) {
// set "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)")))
mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000)
// set data offset
mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020)
// set length of revert string
mstore(0x24, 0x0000000000000000000000000000000000000000000000000000000000000017)
// set revert string: bytes32(abi.encodePacked("Multicall3: call failed"))
mstore(0x44, 0x4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000)
revert(0x00, 0x64)
}
}
unchecked {++i;}
}
}
/// @notice Aggregate calls with a msg value
/// @notice Reverts if msg.value is less than the sum of the call values
/// @param calls An array of Call3Value structs
/// @return returnData An array of Result structs
function aggregate3Value(Call3Value[] calldata calls) public payable returns (Result[] memory returnData) {
uint256 valAccumulator;
uint256 length = calls.length;
returnData = new Result[](length);
Call3Value calldata calli;
for (uint256 i = 0; i < length;) {
Result memory result = returnData[i];
calli = calls[i];
uint256 val = calli.value;
// Humanity will be a Type V Kardashev Civilization before this overflows - andreas
// ~ 10^25 Wei in existence << ~ 10^76 size uint fits in a uint256
unchecked {valAccumulator += val;}
(result.success, result.returnData) = calli.target.call{value: val}(calli.callData);
assembly {
// Revert if the call fails and failure is not allowed
// `allowFailure := calldataload(add(calli, 0x20))` and `success := mload(result)`
if iszero(or(calldataload(add(calli, 0x20)), mload(result))) {
// set "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)")))
mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000)
// set data offset
mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020)
// set length of revert string
mstore(0x24, 0x0000000000000000000000000000000000000000000000000000000000000017)
// set revert string: bytes32(abi.encodePacked("Multicall3: call failed"))
mstore(0x44, 0x4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000)
revert(0x00, 0x84)
}
}
unchecked {++i;}
}
// Finally, make sure the msg.value = SUM(call[0...i].value)
require(msg.value == valAccumulator, "Multicall3: value mismatch");
}
/// @notice Returns the block hash for the given block number
/// @param blockNumber The block number
function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) {
blockHash = blockhash(blockNumber);
}
/// @notice Returns the block number
function getBlockNumber() public view returns (uint256 blockNumber) {
blockNumber = block.number;
}
/// @notice Returns the block coinbase
function getCurrentBlockCoinbase() public view returns (address coinbase) {
coinbase = block.coinbase;
}
/// @notice Returns the block difficulty
function getCurrentBlockDifficulty() public view returns (uint256 difficulty) {
difficulty = block.difficulty;
}
/// @notice Returns the block gas limit
function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) {
gaslimit = block.gaslimit;
}
/// @notice Returns the block timestamp
function getCurrentBlockTimestamp() public view returns (uint256 timestamp) {
timestamp = block.timestamp;
}
/// @notice Returns the (ETH) balance of a given address
function getEthBalance(address addr) public view returns (uint256 balance) {
balance = addr.balance;
}
/// @notice Returns the block hash of the last block
function getLastBlockHash() public view returns (bytes32 blockHash) {
unchecked {
blockHash = blockhash(block.number - 1);
}
}
/// @notice Gets the base fee of the given block
/// @notice Can revert if the BASEFEE opcode is not implemented by the given chain
function getBasefee() public view returns (uint256 basefee) {
basefee = block.basefee;
}
/// @notice Returns the chain id
function getChainId() public view returns (uint256 chainid) {
chainid = block.chainid;
}
}import {getDefaultConfig} from "@rainbow-me/rainbowkit";
import {http} from "wagmi";
import {defineChain} from "viem";
const projectId = import.meta.env.VITE_WALLETCONNECT_PROJECT_ID;
const customNetwork = defineChain({
id: 10143,
name: "Monad Testnet",
nativeCurrency: {
name: "Monad",
symbol: "MON",
decimals: 18,
},
rpcUrls: {
default: {
http: ["https://testnet-rpc.monad.xyz"],
webSocket: ["wss://testnet-rpc.monad.xyz"],
},
public: {
http: ["https://testnet-rpc.monad.xyz"],
webSocket: ["wss://testnet-rpc.monad.xyz"],
},
},
blockExplorers: {
default: {
name: "Monad Explorer",
url: "https://explorer.testnet.monad.xyz",
},
},
contracts: {
multicall3: {
address: "0xeCf6cd06B29f902543C0A79c6a7D0b2090CB3356", // 你部署的地址
blockCreated: 32354777, // 部署的区块高度
},
},
});
const config = getDefaultConfig({
appName: "My RainbowKit App",
projectId,
chains: [customNetwork],
transports: {
[customNetwork.id]: http("https://testnet-rpc.monad.xyz"),
},
});
export default config;搞懂 wagmi 各种库
搞懂 wagmi、@wagmi/core 和 wagmi/actions 的区别与关系
在开发 Web3 应用时,你可能会遇到三个名字相似的包:
wagmi@wagmi/corewagmi/actions
很多同学在写 React dApp 时会疑惑:
- 为什么官方示例可以直接
import { useAccount } from "wagmi"? - 为什么有些场景需要
@wagmi/core或wagmi/actions?
本文帮你彻底梳理它们的定位和关系。
1️⃣ wagmi
wagmi 是 React 项目专用的库,内部基于
@wagmi/core实现,并封装成了 React Hooks。
常用 hook 如:useAccount、useBalance、useConnect 等。
import {useAccount, useBalance} from 'wagmi'
const {address, isConnected} = useAccount()
const {data: balance} = useBalance({address})- ✅ 只能在 React 组件中使用
- ✅ 内部依赖
@wagmi/core - 适合 前端 DApp 开发
2️⃣ @wagmi/core
@wagmi/core 是 wagmi 的核心底层库,不依赖 React,可以在 Node.js、Vue、SSR 或自定义框架中使用。
主要功能:
- 管理链配置(
createConfig) - 钱包连接器(Injected / MetaMask / WalletConnect 等)
- 提供 底层命令式 API(独立动作调用,如
getBalance(config, {...})、getBlockNumber(config)),无需显式创建 client - 可以在非 React 环境下使用
import {createConfig, getAccount, getBalance} from '@wagmi/core'
import {http} from 'viem'
import {mainnet} from 'viem/chains'
const config = createConfig({
chains: [mainnet],
transports: {[mainnet.id]: http()},
})
console.log(getAccount(config)) // 获取当前账户信息
const balance = await getBalance(config, {address: '0x123...'})
console.log(balance.formatted)适用场景:
- SSR / Node.js 脚本
- Vue、Svelte、Next.js server actions
- 不依赖 React 的前端项目
3️⃣ wagmi/actions
wagmi/actions 是一组独立函数(actions),提供命令式调用(imperative API),依赖
@wagmi/core的 client 或 config。
特点:
- 树摇优化(tree-shakable),只打包你导入的函数
- 需要显式传入 client(publicClient / getClient)
- 适合大型前端 DApp 或对 bundle 体积敏感的项目
import {getBalance, writeContract} from 'wagmi/actions'
import {publicClient} from './client'
const balance = await getBalance(publicClient, {address: '0x123...'})
console.log(balance.formatted)
await writeContract(publicClient, {
address: '0xTokenAddress',
abi: erc20Abi,
functionName: 'transfer',
args: ['0xabc...', 1000n],
})适用场景:
- 想要完全命令式调用,不使用 Hook
- 脚本、工具函数、服务端调用
- 优化前端 bundle 体积
4️⃣ 三者对比
| 特性 | wagmi | @wagmi/core | wagmi/actions |
|---|---|---|---|
| 依赖环境 | React | 任意 JS/TS 环境 | 任意 JS/TS 环境 |
| API 类型 | Hook(useAccount, useBalance) | 独立动作 / 配置管理 / 钱包连接 | 独立函数(命令式) |
| 是否依赖 React | ✅ | ❌ | ❌ |
| 是否需要 client | ❌ | 可选(独立动作可直接传 config) | ✅ 必须传 client |
| 树摇优化 | ⚠️ 不完全 | ✅(新版本支持独立动作) | ✅ 完全 tree-shakable |
| 适用场景 | 前端 React DApp | SSR / Node.js / Vue / 脚本 / 非 React 前端 | 前端优化 / 大型 DApp / 脚本 / 命令式调用 |
💡 总结
- React 项目 → 用
wagmi,直接使用 Hook 最方便 - 非 React / SSR / Node.js → 用
@wagmi/core,可直接调用独立动作 - 前端 DApp 优化 bundle / 命令式调用 → 用
wagmi/actions,完全 tree-shakable,支持 client 管理
TypeScript 类型安全
TypeScript 要求:
Wagmi Core 很注重类型安全,意思是它可以帮你在写代码时提前发现错误,而不是等到程序跑起来才报错。
- 需要 TypeScript >= 5.0.4
- TypeScript 的小版本有时候会破坏类型,所以建议:
- 固定 @wagmi/core 和 typescript 的版本
- 升级时注意类型可能会变
- 非类型相关的功能遵循版本规范(不会乱改)
- tsconfig.json 一定要打开严格模式:
{
"compilerOptions": {
"strict": true
}
}如何正确使用 ABI 和 Typed Data(函数、数据定义):
Wagmi 可以根据你写的合约 ABI 自动帮你推断函数名和参数类型,这样:
- 写函数时自动提示函数名
- 拼写错会直接报错
- 自动推断函数参数和返回值类型
怎么实现:必须给 ABI 或 Typed Data 用 as const 或者在调用时直接写在配置里。
import { readContract } from '@wagmi/core'
// 用 const 确定 ABI
const erc721Abi = [
{
name: 'balanceOf',
type: 'function',
inputs: [{ name: 'owner', type: 'address' }],
outputs: [{ name: '', type: 'uint256' }]
},
] as const
const result = await readContract({
address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
abi: erc721Abi,
functionName: 'balanceOf',
args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'],
})如果没有 as const,TypeScript 就不知道函数名只能是 balanceOf,拼写错了也不会报错。
Wagmi CLI
TypeScript 不能直接把 JSON 文件当 const 用
Wagmi 提供 CLI 工具:
- 可以从 Etherscan、Hardhat、Foundry 自动拿 ABI
- 自动生成 const 类型的 ABI
- 保证类型推断正确
npm install --save-dev @wagmi/cli
# yarn
yarn add -D @wagmi/clinpx wagmi init
# yarn
yarn wagmi initimport { defineConfig } from '@wagmi/cli'
export default defineConfig({
out: 'src/generated.ts',
contracts: [],
plugins: [],
})wagmi.config.ts 配置文件引入 abi json 文件,运行 npx wagmi generate 转化成安全类型文件,生成至 ./src/generated。
import { defineConfig } from '@wagmi/cli'
import {abi} from "@/lib/ERC20";
export default defineConfig({
out: 'src/generated', // 输出文件夹,生成的 ABI 和类型会放这里
contracts: [
{
name: 'erc20',
abi: abi
},
],
})