wagmi Tempo 系列:`Actions.token.watchMint` 监听 TIP20 代币铸造事件完整指南
2026/9/18 4:30:29 网站建设 项目流程

wagmi Tempo 系列:Actions.token.watchMint监听 TIP20 代币铸造事件完整指南

【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi

导读

Actions.token.watchMint是 wagmi 为 Tempo 网络提供的实时事件监听 Action,用于订阅 TIP20 代币的Mint(铸造)事件:一旦链上发生铸造,回调函数会立刻收到铸造参数与事件日志。本指南以 site/tempo/actions/token.watchMint.md 为骨架,结合 packages/core/src/tempo/actions/token.ts 与 React 侧 packages/react/src/tempo/hooks/token.ts 的源码实现,从基本用法、参数全解、轮询机制到测试用例与 React Hook 封装,逐层讲解如何在以太坊应用中接入 TIP20 铸造事件的订阅能力。读完你将能独立完成「创建 Config → 订阅铸造事件 → 处理回调 → 注销监听」的完整闭环,并理解其底层调用链与适用边界。

前置知识:Tempo 与 TIP20

Tempo 是 wagmi 内置支持的网络(在仓库中以 tempo 目录承载全部相关 Action 与 Hook,链定义位于wagmi/chains)。TIP20 是 Tempo 上的代币标准,支持创建(token.create)、铸造(token.mint)、转账(token.transfer)、销毁(token.burn)等操作,并引入了issuer(发行者)等角色概念(见测试中对grantRoles的调用,packages/core/src/tempo/actions/token.test.ts)。

watchMint属于「只读订阅」类 Action:它不签名、不发交易,只持续监听链上事件,并在每次铸造发生时把**参数(args)与日志(log)**传给回调。因此它天然适合用于实时余额刷新、铸造监控面板、通知推送等场景。

基本用法:订阅一次铸造事件

原文档给出的最小可用示例如下:

import { Actions } from 'wagmi/tempo' import { config } from './config' const unwatch = Actions.token.watchMint(config, { token: '0x20c0000000000000000000000000000000000001', onMint(args, log) { console.log('args:', args) }, }) // Later, stop watching unwatch()

其中config来自 site/snippets/react/config-tempo.ts 所示的 Tempo 专用配置:

import { createConfig, http } from 'wagmi' import { tempo } from 'wagmi/chains' import { tempoWallet } from 'wagmi/tempo' export const config = createConfig({ connectors: [tempoWallet()], chains: [tempo], multiInjectedProviderDiscovery: false, transports: { [tempo.id]: http(), }, })

要点拆解:

  • 导入路径:在 wagmi 聚合包下从wagmi/tempo导入Actions;使用@wagmi/core时则从@wagmi/core/tempo导入(源码注释中的写法,见 packages/core/src/tempo/actions/token.ts)。
  • token参数:传 TIP20 代币地址(Address)或代币 ID(bigint),两种形式都支持。
  • 返回的unwatch:类型为() => void,调用后即停止监听并释放内部资源;建议在组件卸载或不再需要监听时调用,避免事件泄漏。

从源码看,watchMint的实现非常薄(packages/core/src/tempo/actions/token.ts):

export function watchMint<config extends Config>( config: config, parameters: watchMint.Parameters<config>, ) { const { chainId, ...rest } = parameters const client = config.getClient({ chainId }) return Actions.token.watchMint(client, rest) }

即:先从 Config 中取出对应chainId的 viem Client,再把剩余参数原样转发给 viem/tempo 的Actions.token.watchMint。这说明wagmi 层负责「配置与客户端解析」,真正的事件订阅逻辑由 viem 的 Tempo 实现承担(文档底部也给出了对应的 viem 参考页)。

参数详解:onMinttoken、过滤与轮询

原文档完整列出了全部参数,下面逐一展开说明。

onMint(必填)

类型为function,签名:

