diff --git a/package-lock.json b/package-lock.json index a5c37cc..b8351ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "testnet-funding-tool", - "version": "1.0.0", + "version": "1.0.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "testnet-funding-tool", - "version": "1.0.0", + "version": "1.0.6", "license": "ISC", "dependencies": { "@ethereumjs/common": "^2.6.2", @@ -14,6 +14,7 @@ "command-line-args": "^5.2.1", "command-line-usage": "^6.1.3", "ethereumjs-util": "^7.1.5", + "ethereumjs-wallet": "^1.0.2", "eventsource": "^2.0.2", "nexe": "^4.0.0-beta.16", "node-fetch": "^2.6.7", @@ -802,6 +803,11 @@ "acorn": "^8" } }, + "node_modules/aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2461,6 +2467,29 @@ "rlp": "bin/rlp" } }, + "node_modules/ethereumjs-wallet": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-1.0.2.tgz", + "integrity": "sha512-CCWV4RESJgRdHIvFciVQFnCHfqyhXWchTPlkfp28Qc53ufs+doi5I/cV2+xeK9+qEo25XCWfP9MiL+WEPAZfdA==", + "dependencies": { + "aes-js": "^3.1.2", + "bs58check": "^2.1.2", + "ethereum-cryptography": "^0.1.3", + "ethereumjs-util": "^7.1.2", + "randombytes": "^2.1.0", + "scrypt-js": "^3.0.1", + "utf8": "^3.0.0", + "uuid": "^8.3.2" + } + }, + "node_modules/ethereumjs-wallet/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/ethjs-unit": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", @@ -6649,6 +6678,11 @@ "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "requires": {} }, + "aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -8002,6 +8036,28 @@ } } }, + "ethereumjs-wallet": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-1.0.2.tgz", + "integrity": "sha512-CCWV4RESJgRdHIvFciVQFnCHfqyhXWchTPlkfp28Qc53ufs+doi5I/cV2+xeK9+qEo25XCWfP9MiL+WEPAZfdA==", + "requires": { + "aes-js": "^3.1.2", + "bs58check": "^2.1.2", + "ethereum-cryptography": "^0.1.3", + "ethereumjs-util": "^7.1.2", + "randombytes": "^2.1.0", + "scrypt-js": "^3.0.1", + "utf8": "^3.0.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, "ethjs-unit": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", diff --git a/package.json b/package.json index 0d8c846..25d5e5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "testnet-funding-tool", - "version": "1.0.6", + "version": "1.0.7", "description": "", "main": "index.js", "scripts": { @@ -14,6 +14,7 @@ "command-line-args": "^5.2.1", "command-line-usage": "^6.1.3", "ethereumjs-util": "^7.1.5", + "ethereumjs-wallet": "^1.0.2", "eventsource": "^2.0.2", "nexe": "^4.0.0-beta.16", "node-fetch": "^2.6.7", diff --git a/src/funding-tool.js b/src/funding-tool.js index eaeaffe..98b74fc 100644 --- a/src/funding-tool.js +++ b/src/funding-tool.js @@ -7,6 +7,7 @@ const Web3 = require('web3'); const EthTx = require('@ethereumjs/tx'); const EthCom = require('@ethereumjs/common'); const EthUtil = require('ethereumjs-util'); +const EthWallet = require('ethereumjs-wallet'); const distributorContract = require("../Contracts/Distributor.json"); @@ -41,11 +42,16 @@ const optionDefinitions = [ }, { name: 'privkey', - description: 'The private key of the wallet to send funds from.', + description: 'The private key of the wallet to send funds from.\n(Special: "env" to read from FUNDINGTOOL_PRIVKEY environment variable)', alias: 'p', type: String, typeLabel: '{underline privkey}', }, + { + name: 'random-privkey', + description: 'Use random private key if no privkey supplied', + type: Boolean, + }, { name: 'maxpending', description: 'The maximum number of parallel pending transactions.', @@ -94,6 +100,12 @@ const optionDefinitions = [ type: String, typeLabel: '{underline txlist.txt}', }, + { + name: 'summary', + description: 'Output summary of distribution to file.', + type: String, + typeLabel: '{underline summary.txt}', + }, { name: 'chainid', description: 'ChainID of the network (For offline mode in combination with --output)', @@ -116,6 +128,11 @@ var wallet = null; var fundings = []; var pendingQueue = []; var distributor = null; +var stats = { + transferCount: 0, + transactionCount: 0, + totalAmount: BigInt(0), +}; main(); @@ -125,14 +142,14 @@ async function main() { return; } - if(!options['privkey']) { + var walletKey = loadPrivateKey(); + if(!walletKey) { printHelp(); console.log("No wallet privkey specified."); console.log(""); return; } - var walletKey = Buffer.from(options['privkey'], "hex"); var walletAddr = EthUtil.toChecksumAddress("0x"+EthUtil.privateToAddress(walletKey).toString("hex")); wallet = { privkey: walletKey, @@ -163,6 +180,10 @@ async function main() { return; } + if(options['output'] && fs.existsSync(options['output'])) { + fs.unlinkSync(options['output']); + } + if(options['output'] && options['chainid']) { // use in offline mode web3 = new Web3(); @@ -179,6 +200,21 @@ async function main() { await Promise.all(pendingQueue.map((entry) => entry.promise)); console.log("fundings complete!"); + + if(options['summary']) { + let summary = [ + "WalletAddress: " + wallet.addr, + "TotalAmountWei: " + stats.totalAmount.toString(), + "TotalAmountEth: " + weiToEth(stats.totalAmount), + "TransactionCount: " + stats.transactionCount, + ]; + if(distributor) { + summary.push("TransferCount: " + stats.transferCount); + summary.push("DistributorAddr: " + distributor.addr); + } + + fs.writeFileSync(options['summary'], summary.join("\n")); + } } function printHelp() { @@ -204,6 +240,21 @@ function weiToEth(wei) { return parseInt((wei / 1000000000000000n).toString()) / 1000; } +function loadPrivateKey() { + if(options['privkey'] === "env" && (process.env.FUNDINGTOOL_PRIVKEY || "").match(/^[0-9a-f]{64}$/i)) { + return Buffer.from(process.env.FUNDINGTOOL_PRIVKEY, "hex"); + } + if(options['privkey'] && options['privkey'].match(/^[0-9a-f]{64}$/i)) { + return Buffer.from(options['privkey'], "hex"); + } + if(options['random-privkey']) { + let wallet = EthWallet.default.generate(); + return Buffer.from(wallet.getPrivateKeyString().replace(/^0x/, ""), "hex"); + } + + return null; +} + function loadFundingFile(resArray, fundingList) { var fundingLine, cmtPos, fundingEntry; for(var i = 0; i < fundingList.length; i++) { @@ -302,6 +353,10 @@ async function processFunding(address, amount) { var txres = await publishTransaction(txhex); console.log(" tx hash: " + txres[0]); + stats.transferCount++; + stats.transactionCount++; + stats.totalAmount += amount; + var txobj = { nonce: wallet.nonce, hash: txres[0], @@ -399,6 +454,8 @@ async function deployDistributor() { return distributorState.contractAddr; } + stats.transactionCount++; + var nonce = wallet.nonce; var rawTx = { nonce: nonce, @@ -434,6 +491,7 @@ async function processFundingBatch(batch) { batch.forEach((entry) => { console.log("process funding " + entry.address + ": " + weiToEth(entry.amount) + " ETH"); totalAmount += entry.amount; + stats.transferCount++; }); if(totalAmount > wallet.balance && !wallet.offline) { @@ -464,6 +522,9 @@ async function processFundingBatch(batch) { var txres = await publishTransaction(txhex); console.log(" tx hash: " + txres[0]); + stats.transactionCount++; + stats.totalAmount += totalAmount; + var txobj = { nonce: nonce, hash: txres[0],