-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy patheip712.ts
107 lines (98 loc) · 3.29 KB
/
eip712.ts
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import BN = require("bn.js");
import ABI = require("ethereumjs-abi");
import ethUtil = require("ethereumjs-util");
const TYPED_MESSAGE_SCHEMA = {
type: "object",
properties: {
types: {
type: "object",
additionalProperties: {
type: "array",
items: {
type: "object",
properties: {
name: {type: "string"},
type: {type: "string"},
},
required: ["name", "type"],
},
},
},
primaryType: {type: "string"},
domain: {type: "object"},
message: {type: "object"},
},
required: ["types", "primaryType", "domain", "message"],
};
export function getEIP712Message(typedData: any) {
const sanitizedData = sanitizeData(typedData);
const parts = [Buffer.from("1901", "hex")];
parts.push(hashStruct("EIP712Domain", sanitizedData.domain, sanitizedData.types));
parts.push(hashStruct(sanitizedData.primaryType, sanitizedData.message, sanitizedData.types));
return ethUtil.sha3(Buffer.concat(parts));
}
function sanitizeData(data: any) {
const sanitizedData: any = {};
for (const key in TYPED_MESSAGE_SCHEMA.properties) {
if (key && data[key]) {
sanitizedData[key] = data[key];
}
}
return sanitizedData;
}
function hashStruct(primaryType: any, data: any, types: any) {
const encodedData = encodeData(primaryType, data, types);
return ethUtil.sha3(encodedData);
}
function encodeData(primaryType: any, data: any, types: any) {
const encodedTypes = ["bytes32"];
const encodedValues = [hashType(primaryType, types)];
for (const field of types[primaryType]) {
let value = data[field.name];
if (value !== undefined) {
if (field.type === "string" || field.type === "bytes") {
encodedTypes.push("bytes32");
value = ethUtil.sha3(value);
encodedValues.push(value);
} else if (types[field.type] !== undefined) {
encodedTypes.push("bytes32");
value = ethUtil.sha3(encodeData(field.type, value, types));
encodedValues.push(value);
} else if (field.type.lastIndexOf("]") === field.type.length - 1) {
throw new Error("Arrays currently unimplemented in encodeData");
} else {
encodedTypes.push(field.type);
encodedValues.push(value);
}
}
}
return ABI.rawEncode(encodedTypes, encodedValues);
}
function hashType(primaryType: any, types: any) {
return ethUtil.sha3(encodeType(primaryType, types));
}
function encodeType(primaryType: any, types: any) {
let result = "";
let deps = findTypeDependencies(primaryType, types).filter((dep: any) => dep !== primaryType);
deps = [primaryType].concat(deps.sort());
for (const type of deps) {
const children = types[type];
if (!children) {
throw new Error("No type definition specified: ${type}");
}
result += `${type}(${types[type].map((o: any) => `${o.type} ${o.name}`).join(",")})`;
}
return result;
}
function findTypeDependencies(primaryType: any, types: any, results: any = []) {
if (results.includes(primaryType) || types[primaryType] === undefined) { return results; }
results.push(primaryType);
for (const field of types[primaryType]) {
for (const dep of findTypeDependencies(field.type, types, results)) {
if (!results.includes(dep)) {
results.push(dep);
}
}
}
return results;
}