viem (TypeScript)Web3 钱包与签名
错误信息
invalid signature length
也可能输出为
- Error: invalid signature length
viem (TypeScript) — recoverMessageAddress / recoverAddress / recoverTypedDataAddress / recoverPublicKey
含义
viem 的恢复函数只接受恰好 65 字节(r‖s‖v)的十六进制或字节数组签名,遇到其他长度一律抛出这个普通的 `Error`——包括 64 字节的 EIP-2098 紧凑形式,viem 只能通过显式的转换辅助函数支持这种形式。此时没有恢复出任何东西;签名要么是另一种格式,要么根本不是 EOA 签名。
原因
64 字节的紧凑签名(EIP-2098)
常见一些钱包、中继服务和合约以紧凑形式存储签名(yParity 并入 s 中)。ethers 可以直接接受这种形式;viem 的 `recover*` 函数则不行。
智能账户签名
常见来自 Safe、Coinbase Smart Wallet 或其他智能账户的 ERC-1271 和 ERC-6492 签名长度超过 65 字节,无法从中恢复出地址;必须通过账户合约来验证。
Base64 编码或双重十六进制编码
偶见以 base64 传输的签名,或者被再次编码为十六进制的十六进制字符串,在 viem 读取时长度就不对了。
修复方法
- 1.
恢复前先转换紧凑签名
`parseCompactSignature` 加上 `compactSignatureToSignature` 可以把 64 字节形式转换为 65 字节形式。
ts import { compactSignatureToSignature, parseCompactSignature, recoverMessageAddress, serializeSignature, size } from "viem"; const sig = size(signature) === 64 ? serializeSignature(compactSignatureToSignature(parseCompactSignature(signature))) : signature; const signer = await recoverMessageAddress({ message, signature: sig }); - 2.
对各类钱包统一使用 publicClient.verifyMessage
公共客户端会在本地验证 EOA 签名,并通过 ERC-1271/ERC-6492 验证智能账户签名,因此同一套代码适用于所有类型的钱包。
ts import { createPublicClient, http } from "viem"; import { mainnet } from "viem/chains"; // Handles EOAs, ERC-1271 smart accounts and ERC-6492 (undeployed) accounts. const client = createPublicClient({ chain: mainnet, transport: http(process.env.RPC_URL) }); const valid = await client.verifyMessage({ address, message, signature });