wagmi Tempozone.encryptedDeposit实战指南:向 Zone 加密存入 TIP-20 代币
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
导读
本文围绕 wagmi 的 Tempo 扩展模块中zone.encryptedDeposit这一核心 action 展开,讲解如何以「加密的接收方(recipient)与备注(memo)」向指定 Zone 的 Portal 合约存入 TIP-20 代币。你将掌握zone.encryptedDeposit(异步,返回交易哈希)与zone.encryptedDepositSync(同步,等待上链后返回回执)两种调用方式、完整参数语义、底层加密原理与交易构造细节,并了解其在 React 中的 Hook 用法,可直接用于 Tempo 网络的隐私存款类业务。
一、功能概览:什么是zone.encryptedDeposit
zone.encryptedDeposit是 wagmi Tempo 命名空间下针对「父链(parent Tempo chain)向 Zone 存入资产」提供的 action。与普通的zone.deposit不同,它在发送存款交易之前,会在客户端本地将目标接收方地址与 memo 备注加密,再通过 Zone Portal 合约的depositEncrypted函数提交密文,从而让存款内容对链上观察者不可见,只有持有对应解密能力的 sequencer 才能还原出真实的接收方与备注。
文档原意(见 zone.encryptedDeposit.md)明确指出:
- 需要
viem >= 2.48.0才能使用 Zone actions 与 hooks; *Sync变体(zone.encryptedDepositSync)会等待交易被打包进区块后再返回,返回的是交易回执;- 非 sync 的
zone.encryptedDeposit会立即返回交易哈希,适合对性能敏感、希望自行管理「等待上链」流程的场景。
从源码 zone.ts 的 JSDoc 可以看到同一描述:Deposits tokens into a zone on the parent Tempo chain with an encrypted recipient and memo.,返回值为交易哈希;而encryptedDepositSync(zone.ts)则是「waits for the transaction to be included on a block before returning a response」,返回{ receipt }。
二、快速上手:同步存入(encryptedDepositSync)
文档给出的最简用法如下,使用*Sync变体,直接拿到回执并打印交易哈希:
import { Actions } from 'wagmi/tempo' import { parseUnits } from 'viem' import { config } from './config' const result = await Actions.zone.encryptedDepositSync(config, { amount: parseUnits('10', 6), token: '0x20c0000000000000000000000000000000000001', zoneId: 7, }) console.log('Transaction hash:', result.receipt.transactionHash)其中config是标准的 wagmi 配置,需要包含 Tempo 网络与tempoWallet连接器(参考 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(), }, })要点说明:
amount使用parseUnits('10', 6)构造,即 10 个 6 位小数的代币单位,符合 TIP-20 代币常见精度;token传入一个示例 TIP-20 代币地址;若使用代币 ID,则传bigint(见下文参数表);zoneId: 7为目标 Zone 的 ID,Portal 地址会由框架根据该 ID 解析。
三、异步用法:自行等待上链
当你希望「发出交易后立即继续执行」,可以采用非 sync 版本zone.encryptedDeposit,它只返回交易哈希,之后再用waitForTransactionReceipt手动等待交易被包含进区块:
import { Actions } from 'wagmi/tempo' import { parseUnits } from 'viem' import { waitForTransactionReceipt } from 'wagmi/actions' const hash = await Actions.zone.encryptedDeposit(config, { amount: parseUnits('10', 6), token: '0x20c0000000000000000000000000000000000001', zoneId: 7, }) const receipt = await waitForTransactionReceipt(config, { hash }) console.log(receipt.status)两种用法的取舍非常清晰:Sync 变体开箱即用、语义直观,但会阻塞到交易上链;非 sync 变体让你可以把「发送」与「确认」解耦,适合批量提交、并行等待或对接自定义监听逻辑的性能敏感场景。
四、返回值
zone.encryptedDepositSync的返回类型(来自 zone.ts 的return { receipt })如下:
type ReturnType = { /** Transaction receipt */ receipt: TransactionReceipt }即返回交易回执对象,可通过receipt.transactionHash拿到交易哈希、通过receipt.status判断交易是否成功;zone.encryptedDeposit则直接返回Promise<Hash>(交易哈希)。测试用例 zone.test.ts 中对encryptedDepositSync断言result.receipt.status === 'success',对encryptedDeposit断言hash已定义,印证了上述两种返回形态。
五、参数详解
amount
- 类型:
bigint - 含义:要存入的代币数量。建议使用
parseUnits按代币精度换算,避免浮点精度问题。
token
- 类型:
Address | bigint - 含义:要存入的 TIP-20 代币的地址或 ID。源码中通过
TokenId.toAddress(token)统一将代币 ID 归一化为地址(zone.ts),所以两种形式皆可。
zoneId
- 类型:
number - 含义:目标 Zone 的 ID。框架通过
resolvePortal(config, resolvedChainId, zoneId)解析出该 Zone 对应的 Portal 合约地址(zone.ts)。
memo(可选)
- 类型:
Hex - 含义:可选备注,会与接收方地址一起被加密。若未传入,源码默认
memo = zeroHash(zone.ts)。
recipient(可选)
- 类型:
Address - 含义:目标 Zone 内的接收方地址,默认在加密前回退为当前已连接账户的地址(
recipient = accountAddress,见 zone.ts)。加密后该地址在链上不可见,起到隐私保护作用。
通用交易参数(可选)
以下参数由@shared/tempo-write-parameters.md(tempo-write-parameters.md)统一提供,适用于所有 Tempo 写入类 action:
| 参数 | 类型 | 说明 |
|---|---|---|
account | Account \| Address | 发送交易使用的账户,默认使用已连接的 wagmi 账户 |
feeToken | Address \| bigint | 交易手续费代币,可为 TIP-20 代币地址或 ID |
feePayer | Account \| true | 手续费支付方;可为 viem Account,或设为true表示使用 Fee Payer Service |
gas | bigint | 交易的 gas 上限 |
maxFeePerGas | bigint | 交易的最大每单位 gas 费用 |
maxPriorityFeePerGas | bigint | 交易的最大优先费 |
nonce | number | 交易 nonce |
nonceKey | 'expiring' \| bigint | 交易 nonce key |
validBefore | number | 交易必须在此 Unix 时间戳之前被打包 |
validAfter | number | 交易可被打包的时间点(Unix 时间戳)之后 |
throwOnReceiptRevert | boolean(默认true) | 仅对*Syncaction 生效;若回执显示 revert 则抛出错误 |
另外,参数类型还包含chainId与connector(见encryptedDeposit.Parameters的定义,zone.ts):chainId用于显式指定父链 ID,connector用于指定使用的连接器;测试中即通过chainId: parentChain.id显式指定(zone.test.ts)。
六、底层原理:加密与交易是如何构造的
这一节结合 zone.ts 源码,梳理encryptedDeposit/encryptedDepositSync内部的完整调用链,帮助理解「加密存款」究竟做了什么。
6.1 Portal 地址与加密密钥的解析
resolvePortal(config, chainId, zoneId)(zone.ts)按以下优先级解析 Portal:
- 若链配置中
chain.contracts.zonePortal是字符串,则直接作为 Portal 地址; - 若
zonePortal是按zoneId索引的对象,则取出对应 Zone 的 Portal 配置(可能附带encryptionKeyCount与sequencerEncryptionKey,用于跳过链上读取); - 否则回退到框架内置的
portalAddresses映射表; - 全部失败则抛出
No portal address configured for zone ...错误。
得到 Portal 地址后,若链配置未提供加密密钥,框架会通过viem_readContract调用 Portal 的sequencerEncryptionKey与encryptionKeyCount两个 view 函数,获取 sequencer 的 secp256k1 公钥({ x, yParity })与当前加密密钥计数(zone.ts)。若keyIndex === 0n会抛出No sequencer encryption key configured.,表示该 Zone 尚未配置加密密钥,无法执行加密存款。
6.2 ECIES 风格的本地加密
encryptDepositPayload(publicKey, recipient, memo)(zone.ts)在客户端完成 ECIES 风格的加密流程:
- 用 sequencer 公钥与临时生成的 secp256k1 密钥对,通过
Secp256k1.getSharedSecret计算共享密钥; - 以 HKDF-SHA-256(盐为 12 字节零向量、info 为
'ecies-aes-key')派生 256 位 AES 密钥; - 生成 12 字节随机 nonce,将
recipient(address)与memo(bytes32)用 ABI 编码为明文(encodeAbiParameters); - 用 AES-GCM(
tagLength: 128)加密,输出ciphertext、tag以及临时公钥的ephemeralPubkeyX、ephemeralPubkeyYParity、nonce。
这些字段最终作为depositEncrypted的密文载荷提交上链,只有持有对应私钥的 sequencer 才能解密还原接收方与备注。
6.3 两段式交易:approve + depositEncrypted
无论 sync 还是非 sync 版本,最终都会构造一条包含两个 call 的交易(zone.ts):
- 第一个 call 调用 TIP-20 代币的
approve(portalAddress, amount),授权 Portal 划转代币; - 第二个 call 调用 Zone Portal 的
depositEncrypted(tokenAddress, amount, keyIndex - 1n, encrypted, bouncebackRecipient),完成加密存款。
其中keyIndex - 1n指向本次加密使用的密钥索引,bouncebackRecipient默认等于发送方账户地址,用于失败时的退回地址。整个流程可通过viem_sendTransaction(非 sync)或viem_sendTransactionSync(sync,配合throwOnReceiptRevert)发送。
从测试用例看,还支持「预生成好的加密载荷」直接传入:getPreparedEncryptedDeposit()返回含encrypted字段的参数,源码中if ('encrypted' in rest_)分支会直接把预加密载荷透传给 viem 的 Tempo action(zone.ts),测试断言其返回回执状态为success(zone.test.ts)。这为「离线/预加密再广播」的高级流程保留了扩展点。
七、React 中使用 Hook 版本
除了命令式 action,React 侧还提供了对应的 mutation hooks(见 packages/react/src/tempo/hooks/zone.ts):
import { Hooks } from 'wagmi/tempo' function App() { const { mutate, isPending } = Hooks.zone.useEncryptedDeposit() return ( <button onClick={() => mutate({ amount: 1_000_000n, token: '0x20c0000000000000000000000000000000000001', zoneId: 7, }) } disabled={isPending} > Encrypted Deposit </button> ) }useEncryptedDeposit内部包装Actions.zone.encryptedDeposit,mutation key 为['encryptedDeposit'],返回{ hash };useEncryptedDepositSync(zone.ts)包装Actions.zone.encryptedDepositSync,mutation key 为['encryptedDepositSync'],返回{ receipt }。
两个 Hook 均支持通过mutation配置项透传 TanStack Query 的UseMutationParameters,可自定义onSuccess、onError、onSettled等回调,参数类型与 action 一致。若使用同步 Hook,isPending会持续到交易确认上链为止。
八、注意事项与边界
- 版本约束:
zone.encryptedDeposit及其 hooks 需要viem >= 2.48.0,请确保项目依赖满足该版本; - 先连接后调用:action 内部通过
getConnectorClient获取已连接客户端,并断言存在账户(account_缺失时抛出`account` is required.),因此调用前需先完成钱包连接(测试中也先connect(config, { connector }),见 zone.test.ts); chainId必须可解析:若未显式传入chainId,会回退到client.chain?.id,两者皆无时抛出`chainId` is required.;- 加密密钥缺失时无法使用:若 Zone Portal 未配置 sequencer 加密密钥(
encryptionKeyCount === 0n),加密存款将报错,此时应考虑普通zone.deposit; - 回执回滚:使用
*Sync变体时默认throwOnReceiptRevert = true,交易回滚会直接抛错,如需自行处理可显式传throwOnReceiptRevert: false。
相关文档与源码
- zone.encryptedDeposit 文档
- 通用 Tempo 写入参数
- 核心实现:zone.ts
- React Hook 实现:hooks/zone.ts
- 测试用例:zone.test.ts
- Tempo 配置示例
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考