题目
设计登录签名、订单授权或代币 Permit 时,应该选择 personal_sign 还是 EIP-712 Typed Data?两者在用户可读性、域隔离、重放防护、链上验证和钱包兼容性方面有什么差异?
考察目标
- 是否理解“签名证明控制私钥”不等于“签名天然只能使用一次”。
- 是否能根据业务语义选择普通文本签名或结构化签名。
- 是否知道 EIP-712 本身不提供完整重放防护。
- 是否考虑智能合约钱包和硬件钱包的验证、兼容性差异。
考察消息签名的可读性、域隔离、重放防护、链上验证和钱包兼容性。
设计登录签名、订单授权或代币 Permit 时,应该选择 personal_sign 还是 EIP-712 Typed Data?两者在用户可读性、域隔离、重放防护、链上验证和钱包兼容性方面有什么差异?
personal_sign 适合用户能直接阅读的简单文本挑战,例如 SIWE 登录;EIP-712 适合订单、授权和 Permit 等结构化业务数据,钱包可以分字段展示,并通过 name、version、chainId、verifyingContract 等 domain 字段隔离签名用途。两者都必须由业务加入 nonce、有效期和消费记录来防重放。需要上链验证或表达复杂权限时优先 EIP-712,但仍要考虑硬件钱包和智能合约钱包兼容性。
personal_sign 通常对 EIP-191 前缀后的消息签名。它适合一段可以完整展示给用户的文本,例如:
优点是格式简单、钱包支持广,很多硬件钱包也能处理。缺点是文本没有标准化字段类型,服务端和前端必须对字符编码、换行、字段顺序保持完全一致;如果把二进制或难以理解的十六进制塞进消息,用户几乎无法判断自己授权了什么。
登录场景不应只签一个固定字符串。类似 SIWE 的挑战应包含 domain、URI、chain ID、nonce、签发时间和可选过期时间,并由服务端一次性消费 nonce。
EIP-712 为结构化数据定义类型、消息和 domain。适合:
它的主要价值不是“签名更强”,而是让结构和语义更明确。钱包可以展示字段,合约和后端也可以按相同类型计算摘要。
domain 常见字段包括:
name:协议或应用名称。version:签名域版本。chainId:目标链。verifyingContract:预期验证签名的合约。这些字段降低签名在不同链、不同合约或不同协议版本间被误用的风险,但只有业务正确填写并验证时才有效。
EIP-712 规范明确不自动提供重放保护。业务消息通常还需要:
deadline 或 expirationTime。仅加入时间戳但不检查有效期,或加入 nonce 但不记录消费状态,都不能阻止重放。
personal_sign,产品需要兼容方案或清晰提示。eth_sign 请求用户签任意摘要。const signature = await walletClient.signTypedData({
account,
domain: {
name: "Example Orderbook",
version: "1",
chainId: 1,
verifyingContract: orderbookAddress,
},
types: {
Order: [
{ name: "maker", type: "address" },
{ name: "token", type: "address" },
{ name: "amount", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
],
},
primaryType: "Order",
message: {
maker: account,
token,
amount,
nonce,
deadline,
},
});
服务端或合约仍需检查 maker、chainId、verifyingContract、nonce 和 deadline,不能因为前端使用了 signTypedData 就省略验证。
chainId 或 verifyingContract,使授权范围过宽。personal_sign 登录挑战使用固定字符串,nonce 可重复使用。eth_sign 签任意摘要。chainId,为什么仍然需要 nonce?version 应该怎样处理?ecrecover 验证?personal_sign 用于简单文本,EIP-712 用于结构化消息。发现这道题有问题? 反馈此题