declare function onMint(args: Args, log: Log): void type Args = { /** Address that received the tokens */ to: Address /** Amount minted */ amount: bigint }
  • 第一个参数args是铸造事件的核心数据:to为收款地址,amount为铸造数量(bigint类型,注意与parseUnits('100', 6)等小数位换算配合使用,见下文测试用例)。
  • 第二个参数log是对应的事件日志对象(包含区块、交易、日志索引等链上元信息)。

token

  • 类型Address | bigint
  • 指定要监听的 TIP20 代币,可以是合约地址(如文档示例中的0x20c0...0001),也可以是代币 ID(数值形式)。

args(可选)

类型为object,当前支持按收款地址过滤:

type Args = { /** Filter by recipient address */ to?: Address | Address[] | null }

传入单个地址或地址数组时,只有to匹配的铸造事件才会触发回调;不传则监听该代币的全部铸造事件。

fromBlock(可选)

  • 类型bigint
  • 指定从哪个区块高度开始监听。默认从最新区块开始;设置历史区块后,事件扫描将从该高度起,适用于「上线后补拉遗漏事件」的场景。

onError(可选)

  • 类型function
  • 签名declare function onError(error: Error): void
  • 当「获取新区块」过程中发生错误时调用,用于日志上报与异常兜底。文档原话是 "The callback to call when an error occurred when trying to get for a new block",即它捕获的是轮询/扫描过程中拉取新区块失败的错误,而非事件本身的业务错误。

poll(可选)

  • 类型true
  • 置为true时启用轮询模式。Tempo 的实时事件默认走订阅通道;若当前环境(如某些 RPC 提供方)不支持订阅,可退回轮询模式。

pollingInterval(可选)

  • 类型number
  • 轮询频率,单位为毫秒(ms)。默认取 Client 的pollingInterval配置(即 Config 中 transport 层设置的值),显式传入则可覆盖默认值,例如高频铸造场景下可缩短到 1000ms 以内。

参数速查表

参数类型必填说明
onMintfunction铸造事件回调,接收(args, log)
tokenAddress \| bigintTIP20 代币地址或 ID
args.toAddress \| Address[] \| null按收款地址过滤
fromBlockbigint起始监听区块高度
onErrorfunction拉取新区块出错时的回调
polltrue启用轮询模式
pollingIntervalnumber轮询间隔(ms),默认取 Client 配置

从源码看调用链:Config 到 viem Client

watchMint之所以能跨框架复用,是因为它在 wagmi 层的实现只有「取 Client + 转发」两步:

  1. config.getClient({ chainId })依据链 ID 解析出 viem Client(transport、链配置均来自createConfig);
  2. chainId剥离后的参数(tokenonMintpoll等)直接传给Actions.token.watchMint(client, rest)(packages/core/src/tempo/actions/token.ts)。

类型定义方面,watchMint.ParametersChainIdParameter<config> & Actions.token.watchMint.Parameters的交集(packages/core/src/tempo/actions/token.ts),即它额外继承了chainId参数——当 Config 配置了多条链时,可用chainId显式指定在哪个链上监听,未指定则使用默认链。

真实工作流:测试用例如何验证订阅闭环

仓库中的测试用例完整演示了「创建代币 → 授权发行者 → 订阅 → 触发铸造 → 断言事件 → 注销」的全流程(packages/core/src/tempo/actions/token.test.ts):

// 1. 连接第一个 connector await connect(config, { connector: config.connectors[0]! }) // 2. 创建一个新代币 const { token: tokenAddr } = await token.createSync(config, { currency: 'USD', name: 'Watch Mint Token', symbol: 'WATCHMINT', }) // 3. 给自己授予 issuer 角色(铸造权限) await token.grantRolesSync(config, { token: tokenAddr, roles: ['issuer'], to: account.address, }) // 4. 订阅铸造事件 const events: any[] = [] const unwatch = token.watchMint(config, { token: tokenAddr, onMint: (args) => { events.push(args) }, }) // 5. 触发一次铸造 await token.mintSync(config, { token: tokenAddr, to: account.address, amount: parseUnits('100', 6), }) // 6. 等待事件到达并断言 await vi.waitFor(() => { expect(events.length).toBeGreaterThan(0) }) unwatch() // 7. 校验事件内容 expect(events[0]?.to).toBe(account.address) expect(events[0]?.amount).toBe(parseUnits('100', 6))

