Skip to content

Instantly share code, notes, and snippets.

@zackcoburn
Created September 16, 2017 16:30
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save zackcoburn/c29cad5c18785d8308949cbcc26e4f23 to your computer and use it in GitHub Desktop.
Save zackcoburn/c29cad5c18785d8308949cbcc26e4f23 to your computer and use it in GitHub Desktop.
const BigNumber = require('bignumber.js');
const sha256 = require('js-sha256').sha256;
const ethUtil = require('ethereumjs-util');
const Web3 = require('web3');
const request = require('request');
const web3 = new Web3();
const zeroPad = function zeroPad(num, places) {
const zero = (places - num.toString().length) + 1;
return Array(+(zero > 0 && zero)).join('0') + num;
};
const parseToDigitsArray = function parseToDigitsArray(str, base) {
const digits = str.split('');
const ary = [];
for (let i = digits.length - 1; i >= 0; i -= 1) {
const n = parseInt(digits[i], base);
if (isNaN(n)) return null;
ary.push(n);
}
return ary;
};
const add = function add(x, y, base) {
const z = [];
const n = Math.max(x.length, y.length);
let carry = 0;
let i = 0;
while (i < n || carry) {
const xi = i < x.length ? x[i] : 0;
const yi = i < y.length ? y[i] : 0;
const zi = carry + xi + yi;
z.push(zi % base);
carry = Math.floor(zi / base);
i += 1;
}
return z;
};
const multiplyByNumber = function multiplyByNumber(numIn, x, base) {
let num = numIn;
if (num < 0) return null;
if (num === 0) return [];
let result = [];
let power = x;
while (true) { // eslint-disable-line no-constant-condition
if (num & 1) { // eslint-disable-line no-bitwise
result = add(result, power, base);
}
num = num >> 1; // eslint-disable-line operator-assignment, no-bitwise
if (num === 0) break;
power = add(power, power, base);
}
return result;
};
const convertBase = function convertBase(str, fromBase, toBase) {
const digits = parseToDigitsArray(str, fromBase);
if (digits === null) return null;
let outArray = [];
let power = [1];
for (let i = 0; i < digits.length; i += 1) {
if (digits[i]) {
outArray = add(outArray,
multiplyByNumber(digits[i], power, toBase), toBase);
}
power = multiplyByNumber(fromBase, power, toBase);
}
let out = '';
for (let i = outArray.length - 1; i >= 0; i -= 1) {
out += outArray[i].toString(toBase);
}
if (out === '') out = 0;
return out;
};
const decToHex = function decToHex(dec, lengthIn) {
let length = lengthIn;
if (!length) length = 32;
if (dec < 0) {
// return convertBase((Math.pow(2, length) + decStr).toString(), 10, 16);
return (new BigNumber(2)).pow(length).add(new BigNumber(dec)).toString(16);
}
let result = null;
try {
result = convertBase(dec.toString(), 10, 16);
} catch (err) {
result = null;
}
if (result) {
return result;
}
return (new BigNumber(dec)).toString(16);
};
const pack = function pack(dataIn, lengths) {
let packed = '';
const data = dataIn.map(x => x);
for (let i = 0; i < lengths.length; i += 1) {
if (typeof (data[i]) === 'string' && data[i].substring(0, 2) === '0x') {
if (data[i].substring(0, 2) === '0x') data[i] = data[i].substring(2);
packed += zeroPad(data[i], lengths[i] / 4);
} else if (typeof (data[i]) !== 'number' && !(data[i] instanceof BigNumber) && /[a-f]/.test(data[i])) {
if (data[i].substring(0, 2) === '0x') data[i] = data[i].substring(2);
packed += zeroPad(data[i], lengths[i] / 4);
} else {
// packed += zeroPad(new BigNumber(data[i]).toString(16), lengths[i]/4);
packed += zeroPad(decToHex(data[i], lengths[i]), lengths[i] / 4);
}
}
return packed;
};
function testSig(msgIn, sig, userAddress) {
const msg = new Buffer(msgIn.slice(2), 'hex');
const recoveredAddress =
`0x${ethUtil.pubToAddress(ethUtil.ecrecover(msg, sig.v, sig.r, sig.s)).toString('hex')}`;
return recoveredAddress.toLowerCase() === userAddress.toLowerCase();
}
const sign = function sign(msgToSignIn, privateKeyIn) {
function prefixMessage(msgIn) {
let msg = msgIn;
msg = new Buffer(msg.slice(2), 'hex');
msg = Buffer.concat([
new Buffer(`\x19Ethereum Signed Message:\n${msg.length.toString()}`),
msg]);
msg = web3.sha3(`0x${msg.toString('hex')}`, { encoding: 'hex' });
msg = new Buffer(msg.slice(2), 'hex');
return `0x${msg.toString('hex')}`;
}
const privateKey = privateKeyIn.substring(0, 2) === '0x' ?
privateKeyIn.substring(2, privateKeyIn.length) : privateKeyIn;
const msgToSign = prefixMessage(msgToSignIn);
try {
const sig = ethUtil.ecsign(
new Buffer(msgToSign.slice(2), 'hex'),
new Buffer(privateKey, 'hex'));
const r = `0x${sig.r.toString('hex')}`;
const s = `0x${sig.s.toString('hex')}`;
const v = sig.v;
const result = { r, s, v, msg: msgToSign };
return result;
} catch (err) {
throw new Error(err);
}
};
const contractAddr = '0x8d12a197cb00d4747a1fe03395095ce2a5cc6819';
const tokenGet = '0x8f3470a7388c05ee4e7af3d01d8c722b0ff52374'; // VERI is what I want to get -- this is a buy order for VERI/ETH
const tokenGive = '0x0000000000000000000000000000000000000000'; // 0x0 address means ETH
const amountGet = new BigNumber(1.0).times(new BigNumber(10 ** 18)); // 1 VERI
const amountGive = new BigNumber(0.3).times(new BigNumber(10 ** 18)); // 0.3 ETH
const expires = 5000000; // this is a block number
const orderNonce = 123456; // random number to avoid hash collisions for similar orders
const user = '0x88ebA77DC1b9E627e963020cE1f8dE9Fcf25edbD';
const privateKey = 'b3bb9e142cac6b381060bbee24789e179beebe7fd02e93afdc45241084568240';
const unpacked = [
contractAddr,
tokenGet,
amountGet,
tokenGive,
amountGive,
expires,
orderNonce,
];
const condensed = pack(
unpacked,
[160, 160, 256, 160, 256, 256, 256]);
const hash = `0x${sha256(new Buffer(condensed, 'hex'))}`;
const sig = sign(hash, privateKey);
console.log(unpacked);
console.log(hash);
console.log(sig);
console.log(testSig(sig.msg, sig, user));
const orderObject = {
message: JSON.stringify({
amountGet,
amountGive,
tokenGet,
tokenGive,
contractAddr,
expires,
nonce: orderNonce,
user,
v: sig.v,
r: sig.r,
s: sig.s,
}),
};
request({
url: 'https://api.etherdelta.com/message',
method: 'POST',
json: true,
body: orderObject,
}, (error, response, body) => {
console.log(body);
});
console.log(`
contract Test {
function test1() constant returns (bool) {
uint amountGet = ${amountGet};
uint amountGive = ${amountGive};
address tokenGet = ${tokenGet};
address tokenGive = ${tokenGive};
uint expires = ${expires};
uint nonce = ${orderNonce};
address user = ${user};
uint8 v = ${sig.v};
bytes32 r = ${sig.r};
bytes32 s = ${sig.s};
bytes32 hash = sha256(${contractAddr}, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
bytes32 msg = sha3("\\x19Ethereum Signed Message:\\n32", hash);
return ecrecover(msg, v, r, s)==user;
}
function test2() constant returns (bool) {
address user = ${user};
uint8 v = ${sig.v};
bytes32 r = ${sig.r};
bytes32 s = ${sig.s};
bytes32 msg = ${sig.msg};
return ecrecover(msg, v, r, s)==user;
}
}
`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment