Skip to content

Instantly share code, notes, and snippets.

@JTraversa
Last active February 18, 2021 15:25
Show Gist options
  • Save JTraversa/87f7006d6a887c3a10f130eba4322873 to your computer and use it in GitHub Desktop.
Save JTraversa/87f7006d6a887c3a10f130eba4322873 to your computer and use it in GitHub Desktop.
Manual EIP 712 Hashing
function EIP712Hash(typedData) {
const types = typedData.types;
function abiEncode (types, values) {
var output = []
var data = []
var headLength = 0
types.forEach(function (type) {
if (isArray(type)) {
var size = parseTypeArray(type)
if (size !== 'dynamic') {
headLength += 32 * size
} else {
headLength += 32
}
} else {
headLength += 32
}
})
for (var i = 0; i < types.length; i++) {
var type = elementaryName(types[i])
var value = values[i]
var cur = encodeSingle(type, value)
// Use the head/tail method for storing dynamic data
if (isDynamic(type)) {
output.push(encodeSingle('uint256', headLength))
data.push(cur)
headLength += cur.length
} else {
output.push(cur)
}
}
return Buffer.concat(output.concat(data))
}
// Recursively finds all the dependencies of a type
function dependencies(primaryType, found = []) {
if (found.includes(primaryType)) {
return found;
}
if (types[primaryType] === undefined) {
return found;
}
found.push(primaryType);
for (let field of types[primaryType]) {
for (let dep of dependencies(field.type, found)) {
if (!found.includes(dep)) {
found.push(dep);
}
}
}
return found;
}
function encodeType(primaryType) {
// Get dependencies primary first, then alphabetical
let deps = dependencies(primaryType);
deps = deps.filter(t => t != primaryType);
deps = [primaryType].concat(deps.sort());
// Format as a string with fields
let result = '';
for (let type of deps) {
result += `${type}(${types[type].map(({ name, type }) => `${type} ${name}`).join(',')})`;
}
return result;
}
function typeHash(primaryType) {
return EthJS.Util.keccak256(encodeType(primaryType));
}
function encodeData(primaryType, data) {
let encTypes = [];
let encValues = [];
// Add typehash
encTypes.push('bytes32');
encValues.push(typeHash(primaryType));
// Add field contents
for (let field of types[primaryType]) {
let value = data[field.name];
if (field.type == 'string' || field.type == 'bytes') {
encTypes.push('bytes32');
value = EthJS.Util.keccak256(value);
encValues.push(value);
} else if (types[field.type] !== undefined) {
encTypes.push('bytes32');
value = EthJS.Util.keccak256(encodeData(field.type, value));
encValues.push(value);
} else if (field.type.lastIndexOf(']') === field.type.length - 1) {
throw 'TODO: Arrays currently unimplemented in encodeData';
} else {
encTypes.push(field.type);
encValues.push(value);
}
}
return ABIencode(encTypes, encValues);
}
function structHash(primaryType, data) {
return EthJS.Util.keccak256(encodeData(primaryType, data));
}
function signHash() {
return EthJS.Util.keccak256(
Buffer.concat([
Buffer.from('1901', 'hex'),
structHash('EIP712Domain', typedData.domain),
structHash(typedData.primaryType, typedData.message),
]),
);
}
var hash = signHash()
const privateKey = Buffer.from(PRIVATEKEY, "hex");
const address = EthJS.Util.privateToAddress(privateKey);
const sig = EthJS.Util.ecsign(hash,privateKey);
var pub = EthJS.Util.ecrecover(hash,sig.v,sig.r,sig.s);
var addrBuf = EthJS.Util.pubToAddress(pub);
var addr = EthJS.Util.bufferToHex(addrBuf);
return hash;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment