Wagmi SolidJS useWriteContractSync 详解:执行合约写操作并同步等待确认回执
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
本篇指南围绕 Wagmi 的 SolidJS 适配包@wagmi/solid中的useWriteContractSync原语展开:它是用于执行合约写函数并等待交易被打包进区块的响应式原语,成功后返回完整的TransactionReceipt而非单纯的交易哈希。读完本文,你将掌握它的导入与完整用法、mutation全部可选参数、返回类型的每一项含义,以及它在 Solid 响应式体系下的底层实现链路(从原语到@wagmi/core的 mutation 选项与 action 实现),并能据此在 SolidJS 应用里可靠地发起、确认和回滚合约写操作。
一、useWriteContractSync 是什么
useWriteContractSync是一个用于在 SolidJS 组件中执行合约写函数的 primitive,与useWriteContract的关键区别在于:
- 同步语义:它会等待交易被包含进区块(in-block)之后才 resolve;
- 返回值不同:成功时
data是一个TransactionReceipt(包含status、blockNumber、gasUsed等完整回执信息),而useWriteContract只返回交易哈希。
这意味着如果你需要"写完立刻知道结果"的场景——例如确认 mint 是否成功、读取status判断回滚——useWriteContractSync是更合适的选择。
二、导入与基础用法
导入
import { useWriteContractSync } from '@wagmi/solid'使用示例
import { useWriteContractSync } from '@wagmi/solid' import { abi } from './abi' function App() { const writeContractSync = useWriteContractSync() const handleMint = () => { writeContractSync.mutate({ abi, address: '0x6b175474e89094c44da98b954eedeac495271d0f', functionName: 'mint', }) } // writeContractSync.data contains the TransactionReceipt when successful }配套的WagmiProvider需要一个config(参见仓库中的示例 config.ts):
import { createConfig, http } from '@wagmi/solid' import { mainnet, sepolia } from '@wagmi/solid/chains' export const config = createConfig({ chains: [mainnet, sepolia], transports: { [mainnet.id]: http(), [sepolia.id]: http(), }, })注意:
mutate的 variables 中,abi、address、functionName、args等具体参数的取值规则与校验方式与 core 层的writeContractSyncaction 保持一致(abi、functionName、args、value等字段的完整说明见该文档)。
三、Parameters(参数)
import { useWriteContractSync } from '@wagmi/solid' useWriteContractSync.Parameters useWriteContractSync.SolidParameters参数以getter 函数(accessor)的形式传入,以维持 Solid 的响应式追踪——组件内parameters()依赖的响应式源变化时,mutation 选项会被重新计算。
config
Config | undefined
用于替代从最近的WagmiProvider中获取的Config。
mutation
TanStack Query mutation 参数。注意:Wagmi 不支持传入全部 TanStack Query 参数——mutationFn、mutationKey等由 Wagmi 内部接管(内部固定为mutationKey: ['writeContractSync'],见 writeContractSyncMutationOptions),不可覆盖。以下列出的参数均受支持:
| 参数 | 类型 | 说明 |
|---|---|---|
gcTime | number \| Infinity \| undefined | 未使用/失活的 mutation 缓存数据在内存中保留的毫秒数;缓存失活后超过该时长即被回收,多处指定时取最长值;设为Infinity可禁用垃圾回收 |
meta | Record<string, unknown> \| undefined | 若设置,会在 mutation 缓存条目上附加额外信息,可在所有mutate可用处(如onError、onSuccess)访问 |
networkMode | 'online' \| 'always' \| 'offlineFirst' \| undefined | 默认'online',控制 mutation 的网络执行模式 |
onError | (error, variables, context?) => unknown \| Promise<unknown> | mutation 出错时触发,接收错误对象 |
onMutate | (variables) => context \| void | mutation 执行前触发,适合做乐观更新;返回值会传递给onError与onSettled,便于回滚 |
onSuccess | (data, variables, context?) => unknown \| Promise<unknown> | mutation 成功时触发,接收 mutation 结果(本例中即TransactionReceipt) |
onSettled | (data, error, variables, context?) => unknown \| Promise<unknown> | 无论成功还是出错都会触发 |
queryClient | QueryClient | 指定自定义QueryClient,否则使用最近上下文中的实例 |
retry | boolean \| number \| ((failureCount, error) => boolean) | 默认0;false不重试,true无限重试,数字则重试至失败次数达到该值 |
retryDelay | number \| ((retryAttempt, error) => number) | 返回下一次重试前的延迟毫秒数,可实现指数/线性退避 |
mutate 的 variables
mutate/mutateAsync的第一参对象即WriteContractSyncVariables,其类型定义见 WriteContractSyncVariables,继承自 core action 的WriteContractSyncParameters(见 writeContractSync.ts)。除了上述示例中的abi、address、functionName、args外,还支持:
chainId:指定目标链,默认为当前连接的链;connector:指定使用哪个 connector 发起交易;account:指定签名账户(local account 时直接走 config 的 client);value:调用 payable 函数时随交易附带的 ETH 数量。
四、Return Type(返回类型)
import { useWriteContractSync } from '@wagmi/solid' useWriteContractSync.ReturnType返回类型的data属性是TransactionReceipt,包含已确认交易的完整回执(status、blockNumber、blockHash、logs等)。其余字段为标准 TanStack Query mutation 结果,且均为 Solid 响应式信号,可在组件模板中直接绑定:
| 成员 | 类型 | 说明 |
|---|---|---|
mutate | (variables, { onSuccess, onSettled, onError }) => void | 触发 mutation,可附带一次性回调 |
mutateAsync | (variables, { onSuccess, onSettled, onError }) => Promise<TData> | 与mutate相同但返回可 await 的 Promise |
data | TransactionReceipt \| undefined | 最近一次成功 resolve 的数据,默认undefined |
error | WriteContractSyncErrorType \| null | 最近一次错误的对象 |
failureCount | number | 失败次数,成功时重置为0 |
failureReason | WriteContractSyncErrorType \| null | 失败原因,成功时重置为null |
isError/isIdle/isPending/isSuccess | boolean | 由status派生的布尔信号 |
isPaused | boolean | mutation 处于paused状态时为true |
reset | () => void | 将 mutation 内部状态重置为初始状态 |
status | 'idle' \| 'pending' \| 'error' \| 'success' | 当前状态:初始 / 执行中 / 上次失败 / 上次成功 |
submittedAt | number | 提交时间戳,默认0 |
variables | TVariables \| undefined | 最近一次传给mutate的 variables |
此外,返回对象上还保留了两个已弃用的便捷别名(源码中标注@deprecated,见 useWriteContractSync.ts):writeContractSync(等价mutate)与writeContractSyncAsync(等价mutateAsync),新代码请统一使用mutate/mutateAsync。
五、类型推断
当abi设置正确时,TypeScript 会针对mutate/mutateAsync自动推断出functionName、args、value的精确类型——例如functionName只能是 ABI 中nonpayable | payable的函数名,args会按函数签名给出元组类型。mutate的类型签名定义在 WriteContractSyncMutate,其中abi使用了const泛型修饰符以保证字面量 ABI 不被拓宽。更多细节见 Wagmi TypeScript 文档。
错误类型同样有完整推断:WriteContractSyncErrorType覆盖getConnectorClient()错误、base 错误与 viem 层错误三类(见 WriteContractSyncErrorType)。
六、源码实现链路:从 Solid 原语到 viem
结合仓库源码,可以完整还原这条调用链:
1. Solid 原语层
useWriteContractSync.ts 的实现非常简洁:
export function useWriteContractSync(parameters = () => ({})) { const config = useConfig(parameters) const mutation = useMutation(() => writeContractSyncMutationOptions(config(), parameters()), ) return mergeProps(mutation, { /* 弃用别名 writeContractSync / writeContractSyncAsync */ }) }- 参数默认值是
() => ({})这样的 accessor,配合useConfig(parameters)与writeContractSyncMutationOptions(config(), parameters()),使得 config 与 mutation 选项都保持响应式; - 最终通过 Solid 的
mergeProps把 mutation 结果铺平成对象返回,让调用方拿到一组可直接在模板中使用的响应式属性。
2. Core mutation 选项层
writeContractSyncMutationOptions 负责组装 TanStack Query 的 mutation 配置:
return { ...(options.mutation as any), mutationFn(variables) { return writeContractSync(config, variables) }, mutationKey: ['writeContractSync'], }用户的mutation回调(onSuccess、retry等)通过展开合并进来,而mutationFn与mutationKey被固定为调用 core 的writeContractSyncaction——这也解释了文档中"不支持覆盖全部 TanStack Query 参数"的说明。
3. Core action 层:客户端选择与"同步等待"
writeContractSync 是真正执行写操作的 action,核心逻辑分三步:
- 选择 client:若显式传入的
account是 local 账户(account.type === 'local'),直接用config.getClient({ chainId });否则走getConnectorClient,即要求存在已连接的 connector——这是使用写操作的前提:if (typeof account === 'object' && account?.type === 'local') client = config.getClient({ chainId }) else client = await getConnectorClient(config, { account, assertChainId: false, chainId, connector }) - 对齐 chain:当
chainId与 client 链不一致时,构造仅含id的最小 chain 对象,并设置assertChainId: !!chainId,避免跨链断言失败; - 委托 viem:通过
getAction(client, viem_writeContractSync, 'writeContractSync')调用 viem 的同名 action。viem 的writeContractSync内部会发送交易后轮询等待回执(等待交易被打包),因此返回值WriteContractSyncReturnType即TransactionReceipt:export type WriteContractSyncReturnType = viem_WriteContractSyncReturnType
这正是文档中"等待交易被包含进区块后才 resolve,返回TransactionReceipt而非交易哈希"这一行为差异的底层来源。
七、测试用例中的行为验证
仓库中的运行时测试 useWriteContractSync.test.ts 完整覆盖了"等待回执"这一语义:
connect(config, { connector })建立连接(验证写操作依赖连接态);renderPrimitive(() => useWriteContractSync())渲染原语后调用result.mutate({ abi, address, functionName: 'mint' });testClient.mainnet.mine({ blocks: 1 })主动产出一个区块,随后vi.waitUntil(() => result.isSuccess)等待成功;- 断言
result.data?.status === 'success'且result.data?.blockNumber有定义——直接证明了data是完整回执而非哈希。
类型层测试 useWriteContractSync.test-d.ts 则逐一锁定了各回调的类型:onSuccess的data为TransactionReceipt、onError的error为WriteContractSyncErrorType、onMutate返回的context会正确流转给onError/onSettled,与上文参数表的描述一一对应。
八、可用的 TanStack Query 类型
如需在回调中显式标注类型,可从@wagmi/solid/query导入(该模块由 query 导出 统一提供,测试 query.test.ts 确认了writeContractSyncMutationOptions在导出列表中):
import { type WriteContractSyncData, type WriteContractSyncVariables, type WriteContractSyncMutate, type WriteContractSyncMutateAsync, writeContractSyncMutationOptions, } from '@wagmi/solid/query'九、使用注意事项与适用限制
- 必须已连接:非 local 账户场景下交易经由
getConnectorClient发起,未连接 wallet 时会抛出GetConnectorClientErrorType错误,UI 中应先用useConnect/useConnection处理连接状态; - 与
useWriteContract的取舍:只关心"交易已发送"用useWriteContract(更快返回 hash);需要确认执行结果、读取回执日志或status时用useWriteContractSync; retry的谨慎使用:写操作天然不幂等,retry: true或较大的重试次数可能造成重复提交,建议配合onMutate乐观更新 +onError回滚的模式管理状态;- 弃用别名:
writeContractSync/writeContractSyncAsync属性已标记弃用,升级代码时替换为mutate/mutateAsync即可。
小结
useWriteContractSync以 Solid accessor 参数 + TanStack Query mutation 的形态,把 core 层"发送交易并等待回执"的能力封装成了响应式原语:mutate发起写调用,isPending/isSuccess驱动 UI 状态,data直接提供可断言的TransactionReceipt。从 solid 原语实现、到 core mutation 选项、再到 core action 的 client 选择与 viem 委托,整条链路在仓库源码中均有清晰对应,便于按需深入排查连接、跨链或类型推断相关问题。
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考