Skip to content

Commit

Permalink
Merge pull request bitpay#52 from isocolsky/feat/support
Browse files Browse the repository at this point in the history
Feat/support command
  • Loading branch information
matiu committed Jun 30, 2017
2 parents a9a74a9 + c67ef01 commit 850b25e
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 13 deletions.
54 changes: 43 additions & 11 deletions bin/cli-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ Utils.getClient = function(args, opts, cb) {
var client = new Client({
baseUrl: url.resolve(host, '/bws/api'),
verbose: args.verbose,
supportStaffWalletId: opts.walletId,
});

storage.load(function(err, walletData) {
Expand Down Expand Up @@ -256,17 +257,48 @@ Utils.configureCommander = function(program) {
return program;
};

Utils.renderAmount = function(amount) {
var unit = process.env.BIT_UNIT || 'bit';
if (unit === 'SAT') {
// Do nothing
} else if (process.env.BIT_UNIT === 'btc') {
amount = amount / 1e8;
} else {
amount = amount / 100;
}
amount = (parseFloat(amount.toPrecision(12)));
return amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + ' ' + unit;
Utils.UNITS = {
btc: {
name: 'btc',
toSatoshis: 100000000,
maxDecimals: 8,
minDecimals: 8,
},
bit: {
name: 'bit',
toSatoshis: 100,
maxDecimals: 2,
minDecimals: 2,
},
};

Utils.renderAmount = function(satoshis, unit, opts) {
function clipDecimals(number, decimals) {
var x = number.toString().split('.');
var d = (x[1] || '0').substring(0, decimals);
return parseFloat(x[0] + '.' + d);
};

function addSeparators(nStr, thousands, decimal, minDecimals) {
nStr = nStr.replace('.', decimal);
var x = nStr.split(decimal);
var x0 = x[0];
var x1 = x[1];

x1 = _.dropRightWhile(x1, function(n, i) {
return n == '0' && i >= minDecimals;
}).join('');
var x2 = x.length > 1 ? decimal + x1 : '';

x0 = x0.replace(/\B(?=(\d{3})+(?!\d))/g, thousands);
return x0 + x2;
};

opts = opts || {};

var u = Utils.UNITS[unit || 'bit'];
var amount = clipDecimals((satoshis / u.toSatoshis), u.maxDecimals).toFixed(u.maxDecimals);
return addSeparators(amount, opts.thousandsSeparator || ',', opts.decimalSeparator || '.', u.minDecimals) + ' ' + u.name;
};

Utils.renderTxProposals = function(txps) {
Expand Down
159 changes: 159 additions & 0 deletions bin/wallet-support
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#!/usr/bin/env node

var _ = require('lodash');
var program = require('commander');
var utils = require('./cli-utils');
var moment = require('moment');
var async = require('async');
program = utils.configureCommander(program);

program
.option('-t, --testnet', 'Query testnet wallet')
.option('-h, --history', 'Include full tx history')
.usage('<identifier>')
.parse(process.argv);

var args = program.args;
if (!args[0])
program.help();

var identifier = args[0];
var network = program.testnet ? 'testnet' : 'livenet';
var format = (amount) => {
return utils.renderAmount(amount, 'btc');
};

utils.getClient(program, {
mustExist: true
}, (client) => {
client.getStatusByIdentifier({
identifier: identifier
}, (err, status) => {
utils.die(err);
if (!status) {
console.log('Could not find wallet associated to ' + identifier);
process.exit(0);
}

console.log('Found wallet associated to ' + identifier + '. Querying wallet info...');

utils.getClient(program, {
mustExist: true,
walletId: status.wallet.id,
}, (client) => {

async.parallel([
(done) => {
client.getSendMaxInfo({
returnInputs: true
}, done);
}, (done) => {
client.getMainAddresses({
doNotVerify: true
}, done);
}, (done) => {
client.getTxHistory({}, done);
},
], (err, res) => {
utils.die(err);
displayStatus(status);
displaySendMaxInfo(res[0]);
displayAddresses(res[1]);
displayHistory(res[2]);
});
});
});
});



function displayStatus(status) {
var x = status.wallet;
console.log('\n* Wallet info');
console.log(' ID: %s', x.id);
console.log(' %s %d-of-%d%s [%s %s] wallet (status: %s)', x.network.toUpperCase(), x.m, x.n, x.singleAddress ? ' single-address' : '', x.derivationStrategy, x.addressType, x.status);

if (x.status != 'complete') {
console.log(' Missing ' + (x.n - x.copayers.length) + ' copayers');
}

x = status.balance;
console.log('\n* Balance')
console.log(' Total: %s (%s locked)', format(x.totalAmount), format(x.lockedAmount));
console.log(' Confirmed: %s (%s locked)', format(x.totalConfirmedAmount), format(x.lockedConfirmedAmount));
console.log(' Available: %s (%s confirmed / %s unconfirmed)', format(x.availableAmount), format(x.availableConfirmedAmount), format(x.availableAmount - x.availableConfirmedAmount));
if (!_.isEmpty(x.byAddress)) {
console.log(' By address:');
}
_.each(x.byAddress, function(item) {
console.log(' %s (%s): %s', item.address, item.path, format(item.amount));
});

if (!_.isEmpty(status.pendingTxps)) {
console.log("\n* Pending tx proposals");
_.each(status.pendingTxps, function(x) {
var missingSignatures = x.requiredSignatures - _.filter(_.values(x.actions), function(a) {
return a.type == 'accept';
}).length;
console.log(" [%s] %s (fee/kb %s) => %s (status: %s)", new moment(x.createdOn * 1000), format(x.amount), format(x.feePerKb), x.outputs[0].toAddress, missingSignatures > 0 ? 'missing ' + missingSignatures + ' signatures' : 'ready to broadcast');
});
} else {
console.log('\n* No pending tx proposals.');
}
};

function displaySendMaxInfo(info) {
if (info.amount == 0) return;
console.log('\n* Send max');
console.log(' Maximum spendable amount at "normal" fee level (%s per kb): %s', format(info.feePerKb), format(info.amount));
if (info.utxosBelowFee > 0) {
console.log(' %s UTXOs for a total amount of %s are currently below fee required to spend them', info.utxosBelowFee, info.amountBelowFee);
}
if (info.utxosAboveMaxSize > 0) {
console.log(' %s UTXOs for a total amount of %s exceed the max allowed tx size', info.utxosAboveMaxSize, info.amountAboveMaxSize);
}

if (!_.isEmpty(info.inputs)) {
console.log(' Available UTXOs:');
}
_.each(info.inputs, function(utxo) {
console.log(' %s%s %s', format(utxo.satoshis), utxo.locked ? ' (locked)' : '', utxo.confirmations > 0 ? utxo.confirmations + ' confirmations' : 'unconfirmed');
});
};

function displayAddresses(addresses) {
if (_.isEmpty(addresses)) {
console.log('\n* No addresses.');
return;
}
console.log('\n* Main addresses (not including change addresses)');
_.each(addresses, function(a) {
console.log(' %s (%s)', a.address, a.path);
});
};

function displayHistory(history) {
if (_.isEmpty(history)) {
console.log('\n* No tx history.');
return;
}

console.log("\n* Tx history")
_.each(history, function(tx) {
var time = moment(tx.time * 1000);
var amount = format(tx.amount);
var confirmations = tx.confirmations || 0;
switch (tx.action) {
case 'received':
direction = '<=';
break;
case 'moved':
direction = '==';
break;
case 'sent':
direction = '=>';
break;
}
console.log(" [%s] %s %s %s %s (fee/kb %s) (%s confirmations)", time, tx.txid, direction, tx.action, amount, format(tx.feePerKb), confirmations);
});
};
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "bitcore-wallet",
"description": "A CLI Mutisig HD Bitcoin Wallet, demo for Bitcore Wallet Service",
"author": "BitPay Inc",
"version": "0.8.0",
"version": "0.9.0",
"keywords": [
"bitcoin",
"copay",
Expand All @@ -17,7 +17,8 @@
"url": "https://github.com/bitpay/bitcore-wallet/issues"
},
"dependencies": {
"bitcore-wallet-client": "5.2.1",
"async": "^2.5.0",
"bitcore-wallet-client": "5.4.0",
"commander": "^2.6.0",
"lodash": "^3.3.1",
"moment": "^2.9.0",
Expand Down

0 comments on commit 850b25e

Please sign in to comment.