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

Viem生成的EIP712签名合约验证ecrecover返回address(0),Ethers正常

Viem与Ethers EIP712签名的格式差异及解决办法

你的问题核心是Viem返回的签名v值采用了EIP-155标准的偏移处理,而合约中的ecrecover只接受原始的27/28值,导致验证失败,具体差异和解决办法如下:

差异点

  • Ethers v5生成的EIP712签名,默认返回的v值是原始的27或28(即使启用EIP-155,部分场景下也会自动处理后返回原始值),可以直接被合约的ecrecover识别。
  • Viem的signTypedData返回的签名v值是EIP-155兼容格式,即v = 27/28 + 2 * chainId + 35,这个值直接传给ecrecover会导致计算出的地址为address(0),因为ecrecover仅接受27或28作为合法的v值。
  • 前端verifyTypedData能验证通过,是因为它内部自动处理了EIP-155的v值偏移,无需手动调整。

解决方案

方案一:前端处理v值后传给合约

在解析签名时,将Viem返回的v值转换为原始的27/28:

const sig = await walletClient.signTypedData({ 
  domain,
  types, 
  primaryType: 'Mint',
  message 
});

const r = sig.slice(0, 66) as `0x${string}`;
const s = `0x${sig.slice(66, 130)}` as `0x${string}`;
let v = parseInt(sig.slice(130, 132), 16);

// 计算EIP-155偏移量并转换v值
const eip155Offset = 2 * chainId + 35;
if (v >= eip155Offset) {
  v -= eip155Offset;
}

// 确保v值合法
if (v !== 27 && v !== 28) {
  throw new Error('无效的签名v值');
}

方案二:修改合约验证函数,兼容EIP-155格式

在合约中自动处理偏移后的v值:

function verify(address signer, address to, uint256 amount, uint8 v, bytes32 r, bytes32 s) public view returns (bool) {
    bytes32 structHash = keccak256(abi.encode(TYPEHASH, to, amount));
    bytes32 hash = _hashTypedDataV4(structHash);
    
    uint8 adjustedV = v;
    // 处理EIP-155的v值偏移
    if (adjustedV > 28) {
        adjustedV = adjustedV - 2 * block.chainid - 35;
    }
    
    return ecrecover(hash, adjustedV, r, s) == signer;
}

两种方案任选其一即可解决验证失败的问题。

内容的提问来源于stack exchange,提问作者Muhammad Abdullah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 21:10:39