Skip to content

Instantly share code, notes, and snippets.

@JTraversa
Last active September 8, 2020 20:55
Show Gist options
  • Save JTraversa/fe2a1363b5ec30a9b645f9545b2b5904 to your computer and use it in GitHub Desktop.
Save JTraversa/fe2a1363b5ec30a9b645f9545b2b5904 to your computer and use it in GitHub Desktop.
function createErcOffer() {
const typedData = {
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' }
],
Offer: [
{ name: "maker", type: "address" },
{ name: "taker", type: "address" },
{ name: "side", type: "uint256" },
{ name: "tokenAddress", type: "address" },
{ name: "duration", type: "uint256" },
{ name: "rate", type: "uint256" },
{ name: "interest", type: "uint256" },
{ name: "base", type: "uint256" },
],
},
primaryType: 'Offer',
domain: {
name: 'DefiHedge',
version: '1',
chainId: 3,
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC'
},
message: {
maker: "0x00fE460A15B49d09F39b057D0f1A7B9444F4F2BE",
taker: "0x0000000000000000000000000000000000000000",
side: 0,
tokenAddress: "0xc2118d4d90b274016cb7a54c03ef52e6c537d957",
duration: _duration,
rate: _rate,
interest: _interest,
base: _supplied,
},
};
const types = typedData.types;
// 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 ethereumjs.ABI.rawEncode(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("REDACTED", "hex");
const address = EthJS.Util.privateToAddress(privateKey);
const sig = EthJS.Util.ecsign(hash,privateKey);
console.log(sig);
var pub = EthJS.Util.ecrecover(hash,sig.v,sig.r,sig.s);
var addrBuf = EthJS.Util.pubToAddress(pub);
var addr = EthJS.Util.bufferToHex(addrBuf);
sig.r = EthJS.Util.bufferToHex(sig.r);
sig.s = EthJS.Util.bufferToHex(sig.s);
sigr = sig.r.substring(2);
sigs = sig.s.substring(2);
sigv = sig.v.toString();
var signature = sigr + sigs + sigv;
signature = "0x".concat(signature);
console.log(signature);
DefihedgeContract.fillOffer(typedData.message.maker,
typedData.message.taker,
typedData.message.side,
typedData.message.tokenAddress,
typedData.message.duration.
typedData.message.rate,
typedData.message.interest,
typedData.message.base,
signature,{
from: currentAddress,
gas: "900000",
value: "",
data: "" }, function(error, result){
if(!error)
console.log(result);
else
console.error(error);
});
}
@JTraversa
Copy link
Author

This is usually separated into 2 functions (creating the offer, submitting it as a taker), but I combined them to make it easier to understand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment