wagmi Tempopolicy.create完全指南:用白名单/黑名单策略为 Token 转账做访问控制
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
本文是 wagmi 仓库中 Tempo 模块policy.create动作(Action)的深度实战指南。policy.create用于创建一条全新的转账策略(transfer policy),实现 Token 层面的访问控制:白名单策略只允许被列入的地址转账,黑名单策略则允许除列入地址外的所有地址转账。读完本文,你将掌握create/createSync两种调用形态的完整用法、全部参数语义、返回值结构,以及它在核心包源码中的底层调用链与测试验证,并了解与之配套的setAdmin、modifyWhitelist、modifyBlacklist、getData、isAuthorized、watchCreate等策略生命周期操作。
1.policy.create是什么
在 Tempo 协议(TIP-403 相关规范)中,转账策略是一种用于 Token 访问控制的可编程规则。每条策略都有一个唯一 ID(policyId),并归属于一个管理员(admin)。策略决定了哪些地址被允许或禁止进行 Token 转账操作。
policy.create是创建该策略的入口动作,它支持两种策略类型:
| 类型 | 行为 |
|---|---|
'whitelist' | 仅允许被列入addresses的地址进行转账 |
'blacklist' | 允许所有地址转账,但被列入addresses的地址除外 |
从源码结构看,策略创建只是整个policy模块的一部分。在 packages/core/src/tempo/actions/policy.ts 中,与create并列的还有setAdmin(设置管理员)、modifyWhitelist(增删白名单成员)、modifyBlacklist(增删黑名单成员)、getData(读取策略数据)、isAuthorized(校验地址是否被授权)以及watchCreate/watchAdminUpdated/watchWhitelistUpdated/watchBlacklistUpdated等事件监听函数,共同构成策略的完整生命周期。
本文对应仓库文档:site/tempo/actions/policy.create.md。
2. 环境准备:创建 Tempo 链的 wagmi Config
在调用任何 Tempo 动作之前,需要先构造一个连接到 Tempo 链的 wagmi Config。仓库中的标准示例位于 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(), }, })要点说明:
tempoWallet()是仓库packages/connectors中提供的 Tempo 钱包连接器,负责提供签名账户;chains: [tempo]指定应用运行在 Tempo 链上;multiInjectedProviderDiscovery: false关闭多注入 Provider 探测,避免与 Tempo 钱包之外的浏览器钱包冲突;transports中为 Tempo 链配置http()传输层,用于读写链上数据。
3. 同步用法:Actions.policy.createSync
createSync是create的同步变体:它会等待交易被打包上链后再返回结果。适合对响应延迟不敏感、希望一步拿到策略 ID 和交易收据的场景。
import { Actions } from 'wagmi/tempo' import { config } from './config' const { policyId, policyType, receipt } = await Actions.policy.createSync(config, { addresses: [ '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb', '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', ], type: 'whitelist', }) console.log('Policy ID:', policyId) // @log: Policy ID: 1n返回值中的receipt是完整的TransactionReceipt,可用于进一步确认交易状态(如receipt.transactionHash)。
从源码看,createSync的实现位于 packages/core/src/tempo/actions/policy.ts:
export async function createSync<config extends Config>( config: config, parameters: createSync.Parameters<config>, ): Promise<Actions.policy.createSync.ReturnValue> { const { account, chainId, connector } = parameters const client = await getConnectorClient(config, { account, assertChainId: false, chainId, connector, }) return Actions.policy.createSync(client, parameters as never) }关键点:
- 它先通过
getConnectorClient从 wagmi Config 中解析出连接器客户端(支持account/chainId/connector覆盖); assertChainId: false表示不强制校验当前链 ID,允许调用方显式指定目标链;- 随后将底层执行委托给 viem 的
Actions.policy.createSync,也就是说 wagmi 的 Tempo 动作是建立在 viem Tempo 动作之上的一层框架适配(获取连接器客户端、注入 wagmi Config 上下文)。
4. 异步用法:Actions.policy.create+ 手动等待
如果你更关注性能,不希望动作阻塞到交易确认,则应使用非 Sync 版本的policy.create。它只返回交易哈希,随后由你手动等待交易收据,并通过事件解析出policyId:
import { Actions as viem_Actions } from 'viem/tempo' import { Actions } from 'wagmi/tempo' import { waitForTransactionReceipt } from 'wagmi/actions' const hash = await Actions.policy.create(config, { addresses: [ '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb', '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', ], type: 'whitelist', }) const receipt = await waitForTransactionReceipt(config, { hash }) const { args: { policyId } } = viem_Actions.policy.create.extractEvent(receipt.logs)这种“先拿 hash、后查收据”的模式,可以在交易在途(pending)期间继续做其他事情,例如同时发起多笔策略操作或展示交易进度。extractEvent是 viem Tempo 动作提供的事件解析工具,可从收据日志中精确提取PolicyCreated事件的参数。
两种形态的选择建议:
- 需要尽快拿到
policyId或对后续逻辑强依赖收据 → 用createSync; - 需要并发执行多笔交易、优化吞吐 → 用
create+waitForTransactionReceipt。
5. 返回值结构(Return Type)
无论create还是createSync,最终返回的数据形状如下:
type ReturnType = { /** ID of the created policy */ policyId: bigint /** Type of the policy (0 = whitelist, 1 = blacklist) */ policyType: number /** Transaction receipt */ receipt: TransactionReceipt /** Address that created the policy */ updater: Address }字段含义:
policyId:新建策略的唯一 ID(bigint),后续setAdmin、modifyWhitelist、modifyBlacklist、getData、isAuthorized等动作都需要它作为入参;policyType:策略类型的数值编码,0表示白名单、1表示黑名单;receipt:交易收据,Sync 变体返回已确认的收据;updater:发起创建操作的地址,通常即当前连接的账户地址。
上述数值编码(0 = whitelist, 1 = blacklist)在仓库测试中也有印证:见 packages/core/src/tempo/actions/policy.test.ts,其中断言createSync创建白名单策略后policyType为0、黑名单策略后为1,且updater等于当前连接的账户地址。
6. 参数详解
6.1 type(必填)
- 类型:
'whitelist' | 'blacklist'
要创建的策略类型。白名单策略只允许被列入的地址,黑名单策略允许除被列入地址之外的所有地址。这也是policyType返回值(0 / 1)对应的语义来源。
6.2 addresses(可选)
- 类型:
Address[]
用于初始化策略的地址数组。创建时即把一批地址写入白名单或黑名单,省去创建后再逐条modifyWhitelist/modifyBlacklist的步骤。
6.3 通用写交易参数(optional)
以下参数来自仓库中共享的参数说明文档 site/shared/tempo-write-parameters.md,适用于包括policy.create在内的所有 Tempo 写动作:
| 参数 | 类型 | 说明 |
|---|---|---|
account | Account \| Address | 用于发送交易的账户,默认使用已连接的 wagmi 账户 |
feeToken | Address \| bigint | 交易的费用 Token,可以是 TIP-20 Token 地址或 ID |
feePayer | Account \| true | 代付费用的账户;可传 Viem Account,若使用 Fee Payer Service 则传true |
gas | bigint | 交易的 Gas 上限 |
maxFeePerGas | bigint | 交易的每单位 Gas 最高费用 |
maxPriorityFeePerGas | bigint | 交易的每单位 Gas 最高优先费(小费) |
nonce | number | 交易的 nonce |
nonceKey | 'expiring' \| bigint | 交易的 nonce key |
validBefore | number | 交易必须被包含进区块的 Unix 时间戳上限 |
validAfter | number | 交易可以被包含进区块的 Unix 时间戳下限 |
throwOnReceiptRevert | boolean(默认true) | 收据显示回滚时是否抛错,仅对*Sync动作生效 |
这些参数通过源码中的OptionalTransactionOverrides类型(见 packages/core/src/tempo/actions/utils.ts)被声明为可选,从而在保持类型安全的同时让调用保持简洁。需要特别说明的是:
feeToken/feePayer体现了 Tempo 链的**代付(sponsored transaction)**能力:应用可以为用户代付 Gas,从而改善新用户的上手体验;validBefore/validAfter是时间有效性窗口,超出窗口交易将无法被打包;throwOnReceiptRevert默认true,意味着 Sync 变体在遇到链上回滚时会直接抛错,避免拿到“已入块但失败”的静默结果。
7. 源码级实现:从 wagmi 到 viem 的委托链路
policy.create与policy.createSync的实现非常简洁,核心逻辑完全复用 viem 的 Tempo 动作:
create(见 packages/core/src/tempo/actions/policy.ts):解析account/chainId/connector→getConnectorClient获取客户端 → 委托Actions.policy.create(client, parameters);createSync(见 packages/core/src/tempo/actions/policy.ts):同样的链路,只是委托给 viem 的createSync变体;- 两者的
Parameters类型均通过ChainIdParameter、ConnectorParameter与OptionalTransactionOverrides组合而来,并剔除了底层 viem 参数中的'chain' | 'admin'(admin由创建时的调用账户决定)。
这一层抽象的收益在于:wagmi 负责框架集成(Config、连接器、账户解析),viem 负责链上协议细节(交易组装、事件解析),两者的分工清晰。
8. 测试验证:策略行为与特殊策略 ID
仓库为策略模块提供了完整的集成测试,见 packages/core/src/tempo/actions/policy.test.ts。与create直接相关的验证包括:
- 白名单创建(第 15-35 行):
createSync(config, { type: 'whitelist' })返回的policyType === 0,随后用getData读取到admin为当前账户、type为'whitelist',证明策略确实上链生效; - 黑名单创建(第 37-57 行):
policyType === 1,getData返回type: 'blacklist'; - 特殊策略 ID(第 287-302 行):测试还揭示了两个内置的特殊策略——
policyId: 0n恒为拒绝(always-reject),policyId: 1n恒为允许(always-allow)。这意味着即使尚未创建任何策略,也可以通过固定 ID 快速引用“全局禁止/全局放行”的语义。
测试流程统一为:connect(config, { connector: config.connectors[0] })连接测试账户 → 执行createSync→ 用getData/isAuthorized反向验证结果,形成一个完整的“创建-验证”闭环。
9. React 声明式用法:Hooks.policy.useCreate/useCreateSync
如果你使用 React 框架,仓库在wagmi/tempo入口中同时提供了对应的 Hooks 封装,位于 packages/react/src/tempo/hooks/policy.ts:
import { Hooks } from 'wagmi/tempo' function App() { const { mutate, isPending } = Hooks.policy.useCreateSync() return ( <button onClick={() => mutate({ type: 'whitelist' })} disabled={isPending} > Create Policy </button> ) }实现要点(见 policy.ts):
useCreateSync内部通过useMutation封装Actions.policy.createSync,mutationKey为['createSync'];useCreate对应非同步版本的Actions.policy.create,mutationKey为['create'];- 返回的是标准的 TanStack Query
UseMutationResult,因此天然支持isPending、isError、error、onSuccess等状态管理; - 同文件还提供了
useSetAdmin、useModifyWhitelist、useModifyBlacklist、useData、useIsAuthorized以及useWatchCreate等系列 Hook,可与useCreate组合成完整的策略管理界面。
10. 相关动作与文档索引
创建策略只是起点,完整的策略生命周期还需要配合以下动作(全部基于policyId操作):
| 动作 | 用途 | 文档 |
|---|---|---|
policy.setAdmin | 更换策略管理员(需当前管理员权限) | policy.setAdmin |
policy.modifyWhitelist | 增删白名单成员(allowed: true/false) | policy.modifyWhitelist |
policy.modifyBlacklist | 增删黑名单成员(restricted: true/false) | policy.modifyBlacklist |
policy.getData | 读取策略数据(admin、type 等) | policy.getData |
policy.isAuthorized | 校验某个地址是否被策略授权 | policy.isAuthorized |
policy.watchCreate | 监听策略创建事件 | policy.watchCreate |
典型组合流程为:
policy.createSync(config, { type: 'whitelist', addresses: [...] })创建策略并拿到policyId;policy.modifyWhitelist动态增删成员;policy.isAuthorized在转账前校验地址权限;- 需要转移管理权时调用
policy.setAdmin。
11. 注意事项与最佳实践
*Sync与性能的权衡:createSync会阻塞到交易上链,在高频场景下优先使用非同步版本并配合waitForTransactionReceipt;- 不要丢失
policyId:它是后续所有策略操作的唯一凭证,建议创建成功后立即持久化(例如关联到业务实体存储); - 善用
addresses初始化:一次性初始化成员列表,比创建后逐条 modify 更省 Gas; - 理解
throwOnReceiptRevert:默认开启意味着 Sync 变体对链上回滚零容忍,若你的业务希望宽容处理回滚,可显式传入false; - 特殊策略 ID:
0n(恒拒绝)与1n(恒允许)是内置策略,可用来表达全局开关语义,无需实际创建。
延伸阅读
- Tempo 模块总览:site/tempo/index.md、site/tempo/actions/index.md
- 核心实现:packages/core/src/tempo/actions/policy.ts
- 集成测试:packages/core/src/tempo/actions/policy.test.ts
- React Hooks 实现:packages/react/src/tempo/hooks/policy.ts
- 共享写交易参数:site/shared/tempo-write-parameters.md
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考