Skip to content

Instantly share code, notes, and snippets.

@raypulver
Created February 14, 2018 01:07
Show Gist options
  • Save raypulver/3086316ba97fd1775ba34117469fa459 to your computer and use it in GitHub Desktop.
Save raypulver/3086316ba97fd1775ba34117469fa459 to your computer and use it in GitHub Desktop.
IDXM distribution script
'use strict';
const {
readFile,
appendFile,
readdir,
mkdirp
} = require('fs-extra');
const {
flow,
mergeWith,
mergeAllWith,
filter,
mapKeys,
omitBy,
curryN,
map,
get,
mapValues
} = require('lodash/fp');
const {
join,
parse
} = require('path');
const {
zipObject,
bindKey,
partial,
property,
noop,
method,
compact,
rearg,
partialRight,
toPairs,
sortBy
} = require('lodash');
const {
promisify
} = require('bluebird');
const { EOL } = require('os');
const { name } = require('./package');
const { unitMap } = require('web3-utils');
let {
argv: {
o: outputFilename,
r: gasPriceGwei,
g: gas
}
} = require('yargs');
outputFilename = outputFilename || 'bounty.log';
gasPriceGwei = gasPriceGwei || '3';
gas = gas || 100000;
const logDir = join(process.env.HOME, '.' + name);
const filename = join(logDir, outputFilename);
const appendToLog = partial(appendFile, filename);
const stripAnsi = require('strip-ansi');
const chalk = require('chalk');
const PromiseQueue = require('promiseq');
const logDrain = new PromiseQueue(1);
const queueLogJob = (job) => logDrain.push(() => Promise.resolve().then(job));
const log = bindKey(console, 'log');
const moment = require('moment');
const info = (v) => {
const toLog = chalk.cyan('[' + moment().format('MM-DD HH:mm:ss') + ']') + ' ' + v;
queueLogJob(() => appendToLog(stripAnsi(toLog) + EOL)).catch(noop);
log(v);
return v;
};
const swallowError = (err) => {
const toLog = chalk.red('[error]') + ' ' + err.stack;
queueLogJob(() => appendToLog(stripToAnsi(toLog) + EOL)).catch(noop);
console.error(toLog);
return err;
};
const Web3 = require('web3');
const web3 = new Web3('https://mainnet.infura.io/aR7WPNCrZhhnYRnn8yRT');
const {
encodeFunctionCall
} = ['encodeFunctionCall'].reduce((r, v) => {
r[v] = bindKey(web3.eth.abi, v);
return r;
}, {});
const {
getTransactionCount,
sendSignedTransaction
} = [
'getTransactionCount',
'sendSignedTransaction'
].reduce((r, v) => {
r[v] = promisify(bindKey(web3.eth, v));
return r;
}, {});
const Transaction = require('ethereumjs-tx');
const { addHexPrefix, bufferToHex } = require('ethereumjs-util');
const BN = require('bignumber.js');
const senderWallet = require('ethereumjs-wallet').fromV3(require('./idxm-sender'), 'idxm-to-the-moon');
const from = senderWallet.getAddressString();
const key = senderWallet.getPrivateKey();
const mapSeriesCurried = curryN(2, rearg(require('bluebird').mapSeries, [1, 0]));
const { mapSeries } = require('bluebird');
const readFileAsUtf8 = partialRight(readFile, 'utf8');
const readProgramDirectory = partial(readdir, __dirname);
const ln = (v) => ((console.log(require('util').inspect(v, { colors: true }))), v);
const exit = () => {
info('exiting ...');
logDrain.push(() => Promise.resolve().then(() => process.exit(0))).catch(() => process.exit(0));
};
const cheaters = require('./cheaters');
const hacked = require('./hacked');
const remaining = {};
mkdirp(logDir)
.then(() => info('this is ' + chalk.magenta(name)))
.then(noop)
.then(readProgramDirectory)
.then(filter((v) => parse(v).ext === '.csv'))
.then(mapSeriesCurried((v) => readFileAsUtf8(v)))
.then(map(flow(
method('trim'),
method('split', '\n'),
map(flow(
method('replace', /"(.*?),(.*?)"/g, '$1.$2'),
method('split', ','),
map(method('trim'))
))
)))
.then(map((v) => v.slice(1).map((u) => zipObject(v[0], u))))
.then(map((v) => v.reduce((r, v) => {
let address = v['Ethereum Address'].toLowerCase();
const match = address.match(/0x[a-f0-9]{40}/);
if (!match) {
remaining[address] = v.Tokens;
return r;
}
address = match[0];
const balance = new BN(r[address] || 0).plus(!isNaN(v.Tokens) && v.Tokens || !isNaN(v['Tokens Earned']) && v['Tokens Earned'] || 0).toPrecision();
r[address] = balance;
return r;
}, {})))
.then(mergeAllWith((a, b) => new BN(a || 0).plus(b || 0).toPrecision()))
.then(omitBy((v) => !+v))
.then(mapKeys((k) => hacked[k] || k))
.then(mapValues((v) => new BN(v).times(new BN(10).pow(8)).dividedToIntegerBy(1).toPrecision()))
.then(toPairs)
.then(filter((v) => !cheaters.includes(v[0])))
.then((v) => {
const r = require('repl').start('> ');
Object.assign(r.context, {
ary: v,
remaining,
info
});
return sortBy(v, (v) => v[0]);
})
.then((targets) => getTransactionCount(from, 'pending').then((nonce) => {
let i = nonce;
const gasPrice = +new BN(gasPriceGwei).times(unitMap.gwei).toPrecision();
return mapSeries(targets, ([ user, amount ]) => {
const tx = new Transaction({
nonce: i,
to: '0xcc13fc627effd6e35d2d2706ea3c4d7396c610ea',
gas,
gasPrice,
from,
data: encodeFunctionCall({
name: 'transfer',
inputs: [{
name: 'target',
type: 'address'
}, {
name: 'amount',
type: 'uint256'
}]
}, [ user, amount ])
});
++i;
info('sending ' + chalk.yellow(new BN(amount).div(new BN(10).pow(8)).toPrecision()) + ' IDXM to ' + chalk.magenta(user) + ' with gasPrice of ' + chalk.yellow(gasPriceGwei) + ' gwei and gas of ' + gas);
tx.sign(key);
return sendSignedTransaction(bufferToHex(tx.serialize())).then((tx) => info('tx available at ' + chalk.magenta(tx)));
});
}))
.catch(swallowError);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment