wagmi Core 交易签名实战:signTransaction Action 完整参数指南与源码解析
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
本文面向使用 wagmi 构建以太坊应用的开发者,系统讲解
@wagmi/core中signTransactionAction 的完整用法。signTransaction用于创建并签名一笔发往目标网络的交易——注意它只负责签名,并不会把交易广播到链上。读完本文,你将掌握该 Action 的导入方式、全部 13 个参数的语义与类型约束、返回值与错误处理,并能结合源码理解其内部调用链,进而在 React/Vue 等框架中通过 TanStack Query 变体(mutation)落地实际场景。
什么是 signTransaction
signTransaction是 wagmi Core 提供的 Action(action),定义于 packages/core/src/actions/signTransaction.ts,官方定位是 "Action for creating and signing transactions to networks"(创建并签名发往网络的交易)。
它与sendTransaction的核心区别在于:sendTransaction签名后会继续广播交易,而signTransaction在拿到序列化签名结果后即返回,交易是否上链由你自行决定。因此它常用于"离线签名"、待签名交易的批量收集、或者交给第三方 Relay/中继服务广播等场景。
Import(导入方式)
import { signTransaction } from '@wagmi/core'同时可按需导入配套类型:
import { type SignTransactionParameters, type SignTransactionReturnType, type SignTransactionErrorType, } from '@wagmi/core'从源码看,这些类型均从 packages/core/src/actions/signTransaction.ts 导出,并通过 packages/core/src/exports/actions.ts 暴露到@wagmi/core顶层入口。
Usage(基本用法)
import { signTransaction } from '@wagmi/core' import { parseEther } from 'viem' import { config } from './config' const result = await signTransaction(config, { to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })其中config为createConfig创建的配置实例,可参考仓库中站点使用的示例配置 site/snippets/core/config.ts:
import { createConfig, http } from '@wagmi/core' import { mainnet, sepolia } from '@wagmi/core/chains' export const config = createConfig({ chains: [mainnet, sepolia], transports: { [mainnet.id]: http(), [sepolia.id]: http(), }, })调用时第一个参数始终是config,第二个参数是交易请求参数(SignTransactionParameters)。函数返回一个 Promise,resolve 为序列化(即签名完成)后的交易哈希字符串。
内部执行流程(源码级解析)
signTransaction的实现核心位于 packages/core/src/actions/signTransaction.ts,其流程可分为三步:
- 客户端选择:解构出
account、chainId、connector与其余交易参数后,若account是对象且type === 'local'(即本地私钥账户),直接使用config.getClient({ chainId });否则调用getConnectorClient获取连接器客户端。 - 链上下文组装:当未传
chainId或客户端链 ID 与chainId一致时沿用客户端当前链,否则构造{ id: chainId }占位链对象;随后通过getAction(client, viem_signTransaction, 'signTransaction')取得 viem 的底层 action。 - 委托 viem 执行:将参数透传给 viem 的
signTransaction,并注入assertChainId: !!chainId、chain、gas等字段,最终返回序列化交易字符串。
值得注意的是getConnectorClient阶段(见 packages/core/src/actions/getConnectorClient.ts)会执行多重校验:未找到连接抛ConnectorNotConnectedError;连接器链与目标链不一致抛ConnectorChainMismatchError;指定账户不在连接器账户列表内抛ConnectorAccountNotFoundError;重连期间连接器不可用抛ConnectorUnavailableReconnectingError。这些错误类型共同组成了SignTransactionErrorType。
Parameters(参数详解)
以下参数均属于SignTransactionParameters,类型定义见 packages/core/src/actions/signTransaction.ts。需要说明:参数类型会随config的链集合与chainId收窄,这意味着不同链上会暴露该链特有的交易字段(例如 Celo 链的feeCurrency),类型层面即得到保障。
accessList
AccessList | undefined
访问列表(Access List),用于 EIP-2930 类型的交易,可预先声明交易将访问的合约地址与存储槽,帮助降低 gas 成本。
import { signTransaction } from '@wagmi/core' import { parseEther } from 'viem' import { config } from './config' const result = await signTransaction(config, { accessList: [{ address: '0x1', storageKeys: ['0x1'], }], to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })account
Address | Account | undefined
用于签名的账户。可以传地址字符串,也可以传 viem 的Account对象(如privateKeyToAccount生成的本地账户)。文档明确说明:若该账户在connector上不存在,会抛出错误。
import { signTransaction } from '@wagmi/core' import { parseEther } from 'viem' import { config } from './config' const result = await signTransaction(config, { account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })源码中对account的处理是分支的关键:当传入的是type === 'local'的对象时跳过连接器,直接走config.getClient的本地签名路径;而传入地址时则会进入getConnectorClient并校验该地址是否存在于连接器账户中,否则抛出ConnectorAccountNotFoundError(测试用例见 packages/core/src/actions/signTransaction.test.ts)。
chainId
config['chains'][number]['id'] | undefined
签名前用于校验的链 ID。传值时会在调用 viem 时设置assertChainId: true,确保连接器当前链与目标链一致,防止跨链误签。
import { signTransaction } from '@wagmi/core' import { mainnet } from '@wagmi/core/chains' import { parseEther } from 'viem' import { config } from './config' const result = await signTransaction(config, { chainId: mainnet.id, to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })connector
Connector | undefined
指定用于签名的连接器。默认使用当前连接器(即config.state.current对应的连接)。
import { getConnections, signTransaction } from '@wagmi/core' import { parseEther } from 'viem' import { config } from './config' const connections = getConnections(config) const result = await signTransaction(config, { connector: connections[0]?.connector, to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })data
`0x${string}` | undefined
合约方法调用数据,即 ABI 编码后的函数选择器与参数(calldata)。典型用法是先通过encodeFunctionData生成,再传给signTransaction做合约交互签名。
import { signTransaction } from '@wagmi/core' import { parseEther } from 'viem' import { config } from './config' const result = await signTransaction(config, { data: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })gas
bigint | undefined | null
为交易执行提供的 gas 上限(单位为 wei)。此参数在源码类型中被显式放宽为可传null(见 packages/core/src/actions/signTransaction.ts),并会在转发给 viem 时通过gas: rest.gas ?? undefined归一化处理。
import { signTransaction } from '@wagmi/core' import { parseEther, parseGwei } from 'viem' import { config } from './config' const result = await signTransaction(config, { gas: parseGwei('20'), to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })gasPrice
bigint | undefined
每单位 gas 的价格(wei)。仅适用于 Legacy 交易(type: 'legacy')。注意gasPrice与 EIP-1559 的maxFeePerGas/maxPriorityFeePerGas互斥,同时传入会在类型层面报错(类型测试见 packages/core/src/actions/signTransaction.test-d.ts)。
import { signTransaction } from '@wagmi/core' import { parseEther, parseGwei } from 'viem' import { config } from './config' const result = await signTransaction(config, { gasPrice: parseGwei('20'), to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })maxFeePerGas
bigint | undefined
每单位 gas 的总费用上限(wei),已包含maxPriorityFeePerGas。仅适用于 EIP-1559 交易(type: 'eip1559')。
import { signTransaction } from '@wagmi/core' import { parseEther, parseGwei } from 'viem' import { config } from './config' const result = await signTransaction(config, { maxFeePerGas: parseGwei('20'), to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })maxPriorityFeePerGas
bigint | undefined
每单位 gas 的最大优先费用(小费,wei)。仅适用于 EIP-1559 交易,通常与maxFeePerGas一起传入。
import { signTransaction } from '@wagmi/core' import { parseEther, parseGwei } from 'viem' import { config } from './config' const result = await signTransaction(config, { maxFeePerGas: parseGwei('20'), maxPriorityFeePerGas: parseGwei('2'), to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })nonce
number
标识这笔交易的唯一序号(nonce)。同一账户下 nonce 必须连续且唯一,签名时显式指定 nonce 可用于实现交易队列、替换(replace)或取消(cancel)等高级场景。
import { signTransaction } from '@wagmi/core' import { parseEther } from 'viem' import { config } from './config' const result = await signTransaction(config, { nonce: 123, to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })to
Address
交易接收方或合约地址(必填字段)。
import { signTransaction } from '@wagmi/core' import { parseEther } from 'viem' import { config } from './config' const result = await signTransaction(config, { to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })type
'legacy' | 'eip1559' | 'eip2930' | undefined
可选的交易请求类型,用于收窄参数类型。传入后,TypeScript 会依据类型校验参数组合的合法性(如type: 'legacy'时不允许再传maxFeePerGas)。
import { signTransaction } from '@wagmi/core' import { parseEther } from 'viem' import { config } from './config' const result = await signTransaction(config, { to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', type: 'eip1559', value: parseEther('0.01'), })值得补充的是:类型系统不仅支持上述三种,还通过请求字段自动推断更现代的交易形态。例如仅传maxFeePerGas/maxPriorityFeePerGas即被推断为 EIP-1559,传accessList推断为 EIP-2930,传blobVersionedHashes/maxFeePerBlobGas推断为 EIP-4844(Blob 交易),传authorizationList推断为 EIP-7702,并分别对应不同的序列化返回类型TransactionSerializedEIP1559、TransactionSerializedEIP2930、TransactionSerializedEIP4844、TransactionSerializedEIP7702、TransactionSerializedLegacy(见 packages/core/src/actions/signTransaction.test-d.ts)。
value
bigint | undefined
随交易发送的金额(wei)。习惯上用 viem 的parseEther等工具函数从人类可读单位转换。
import { signTransaction } from '@wagmi/core' import { parseEther } from 'viem' import { config } from './config' const result = await signTransaction(config, { to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), })Return Type(返回值)
import { type SignTransactionReturnType } from '@wagmi/core'返回值为Hash类型——即序列化并签名后的交易哈希字符串(0x开头的十六进制)。它不同于交易哈希(tx hash),而是代表"已签名交易"本身的序列化表示,可直接用于后续的广播操作。从类型定义看,SignTransactionReturnType直接复用 viem 的viem_SignTransactionReturnType<request>,因此具体返回类型会随请求参数的交易类型收窄(Legacy/EIP-1559/EIP-2930/EIP-4844/EIP-7702 各有对应的序列化类型)。
Error(错误处理)
import { type SignTransactionErrorType } from '@wagmi/core'SignTransactionErrorType由三部分组成(见 packages/core/src/actions/signTransaction.ts):
GetConnectorClientErrorType:连接器客户端获取阶段可能抛出的错误,包括ConnectorNotConnectedError(未连接)、ConnectorAccountNotFoundError(账户不在连接器上)、ConnectorChainMismatchError(链不匹配)、ConnectorUnavailableReconnectingError(重连期间不可用);BaseErrorType与ErrorType:wagmi 基础错误类型;viem_SignTransactionErrorType:底层 viem 的签名错误。
仓库测试 packages/core/src/actions/signTransaction.test.ts 验证了三个典型错误场景:
- 默认成功路径:连接 Mock Connector 后签名成功,结果匹配
signedTransactionRegex; - 连接器未连接:传入一个未连接的 connector 时抛出
ConnectorNotConnectedError; - 账户不存在:传入连接器上不存在的账户地址时抛出
ConnectorAccountNotFoundError; - 本地账户路径:使用
privateKeyToAccount(privateKey)生成的本地账户无需连接器即可完成签名。
框架集成:TanStack Query Mutation
在 React/Vue/Solid 等框架中,你通常不会直接调用signTransaction,而是使用其 TanStack Query mutation 变体(如 React 的useSignTransaction,实现见 packages/react/src/hooks/useSignTransaction.ts)。
框架层类型从wagmi/query导入:
import { type SignTransactionData, type SignTransactionVariables, type SignTransactionMutate, type SignTransactionMutateAsync, SignTransactionMutationOptions, } from 'wagmi/query'其底层实现signTransactionMutationOptions位于 packages/core/src/query/signTransaction.ts,核心逻辑是:
mutationFn(variables) { return signTransaction(config, variables as any) }, mutationKey: ['signTransaction'],也就是说,所有 mutation 变体最终都会调用本文讲解的signTransactionaction,并固定使用['signTransaction']作为 mutation key。mutation 相关约定(SignTransactionData对应返回值、SignTransactionVariables对应参数、SignTransactionMutate/SignTransactionMutateAsync对应同步/异步触发函数)详见共享文档 site/shared/mutation-imports.md。
与 viem 的关系
signTransaction是对 viem 同名 actionsignTransaction的 wagmi 封装。从源码可见,实现通过getAction(client, viem_signTransaction, 'signTransaction')获取 viem action 并透传参数,同时额外处理了连接器客户端、链 ID 校验与账户解析等 wagmi 层逻辑。因此,两者参数语义保持一致——若你已熟悉 viem 的signTransaction,迁移到 wagmi 只需补充config参数即可。
小结
signTransaction(config, parameters)只签名不广播,返回序列化签名交易;- 参数覆盖 EIP-1559/2930/4844/7702 与 Legacy 多种交易形态,类型系统会根据
type与请求字段自动收窄并校验参数组合; - 账户可来自连接器(需已连接且账户存在)或本地
Account对象(无需连接器); - 错误类型集中于连接器客户端获取阶段,可用
SignTransactionErrorType统一处理; - 框架层推荐通过 TanStack Query mutation 变体(如
useSignTransaction)使用,底层与 action 完全一致。
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考