diff --git a/accounts/transactions-rest-api.js b/accounts/transactions-rest-api.js index 5277e1b..6d96778 100644 --- a/accounts/transactions-rest-api.js +++ b/accounts/transactions-rest-api.js @@ -83,8 +83,15 @@ class TransactionsRestApi { const active = apiHelper.parseEntities(req.query.active) const page = req.query.page != null ? parseInt(req.query.page) : 0 const count = req.query.count != null ? parseInt(req.query.count) : keys.multiaddr.transactions + const excludeNullXfer = req.query.excludeNullXfer != null const result = await walletService.getWalletTransactions(active, page, count) + if (excludeNullXfer) { + result.txs = result.txs.filter(tx => { + return tx['result'] != 0 + }) + } + const ret = JSON.stringify(result, null, 2) HttpServer.sendRawData(res, ret) @@ -141,7 +148,11 @@ class TransactionsRestApi { !req.query.count || validator.isInt(req.query.count) - if (!(isValidPage && isValidCount)) { + const isValidExcludeNull = + !req.query.excludeNullXfer + || validator.isAlphanumeric(req.query.excludeNullXfer) + + if (!(isValidPage && isValidCount && isValidExcludeNull)) { HttpServer.sendError(res, errors.body.INVDATA) Logger.error( req.query, diff --git a/doc/GET_txs.md b/doc/GET_txs.md index 8d1337b..adeabee 100644 --- a/doc/GET_txs.md +++ b/doc/GET_txs.md @@ -12,6 +12,7 @@ GET /txs?active=... * **active** - `string` - A pipe-separated list of extended public keys and/or loose addresses and/or pubkeys (`xpub1|address1|address2|pubkey1|...`) * **page** - `integer` - Index of the requested page (first page is index 0) * **count** - `integer` - Number of transactions returned per page +* **excludeNullXfer** - `boolean` - Boolean flag indicating if transactions that don't change the balance should be excluded from the result (default = false) * **at** - `string` (optional) - Access Token (json web token). Required if authentication is activated. Alternatively, the access token can be passed through the `Authorization` HTTP header (with the `Bearer` scheme). ### Examples diff --git a/static/admin/css/style.css b/static/admin/css/style.css index 536c8c0..b4ac1e5 100644 --- a/static/admin/css/style.css +++ b/static/admin/css/style.css @@ -551,6 +551,17 @@ button { display: inline-block; } +#xpubs-export-actions span { + display: inline; +} + +#xpubs-export-actions select { + width: 240px; + margin-left: 5px; + margin-right: 5px; + display: inline-block; +} + #xpubs-tool-details #xpub-value { overflow: hidden; } diff --git a/static/admin/dmt/xpubs-tools/xpubs-tools.html b/static/admin/dmt/xpubs-tools/xpubs-tools.html index fd7017e..8524c4d 100644 --- a/static/admin/dmt/xpubs-tools/xpubs-tools.html +++ b/static/admin/dmt/xpubs-tools/xpubs-tools.html @@ -59,16 +59,17 @@
- - - + + + +
- Rescan this xpub starting at index + Rescan this XPUB starting at index with a lookahead of @@ -80,12 +81,25 @@
- Do you want to delete this xpub? + Do you want to delete this XPUB?
+
+
+ Do you want to export a list of + + of this XPUB? + + +
+
+
diff --git a/static/admin/dmt/xpubs-tools/xpubs-tools.js b/static/admin/dmt/xpubs-tools/xpubs-tools.js index 014c4cd..1858517 100644 --- a/static/admin/dmt/xpubs-tools/xpubs-tools.js +++ b/static/admin/dmt/xpubs-tools/xpubs-tools.js @@ -12,10 +12,13 @@ const screenXpubsToolsScript = { $('#btn-xpub-details-reset').click(() => {this.showSearchForm()}) $('#btn-xpub-details-rescan').click(() => {this.showRescanForm()}) $('#btn-xpub-details-delete').click(() => {this.showDeletionForm()}) + $('#btn-xpub-details-export').click(() => {this.showExportForm()}) $('#btn-xpub-rescan-go').click(() => {this.rescanXpub()}) $('#btn-xpub-rescan-cancel').click(() => {this.hideRescanForm()}) $('#btn-xpub-delete-go').click(() => {this.deleteXpub()}) $('#btn-xpub-delete-cancel').click(() => {this.hideDeletionForm()}) + $('#btn-xpub-export-go').click(() => {this.exportXpubHistory()}) + $('#btn-xpub-export-cancel').click(() => {this.hideExportForm()}) $('#btn-xpub-import-go').click(() => {this.importXpub()}) $('#btn-xpub-details-retype').click(() => {this.showImportForm(true)}) $('#btn-xpub-import-cancel').click(() => {this.hideImportForm(this.isReimport)}) @@ -33,6 +36,7 @@ const screenXpubsToolsScript = { this.hideRescanForm() this.hideDeletionForm() + this.hideExportForm() this.showSearchForm() $("#xpub").focus() }, @@ -148,6 +152,35 @@ const screenXpubsToolsScript = { }) }, + exportXpubHistory: function() { + lib_msg.displayMessage('Exporting the transactional history of this xpub. Please wait...') + + const args = { + 'active': this.currentXpub, + 'page': 0, + 'count': 1000000000 + } + + if ($('#export-type').val() == 'notNull') + args['excludeNullXfer'] = 1 + + return lib_api.getTransactions(args) + .then(result => { + if (result['txs'] && result['txs'].length > 0) { + let content = 'data:text/csv;charset=utf-8,' + content += 'height,txid,date,flow\n' + for (let tx of result['txs']) + content += `${tx['block_height']},${tx['hash']},${new Date(tx['time']*1000).toString()},${tx['result']/100000000}\n` + const encodedURI = encodeURI(content) + window.open(encodedURI) + } + this.hideExportForm() + lib_msg.displayInfo('Transactional history successfully exported.') + }).catch(e => { + lib_errors.processError(e) + }) + }, + checkRescanStatus: function(callback) { this.rescanStatusTimerId = setTimeout(() => { lib_api.getXpubRescanStatus(this.currentXpub) @@ -312,6 +345,17 @@ const screenXpubsToolsScript = { $('#xpubs-tool-actions').show() }, + showExportForm: function() { + $('#xpubs-tool-actions').hide() + $('#xpubs-export-actions').show() + lib_msg.cleanMessagesUi() + }, + + hideExportForm: function() { + $('#xpubs-export-actions').hide() + $('#xpubs-tool-actions').show() + }, + } screenScripts.set('#screen-xpubs-tools', screenXpubsToolsScript) diff --git a/static/admin/lib/api-wrapper.js b/static/admin/lib/api-wrapper.js index 2fdae1d..6dfbcb3 100644 --- a/static/admin/lib/api-wrapper.js +++ b/static/admin/lib/api-wrapper.js @@ -156,6 +156,14 @@ const lib_api = { ) }, + /** + * Transactions + */ + getTransactions: function(arguments) { + let uri = this.baseUri + '/txs' + return this.sendGetUriEncoded(uri, arguments) + }, + /** * Rescans a range of blocks */