viem (TypeScript)Web3 钱包与签名
错误信息
Invalid yParityOrV value
也可能输出为
- Error: Invalid yParityOrV value
各个库的行为并不一致:ethers 6 会规范化 EIP-155 风格的值(35 及以上),对同一个签名照常完成恢复,不会报错(见复现过程);对于其他意外的值(例如 5),它会抛出“invalid v”。因此,从 ethers 迁移到 viem 的代码,可能会在以前能通过验证的签名上开始报错。
viem (TypeScript) — recoverAddress / recoverMessageAddress / recoverPublicKey
含义
签名的最后一个字节(恢复 ID)不是 viem 接受的值:0 或 1(`yParity`),或者 27 或 28(旧式 `v`)。消息签名和类型化数据签名总是使用其中之一;其他值来自交易签名的约定,或者是字节顺序出错所致。viem 在恢复之前就停止了。
原因
把 EIP-155 交易的 v 用在了消息上
常见旧式交易把链信息编码在 v 中(`chainId * 2 + 35 + yParity`,例如主网上为 37/38)。如果代码把交易签名的辅助函数复用于消息,或者从交易中读取 v,就会产生 viem 拒绝的值。
硬件钱包或库返回 v 的方式不同
偶见有些签名方集成会在单独的字段中以 0/1 返回恢复位,或者把 27 加了两次。这样拼接出来的签名,最后一个字节就是意料之外的值。
各部分拼接顺序错误
偶见按 v‖r‖s(某些 API 就是这样返回的)而不是 r‖s‖v 组装签名,会让 s 中的一个随机字节落到 v 的位置上。
修复方法
- 1.
恢复前先规范化 v
把 EIP-155 值和 0/1 映射为 27/28(或映射为 yParity 0/1),并确保签名按 r‖s‖v 组装。
ts import { hexToNumber, numberToHex, type Hex } from "viem"; // Message signatures carry v = 27/28 (or 0/1); map EIP-155 style values back. function normaliseV(signature: Hex): Hex { let v = hexToNumber(`0x${signature.slice(130)}`); if (v >= 35) v = ((v - 35) % 2) + 27; // EIP-155: chainId * 2 + 35 + yParity if (v === 0 || v === 1) v += 27; return `${signature.slice(0, 130)}${numberToHex(v).slice(2)}` as Hex; }