You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

web3调用eth.personal.sign签名后智能合约签名验证失败如何解决?

验签异常问题定位与解决

核心问题定位

  • 消息编码规则不一致:前端使用模板字符串拼接时,amount 被转为十进制字符串拼接,而合约侧 abi.encodePacked 传入 uint 类型的 _amount 时,会将其编码为32字节的大端格式数值,而非十进制字符串,导致两侧计算出的消息哈希完全不匹配。
  • 不可见字符差异:前后端声明消息内容时都包含换行符,换行、空格的位置/数量存在差异时,也会导致哈希不匹配。
  • 签名格式错误:前端返回的签名为带0x前缀的十六进制字符串,若上链传输时未正确转换为bytes类型,会导致合约解析签名失败。

解决步骤

1. 统一前后端消息构造规则

推荐将合约侧的uint类型金额转为十进制字符串后再拼接,与前端模板字符串的拼接逻辑完全对齐,需引入OpenZeppelin的Strings工具库实现数值转字符串。

2. 移除消息中的换行符

将消息内容改为单行,避免不可见字符带来的差异。

3. 调试验证哈希一致性

上线前先分别打印前端拼接后的完整消息哈希、合约侧计算的消息哈希,确认两者完全一致后再进行签名验证。

修正后代码示例

前端代码

// Web3 端代码
let amount = 55;
// 移除换行,保证字符串内容和合约完全一致
let messageToSign = `Please sign the message for claiming the reward amount of ${amount} tokens!`;
let signature = await web3_obj.eth.personal.sign(messageToSign, account.toString());

智能合约代码

// 引入OpenZeppelin Strings工具库
import "@openzeppelin/contracts/utils/Strings.sol";

function getSigner(uint _amount, bytes memory _signature) 
public 
pure 
returns(address) {
    // 先将uint类型金额转为字符串,再拼接计算消息哈希,和前端逻辑对齐
    bytes32 messageHash = keccak256(abi.encodePacked(
        "Please sign the message for claiming the reward amount of ",
        Strings.toString(_amount),
        " tokens!"
    ));
    bytes32 _prefixHash = ECDSA.toEthSignedMessageHash(messageHash);
    return ECDSA.recover(_prefixHash, _signature);
}

可选优化

可改用EIP-712结构化签名标准,既能避免消息编码不一致问题,也能让用户在钱包签名时看到可读的结构化内容,提升签名安全性。

内容的提问来源于stack exchange,提问作者Ankit Pareek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 22:54:04