使用 Wagmi Vue 发送交易:useSendTransaction 与 useWaitForTransactionReceipt 实战指南
2026/9/18 3:22:26 网站建设 项目流程

使用 Wagmi Vue 发送交易:useSendTransaction 与 useWaitForTransactionReceipt 实战指南

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

本文基于 Wagmi 仓库中 Vue 发送交易指南 展开,面向使用 Vue 3 +@wagmi/vue构建 DApp 的开发者。你将学会如何从零搭建一个「转账」表单:接入useSendTransaction发起交易、用useWaitForTransactionReceipt监听上链确认、处理加载状态与用户拒绝/余额不足等错误,并在文章末尾看到源码级的底层调用链解析。读完即可在真实项目中落地一个完整、健壮的 Send Transaction 模块。

前置准备:先让钱包连接起来

发送交易的前提是用户已经通过钱包连接。本指南直接建立在 Connect Wallet 指南 之上,你需要先完成以下三件事:

  1. 创建 Wagmi 配置:通过createConfig配置链、连接器(connector)与传输层,例如injectedmetaMasksafewalletConnect
  2. 注入插件:在应用入口同时注入WagmiPlugin(提供 Wagmi 响应式状态)与VueQueryPlugin(提供 TanStack Vue Query 的内存缓存与查询状态管理);
  3. 实现连接 UI:使用useConnectuseConnectorsuseConnection等组合式函数让用户选择并连接钱包。
import { VueQueryPlugin } from '@tanstack/vue-query' import { WagmiPlugin } from '@wagmi/vue' import { createApp } from 'vue' import App from './App.vue' import { config } from './wagmi' createApp(App) .use(WagmiPlugin, { config }) .use(VueQueryPlugin, {}) .mount('#app')

注意:如果使用 WalletConnect,请务必把config.ts中的<WALLETCONNECT_PROJECT_ID>替换为你自己的 Project ID。连接完成、useConnection().isConnected变为true之后,再渲染发送交易组件。

第 1 步:创建 SendTransaction 组件骨架

先创建一个SendTransaction.vue,包含一个最简单的转账表单:一个收款地址输入框、一个转账金额输入框和一个提交按钮。

<script setup lang="ts"> </script> <template> <form> <input name="address" placeholder="0xA0Cf…251e" required /> <input name="value" placeholder="0.05" required /> <button type="submit">Send</button> </form> </template>

两个输入框都带有name属性(addressvalue),这是下一步通过FormData读取表单数据的关键。required保证用户在提交前必须填写完整。

第 2 步:添加表单处理器

接下来为表单添加提交事件处理器。当用户点击 "Send" 时,从表单中提取地址与金额,并调用sendTransaction

<script setup lang="ts"> function submit(event: Event) { const formData = new FormData(event.target as HTMLFormElement) const to = formData.get('address') as `0x${string}` const value = formData.get('value') as string sendTransaction({ to, value: parseEther(value) }) } </script> <template> <form @submit.prevent="submit"> <input name="address" placeholder="0xA0Cf…251e" required /> <input name="value" placeholder="0.05" required /> <button type="submit">Send</button> </form> </template>

这里有两个关键细节:

  • @submit.prevent:阻止表单默认的整页刷新行为;
  • parseEther(value):来自viem的单位换算工具。用户在输入框中填写的是人类可读的0.05(ETH),而链上交易要求以wei为单位的bigintparseEther('0.05')会返回50000000000000000n

第 3 步:接入 useSendTransaction 组合式函数

现在把useSendTransaction接入进来,它是整个发送交易流程的核心。从@wagmi/vue导入组合式函数,从viem导入parseEther

<script setup lang="ts"> import { useSendTransaction } from '@wagmi/vue' import { parseEther } from 'viem' const { data: hash, sendTransaction } = useSendTransaction() function submit(event: Event) { const formData = new FormData(event.target as HTMLFormElement) const to = formData.get('address') as `0x${string}` const value = formData.get('value') as string sendTransaction({ to, value: parseEther(value) }) } </script> <template> <form @submit.prevent="submit"> <input name="address" placeholder="0xA0Cf…251e" required /> <input name="value" placeholder="0.05" required /> <button type="submit">Send</button> <div v-if="hash">Transaction Hash: {{ hash }}</div> </form> </template>

