01.EIP-712
2023-10-18 17:28:53 # 26.EIP

EIP-712

简述

EIP-191 签名标准,它可以给一段消息签名。但是它过于简单,当签名数据比较复杂时,用户只能看到一串十六进制字符串(数据的哈希),无法核实签名内容是否与预期相符。

  • 定义:EIP-712一种更先进、安全的签名标准。在请求签名时,钱包会展示签名消息的原始数据,用户可以在验证数据后签名

  • 使用场景:一般包含链下签名(前端或脚本)和链上验证(合约)两部分

例子

前端链下签名

实际签名的函数,会返回一个签名结果

1
await signer._signTypedData(domain, types, message)
  • domain
1
2
3
4
5
6
const domain = {
name: "EIP712Storage",
version: "1",
chainId: "1",
verifyingContract: "0xf8e81D47203A594245E36C48e151709F0C19fBe8",
};
  • types

你需要根据使用场景自定义一个签名的数据类型,他要与合约匹配。在 EIP712Storage 例子中,我们定义了一个 Storage 类型,它有两个成员: address 类型的 spender,指定了可以修改变量的调用者;uint256 类型的 number,指定了变量修改后的值。

1
2
3
4
5
6
const types = {
Storage: [
{ name: "spender", type: "address" },
{ name: "number", type: "uint256" },
],
};
  • message
1
2
3
4
const message = {
spender: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
number: "100",
};

链上验签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// SPDX-License-Identifier: MIT
// By 0xAA
pragma solidity ^0.8.0;

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

contract EIP712Storage {
using ECDSA for bytes32;

bytes32 private constant EIP712DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
bytes32 private constant STORAGE_TYPEHASH = keccak256("Storage(address spender,uint256 number)");
bytes32 private DOMAIN_SEPARATOR;
uint256 number;
address owner;

constructor(){
DOMAIN_SEPARATOR = keccak256(abi.encode(
EIP712DOMAIN_TYPEHASH, // type hash
keccak256(bytes("EIP712Storage")), // name
keccak256(bytes("1")), // version
block.chainid, // chain id
address(this) // contract address
));
owner = msg.sender;
}

/**
* @dev Store value in variable
*/
function permitStore(uint256 _num, bytes memory _signature) public {
// 检查签名长度,65是标准r,s,v签名的长度
require(_signature.length == 65, "invalid signature length");
bytes32 r;
bytes32 s;
uint8 v;
// 目前只能用assembly (内联汇编)来从签名中获得r,s,v的值
assembly {
/*
前32 bytes存储签名的长度 (动态数组存储规则)
add(sig, 32) = sig的指针 + 32
等效为略过signature的前32 bytes
mload(p) 载入从内存地址p起始的接下来32 bytes数据
*/
// 读取长度数据后的32 bytes
r := mload(add(_signature, 0x20))
// 读取之后的32 bytes
s := mload(add(_signature, 0x40))
// 读取最后一个byte
v := byte(0, mload(add(_signature, 0x60)))
}

// 获取签名消息hash
bytes32 digest = keccak256(abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(abi.encode(STORAGE_TYPEHASH, msg.sender, _num))
));

address signer = digest.recover(v, r, s); // 恢复签名者
require(signer == owner, "EIP712Storage: Invalid signature"); // 检查签名

// 修改状态变量
number = _num;
}

/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}
Prev
2023-10-18 17:28:53 # 26.EIP
Next