Last active
February 18, 2021 15:25
-
-
Save JTraversa/87f7006d6a887c3a10f130eba4322873 to your computer and use it in GitHub Desktop.
Manual EIP 712 Hashing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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