网站Logo 北之屿

EVM标准规范

beiyu
2
2025-11-07

EIP 是什么

EIP是以太坊社区的标准化提案机制

任何人都可以提交一个 EIP,提议对以太坊的:

  • 核心协议(共识规则、EVM 等)

  • 智能合约标准(如 ERC-20、ERC-721)

  • 应用层扩展(如签名、消息格式)进行改进或扩展。

EIP vs ERC 的区别

ERC是EIP的子集

类别

全称

作用

EIP

以太坊改进提案

所有以太坊改进的总称(涵盖底层与应用层)

ERC

以太坊应用层标准

主要用于智能合约接口标准,例如代币、NFT 等

EIP-712

EIP-712标准:用于让用户在签名时知道签名的具体内容,而不是一串难懂的十六进制数据。它定义了一种结构化数据格式,用户签名后,智能合约可以验证签名的有效性。

一个EIP-712结构化数据包含三层:

{
  "types": {...},       # 定义结构体类型
  "primaryType": "...", # 主要结构体类型
  "domain": {...},      # 域信息(domain separator)
  "message": {...},     # 实际签名内容
}

每个合约都可以在 EIP-712 的框架下,定义自己的一套 typesdomainmessage 结构。

types 里的定义就是合约函数要传入的参数结构体。

primaryType 表示这次签名的“主结构体类型”,也就是当前要签名的那一组参数,对应哪个合约函数的数据格式

🌰举个例子

比如在solidity合约里有个函数:

function transferWithAuthorization(
    address from,
    address to,
    uint256 value,
    uint256 validAfter,
    uint256 validBefore,
    bytes32 nonce,
    bytes memory signature
) external;

它在合约内部定义的结构体是:

struct TransferWithAuthorization {
    address from;
    address to;
    uint256 value;
    uint256 validAfter;
    uint256 validBefore;
    bytes32 nonce;
}

在 EIP-712 的签名 JSON 中就会这样定义:

{
  "types": {
    "EIP712Domain": [
      {"name": "name", "type": "string"},
      {"name": "version", "type": "string"},
      {"name": "chainId", "type": "uint256"},
      {"name": "verifyingContract", "type": "address"}
    ],
    "TransferWithAuthorization": [
      {"name": "from", "type": "address"},
      {"name": "to", "type": "address"},
      {"name": "value", "type": "uint256"},
      {"name": "validAfter", "type": "uint256"},
      {"name": "validBefore", "type": "uint256"},
      {"name": "nonce", "type": "bytes32"}
    ],
    "MetaTx": [
      {"name": "inner", "type": "TransferWithAuthorization"},
      {"name": "gasLimit", "type": "uint256"}
    ]
  },
  "primaryType": "TransferWithAuthorization",
  "domain": {...},
  "message": {...}
}

TransferWithAuthorization 就是"types"中的结构体名,对应了合约的方法名以及所签名的数据结构。因此也被指定为 "primaryType"

primaryType 是告诉签名算法:“我这次签的主结构体是哪一个。”

因为在复杂的合约里,可能还会有嵌套结构体。如"MetaTx"。

补充:"EIP712Domain" 虽然出现在 "types" 中,但它不是要签名的业务结构,而是签名算法用于生成 domain separator 的元信息。

什么是domain(域)🤔

“域(domain)” :是用来防止跨合约、跨链、跨应用伪造签名的安全边界。

在 EIP-712 中,domain(域)其实是一个特殊的结构体:

EIP712Domain(
  string name,  # 应用 / Token 名称
  string version,  # 应用版本号
  uint256 chainId,  # 区块链 ID(防跨链)
  address verifyingContract  # 验证签名的合约地址(防跨合约)
)

它的哈希叫 Domain Separator(域分隔符)
每个合约在部署时,都会在内部计算出自己的 DOMAIN_SEPARATOR

这个分隔符的作用是:

“把签名限定在某一个链、某一个合约、某一个应用版本内。”

domain 的存在,就是强制让签名与合约绑定。即使别人拿到同样的签名,只要换个合约(或换链),验证就会失败。

message中的内容

message 就是那份结构体填上具体值后的“内容表”。

“types”中定义了"TransferWithAuthorization",message要填的就是这份结构体对应的值

"message": {
  "from": "0xA111...",          // 授权人
  "to": "0xB222...",            // 接收人
  "value": "1000000",           // 代币数量(最小单位)
  "validAfter": 1730980000,     // 生效时间戳
  "validBefore": 1730980300,    // 失效时间戳
  "nonce": "0xabc123..."        // 随机bytes32,用于防重放
}

EIP-3009

EIP-3009 是一种 基于授权转账的 Token 扩展。与传统 ERC-20 的 transfer() 不同,TransferWithAuthorization 允许 别人代你转账,只要你提供了签名授权(包含 nonce、有效期等信息)。

我们上面聊到的 TransferWithAuthorization 就是EIP-3009 的核心方法。

EIP-3009 定义的主要函数

(有的代币只实现前两个):

函数

说明

transferWithAuthorization

通过签名授权执行代币转账(主函数 )

receiveWithAuthorization

接收方发起转账(也是签名驱动)

cancelAuthorization

取消尚未使用的授权(防止签名被滥用)

EIP-3009 依靠 nonce + validAfter + validBefore + DOMAIN_SEPARATOR 实现防重放、防伪造签名的机制。
合约会记录已使用的 nonce,防止同一授权被重复使用。

动物装饰