#!/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); var N = 100; program .option('-t, --testnet', 'Query testnet wallet') .option('-h, --history', 'Include full tx history') .option('-f, --full', 'Dont trim output to ' + N + ' items') .usage('') .parse(process.argv); var args = program.args; if (!args[0]) program.help(); var identifier = args[0]; var network = program.testnet ? 'testnet' : 'livenet'; var trim = program.full ? false : true; var format = (amount, coin) => { return utils.renderAmount(amount, coin); }; 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([ function(done) { client.getSendMaxInfo({ returnInputs: true }, done); }, function(done) { client.getMainAddresses({ doNotVerify: true }, done); }, function(done) { client.getTxHistory({}, done); }, ], (err, res) => { utils.die(err); displayStatus(status); if (trim) { if ((res[1] && res[1].length>N) || (res[2] && res[2].length> N)) console.log('\n Trimming results to %s items', N ); if (res[1] && res[1].length > N) res[1] = res[1].reverse().splice(0,N); if (res[2] && res[2].length > N) res[2] = res[2].splice(0,N); }; displaySendMaxInfo(res[0], status.wallet); displayAddresses(res[1], status.wallet); displayHistory(res[2], status.wallet); }); }); }); }); function displayStatus(status) { var w = status.wallet; console.log('\n* Wallet info'); console.log(' ID: %s Coin:', w.id, w.coin); console.log(' %s %d-of-%d%s [%s %s] wallet (status: %s)', w.network.toUpperCase(), w.m, w.n, w.singleAddress ? ' single-address' : '', w.derivationStrategy, w.addressType, w.status); console.log(' Created on: %s', moment(w.createdOn * 1000)); if (w.status != 'complete') { console.log(' Missing ' + (w.n - w.copayers.length) + ' copayers'); } var x = status.balance; console.log('\n* Balance') console.log(' Total: %s (%s locked)', format(x.totalAmount, w.coin), format(x.lockedAmount, w.coin)); console.log(' Confirmed: %s (%s locked)', format(x.totalConfirmedAmount, w.coin), format(x.lockedConfirmedAmount, w.coin)); console.log(' Available: %s (%s confirmed / %s unconfirmed)', format(x.availableAmount, w.coin), format(x.availableConfirmedAmount, w.coin), format(x.availableAmount - x.availableConfirmedAmount, w.coin)); 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, w.coin)); }); 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, w.coin), format(x.feePerKb, w.coin), x.outputs[0].toAddress, missingSignatures > 0 ? 'missing ' + missingSignatures + ' signatures' : 'ready to broadcast'); }); } else { console.log('\n* No pending tx proposals.'); } }; function displaySendMaxInfo(info, wallet) { 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, wallet.coin), format(info.amount, wallet.coin)); 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, wallet.coin), 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 (%s)', moment(a.createdOn * 1000), a.address, a.path); }); }; function displayHistory(history, wallet) { 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, wallet.coin); 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, wallet.coin), confirmations); }); };