-
-
Save zackcoburn/c29cad5c18785d8308949cbcc26e4f23 to your computer and use it in GitHub Desktop.
This file contains 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
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