EIP 是什么
EIP是以太坊社区的标准化提案机制。
任何人都可以提交一个 EIP,提议对以太坊的:
核心协议(共识规则、EVM 等)
智能合约标准(如 ERC-20、ERC-721)
应用层扩展(如签名、消息格式)进行改进或扩展。
EIP vs ERC 的区别
ERC是EIP的子集
EIP-712
EIP-712标准:用于让用户在签名时知道签名的具体内容,而不是一串难懂的十六进制数据。它定义了一种结构化数据格式,用户签名后,智能合约可以验证签名的有效性。
一个EIP-712结构化数据包含三层:
{
"types": {...}, # 定义结构体类型
"primaryType": "...", # 主要结构体类型
"domain": {...}, # 域信息(domain separator)
"message": {...}, # 实际签名内容
}每个合约都可以在 EIP-712 的框架下,定义自己的一套 types、domain 和 message 结构。
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 定义的主要函数
(有的代币只实现前两个):
EIP-3009 依靠 nonce + validAfter + validBefore + DOMAIN_SEPARATOR 实现防重放、防伪造签名的机制。
合约会记录已使用的 nonce,防止同一授权被重复使用。