wagmi Tempo 链上事件监听实战:policy.watchCreate监听策略创建事件的完整指南
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
本篇指南以 wagmi 仓库中site/tempo/actions/policy.watchCreate.md文档为核心,系统讲解 Tempo 链上 TIP403 策略(Policy)注册表中"策略创建事件"的实时监听方案。你将掌握Actions.policy.watchCreate的调用方式、完整参数语义、底层实现原理,以及如何在 React 应用中通过Hooks.policy.useWatchCreate声明式监听,并能结合仓库源码与测试用例验证监听行为。本文适用于需要在 Tempo 链上构建代币访问控制、合规风控或链上审计类应用的前端开发者。
背景:TIP403 策略注册表与事件监听的意义
Tempo 链引入了一套**传输策略(Transfer Policy)**机制,用于对 TIP-20 代币的转账进行访问控制。在site/tempo/actions/index.md中,Policy Actions 被定义为"Creates a new transfer policy for token access control"(创建用于代币访问控制的传输策略)。一个策略可以是白名单类型(whitelist)或黑名单类型(blacklist),管理员可以后续修改名单、更换管理员,最终通过policy.isAuthorized判断某个地址是否被授权。
这些策略统一登记在TIP403 Registry(策略注册表)合约中。当任何账户通过policy.create/policy.createSync创建新策略时,注册表会发出对应的事件。policy.watchCreate就是 wagmi 为 Tempo 提供的、监听该"策略创建事件"的响应式原语——它订阅事件流,在每次新策略创建时回调你的处理函数。
监听类 Action 在整个 Tempo Action 体系中是独立的成员,与查询类(policy.getData、policy.isAuthorized)、写入类(policy.create、policy.setAdmin、policy.modifyWhitelist、policy.modifyBlacklist)形成互补,让应用既能发起操作,也能实时感知链上状态变化。
快速上手:监听策略创建事件
Actions.policy.watchCreate接收config与参数对象,返回一个取消订阅函数。最简用法如下(取自site/tempo/actions/policy.watchCreate.md的 Usage 示例):
import { Actions } from 'wagmi/tempo' import { config } from './config' const unwatch = Actions.policy.watchCreate(config, { onPolicyCreated(args, log) { console.log('args:', args) }, }) // Later, stop watching unwatch()其中config需要是配置了 Tempo 链的 wagmi 配置。仓库中的模板配置site/snippets/react/config-tempo.ts给出了完整可运行版本:
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(), }, })要点:chains必须包含tempo链,transports为tempo.id配置http()传输层,并通过tempoWallet()注入钱包连接器。config也支持通过chainId参数显式指定要监听的链,默认使用配置中的链。
返回类型:一个"取消订阅"函数
policy.watchCreate的返回类型为() => void。
它返回一个用于取消订阅事件的函数。调用后,事件监听立即终止。在组件卸载、页面切换或业务不再需要监听时务必调用,避免内存泄漏与无效 RPC 订阅。
参数详解
Actions.policy.watchCreate(config, parameters)的参数对象完整语义如下。
onPolicyCreated(必填)
- 类型:
function
declare function onPolicyCreated(args: Args, log: Log): void type Args = { /** ID of the created policy */ policyId: bigint /** Type of policy */ type: PolicyType /** Address that created the policy */ updater: Address }策略创建成功时调用。回调收到两个参数:
args:事件解码参数。policyId为新建策略的 ID(bigint);type为策略类型(PolicyType);updater为创建策略的地址(Address)。log:对应的原始事件日志对象,可用于获取区块号、交易哈希等链上元信息。
PolicyType的取值可以从仓库测试中得到印证:在packages/core/src/tempo/actions/policy.test.ts中,createSync以type: 'whitelist'创建时返回policyType: 0,以type: 'blacklist'创建时返回policyType: 1;而getData校验的结果data.type分别为'whitelist'与'blacklist'。
args(可选)
- 类型:
object
type Args = { /** Filter by policy ID */ policyId?: bigint | bigint[] | null /** Filter by updater address */ updater?: Address | Address[] | null }可选过滤参数,用于缩小监听范围:
policyId:按策略 ID 过滤,可传单个bigint或bigint[]。例如只关心某个特定策略是否被创建(注意创建事件产生的是新 ID,实际更常用于配合特定地址过滤)。updater:按创建者地址过滤,可传单个Address或Address[]。例如只监听当前用户或某组受信任地址发起的策略创建。
fromBlock(可选)
- 类型:
bigint
开始监听的起始区块。传入后将从该区块起扫描事件(含历史事件回放),不传则从最新区块开始监听。
onError(可选)
- 类型:
function
declare function onError(error: Error): void当获取新区块时发生错误所调用的回调。订阅在运行期间可能因网络抖动、RPC 异常等原因出错,可通过该回调捕获Error并做日志记录或告警。
poll(可选)
- 类型:
true
启用轮询模式。默认情况下监听基于订阅推送,若你的环境(如某些不支持订阅的 RPC 端点)需要轮询,可传入poll: true。
pollingInterval(可选)
- 类型:
number
轮询频率(毫秒)。仅在启用轮询模式时生效;未显式传入时,默认使用Client的pollingInterval配置。
源码级实现:从 wagmi 到 viem 的委托调用链
policy.watchCreate并不是重新实现的事件订阅逻辑,而是对底层viem/tempoAction 的类型安全封装。核心实现位于packages/core/src/tempo/actions/policy.ts的watchCreate函数:
export function watchCreate<config extends Config>( config: config, parameters: watchCreate.Parameters<config>, ) { const { chainId, ...rest } = parameters const client = config.getClient({ chainId }) return Actions.policy.watchCreate(client, rest) } export declare namespace watchCreate { export type Parameters<config extends Config> = ChainIdParameter<config> & Actions.policy.watchCreate.Parameters }调用链可以概括为三层:
- 参数拆分:从参数中解构出
chainId,其余参数(onPolicyCreated、args、fromBlock、onError、poll、pollingInterval等)原样透传。 - 客户端获取:通过
config.getClient({ chainId })取得对应链的 viem 客户端——这与查询类 Action(如getData、isAuthorized使用config.getClient)一致;而写入类 Action(create、setAdmin)则使用getConnectorClient获取连接器客户端,因为写入需要钱包签名。 - 委托底层:将客户端与参数一并交给
Actions.policy.watchCreate(client, rest)执行真实的事件监听,并把返回的取消订阅函数直接返回给调用方。
Parameters类型通过交叉类型合并了ChainIdParameter(可显式指定chainId)与 viem 底层的Actions.policy.watchCreate.Parameters,保证类型提示与底层完全对齐。这也解释了为何原文档在"Viem"一节标注了policy.watchCreate的 viem 对应关系:wagmi 的 Tempo Actions 本质上是 viem Tempo Actions 的配置感知封装。
测试验证:监听行为如何被确认
仓库在packages/core/src/tempo/actions/policy.test.ts中为watchCreate编写了端到端测试,可以直接印证文档描述的行为:
describe('watchCreate', () => { test('default', async () => { await connect(config, { connector: config.connectors[0]!, }) const events: any[] = [] const unwatch = policy.watchCreate(config, { onPolicyCreated: (args, log) => { events.push({ args, log }) }, }) // create policy await policy.createSync(config, { type: 'whitelist', }) await vi.waitFor(() => { expect(events.length).toBeGreaterThanOrEqual(1) }) unwatch() expect(events[0].args.policyId).toBeDefined() expect(events[0].args.updater).toBe(account.address) expect(events[0].args.type).toBe('whitelist') }) })该测试完整还原了"先订阅、再触发、后断言"的标准流程:
- 先
connect连接config.connectors[0]对应的测试账户; - 调用
watchCreate注册onPolicyCreated回调,将事件推入events数组; - 通过
createSync实际创建一条whitelist类型策略,触发注册表事件; - 用
vi.waitFor等待事件到达,断言args.policyId已定义、args.updater为发起创建的交易账户地址、args.type为'whitelist'; - 最后调用
unwatch()取消订阅。
这组断言与文档中Args的三个字段(policyId、type、updater)一一对应,是理解事件负载的最佳范例。同一测试文件中,watchAdminUpdated、watchWhitelistUpdated、watchBlacklistUpdated的测试采用了相同的模式,说明"watch 系列"Action 的参数与行为约定完全一致。
React 集成:Hooks.policy.useWatchCreate声明式监听
在 React 应用中无需手动管理unwatch,可直接使用Hooks.policy.useWatchCreate。其实现位于packages/react/src/tempo/hooks/policy.ts:
import { Hooks } from 'wagmi/tempo' function App() { Hooks.policy.useWatchCreate({ onPolicyCreated(args) { console.log('Policy created:', args) }, }) return <div>Watching for policy creation...</div> }Hook 内部通过useEffect封装了完整的订阅生命周期:
export function useWatchCreate< config extends Config = ResolvedRegister['config'], >(parameters: useWatchCreate.Parameters<config> = {}) { const { enabled = true, onPolicyCreated, ...rest } = parameters const config = useConfig({ config: parameters.config }) const configChainId = useChainId({ config }) const chainId = parameters.chainId ?? configChainId useEffect(() => { if (!enabled) return if (!onPolicyCreated) return return Actions.policy.watchCreate(config, { ...rest, chainId, onPolicyCreated, }) }, [ config, enabled, chainId, onPolicyCreated, rest.fromBlock, rest.onError, rest.poll, rest.pollingInterval, ]) }值得注意的设计细节:
- 自动清理:
useEffect的清理函数正是Actions.policy.watchCreate返回的unwatch,组件卸载时自动取消订阅; enabled开关:额外提供的enabled?: boolean参数(默认true),置为false时可暂停监听而不卸载组件;- 响应式依赖:
chainId默认取自useChainId,链切换时自动重订阅;fromBlock、onError、poll、pollingInterval等参数变化同样会触发重建订阅; config透传:可通过parameters.config覆盖默认的 wagmi 配置。
Hook 的参数类型useWatchCreate.Parameters是Actions.policy.watchCreate.Parameters的 ExactPartial 版本,并额外合并了ConfigParameter与enabled,因此核心参数语义与命令式版本完全一致。
与策略生命周期其他 Action 的配合
watchCreate不是孤立存在的,它是策略生命周期管理的一部分。完整的 Policy Actions 清单见site/tempo/actions/index.md,以下是与事件监听最常配合的相邻 Action:
| Action | 作用 | 配合场景 |
|---|---|---|
policy.create/createSync | 创建传输策略 | 触发watchCreate事件的来源 |
policy.getData | 读取策略数据(管理员、类型) | 收到创建事件后用policyId反查策略详情 |
policy.isAuthorized | 判断地址是否被策略授权 | 事件驱动的权限校验 |
policy.setAdmin | 更换策略管理员 | 配合watchAdminUpdated监控管理变更 |
policy.modifyWhitelist | 修改白名单 | 配合watchWhitelistUpdated监控名单变更 |
policy.modifyBlacklist | 修改黑名单 | 配合watchBlacklistUpdated监控名单变更 |
一个典型的实战组合是:监听watchCreate获得policyId→ 调用getData获取策略类型与管理员 → 通过isAuthorized判断某个用户是否受该策略约束。另外注意,isAuthorized存在两个特殊策略 ID(见packages/core/src/tempo/actions/policy.test.ts):policyId 0n为"永远拒绝"、policyId 1n为"永远允许",在业务逻辑中可作为特殊分支处理。
最佳实践与注意事项
- 及时取消订阅:命令式用法务必在不需要时调用返回的
unwatch();React 用法由 Hook 自动处理,但要注意enabled与依赖数组的配合。 - 按需过滤:事件量大时优先使用
args(policyId/updater)过滤,减少无效回调与网络负载。 - 轮询 vs 订阅:默认订阅模式实时性最好;RPC 不支持订阅或需要兼容性时启用
poll: true并合理设置pollingInterval(未设置时沿用 Client 的pollingInterval配置)。 - 错误处理:订阅期间区块拉取可能出错,通过
onError捕获并记录,避免静默失败。 - 历史回放:需要补偿缺失事件时使用
fromBlock从指定区块开始监听,可结合最新区块号回溯补全。 - 事件与交易联动:
watchCreate回调中的log对象可用于关联交易哈希、区块高度,与createSync返回的receipt相互印证。
延伸阅读
- 本文核心参考:
policy.watchCreate官方文档 - Tempo Actions 总览(含全部 Policy/Token/DEX 等 Action):
site/tempo/actions/index.md - 底层实现:
packages/core/src/tempo/actions/policy.ts - 端到端测试:
packages/core/src/tempo/actions/policy.test.ts - React Hook 实现:
packages/react/src/tempo/hooks/policy.ts - Tempo 配置模板:
site/snippets/react/config-tempo.ts
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考