useSendTransaction返回的data(解构为hash)就是交易哈希0x${string}。一旦钱包签名并广播交易成功,hash就会填充,我们可以立即把它渲染在页面上,供用户在区块浏览器中查询。

useSendTransaction 的入参与返回值

从 useSendTransaction API 文档 与仓库源码可以看到它的完整形态。其类型定义位于 packages/vue/src/composables/useSendTransaction.ts,本质上是对 TanStack Vue QueryuseMutation的封装:

export function useSendTransaction(parameters = {}) { const config = useConfig(parameters) const options = sendTransactionMutationOptions(config, parameters) const mutation = useMutation(options) return { ...mutation, sendTransaction: mutation.mutate, sendTransactionAsync: mutation.mutateAsync, } }

参数(Parameters)

参数类型说明
configConfig \| undefined显式指定要使用的 Config,默认从WagmiPlugin注入的配置中获取
mutationMutationParametersTanStack Query 的 mutation 级选项,如onSuccessonErroronSettledretry

提交变量(Variables)

sendTransaction({ to, value })接收的是核心 action sendTransaction 的参数,常见字段包括:

  • to(必填):0x${string}收款地址;
  • valuebigint转账金额(注意必须用parseEther等工具转成 wei);
  • gas/gasPrice/maxFeePerGas/maxPriorityFeePerGas:Gas 相关覆盖项,不传则自动估算;
  • chainIdconfig['chains'][number]['id']目标链 ID,不传则使用当前链;
  • connector:指定使用某个连接器签名,不传则使用当前已连接的钱包;
  • data:合约调用的 calldata(本示例为普通转账,无需填写;若调用合约可配合 ABI 编码使用)。

返回值(Return Type)

