一、EIP-712 是什么?

📘 定义

EIP-712 全名:Ethereum Typed Structured Data Hashing and Signing

它是一种结构化数据签名标准


🧠 用一句话解释

EIP-712 = 让钱包可以安全地签名「结构化数据」,而不是只签名一段看不懂的字符串。


二、为什么需要 EIP-712?

在 EIP-712 之前,签名是这样的:

sign("I agree to send 1 ETH to 0xabc...")

问题:

  • 用户看不懂
  • 容易被钓鱼
  • 钱包无法解析字段
  • 不知道自己签的是什么结构

EIP-712 解决方案: 签名结构化 JSON 数据,例如:

{
  "domain": {
    "name": "MyDApp",
    "version": "1",
    "chainId": 1
  },
  "message": {
    "from": "0x123...",
    "amount": "1000000000000000000"
  }
}

钱包会显示:

  • Send 1 ETH
  • From: 0x123…
  • To: MyDApp
✅ 用户可读 ✅ 更安全 ✅ 防止跨链重放攻击

三、EIP-712 有什么用?

它是很多核心 Web3 功能的基础。


1️⃣ ERC-20 Permit(ERC-2612)

作用:

  • 不用发链上 approve 交易
  • 直接签名授权
  • 节省 gas

核心就是 EIP-712 签名。


2️⃣ NFT Permit

很多 NFT 市场使用 EIP-712:

  • 你签名挂单
  • 别人帮你执行

3️⃣ Gasless 交易 / Meta-transaction

用户:

  • 只签名
  • 不付 gas

Relayer 帮你发交易。底层 = EIP-712。


4️⃣ Account Abstraction (AA)

ERC-4337,AA 钱包的 UserOperation 就是 EIP-712 结构签名。你如果要做智能钱包,这个必须懂。


四、EIP-712 的核心结构

它有 3 个核心部分:


① Domain Separator

防止跨链重放:

{
  name: "MyDApp",
  version: "1",
  chainId: 11155111,
  verifyingContract: "0x..."
}

作用: 绑定链、绑定合约、绑定应用


② Types

定义数据结构:

types: {
  Mail: [
    { name: "from", type: "address" },
    { name: "amount", type: "uint256" },
  ];
}

③ Message

真正签名的内容:

message: {
  from: "0x123...",
  amount: "1000"
}

五、怎么用?

以 MetaMask 为例。

前端调用

使用 eth_signTypedData_v4

await window.ethereum.request({
  method: "eth_signTypedData_v4",
  params: [address, JSON.stringify(data)],
});

wagmi + viem

建议使用:

  • useSignTypedData
  • viem 的 signTypedData
const { signTypedData } = useSignTypedData();

signTypedData({
  domain,
  types,
  primaryType: "Mail",
  message,
});

Solidity 验证签名

用 OpenZeppelin:

import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";

// 合约里
bytes32 digest = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(digest, signature);

六、底层原理(核心)

签名的不是 JSON。真实签名的是:

keccak256(
  "\x19\x01" ||
  domainSeparator ||
  structHash
)

这保证:

  • 签名不可伪造
  • 不可跨链
  • 不可跨合约