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

为何直接编码结构体与展开编码结果不同?如何适配EIP712签名

EIP712签名校验:结构体编码结果不一致问题

问题描述

在进行EIP712签名校验时,Solidity合约中两个编码函数输出结果不一致,导致签名校验返回的地址不匹配:

  • encodeDecode1:直接对DATA结构体进行ABI编码后哈希
  • encodeDecode2:手动展开结构体成员,对bytes类型字段先执行keccak256哈希,再编码哈希

相关代码

Solidity合约代码

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;
pragma abicoder v2;

contract testabi {
    struct DATA {
        address a1;
        uint256 num;
        bytes nom;
    }

    bytes32 constant public DATA_TYPEHASH = keccak256("DATA(address a1,uint256 num,bytes nom)");

    DATA data1 = DATA(0x782DDbeBcCA0aC1952e6f8CD525f707B4e2B3077, 100, "0x0000000000000000000000000000000000000000000000000000000000011170000000000000000000000000420000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000");

    function encodeDecode1() public view returns(bytes32 ) {
        return keccak256(abi.encode(
            DATA_TYPEHASH,
            data1
        ));
    }

    function encodeDecode2() public view returns(bytes32 ) {
        return keccak256(abi.encode(
            DATA_TYPEHASH,
            data1.a1,
            data1.num,
            keccak256(data1.nom)
        ));
    }
}

生成签名的JS代码

const signature = await wallet._signTypedData(
    domain,
    {
      DATA: [
        { name: 'a1', type: 'address' },
        { name: 'num', type: 'uint256' },
        { name: 'nom', type: 'bytes' }
      ]
    },
    data
)
return signature

原因分析

两者结果不同的核心原因是EIP712对动态类型的哈希规则与直接ABI编码的行为不一致:

  1. EIP712规范要求,计算结构体哈希时,对于bytes这类动态类型字段,必须先对字段内容执行keccak256哈希,再将哈希值作为该字段的输入参与整体编码。
  2. abi.encode(data1)是直接对整个结构体做标准ABI编码,其中bytes类型会被编码成「长度前缀 + 原始字节内容」的格式,而不是哈希后的32字节值,这就导致最终的整体哈希结果和EIP712要求的计算方式完全不同。
  3. 你的JS签名代码是按照EIP712规范实现的(对bytes字段自动处理哈希),所以只有encodeDecode2的计算逻辑和签名生成逻辑一致,encodeDecode1不符合EIP712规范,自然结果不匹配。

解决方法

要让两个函数输出相等,必须让encodeDecode1遵循EIP712的哈希规则,也就是不能直接编码整个结构体,而是要手动展开字段并对动态类型做哈希处理。正确的实现应该和encodeDecode2一致,或者修改encodeDecode1如下:

function encodeDecode1() public view returns(bytes32 ) {
    return keccak256(abi.encode(
        DATA_TYPEHASH,
        data1.a1,
        data1.num,
        keccak256(data1.nom)
    ));
}

补充说明:EIP712的结构体哈希计算,本质就是对TYPEHASH和每个字段的「EIP712编码值」进行ABI编码后再哈希。对于值类型(如address、uint256),直接用其本身的32字节编码;对于动态类型(如bytes、string),必须先哈希再用哈希值参与编码。

内容的提问来源于stack exchange,提问作者MOHIT BHAT

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 00:25:40