返回值类型说明
sendTransaction(variables, options?) => void触发交易的函数(即mutate的别名,在源码中标注为 deprecated,推荐直接使用mutate
sendTransactionAsync(variables, options?) => Promise<SendTransactionData>异步版本(mutateAsync的别名,同样 deprecated),返回可 await 的 Promise
dataSendTransactionData \| undefined最近一次成功解析的数据,即交易哈希,默认undefined
errorSendTransactionErrorType \| null最近一次 mutation 的错误对象
isPendingbooleanmutation 是否正在执行(status === 'pending'
isError/isSuccess/isIdlebooleanstatus派生的状态布尔值
status'idle' \| 'pending' \| 'error' \| 'success'mutation 的完整状态机
reset() => void将 mutation 内部状态重置为初始状态
variablesVariables \| undefined最近一次传给mutate的变量对象

完整的返回值清单可参考仓库中 mutation-result 共享文档,其字段语义与 TanStack Query v5 的useMutation保持一致。

第 4 步:添加加载状态(可选)

用户点击 "Send" 后,钱包会弹出签名确认窗口,这段时间内应禁用按钮并给出反馈,防止重复提交:

<script setup lang="ts"> import { useSendTransaction } from '@wagmi/vue' import { parseEther } from 'viem' const { data: hash, isPending, sendTransaction } = useSendTransaction() function submit(event: Event) { const formData = new FormData(event.target as HTMLFormElement) const to = formData.get('address') as `0x${string}` const value = formData.get('value') as string sendTransaction({ to, value: parseEther(value) }) } </script> <template> <form @submit.prevent="submit"> <input name="address" placeholder="0xA0Cf…251e" required /> <input name="value" placeholder="0.05" required /> <button :disabled="isPending" type="submit"> <span v-if="isPending">Sending...</span> <span v-else>Send</span> </button> <div v-if="hash">Transaction Hash: {{ hash }}</div> </form> </template>

isPending在钱包签名、交易广播期间为true,此时按钮禁用并显示 "Sending...",hash一旦产生则切回 "Send"。

第 5 步:等待交易确认(可选)

hash产生只代表交易已被广播,并不代表交易已上链。要拿到确切的交易回执(receipt),需要使用 useWaitForTransactionReceipt:

<script setup lang="ts"> import { useSendTransaction, useWaitForTransactionReceipt, } from '@wagmi/vue' import { parseEther } from 'viem' const { data: hash, isPending, sendTransaction } = useSendTransaction() function submit(event: Event) { const formData = new FormData(event.target as HTMLFormElement) const to = formData.get('address') as `0x${string}` const value = formData.get('value') as string sendTransaction({ to, value: parseEther(value) }) } const { isLoading: isConfirming, isSuccess: isConfirmed } = useWaitForTransactionReceipt({ hash, }) </script> <template> <form @submit.prevent="submit"> <input name="address" placeholder="0xA0Cf…251e" required /> <input name="value" placeholder="0.05" required /> <button :disabled="isPending" type="submit"> <span v-if="isPending">Sending...</span> <span v-else>Send</span> </button> <div v-if="hash">Transaction Hash: {{ hash }}</div> <div v-if="isConfirming">Waiting for confirmation...</div> <div v-if="isConfirmed">Transaction Confirmed!</div> </form> </template>

把上一步拿到的hash直接传入useWaitForTransactionReceipt,组合式函数会持续轮询,直到交易被打包进区块并返回回执:

  • isLoading(解构为isConfirming):正在等待交易上链;
  • isSuccess(解构为isConfirmed):交易已确认。

useWaitForTransactionReceipt 的常用参数

参考 useWaitForTransactionReceipt API 文档,该组合式函数的关键参数如下:

参数类型说明
hash`0x${string}` \| undefined要等待的交易哈希。hashundefined时,查询的enabled自动为false,不会发起轮询——这正是本示例中hash未产生时组件不会空转的原因
chainIdconfig['chains'][number]['id'] \| undefined查询目标链 ID,默认取当前链
confirmationsnumber \| undefined等待的确认区块数(默认 1),传2即等两个区块后再 resolve
pollingIntervalnumber \| undefined轮询频率(毫秒),默认取 createConfig 的pollingInterval配置
onReplaced((replacement) => void) \| undefined交易被替换(replaced/repriced/cancelled,例如用户加速或取消交易)时的回调
configConfig \| undefined显式指定 Config

从源码 packages/vue/src/composables/useWaitForTransactionReceipt.ts 可以看到,它是一个基于 TanStack Vue QueryuseQuery的封装:参数类型为DeepMaybeRef,内部通过deepUnref把响应式参数解包成普通对象,再交给waitForTransactionReceiptQueryOptions生成查询选项;chainId默认来自useChainId,且支持参数级覆盖。这也意味着它天然支持响应式——把hash这个ref传进去,hash一旦变化查询会自动重新触发。

第 6 步:处理错误(可选)

如果用户在钱包中拒绝了交易,或者账户余额不足以覆盖「转账金额 + Gas 费」,mutation 会进入错误状态。此时应该把错误信息展示给用户:

<script setup lang="ts"> import { useSendTransaction, useWaitForTransactionReceipt, } from '@wagmi/vue' import { parseEther } from 'viem' const { data: hash, error, isPending, sendTransaction } = useSendTransaction() function submit(event: Event) { const formData = new FormData(event.target as HTMLFormElement) const to = formData.get('address') as `0x${string}` const value = formData.get('value') as string sendTransaction({ to, value: parseEther(value) }) } const { isLoading: isConfirming, isSuccess: isConfirmed } = useWaitForTransactionReceipt({ hash, }) </script> <template> <form @submit.prevent="submit"> <input name="address" placeholder="0xA0Cf…251e" required /> <input name="value" placeholder="0.05" required /> <button :disabled="isPending" type="submit"> <span v-if="isPending">Sending...</span> <span v-else>Send</span> </button> <div v-if="hash">Transaction Hash: {{ hash }}</div> <div v-if="isConfirming">Waiting for confirmation...</div> <div v-if="isConfirmed">Transaction Confirmed!</div> <div v-if="error"> Error: {{ (error as BaseError).shortMessage || error.message }} </div> </form> </template>

展示错误时优先取(error as BaseError).shortMessage——BaseErrorviem提供的错误基类(从viem导入类型即可),shortMessage是提炼过的简短可读信息,比冗长的原始error.message更适合直接面向用户。

第 7 步:组装到应用入口

最后,把SendTransaction组件挂到应用入口。沿用 Connect Wallet 指南的模式:未连接时显示Connect,已连接时显示ConnectionSendTransaction

<script setup lang="ts"> import { useConnection } from '@wagmi/vue' import Connection from './Connection.vue' import Connect from './Connect.vue' import SendTransaction from './SendTransaction.vue' const { isConnected } = useConnection() </script> <template> <Connection v-if="isConnected" /> <Connect v-else /> <SendTransaction v-if="isConnected" /> </template>

完整代码汇总

将上述步骤合并后的完整SendTransaction.vue如下(可直接复制运行):

<script setup lang="ts"> import { useSendTransaction, useWaitForTransactionReceipt, } from '@wagmi/vue' import { parseEther } from 'viem' const { data: hash, error, isPending, sendTransaction } = useSendTransaction() function submit(event: Event) { const formData = new FormData(event.target as HTMLFormElement) const to = formData.get('address') as `0x${string}` const value = formData.get('value') as string sendTransaction({ to, value: parseEther(value) }) } const { isLoading: isConfirming, isSuccess: isConfirmed } = useWaitForTransactionReceipt({ hash, }) </script> <template> <form @submit.prevent="submit"> <input name="address" placeholder="0xA0Cf…251e" required /> <input name="value" placeholder="0.05" required /> <button :disabled="isPending" type="submit"> <span v-if="isPending">Sending...</span> <span v-else>Send</span> </button> <div v-if="hash">Transaction Hash: {{ hash }}</div> <div v-if="isConfirming">Waiting for confirmation...</div> <div v-if="isConfirmed">Transaction Confirmed!</div> <div v-if="error"> Error: {{ (error as BaseError).shortMessage || error.message }} </div> </form> </template>

底层原理:一次点击背后的完整调用链

理解useSendTransaction的封装层级,有助于排查问题和按需定制。从仓库源码可以还原出如下调用链:

  1. 组合式函数层:packages/vue/src/composables/useSendTransaction.ts 调用sendTransactionMutationOptions(config, parameters)生成 mutation 选项,再交给 TanStack Vue Query 的useMutation
  2. Query 选项层:packages/core/src/query/sendTransaction.ts 定义mutationKey: ['sendTransaction'],并把mutationFn指向核心 action:
return { ...(options.mutation as any), mutationFn(variables) { return sendTransaction(config, variables) }, mutationKey: ['sendTransaction'], }
  1. 核心 action 层:packages/core/src/actions/sendTransaction.ts 通过getConnectorClient获取当前连接器对应的 viem 客户端(携带已连接账户与签名能力),然后委托给viem/actionssendTransaction完成「构造交易 → 钱包签名 → 广播」全流程,最终返回交易哈希。其参数类型SendTransactionParameters在 viem 参数基础上移除了chain/gas字段,并叠加了ChainIdParameter与可选的gas覆盖项。

同理,useWaitForTransactionReceipt的封装路径是:Vue 组合式函数(packages/vue/src/composables/useWaitForTransactionReceipt.ts)→waitForTransactionReceiptQueryOptions→ 核心 actionwaitForTransactionReceipt(轮询直到交易上链或回滚)。文档描述其行为为:等待交易被包含进区块后返回回执;若交易 revert 则抛出错误;同时支持替换交易(如加速/取消)的检测。

延伸阅读

  • 想读取合约数据(view/pure函数)?参见 从合约读取数据指南,其中演示了useReadContract以及用query.enabled控制依赖就绪的写法;
  • 想向合约写入状态?参见 写入合约指南;
  • 完整的组合式函数参数与返回值: useSendTransaction API、useWaitForTransactionReceipt API;
  • 底层核心 action: sendTransaction 与 waitForTransactionReceipt。

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

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

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

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

立即咨询