该测试同时验证了几个关键事实:

  • 事件参数结构与文档一致args.to对应收款地址,args.amount对应铸造数量;金额是bigint,用parseUnits('100', 6)按 6 位小数构造。
  • 异步到达:监听回调是异步触发的,测试用vi.waitFor轮询等待事件数组非空。
  • unwatch()幂等可安全调用:事件断言完成后立即注销。
  • 前置依赖:铸造需要issuer角色,订阅事件前需先完成授权,否则铸造不会成功、事件也不会产生。

React 封装:Hooks.token.useWatchMint

在 React 应用中,不必手动管理 Config 与useEffect,仓库提供了现成的 Hook 封装(packages/react/src/tempo/hooks/token.ts):

import { Hooks } from 'wagmi/tempo' function App() { Hooks.token.useWatchMint({ onMint(args) { console.log('Mint:', args) }, }) return <div>Watching for mints...</div> }

Hook 的源码实现揭示了几点设计细节:

export function useWatchMint<config extends Config = ResolvedRegister['config']>( parameters: useWatchMint.Parameters<config> = {}, ) { const { enabled = true, onMint, token, ...rest } = parameters const config = useConfig({ config: parameters.config }) const configChainId = useChainId({ config }) const chainId = parameters.chainId ?? configChainId useEffect(() => { if (!enabled) return if (!onMint) return if (!token) return return Actions.token.watchMint(config, { ...rest, chainId, onMint, token, }) }, [ config, enabled, chainId, token, onMint, rest.fromBlock, rest.onError, rest.poll, rest.pollingInterval, ]) }
  • 自动生命周期管理:Hook 在useEffect中调用Actions.token.watchMint,并把其返回的unwatch作为 effect 清理函数返回——组件卸载或依赖变化时自动注销,无需手动清理。
  • 守卫条件enabledonMinttoken任一不满足时直接跳过订阅,避免无效监听。
  • 依赖数组精确fromBlockonErrorpollpollingInterval变化都会重建订阅,保证参数最新。
  • chainId智能推导:未显式传入chainId时,使用useChainId得到当前链。
  • 参数类型useWatchMint.ParametersExactPartial<Actions.token.watchMint.Parameters> & ConfigParameter & { enabled?: boolean },即所有参数可省略(onMinttoken可在后续渲染中再提供),并额外支持config覆盖与enabled开关。

边界与最佳实践

  • 监听起点:默认从最新区块开始,历史事件不会补发;需要历史数据时显式传fromBlock,或将事件落库后再用getContractEvents类查询接口回溯。
  • 网络容错:订阅通道不可用时启用poll: true,并通过pollingInterval控制频率与 RPC 压力;onError用于捕获扫描过程中的异常并做重连/告警。
  • 金额处理amountbigint,展示前需按代币小数位格式化(如parseUnits('100', 6)对应的 6 位小数)。
  • 资源释放:非 React 场景务必在合适时机调用返回的unwatch();React 场景交给 Hook 自动处理。
  • 多链场景:Config 配置多链时,通过chainId参数指定监听目标链,配合 config-tempo.ts 中transports[tempo.id]的配置方式理解链与 transport 的绑定关系。

延伸阅读

  • 同系列事件监听:token.watchTransfertoken.watchCreatetoken.watchBurntoken.watchRole等均位于 site/tempo/actions 目录,参数结构与本文一致;
  • React Hook 完整列表见 packages/react/src/tempo/hooks/token.ts;
  • Tempo 的完整 Action 与 Hook 索引可参考 site/tempo/actions/index.md 与 site/tempo/hooks/index.md;
  • 若需自定义轮询间隔,可在 site/snippets/react/config-tempo.ts 的createConfig中为 transport 配置pollingInterval,作为watchMint的默认值。

【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询