ethers v6 (JavaScript)Web3 钱包与签名
错误信息
ambiguous primary types or unused types: "EIP712Domain", "Mail"
也可能输出为
- ambiguous primary types or unused types
列出的名称就是你那些未被引用的类型。`err.message` 还会附上完整的 `types` 值和 ethers 版本;`err.code` 为 `INVALID_ARGUMENT`。
ethers v6 (JavaScript) — TypedDataEncoder used by signTypedData / verifyTypedData
含义
ethers 把 EIP-712 类型化数据的主类型推断为唯一一个不被其他类型引用的类型。你的 `types` 对象中有多个类型没有被引用,因此 ethers 无法判断要签名的是哪一个——而实际上,多出来的那个几乎总是 `EIP712Domain`。ethers 会根据 `domain` 参数自行构建域类型,传给它的 `types` 中不得包含该类型;而 `eth_signTypedData_v4` 的 JSON 格式(MetaMask、WalletConnect)则包含它。
原因
types 中残留了 EIP712Domain
常见从钱包请求、生成 `eth_signTypedData_v4` 载荷的后端或测试夹具中复制来的类型化数据会包含 `EIP712Domain`。ethers 会把它当作第二个根类型。
没有被任何类型引用的类型
偶见残留的结构体定义(例如来自旧版消息)同样是根类型,会让主类型变得不明确。
一个对象中有两个独立的结构体
偶见不同消息(Order 和 Cancel)的类型被合并到了同一个 `types` 对象中。只传入你要签名的那条消息的类型。
修复方法
- 1.
调用 ethers 之前移除 EIP712Domain
用解构把它去掉即可;得到的签名与钱包针对 v4 载荷生成的签名完全相同,因为域类型是从相同的字段推导出来的。
js const { EIP712Domain, ...types } = typedData.types; // ethers derives the domain type itself const signature = await signer.signTypedData(typedData.domain, types, typedData.message); const signer2 = verifyTypedData(typedData.domain, types, typedData.message, signature); - 2.
检查推断出的主类型
`TypedDataEncoder.from(types).primaryType` 要么抛出同样的错误,要么返回 ethers 将要哈希的类型——适合在测试中快速检查。
js import { TypedDataEncoder } from "ethers"; // Inspect what ethers considers the primary type before signing. console.log(TypedDataEncoder.from(types).primaryType);