Merge pull request #12 from status-im/accounts

Accounts
This commit is contained in:
Iuri Matias 2019-05-23 15:15:36 -04:00 committed by GitHub
commit 1762cd480e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 218 additions and 46 deletions

7
accounts.example.json Normal file
View File

@ -0,0 +1,7 @@
{
"accounts": [
{
"mnemonic": "add mnemonic here"
}
]
}

View File

@ -44,6 +44,7 @@
"homepage": ".",
"dependencies": {
"@aragon/os": "3.1.9",
"async": "^3.0.0",
"clear": "^0.1.0",
"cli-spinner": "^0.2.10",
"cli-table": "^0.3.1",

View File

@ -1,11 +1,10 @@
var inquirer = require('inquirer');
const Web3 = require("web3");
const PledgeAdminUtils = require('./pledgeadmin-utils');
const PledgeUtils = require('./pledge-utils');
const TrxUtils = require('./trx-utils');
const Contracts = require("./contracts.js");
const web3 = new Web3();
const Provider = require("./provider.js");
const Web3 = require('web3');
function doAction(actionText, action) {
console.dir(actionText)
@ -34,21 +33,36 @@ function doAction(actionText, action) {
class Actions {
constructor(chain) {
constructor(chain, accounts) {
this.chain = chain || "development";
this.accounts = accounts || [];
}
connect(url, cb) {
connect(options, cb) {
const url = options.url;
console.dir("connecting to: " + url);
web3.setProvider(url);
if (this.accounts.length > 0) {
this.provider = new Provider();
this.provider.initAccounts(this.accounts);
this.provider.startWeb3Provider("ws", url)
} else {
this.web3 = new Web3();
this.web3.setProvider(url);
}
setTimeout(async () => {
let accounts = await web3.eth.getAccounts();
if (this.accounts.length > 0) {
this.web3 = this.provider.web3;
}
let accounts = await this.web3.eth.getAccounts();
console.dir("== accounts");
console.dir(accounts);
web3.eth.defaultAccount = accounts[0]
this.web3.eth.defaultAccount = accounts[0]
let contracts = new Contracts(this.chain, web3);
let contracts = new Contracts(this.chain, this.web3);
contracts.loadContracts();
this.contracts = contracts.contracts;
@ -56,15 +70,15 @@ class Actions {
}, 1000);
}
web3() {
return web3;
web3Object() {
return this.web3;
}
async addProject(params) {
let text = `await LiquidPledging.methods.addProject(\"${params.name}\", \"${params.url}\", \"${params.account}\", ${params.parentProject}, ${params.commitTime}, \"${params.plugin}\").send({from: \"${web3.eth.defaultAccount}\", gas: 2000000})`
let text = `await LiquidPledging.methods.addProject(\"${params.name}\", \"${params.url}\", \"${params.account}\", ${params.parentProject}, ${params.commitTime}, \"${params.plugin}\").send({gas: 2000000})`
return doAction(text, async () => {
const toSend = this.contracts.LiquidPledging.methods.addProject(params.name, params.url, params.account, params.parentProject, params.commitTime, params.plugin);
const receipt = await TrxUtils.executeAndWait(toSend, web3.eth.defaultAccount);
const receipt = await TrxUtils.executeAndWait(toSend, this.web3.eth.defaultAccount);
console.dir("txHash: " + receipt.transactionHash);
const projectId = receipt.events.ProjectAdded.returnValues.idProject;
console.log("Project ID: " , projectId);
@ -100,8 +114,8 @@ class Actions {
async withdraw(params) {
let text = `await LiquidPledging.methods.withdraw(\"${params.id}\", web3.utils.toWei(\"${params.amount}\", "ether")).send({gas: 2000000})`;
return doAction(text, async () => {
const toSend = this.contracts.LiquidPledging.methods.withdraw(params.id.toString(), web3.utils.toWei(params.amount.toString(), "ether"));
const receipt = await TrxUtils.executeAndWait(toSend, web3.eth.defaultAccount);
const toSend = this.contracts.LiquidPledging.methods.withdraw(params.id.toString(), this.web3.utils.toWei(params.amount.toString(), "ether"));
const receipt = await TrxUtils.executeAndWait(toSend, this.web3.eth.defaultAccount);
console.dir("txHash: " + receipt.transactionHash);
const paymentId = receipt.events.AuthorizePayment.returnValues.idPayment;
console.log("Payment ID: " , paymentId);
@ -112,7 +126,7 @@ class Actions {
return new Promise(async (resolve, reject) => {
try {
const pledges = await PledgeUtils.getPledges(this.contracts.LiquidPledging);
PledgeUtils.printTable(pledges, web3);
PledgeUtils.printTable(pledges, this.web3);
} catch(error){
console.log(error);
console.log("Couldn't obtain the list of pledges: ", error.message);
@ -135,10 +149,10 @@ class Actions {
}
async addGiver(params) {
let text = `await LiquidPledging.methods.addGiver(\"${params.name}\", \"${params.url}\", ${params.commitTime}, \"${params.plugin}\").send({from: \"${web3.eth.defaultAccount}\", gas: 2000000})`
let text = `await LiquidPledging.methods.addGiver(\"${params.name}\", \"${params.url}\", ${params.commitTime}, \"${params.plugin}\").send({gas: 2000000})`
return doAction(text, async () => {
const toSend = this.contracts.LiquidPledging.methods.addGiver(params.name, params.url, params.commitTime, params.plugin);
const receipt = await TrxUtils.executeAndWait(toSend, web3.eth.defaultAccount);
const receipt = await TrxUtils.executeAndWait(toSend, this.web3.eth.defaultAccount);
console.dir("txHash: " + receipt.transactionHash);
const funderId = receipt.events.GiverAdded.returnValues.idGiver;
console.log("Funder ID: " , funderId);
@ -148,8 +162,8 @@ class Actions {
async mintToken(params) {
let text = `await StandardToken.methods.mint(\"${params.account}\", web3.utils.toWei(\"${params.amount}\", \"ether\")).send({gas: 2000000})`
return doAction(text, async () => {
const toSend = this.contracts.StandardToken.methods.mint(params.account, web3.utils.toWei(params.amount.toString(), "ether"));
const receipt = await TrxUtils.executeAndWait(toSend, web3.eth.defaultAccount);
const toSend = this.contracts.StandardToken.methods.mint(params.account, this.web3.utils.toWei(params.amount.toString(), "ether"));
const receipt = await TrxUtils.executeAndWait(toSend, this.web3.eth.defaultAccount);
console.dir("txHash: " + receipt.transactionHash);
});
}
@ -157,8 +171,8 @@ class Actions {
async approveToken(params) {
let text = `await StandardToken.methods.approve(\"${this.contracts.LiquidPledging.options.address}\", web3.utils.toWei(\"${params.amount}\", \"ether\")).send({gas: 2000000})`
return doAction(text, async () => {
const toSend = this.contracts.StandardToken.methods.approve(this.contracts.LiquidPledging.options.address, web3.utils.toWei(params.amount.toString(), "ether"));
const receipt = await TrxUtils.executeAndWait(toSend, web3.eth.defaultAccount);
const toSend = this.contracts.StandardToken.methods.approve(this.contracts.LiquidPledging.options.address, this.web3.utils.toWei(params.amount.toString(), "ether"));
const receipt = await TrxUtils.executeAndWait(toSend, this.web3.eth.defaultAccount);
console.dir("txHash: " + receipt.transactionHash);
});
}
@ -166,8 +180,8 @@ class Actions {
async donate(params) {
let text = `await LiquidPledging.methods.donate(${params.funderId}, ${params.projectId}, \"${params.tokenAddress}\", web3.utils.toWei(\"${params.amount}\", \"ether\")).send({gas: 2000000});`
return doAction(text, async () => {
const toSend = this.contracts.LiquidPledging.methods.donate(params.funderId, params.projectId, params.tokenAddress, web3.utils.toWei(params.amount.toString(), "ether"));
const receipt = await TrxUtils.executeAndWait(toSend, web3.eth.defaultAccount);
const toSend = this.contracts.LiquidPledging.methods.donate(params.funderId, params.projectId, params.tokenAddress, this.web3.utils.toWei(params.amount.toString(), "ether"));
const receipt = await TrxUtils.executeAndWait(toSend, this.web3.eth.defaultAccount);
console.dir("txHash: " + receipt.transactionHash);
});
}

View File

@ -11,18 +11,18 @@ function mainMenu(actions) {
if (subAction === 'List Projects') {
await actions.listProjects();
}
}
if (subAction === 'Create Project') {
let params = (await menus.createProject(actions.web3().eth.defaultAccount))
let params = (await menus.createProject(actions.web3Object().eth.defaultAccount))
await actions.addProject(params);
}
}
if (subAction === 'View Project') {
let params = (await menus.viewProject())
await actions.viewProject(params);
}
}
if (subAction === 'Fund a Project') {
let params = (await menus.donate())
await actions.donate(params);
@ -37,7 +37,7 @@ function mainMenu(actions) {
if (subAction === 'Withdraw') {
let params = (await menus.withdraw())
await actions.withdraw(params);
}
}
} else if (action === 'Funders') {
subAction = (await menus.funders()).action

View File

@ -5,12 +5,20 @@ const Actions = require('./actions.js');
program
.version('0.1.0')
.option('-u, --url [url]', "host to connect to (default: ws://localhost:8556)")
.option('-a, --accounts [accounts]', "accounts file, if not defined uses accounts in the connecting node")
.option('-c, --chain [chain]', "environment to run, can be mainnet, ropsten, development (default: development)")
.parse(process.argv);
const actions = new Actions(program.chain || "development");
let accounts = [];
if (program.accounts) {
accounts = require(process.cwd() + "/" + program.accounts).accounts;
}
actions.connect(program.url || "ws://localhost:8556", async () => {
const actions = new Actions(program.chain || "development", accounts || []);
actions.connect({
url: (program.url || "ws://localhost:8556")
}, async () => {
cmd(actions)
});

134
src/provider.js Normal file
View File

@ -0,0 +1,134 @@
const async = require('async');
const Web3 = require('web3');
const bip39 = require("bip39");
const hdkey = require('ethereumjs-wallet/hdkey');
const ethUtil = require('ethereumjs-util');
const Transaction = require('ethereumjs-tx');
class Provider {
constructor() {
this.accounts = []
this.addresses = []
this.nonceCache = {};
this.web3 = new Web3();
}
initAccounts(accounts) {
for (let accountConfig of accounts) {
if (accountConfig.mnemonic) {
const hdwallet = hdkey.fromMasterSeed(bip39.mnemonicToSeed(accountConfig.mnemonic.trim()));
const addressIndex = accountConfig.addressIndex || 0;
const numAddresses = accountConfig.numAddresses || 1;
const wallet_hdpath = accountConfig.hdpath || "m/44'/60'/0'/0/";
for (let i = addressIndex; i < addressIndex + numAddresses; i++) {
const wallet = hdwallet.derivePath(wallet_hdpath + i).getWallet();
//if (returnAddress) {
// this.accounts.push(wallet.getAddressString());
//} else {
this.accounts.push(this.web3.eth.accounts.privateKeyToAccount('0x' + wallet.getPrivateKey().toString('hex')));
//}
}
}
}
}
startWeb3Provider(type, web3Endpoint) {
const self = this;
if (type === 'rpc') {
self.provider = new this.web3.providers.HttpProvider(web3Endpoint);
} else if (type === 'ws') {
// Note: don't pass to the provider things like {headers: {Origin: "embark"}}. Origin header is for browser to fill
// to protect user, it has no meaning if it is used server-side. See here for more details: https://github.com/ethereum/go-ethereum/issues/16608
// Moreover, Parity reject origins that are not urls so if you try to connect with Origin: "embark" it gives the following error:
// << Blocked connection to WebSockets server from untrusted origin: Some("embark") >>
// The best choice is to use void origin, BUT Geth rejects void origin, so to keep both clients happy we can use http://embark
self.provider = new this.web3.providers.WebsocketProvider(web3Endpoint, {
headers: {Origin: "http://embark"},
// TODO remove this when Geth fixes this: https://github.com/ethereum/go-ethereum/issues/16846
clientConfig: {
fragmentationThreshold: 81920
}
});
self.provider.on('error', () => console.error('Websocket Error'));
self.provider.on('end', () => console.error('Websocket connection ended'));
} else {
}
self.web3.setProvider(self.provider);
self.accounts.forEach(account => {
self.addresses.push(account.address || account);
if (account.privateKey) {
self.web3.eth.accounts.wallet.add(account);
}
});
self.addresses = [...new Set(self.addresses)]; // Remove duplicates
if (self.accounts.length) {
self.web3.eth.defaultAccount = self.addresses[0];
}
const realSend = self.provider.send.bind(self.provider);
self.runTransaction = async.queue(({payload}, callback) => {
const rawTx = payload.params[0];
const rawData = Buffer.from(ethUtil.stripHexPrefix(rawTx), 'hex');
const tx = new Transaction(rawData, 'hex');
const address = '0x' + tx.getSenderAddress().toString('hex').toLowerCase();
self.getNonce(address, (newNonce) => {
tx.nonce = newNonce;
const key = ethUtil.stripHexPrefix(self.web3.eth.accounts.wallet[address].privateKey);
const privKey = Buffer.from(key, 'hex');
tx.sign(privKey);
payload.params[0] = '0x' + tx.serialize().toString('hex');
return realSend(payload, (error, result) => {
self.web3.eth.getTransaction(result.result, () => {
callback(error, result);
});
});
});
}, 1);
self.provider.send = function(payload, cb) {
if (payload.method === 'eth_accounts') {
return realSend(payload, function(err, result) {
if (err) {
return cb(err);
}
if (self.accounts.length) {
result.result = self.addresses;
}
cb(null, result);
});
} else if (payload.method === "eth_sendRawTransaction") {
return self.runTransaction.push({payload}, cb);
}
realSend(payload, cb);
};
}
getNonce(address, callback) {
this.web3.eth.getTransactionCount(address, (_error, transactionCount) => {
if(this.nonceCache[address] === undefined) {
this.nonceCache[address] = -1;
}
if (transactionCount > this.nonceCache[address]) {
this.nonceCache[address] = transactionCount;
return callback(this.nonceCache[address]);
}
this.nonceCache[address]++;
callback(this.nonceCache[address]);
});
}
}
module.exports = Provider;

View File

@ -1,20 +1,23 @@
const Spinner = require('cli-spinner').Spinner;
const executeAndWait = async (toSend, account) => {
const spinner = new Spinner('%s');
spinner.setSpinnerString(18);
spinner.start();
return new Promise(async (resolve, reject) => {
const spinner = new Spinner('%s');
spinner.setSpinnerString(18);
spinner.start();
try {
const estimatedGas = await toSend.estimateGas({from: account});
const receipt = await toSend.send({from: account, gas: estimatedGas + 10000});
try {
const estimatedGas = await toSend.estimateGas({from: account});
const receipt = await toSend.send({from: account, gas: estimatedGas + 10000});
spinner.stop(true);
return receipt;
} catch(error) {
console.log("Error minting tokens: ", error.message);
spinner.stop(true);
}
spinner.stop(true);
return resolve(receipt);
} catch(error) {
console.log("Error: ", error.message);
spinner.stop(true);
}
resolve();
});
}
module.exports = {

View File

@ -1351,6 +1351,11 @@ async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.0, async@^2.6
dependencies:
lodash "^4.17.11"
async@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/async/-/async-3.0.0.tgz#4c959b37d8c477dc189f2efb9340847f7ad7f785"
integrity sha512-LNZ6JSpKraIia6VZKKbKxmX6nWIdfsG7WqrOvKpCuDjH7BnGyQRFMTSXEe8to2WF/rqoAKgZvj+L5nnxe0suAg==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"