From ef8e7ac5017f6fc8d0bd725ef0d8d8b0da40e420 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Dominguez Date: Sat, 22 Sep 2018 11:59:18 +0200 Subject: [PATCH 01/32] Fix RC version --- app-template/bitcoincom/appConfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-template/bitcoincom/appConfig.json b/app-template/bitcoincom/appConfig.json index 2eab6232d..343dc724e 100644 --- a/app-template/bitcoincom/appConfig.json +++ b/app-template/bitcoincom/appConfig.json @@ -26,7 +26,7 @@ "description": "A Secure Bitcoin Wallet", "version": "5.1.3", "fullVersion": "5.1-rc2", - "androidVersion": "501003", + "androidVersion": "501300", "_extraCSS": "", "_enabledExtensions": { "coinbase": false, From e3a82a11183837d6f25b75f1b29c842798980856 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Dominguez Date: Sat, 22 Sep 2018 12:00:10 +0200 Subject: [PATCH 02/32] Reactivate shapeshift --- src/js/services/servicesService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/services/servicesService.js b/src/js/services/servicesService.js index ddd9db394..56d699e59 100644 --- a/src/js/services/servicesService.js +++ b/src/js/services/servicesService.js @@ -5,7 +5,7 @@ angular.module('copayApp.services').factory('servicesService', function(configSe name: 'shapeshift', title: 'Shapeshift', icon: 'icon-shapeshift', - href: 'https://shapeshift.io/' + sref: 'tabs.shapeshift' }]; root.register = function(serviceInfo) { From 9e01303f371dd3060776e063ea8a958a17e41b56 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Dominguez Date: Sat, 22 Sep 2018 12:00:58 +0200 Subject: [PATCH 03/32] Fix erase from a merge --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 7968f2510..361ff1ab4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -57,7 +57,7 @@ module.exports = function(grunt) { command: 'node ./util/buildExternalServices.js' }, get_nwjs_for_pkg: { - command: 'if [ ! -d ./cache/0.19.5-pkg/osx64/nwjs.app ]; then cd ./cache; curl https://dl.nwjs.io/v0.19.5-mas-beta/nwjs-mas-v0.19.5-osx-x64.zip --output nwjs.zip; unzip nwjs.zip; mkdir -p ./0.19.5-pkg/osx64; cp -R ./nwjs-mas-v0.19.5-osx-x64/nwjs.app ./0.19.5-pkg/osx64/; fi' + command: 'if [ ! -d ./cache/0.19.4/osx64/nwjs.app ]; then mkdir -p ./cache/0.19.4/osx64; curl https://dl.nwjs.io/v0.19.5-mas-beta/nwjs-mas-v0.19.5-osx-x64.zip --output ./cache/nwjs.zip; unzip ./cache/nwjs.zip -d ./cache; cp -R ./cache/nwjs-mas-v0.19.5-osx-x64/nwjs.app ./cache/0.19.4/osx64/; fi' }, log_android: { command: 'adb logcat | grep chromium', From 0ba4db83e0f9ad5a06b6104a393d89ac0f8e0250 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Sat, 22 Sep 2018 11:03:26 -0700 Subject: [PATCH 04/32] Handling confirmation updates correctly for early transactions. --- src/js/services/wallet-history.service.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/services/wallet-history.service.js b/src/js/services/wallet-history.service.js index 512a2d8b1..798c92671 100644 --- a/src/js/services/wallet-history.service.js +++ b/src/js/services/wallet-history.service.js @@ -28,8 +28,8 @@ function addEarlyTransactions(walletId, cachedTxs, newTxs) { var cachedTxIndexFromId = {}; - cachedTxs.forEach(function forCachedTx(tx){ - cachedTxIndexFromId[tx.txid] = true; + cachedTxs.forEach(function forCachedTx(tx, txIndex){ + cachedTxIndexFromId[tx.txid] = txIndex; }); var confirmationsUpdated = false; @@ -147,7 +147,7 @@ * @param {function(error, txs)} cb - txs is always an array, may be empty */ function getCachedTxHistory(walletId, cb) { - console.log('txhistory updateLocalTxHistoryByPage()'); + console.log('txhistory getCachedTxHistory()'); storageService.getTxHistory(walletId, function onGetTxHistory(err, txHistoryString){ if (err) { return cb(err, []); @@ -230,7 +230,7 @@ } function updateLocalTxHistoryByPage(wallet, getLatest, flushCacheOnNew, cb) { - console.log('txhistory updaetLocalTxHistoryByPage()'); + console.log('txhistory updateLocalTxHistoryByPage()'); if (flushCacheOnNew) { fetchTxHistoryByPage(wallet, 0, function onFetchTxHistory(err, txs){ if (err) { From a5a1d3edb673f589e17110deb55194a76ab170ab Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Sat, 22 Sep 2018 11:07:24 -0700 Subject: [PATCH 05/32] Remove debug logging. --- src/js/services/wallet-history.service.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/js/services/wallet-history.service.js b/src/js/services/wallet-history.service.js index 798c92671..5cb08e6ee 100644 --- a/src/js/services/wallet-history.service.js +++ b/src/js/services/wallet-history.service.js @@ -147,7 +147,6 @@ * @param {function(error, txs)} cb - txs is always an array, may be empty */ function getCachedTxHistory(walletId, cb) { - console.log('txhistory getCachedTxHistory()'); storageService.getTxHistory(walletId, function onGetTxHistory(err, txHistoryString){ if (err) { return cb(err, []); @@ -230,7 +229,6 @@ } function updateLocalTxHistoryByPage(wallet, getLatest, flushCacheOnNew, cb) { - console.log('txhistory updateLocalTxHistoryByPage()'); if (flushCacheOnNew) { fetchTxHistoryByPage(wallet, 0, function onFetchTxHistory(err, txs){ if (err) { From c661798cd14128a2266acb36786bd285685a6506 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Sat, 22 Sep 2018 17:45:19 -0700 Subject: [PATCH 06/32] Android Debug and release builds work when Gradle 3.3 is in PATH. --- Gruntfile.js | 2 +- app-template/config-template.xml | 2 +- src/android/build-extras.gradle | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 7968f2510..4ac23e47a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -71,7 +71,7 @@ module.exports = function(grunt) { sign_android: { // When the build log outputs "Built the following apk(s):", it seems to need the filename to start with "android-release". // It looks like it simply lists all apk files starting with "android-release" - command: 'rm -f platforms/android/build/outputs/apk/android-release-signed-*.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../bitcoin-com-release-key.jks -signedjar platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-unsigned.apk bitcoin-com && zipalign -v 4 platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/bitcoin-com-wallet-<%= pkg.fullVersion %>-android-signed-aligned.apk', + command: 'rm -f platforms/android/build/outputs/apk/release/*-android-signed-aligned.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../bitcoin-com-release-key.jks -signedjar platforms/android/build/outputs/apk/release/android-release-signed.apk platforms/android/build/outputs/apk/release/android-release-unsigned.apk bitcoin-com && zipalign -v 4 platforms/android/build/outputs/apk/release/android-release-signed.apk platforms/android/build/outputs/apk/release/bitcoin-com-wallet-<%= pkg.fullVersion %>-android-signed-aligned.apk', stdin: true, }, sign_desktop_dist: { diff --git a/app-template/config-template.xml b/app-template/config-template.xml index 8686b7b36..b24b86fae 100644 --- a/app-template/config-template.xml +++ b/app-template/config-template.xml @@ -77,7 +77,7 @@ - + diff --git a/src/android/build-extras.gradle b/src/android/build-extras.gradle index e7dd50572..c90145418 100644 --- a/src/android/build-extras.gradle +++ b/src/android/build-extras.gradle @@ -1,5 +1,6 @@ ext { ANDROID_SUPPORT_V4_VERSION = '26.1.0' + ANDROID_SUPPORT_ANNOTATIONS_VERSION = '26.1.0' } configurations.all { From 1b0541a7b5746c0ef2eae602695bc816cab1b0dc Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Sat, 22 Sep 2018 22:22:06 -0700 Subject: [PATCH 07/32] Return error from Shapeshift when pairs are unavailable. --- src/js/services/shapeShiftApiService.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/js/services/shapeShiftApiService.js b/src/js/services/shapeShiftApiService.js index cc5fb0792..92564464a 100644 --- a/src/js/services/shapeShiftApiService.js +++ b/src/js/services/shapeShiftApiService.js @@ -23,6 +23,13 @@ var ShapeShift = (function() { if (xmlhttp.status == 200) { var parsedResponse = JP(xmlhttp.responseText); cb.apply(null, [parsedResponse]); + } else if (xmlhttp.status === 500) { + var parsedResponse = JP(xmlhttp.responseText); + if (typeof parsedResponse.error === 'string') { + cb.apply(null, [parsedResponse]); + } else { + cb.apply(null, [new Error('Request Failed')]); + } } else { cb.apply(null, [new Error('Request Failed')]) } From 6452a0c7f4797a7321b58f4bff2ef72a54c01dda Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Sun, 23 Sep 2018 13:06:36 -0700 Subject: [PATCH 08/32] Handling JSON exception. --- src/js/services/shapeShiftApiService.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/js/services/shapeShiftApiService.js b/src/js/services/shapeShiftApiService.js index 92564464a..67e6f23de 100644 --- a/src/js/services/shapeShiftApiService.js +++ b/src/js/services/shapeShiftApiService.js @@ -23,15 +23,17 @@ var ShapeShift = (function() { if (xmlhttp.status == 200) { var parsedResponse = JP(xmlhttp.responseText); cb.apply(null, [parsedResponse]); - } else if (xmlhttp.status === 500) { - var parsedResponse = JP(xmlhttp.responseText); - if (typeof parsedResponse.error === 'string') { - cb.apply(null, [parsedResponse]); - } else { - cb.apply(null, [new Error('Request Failed')]); - } } else { - cb.apply(null, [new Error('Request Failed')]) + var cbResponse = new Error('Request Failed'); + if (xmlhttp.status === 500) { + try { + var errorResponse = JSON.parse(xmlhttp.responseText); + if (typeof errorResponse.error === 'string') { + cbResponse = errorResponse; + } + } catch (e) { /* nop */ } + } + cb.apply(null, [cbResponse]); } } }; From babdc8a13bb52474bd79d5433bc5a37f10293cd2 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Sun, 23 Sep 2018 20:14:42 -0700 Subject: [PATCH 09/32] Restored production setting for page size. --- src/js/services/wallet-history.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/services/wallet-history.service.js b/src/js/services/wallet-history.service.js index 5cb08e6ee..a0b36288d 100644 --- a/src/js/services/wallet-history.service.js +++ b/src/js/services/wallet-history.service.js @@ -7,8 +7,8 @@ .factory('walletHistoryService', walletHistoryService); function walletHistoryService(configService, storageService, lodash, $log, txFormatService) { - //var PAGE_SIZE = 50; - var PAGE_SIZE = 20; // For dev only + var PAGE_SIZE = 50; + //var PAGE_SIZE = 20; // For dev only // How much to overlap on each end of the page, for mitigating inconsistent sort order. var PAGE_OVERLAP_FRACTION = 0.2; var PAGE_OVERLAP = Math.floor(PAGE_SIZE * PAGE_OVERLAP_FRACTION); From 8f8027d573832c100e4293f9166bdd8a1cbb290a Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Sun, 23 Sep 2018 20:56:15 -0700 Subject: [PATCH 10/32] Reverse out bugfix. --- src/js/services/shapeShiftApiService.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/js/services/shapeShiftApiService.js b/src/js/services/shapeShiftApiService.js index 67e6f23de..a90e587d4 100644 --- a/src/js/services/shapeShiftApiService.js +++ b/src/js/services/shapeShiftApiService.js @@ -24,16 +24,7 @@ var ShapeShift = (function() { var parsedResponse = JP(xmlhttp.responseText); cb.apply(null, [parsedResponse]); } else { - var cbResponse = new Error('Request Failed'); - if (xmlhttp.status === 500) { - try { - var errorResponse = JSON.parse(xmlhttp.responseText); - if (typeof errorResponse.error === 'string') { - cbResponse = errorResponse; - } - } catch (e) { /* nop */ } - } - cb.apply(null, [cbResponse]); + cb.apply(null, [new Error('Request Failed')]); } } }; From d2178d670f2949f8c89164bdc7e94ed44ce31ecf Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Sun, 23 Sep 2018 21:05:26 -0700 Subject: [PATCH 11/32] Fix identification of BitPay. --- src/js/services/send-flow.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/services/send-flow.service.js b/src/js/services/send-flow.service.js index e8be2e487..babf5096a 100644 --- a/src/js/services/send-flow.service.js +++ b/src/js/services/send-flow.service.js @@ -52,7 +52,7 @@ angular // Detect some merchant that we know if (payProData.memo.indexOf('eGifter') > -1) { name = 'eGifter' - } else if (paymentUrl.indexOf('https://bitpay.com') > -1) { + } else if (payProData.url.indexOf('https://bitpay.com') > -1) { name = 'BitPay'; } From 5401bf9a0998d8515d21b195656568674ec67d8a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Dominguez Date: Mon, 24 Sep 2018 16:54:40 +0200 Subject: [PATCH 12/32] Fix the case where the cache is empty --- src/js/services/wallet-history.service.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/services/wallet-history.service.js b/src/js/services/wallet-history.service.js index 5cb08e6ee..760f6994e 100644 --- a/src/js/services/wallet-history.service.js +++ b/src/js/services/wallet-history.service.js @@ -49,8 +49,9 @@ var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP); console.log('overlappingTxFraction:', overlappingTxFraction); + console.log('overlappingTxsCount:', overlappingTxsCount); - if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION) { // We are good + if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION || (someTransactionsWereNew && overlappingTxsCount === 0)) { // We are good if (someTransactionsWereNew) { saveTxHistory(walletId, cachedTxs); } else if (confirmationsUpdated) { From fb41fca6015c7284657083139f8bc763437acfb6 Mon Sep 17 00:00:00 2001 From: Sebastiaan Pasma Date: Mon, 24 Sep 2018 17:13:28 +0200 Subject: [PATCH 13/32] Also apply fix to addLatestTransactions function --- src/js/services/wallet-history.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/services/wallet-history.service.js b/src/js/services/wallet-history.service.js index 01e752465..a9792c170 100644 --- a/src/js/services/wallet-history.service.js +++ b/src/js/services/wallet-history.service.js @@ -94,7 +94,7 @@ var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP); - if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION) { // We are good + if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION || (someTransactionsWereNew && overlappingTxsCount === 0)) { // We are good if (someTransactionsWereNew) { var allTxs = uniqueNewTxs.concat(cachedTxs); saveTxHistory(walletId, allTxs); From f0d020f53829b0232c8d3edea0526e4c97271b06 Mon Sep 17 00:00:00 2001 From: Sebastiaan Pasma Date: Mon, 24 Sep 2018 17:49:37 +0200 Subject: [PATCH 14/32] config version update --- app-template/bitcoincom/appConfig.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app-template/bitcoincom/appConfig.json b/app-template/bitcoincom/appConfig.json index 343dc724e..5818a4006 100644 --- a/app-template/bitcoincom/appConfig.json +++ b/app-template/bitcoincom/appConfig.json @@ -24,9 +24,9 @@ "windowsAppId": "804636ee-b017-4cad-8719-e58ac97ffa5c", "pushSenderId": "1036948132229", "description": "A Secure Bitcoin Wallet", - "version": "5.1.3", - "fullVersion": "5.1-rc2", - "androidVersion": "501300", + "version": "5.1.4", + "fullVersion": "5.1-rc3", + "androidVersion": "501400", "_extraCSS": "", "_enabledExtensions": { "coinbase": false, From b5cd9faca78ddba74af060d9c5cf9c177272294b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Dominguez Date: Tue, 25 Sep 2018 16:48:42 +0200 Subject: [PATCH 15/32] Shapeshift, return address should be verified with the coin out --- src/js/services/shapeshift.service.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/services/shapeshift.service.js b/src/js/services/shapeshift.service.js index 73410e478..8fdfe08b9 100644 --- a/src/js/services/shapeshift.service.js +++ b/src/js/services/shapeshift.service.js @@ -63,6 +63,7 @@ angular } else if (amount > service.marketData.maxLimit) { cb(new Error(gettextCatalog.getString('Amount is above the limit'))); } else { + // Init service data service.withdrawalAddress = withdrawalAddress; service.returnAddress = returnAddress; @@ -72,7 +73,7 @@ angular // Check the address shapeshiftApiService - .ValidateAddress(withdrawalAddress, coinOut) + .ValidateAddress(returnAddress, coinOut) .then(function onSuccess(response) { if (response && response.isvalid) { // Prepare the transaction shapeshift side From c657cc5bfb2e1984ce5a4ec7d3788eb9c659b7b5 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 26 Sep 2018 15:11:35 +1200 Subject: [PATCH 16/32] Naming anonymous functions for easier debugging. --- src/js/controllers/review.controller.js | 38 ++++++++++++------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index c82838f7c..c6c484700 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -78,7 +78,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit function onBeforeEnter(event, data) { - console.log('walletSelector onBeforeEnter sendflow ', sendFlowService.state); + console.log('reviewController onBeforeEnter sendflow ', sendFlowService.state); defaults = configService.getDefaults(); sendFlowData = sendFlowService.state.getClone(); originWalletId = sendFlowData.fromWalletId; @@ -95,11 +95,11 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit vm.thirdParty = sendFlowData.thirdParty; switch (vm.thirdParty.id) { case 'shapeshift': - initShapeshift(function (err) { + initShapeshift(function onInitShapeshift(err) { if (err) { // Error stop here ongoingProcess.set('connectingShapeshift', false); - popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString(), function () { + popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString(), function onAlert() { $ionicHistory.goBack(); }); } else { @@ -142,18 +142,18 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit if (!tx || !vm.originWallet) return; if (vm.paymentExpired) { - popupService.showAlert(null, gettextCatalog.getString('This bitcoin payment request has expired.', function () { + popupService.showAlert(null, gettextCatalog.getString('This bitcoin payment request has expired.', function onAlert() { $ionicHistory.goBack(); })); vm.sendStatus = ''; - $timeout(function() { + $timeout(function onTimeout() { $scope.$apply(); }); return; } ongoingProcess.set('creatingTx', true, statusChangeHandler); - getTxp(lodash.clone(tx), vm.originWallet, false, function(err, txp) { + getTxp(lodash.clone(tx), vm.originWallet, false, function onGetTxp(err, txp) { ongoingProcess.set('creatingTx', false, statusChangeHandler); if (err) return; @@ -170,12 +170,12 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit if (!vm.originWallet.canSign() && !vm.originWallet.isPrivKeyExternal()) { $log.info('No signing proposal: No private key'); - return walletService.onlyPublish(vm.originWallet, txp, function(err) { + return walletService.onlyPublish(vm.originWallet, txp, function onOnlyPublish(err) { if (err) setSendError(err); }, statusChangeHandler); } - walletService.publishAndSign(vm.originWallet, txp, function(err, txp) { + walletService.publishAndSign(vm.originWallet, txp, function onPublishAndSign(err, txp) { if (err) return setSendError(err); if (config.confirmedTxsNotifications && config.confirmedTxsNotifications.enabled) { txConfirmNotification.subscribe(vm.originWallet, { @@ -189,7 +189,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit confirmTx(function(nok) { if (nok) { vm.sendStatus = ''; - $timeout(function() { + $timeout(function onTimeout() { $scope.$apply(); }); return; @@ -375,7 +375,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit } txp.excludeUnconfirmedUtxos = !tx.spendUnconfirmed; txp.dryRun = dryRun; - walletService.createTx(wallet, txp, function(err, ctxp) { + walletService.createTx(wallet, txp, function onCreateTx(err, ctxp) { if (err) { setSendError(err); return cb(err); @@ -434,7 +434,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit } // Check if the recipient is a contact - addressbookService.get(originCoin + address, function(err, contact) { + addressbookService.get(originCoin + address, function onGetContact(err, contact) { if (!err && contact) { handleDestinationAsAddressOfContact(contact); } else { @@ -680,7 +680,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit $timeout(function() { $scope.$apply(); }); - popupService.showAlert(gettextCatalog.getString('Error at confirm'), bwcError.msg(msg), function () { + popupService.showAlert(gettextCatalog.getString('Error at confirm'), bwcError.msg(msg), function onAlert() { $ionicHistory.goBack(); }); }; @@ -692,7 +692,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit tx.displayAddress = tx.toAddress; } - addressbookService.get(tx.coin+tx.toAddress, function(err, addr) { // Check if the recipient is a contact + addressbookService.get(tx.coin+tx.toAddress, function onGetContact(err, addr) { // Check if the recipient is a contact if (!err && addr) { tx.toName = addr.name; tx.toEmail = addr.email; @@ -711,7 +711,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit updateTx(tx, vm.originWallet, { dryRun: true }, function(err) { - $timeout(function() { + $timeout(function onTimeout() { $scope.$apply(); }, 10); @@ -838,7 +838,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit // refresh(); var feeServiceLevel = usingMerchantFee && vm.originWallet.coin == 'btc' ? 'urgent' : tx.feeLevel; - feeService.getFeeRate(vm.originWallet.coin, tx.network, feeServiceLevel, function(err, feeRate) { + feeService.getFeeRate(vm.originWallet.coin, tx.network, feeServiceLevel, function onGetFeeRate(err, feeRate) { if (err) { ongoingProcess.set('calculatingFee', false); return cb(err); @@ -857,7 +857,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit tx.feeRate = feeRate; } - getSendMaxInfo(lodash.clone(tx), wallet, function(err, sendMaxInfo) { + getSendMaxInfo(lodash.clone(tx), wallet, function onGetSendmaxInfo(err, sendMaxInfo) { if (err) { ongoingProcess.set('calculatingFee', false); var msg = gettextCatalog.getString('Error getting SendMax information'); @@ -871,7 +871,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit if (tx.sendMax && sendMaxInfo.amount == 0) { ongoingProcess.set('calculatingFee', false); setNotReady(gettextCatalog.getString('Insufficient confirmed funds')); - popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee'), function () { + popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee'), function onAlert() { $ionicHistory.goBack(); }); return cb('no_funds'); @@ -897,7 +897,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit } console.log('calling getTxp() from getSendMaxInfo cb.'); - getTxp(lodash.clone(tx), wallet, opts.dryRun, function(err, txp) { + getTxp(lodash.clone(tx), wallet, opts.dryRun, function onGetTxp(err, txp) { ongoingProcess.set('calculatingFee', false); if (err) { if (err.message == 'Insufficient funds') { @@ -909,7 +909,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit } txp.feeStr = txFormatService.formatAmountStr(wallet.coin, txp.fee); - txFormatService.formatAlternativeStr(wallet.coin, txp.fee, function(v) { + txFormatService.formatAlternativeStr(wallet.coin, txp.fee, function onFormatAlternativeStr(v) { // txp.alternativeFeeStr = v; // if (txp.alternativeFeeStr.substring(0, 4) == '0.00') // txp.alternativeFeeStr = '< ' + txp.alternativeFeeStr; From 5b7eb57d76a3ba7252b7937e78942a30d707ecc9 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 26 Sep 2018 15:12:37 +1200 Subject: [PATCH 17/32] Fix for sending ShapeShift transaction to Shapeshift deposit address, rather than own wallet. --- src/js/controllers/review.controller.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index c6c484700..2490fc053 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -46,7 +46,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit vm.sendingTitle = gettextCatalog.getString('You are sending'); vm.sendStatus = ''; vm.showAddress = true; - vm.thirdParty = false; + vm.thirdParty = null; vm.wallet = null; vm.memoExpanded = false; @@ -270,6 +270,9 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit txp: {}, }; + if (vm.thirdParty && vm.thirdParty.id === "shapeshift") { + tx.toAddress = vm.thirdParty.toAddress; + } if (data.stateParams.requiredFeeRate) { vm.usingMerchantFee = true; @@ -283,7 +286,14 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit var B = tx.coin === 'bch' ? bitcoreCash : bitcore; var networkName; try { - if (vm.destination.kind === 'wallet') { // This is a wallet-to-wallet transfer + // Final destination is a wallet, but this transaction must go to an address for the first stage of the exchange. + if (sendFlowData.thirdParty && sendFlowData.thirdParty.id === 'shapeshift') { + networkName = (new B.Address(tx.toAddress)).network.name; + tx.network = networkName; + console.log('calling setupTx() for shapeshift.'); + setupTx(tx); + + } else if (vm.destination.kind === 'wallet') { // This is a wallet-to-wallet transfer ongoingProcess.set('generatingNewAddress', true); var toWallet = profileService.getWallet(destinationWalletId); @@ -498,9 +508,6 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit function initShapeshift(cb) { vm.sendingTitle = gettextCatalog.getString('You are shifting'); - if (!vm.thirdParty.data) { - vm.thirdParty.data = {}; - } var toWallet = profileService.getWallet(destinationWalletId); vm.destination.name = toWallet.name; @@ -525,9 +532,8 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit if (err) { return cb(err); } else { - vm.destination.kind = 'shapeshift'; - vm.destination.address = toAddress; - tx.toAddress = shapeshiftData.toAddress; + // Want it to appear like a wallet-to-wallet transfer, so don't set the main toAddress. + vm.thirdParty.toAddress = shapeshiftData.toAddress; vm.memo = 'ShapeShift Order:\nhttps://www.shapeshift.io/#/status/' + shapeshiftData.orderId; vm.memoExpanded = !!vm.memo; ongoingProcess.set('connectingShapeshift', false); From 6bf0de9974459ea1a3f77bf78348cbec67ae8b55 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 26 Sep 2018 15:31:29 +1200 Subject: [PATCH 18/32] Remove debug logging and other cleanup. --- src/js/controllers/review.controller.js | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index 2490fc053..42388933b 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -78,7 +78,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit function onBeforeEnter(event, data) { - console.log('reviewController onBeforeEnter sendflow ', sendFlowService.state); + $log.debug('reviewController onBeforeEnter sendflow ', sendFlowService.state); defaults = configService.getDefaults(); sendFlowData = sendFlowService.state.getClone(); originWalletId = sendFlowData.fromWalletId; @@ -125,7 +125,6 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit config = configCache; priceDisplayIsFiat = config.wallet.settings.priceDisplay === 'fiat'; vm.origin.currencyColor = (vm.originWallet.coin === 'btc' ? defaults.bitcoinWalletColor : defaults.bitcoinCashWalletColor); - console.log("coin", vm.originWallet.coin, vm.origin.currencyColor, config.bitcoinWalletColor, vm.originWallet.coin === 'btc'); unitFromSat = 1 / config.wallet.settings.unitToSatoshi; } updateSendAmounts(); @@ -246,7 +245,6 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit }; function createVanityTransaction(data) { - console.log('createVanityTransaction()'); var configFeeLevel = config.wallet.settings.feeLevel ? config.wallet.settings.feeLevel : 'normal'; // Grab stateParams @@ -290,7 +288,6 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit if (sendFlowData.thirdParty && sendFlowData.thirdParty.id === 'shapeshift') { networkName = (new B.Address(tx.toAddress)).network.name; tx.network = networkName; - console.log('calling setupTx() for shapeshift.'); setupTx(tx); } else if (vm.destination.kind === 'wallet') { // This is a wallet-to-wallet transfer @@ -298,24 +295,24 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit var toWallet = profileService.getWallet(destinationWalletId); // We need an address to send to, so we ask the walletService to create a new address for the toWallet. - console.log('Getting address for wallet...'); walletService.getAddress(toWallet, true, function onWalletAddress(err, addr) { - console.log('getAddress cb called', err); + if (err) { + $log.error('Error getting address for wallet.', err); + throw new Error(err.message); + } ongoingProcess.set('generatingNewAddress', false); tx.toAddress = addr; networkName = (new B.Address(tx.toAddress)).network.name; tx.network = networkName; - console.log('calling setupTx() for wallet.'); setupTx(tx); }); } else { // This is a Wallet-to-address transfer networkName = (new B.Address(tx.toAddress)).network.name; tx.network = networkName; - console.log('calling setupTx() for address.'); setupTx(tx); } } catch (e) { - console.error('Error setting up tx', e); + $log.error('Error setting up tx', e); var message = gettextCatalog.getString('Invalid address'); popupService.showAlert(null, message, function () { $ionicHistory.nextViewOptions({ @@ -568,7 +565,6 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit }, 1000); function setExpirationTime() { - console.log('setExpirationTime()'); var now = Math.floor(Date.now() / 1000); if (now > expirationTime) { @@ -902,7 +898,6 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit return cb(); } - console.log('calling getTxp() from getSendMaxInfo cb.'); getTxp(lodash.clone(tx), wallet, opts.dryRun, function onGetTxp(err, txp) { ongoingProcess.set('calculatingFee', false); if (err) { @@ -924,8 +919,6 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit if (v.substring(0, 1) === "<") { vm.feeLessThanACent = true; } - - console.log("fiat", vm.feeFiat); }); @@ -935,14 +928,11 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit txp.feeToHigh = per > FEE_TOO_HIGH_LIMIT_PERCENTAGE; vm.feeCrypto = (unitFromSat * txp.fee).toFixed(8); vm.feeIsHigh = txp.feeToHigh; - console.log("crypto", vm.feeCrypto); - tx.txp[wallet.id] = txp; $log.debug('Confirm. TX Fully Updated for wallet:' + wallet.id, tx); vm.readyToSend = true; updateSendAmounts(); - console.log('readyToSend:', vm.readyToSend); $scope.$apply(); return cb(); From a9b84f394299f6fd0c2231197c19dc0804cc2e0f Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 26 Sep 2018 15:39:12 +1200 Subject: [PATCH 19/32] IIFE to reduce conflicts for merging. --- src/js/controllers/review.controller.js | 1713 ++++++++++++----------- 1 file changed, 858 insertions(+), 855 deletions(-) diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index 42388933b..36e734c81 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -1,944 +1,947 @@ 'use strict'; +(function () { + angular .module('copayApp.controllers') .controller('reviewController', reviewController); -function reviewController(addressbookService, bitcoinCashJsService, bitcore, bitcoreCash, bwcError, clipboardService, configService, feeService, gettextCatalog, $interval, $ionicHistory, $ionicModal, ionicToast, lodash, $log, ongoingProcess, platformInfo, popupService, profileService, $scope, sendFlowService, shapeshiftService, soundService, $state, $timeout, txConfirmNotification, txFormatService, walletService) { - var vm = this; + function reviewController(addressbookService, bitcoinCashJsService, bitcore, bitcoreCash, bwcError, clipboardService, configService, feeService, gettextCatalog, $interval, $ionicHistory, $ionicModal, ionicToast, lodash, $log, ongoingProcess, platformInfo, popupService, profileService, $scope, sendFlowService, shapeshiftService, soundService, $state, $timeout, txConfirmNotification, txFormatService, walletService) { + var vm = this; - vm.buttonText = ''; - vm.destination = { - address: '', - balanceAmount: '', - balanceCurrency: '', - coin: '', - color: '', - currency: '', - currencyColor: '', - kind: '', // 'address', 'contact', 'wallet' - name: '' - }; - vm.displayAddress = ''; - vm.feeCrypto = ''; - vm.feeFiat = ''; - vm.fiatCurrency = ''; - vm.feeIsHigh = false; - vm.feeLessThanACent = false; - vm.isCordova = platformInfo.isCordova; - vm.notReadyMessage = ''; - vm.origin = { - balanceAmount: '', - balanceCurrency: '', - currency: '', - currencyColor: '', - }; - vm.originWallet = null; - vm.paymentExpired = false; - vm.personalNotePlaceholder = gettextCatalog.getString('Enter text here'); - vm.primaryAmount = ''; - vm.primaryCurrency = ''; - vm.usingMerchantFee = false; - vm.readyToSend = false; - vm.remainingTimeStr = ''; - vm.secondaryAmount = ''; - vm.secondaryCurrency = ''; - vm.sendingTitle = gettextCatalog.getString('You are sending'); - vm.sendStatus = ''; - vm.showAddress = true; - vm.thirdParty = null; - vm.wallet = null; - vm.memoExpanded = false; + vm.buttonText = ''; + vm.destination = { + address: '', + balanceAmount: '', + balanceCurrency: '', + coin: '', + color: '', + currency: '', + currencyColor: '', + kind: '', // 'address', 'contact', 'wallet' + name: '' + }; + vm.displayAddress = ''; + vm.feeCrypto = ''; + vm.feeFiat = ''; + vm.fiatCurrency = ''; + vm.feeIsHigh = false; + vm.feeLessThanACent = false; + vm.isCordova = platformInfo.isCordova; + vm.notReadyMessage = ''; + vm.origin = { + balanceAmount: '', + balanceCurrency: '', + currency: '', + currencyColor: '', + }; + vm.originWallet = null; + vm.paymentExpired = false; + vm.personalNotePlaceholder = gettextCatalog.getString('Enter text here'); + vm.primaryAmount = ''; + vm.primaryCurrency = ''; + vm.usingMerchantFee = false; + vm.readyToSend = false; + vm.remainingTimeStr = ''; + vm.secondaryAmount = ''; + vm.secondaryCurrency = ''; + vm.sendingTitle = gettextCatalog.getString('You are sending'); + vm.sendStatus = ''; + vm.showAddress = true; + vm.thirdParty = null; + vm.wallet = null; + vm.memoExpanded = false; - // Functions - vm.goBack = goBack; - vm.onSuccessConfirm = onSuccessConfirm; - vm.onShareTransaction = onShareTransaction; + // Functions + vm.goBack = goBack; + vm.onSuccessConfirm = onSuccessConfirm; + vm.onShareTransaction = onShareTransaction; - var sendFlowData; - var config = null; - var coin = ''; - var countDown = null; - var defaults = {}; - var usingCustomFee = false; - var usingMerchantFee = false; - var destinationWalletId = ''; - var lastTxId = ''; - var originWalletId = ''; - var priceDisplayIsFiat = true; - var satoshis = null; - var toAddress = ''; - var tx = {}; - var txPayproData = null; - var unitFromSat = 0; + var sendFlowData; + var config = null; + var coin = ''; + var countDown = null; + var defaults = {}; + var usingCustomFee = false; + var usingMerchantFee = false; + var destinationWalletId = ''; + var lastTxId = ''; + var originWalletId = ''; + var priceDisplayIsFiat = true; + var satoshis = null; + var toAddress = ''; + var tx = {}; + var txPayproData = null; + var unitFromSat = 0; - var FEE_TOO_HIGH_LIMIT_PERCENTAGE = 15; + var FEE_TOO_HIGH_LIMIT_PERCENTAGE = 15; - $scope.$on("$ionicView.beforeEnter", onBeforeEnter); + $scope.$on("$ionicView.beforeEnter", onBeforeEnter); - function onBeforeEnter(event, data) { - $log.debug('reviewController onBeforeEnter sendflow ', sendFlowService.state); - defaults = configService.getDefaults(); - sendFlowData = sendFlowService.state.getClone(); - originWalletId = sendFlowData.fromWalletId; - satoshis = parseInt(sendFlowData.amount, 10); - toAddress = sendFlowData.toAddress; - destinationWalletId = sendFlowData.toWalletId; + function onBeforeEnter(event, data) { + $log.debug('reviewController onBeforeEnter sendflow ', sendFlowService.state); + defaults = configService.getDefaults(); + sendFlowData = sendFlowService.state.getClone(); + originWalletId = sendFlowData.fromWalletId; + satoshis = parseInt(sendFlowData.amount, 10); + toAddress = sendFlowData.toAddress; + destinationWalletId = sendFlowData.toWalletId; - vm.displayAddress = sendFlowData.displayAddress; - vm.originWallet = profileService.getWallet(originWalletId); - vm.origin.currency = vm.originWallet.coin.toUpperCase(); - coin = vm.originWallet.coin; + vm.displayAddress = sendFlowData.displayAddress; + vm.originWallet = profileService.getWallet(originWalletId); + vm.origin.currency = vm.originWallet.coin.toUpperCase(); + coin = vm.originWallet.coin; - if (sendFlowData.thirdParty) { - vm.thirdParty = sendFlowData.thirdParty; - switch (vm.thirdParty.id) { - case 'shapeshift': - initShapeshift(function onInitShapeshift(err) { - if (err) { - // Error stop here - ongoingProcess.set('connectingShapeshift', false); - popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString(), function onAlert() { - $ionicHistory.goBack(); - }); - } else { - _next(data); - } - }); - break; - case 'bip70': - initBip70(); - default: - _next(data); - break; - } - } else { - _next(data); - } - - function _next() { - configService.get(function onConfig(err, configCache) { - if (err) { - $log.err('Error getting config.', err); - } else { - config = configCache; - priceDisplayIsFiat = config.wallet.settings.priceDisplay === 'fiat'; - vm.origin.currencyColor = (vm.originWallet.coin === 'btc' ? defaults.bitcoinWalletColor : defaults.bitcoinCashWalletColor); - unitFromSat = 1 / config.wallet.settings.unitToSatoshi; - } - updateSendAmounts(); - getOriginWalletBalance(vm.originWallet); - handleDestinationAsAddress(toAddress, coin); - handleDestinationAsWallet(sendFlowData.toWalletId); - createVanityTransaction(data); - }); - } - } - - vm.approve = function() { - - if (!tx || !vm.originWallet) return; - - if (vm.paymentExpired) { - popupService.showAlert(null, gettextCatalog.getString('This bitcoin payment request has expired.', function onAlert() { - $ionicHistory.goBack(); - })); - vm.sendStatus = ''; - $timeout(function onTimeout() { - $scope.$apply(); - }); - return; - } - - ongoingProcess.set('creatingTx', true, statusChangeHandler); - getTxp(lodash.clone(tx), vm.originWallet, false, function onGetTxp(err, txp) { - ongoingProcess.set('creatingTx', false, statusChangeHandler); - if (err) return; - - // confirm txs for more that 20usd, if not spending/touchid is enabled - function confirmTx(cb) { - if (walletService.isEncrypted(vm.originWallet)) - return cb(); - - var amountUsd = parseFloat(txFormatService.formatToUSD(vm.originWallet.coin, txp.amount)); - return cb(); - }; - - function publishAndSign() { - if (!vm.originWallet.canSign() && !vm.originWallet.isPrivKeyExternal()) { - $log.info('No signing proposal: No private key'); - - return walletService.onlyPublish(vm.originWallet, txp, function onOnlyPublish(err) { - if (err) setSendError(err); - }, statusChangeHandler); - } - - walletService.publishAndSign(vm.originWallet, txp, function onPublishAndSign(err, txp) { - if (err) return setSendError(err); - if (config.confirmedTxsNotifications && config.confirmedTxsNotifications.enabled) { - txConfirmNotification.subscribe(vm.originWallet, { - txid: txp.txid + if (sendFlowData.thirdParty) { + vm.thirdParty = sendFlowData.thirdParty; + switch (vm.thirdParty.id) { + case 'shapeshift': + initShapeshift(function onInitShapeshift(err) { + if (err) { + // Error stop here + ongoingProcess.set('connectingShapeshift', false); + popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString(), function onAlert() { + $ionicHistory.goBack(); + }); + } else { + _next(data); + } }); - lastTxId = txp.txid; + break; + case 'bip70': + initBip70(); + default: + _next(data); + break; + } + } else { + _next(data); + } + + function _next() { + configService.get(function onConfig(err, configCache) { + if (err) { + $log.err('Error getting config.', err); + } else { + config = configCache; + priceDisplayIsFiat = config.wallet.settings.priceDisplay === 'fiat'; + vm.origin.currencyColor = (vm.originWallet.coin === 'btc' ? defaults.bitcoinWalletColor : defaults.bitcoinCashWalletColor); + unitFromSat = 1 / config.wallet.settings.unitToSatoshi; } - }, statusChangeHandler); + updateSendAmounts(); + getOriginWalletBalance(vm.originWallet); + handleDestinationAsAddress(toAddress, coin); + handleDestinationAsWallet(sendFlowData.toWalletId); + createVanityTransaction(data); + }); + } + } + + vm.approve = function() { + + if (!tx || !vm.originWallet) return; + + if (vm.paymentExpired) { + popupService.showAlert(null, gettextCatalog.getString('This bitcoin payment request has expired.', function onAlert() { + $ionicHistory.goBack(); + })); + vm.sendStatus = ''; + $timeout(function onTimeout() { + $scope.$apply(); + }); + return; + } + + ongoingProcess.set('creatingTx', true, statusChangeHandler); + getTxp(lodash.clone(tx), vm.originWallet, false, function onGetTxp(err, txp) { + ongoingProcess.set('creatingTx', false, statusChangeHandler); + if (err) return; + + // confirm txs for more that 20usd, if not spending/touchid is enabled + function confirmTx(cb) { + if (walletService.isEncrypted(vm.originWallet)) + return cb(); + + var amountUsd = parseFloat(txFormatService.formatToUSD(vm.originWallet.coin, txp.amount)); + return cb(); + }; + + function publishAndSign() { + if (!vm.originWallet.canSign() && !vm.originWallet.isPrivKeyExternal()) { + $log.info('No signing proposal: No private key'); + + return walletService.onlyPublish(vm.originWallet, txp, function onOnlyPublish(err) { + if (err) setSendError(err); + }, statusChangeHandler); + } + + walletService.publishAndSign(vm.originWallet, txp, function onPublishAndSign(err, txp) { + if (err) return setSendError(err); + if (config.confirmedTxsNotifications && config.confirmedTxsNotifications.enabled) { + txConfirmNotification.subscribe(vm.originWallet, { + txid: txp.txid + }); + lastTxId = txp.txid; + } + }, statusChangeHandler); + }; + + confirmTx(function(nok) { + if (nok) { + vm.sendStatus = ''; + $timeout(function onTimeout() { + $scope.$apply(); + }); + return; + } + publishAndSign(); + }); + }); + }; + + vm.chooseFeeLevel = function(tx, wallet) { + + if (wallet.coin == 'bch') return; + if (usingMerchantFee) return; + + var scope = $rootScope.$new(true); + scope.network = tx.network; + scope.feeLevel = tx.feeLevel; + scope.noSave = true; + scope.coin = vm.originWallet.coin; + + if (usingCustomFee) { + scope.customFeePerKB = tx.feeRate; + scope.feePerSatByte = tx.feeRate / 1000; + } + + $ionicModal.fromTemplateUrl('views/modals/chooseFeeLevel.html', { + scope: scope, + backdropClickToClose: false, + hardwareBackButtonClose: false + }).then(function(modal) { + scope.chooseFeeLevelModal = modal; + scope.openModal(); + }); + scope.openModal = function() { + scope.chooseFeeLevelModal.show(); }; - confirmTx(function(nok) { - if (nok) { - vm.sendStatus = ''; - $timeout(function onTimeout() { - $scope.$apply(); - }); - return; - } - publishAndSign(); - }); - }); - }; + scope.hideModal = function(newFeeLevel, customFeePerKB) { + scope.chooseFeeLevelModal.hide(); + $log.debug('New fee level choosen:' + newFeeLevel + ' was:' + tx.feeLevel); - vm.chooseFeeLevel = function(tx, wallet) { + usingCustomFee = newFeeLevel == 'custom' ? true : false; - if (wallet.coin == 'bch') return; - if (usingMerchantFee) return; + if (tx.feeLevel == newFeeLevel && !usingCustomFee) return; - var scope = $rootScope.$new(true); - scope.network = tx.network; - scope.feeLevel = tx.feeLevel; - scope.noSave = true; - scope.coin = vm.originWallet.coin; + tx.feeLevel = newFeeLevel; + if (usingCustomFee) tx.feeRate = parseInt(customFeePerKB); - if (usingCustomFee) { - scope.customFeePerKB = tx.feeRate; - scope.feePerSatByte = tx.feeRate / 1000; - } - - $ionicModal.fromTemplateUrl('views/modals/chooseFeeLevel.html', { - scope: scope, - backdropClickToClose: false, - hardwareBackButtonClose: false - }).then(function(modal) { - scope.chooseFeeLevelModal = modal; - scope.openModal(); - }); - scope.openModal = function() { - scope.chooseFeeLevelModal.show(); + updateTx(tx, vm.originWallet, { + clearCache: true, + dryRun: true + }, function() {}); + }; }; - scope.hideModal = function(newFeeLevel, customFeePerKB) { - scope.chooseFeeLevelModal.hide(); - $log.debug('New fee level choosen:' + newFeeLevel + ' was:' + tx.feeLevel); + function createVanityTransaction(data) { + var configFeeLevel = config.wallet.settings.feeLevel ? config.wallet.settings.feeLevel : 'normal'; - usingCustomFee = newFeeLevel == 'custom' ? true : false; + // Grab stateParams + tx = { + amount: parseInt(sendFlowData.amount), + sendMax: sendFlowData.sendMax, + fromWalletId: sendFlowData.fromWalletId, + toAddress: sendFlowData.toAddress, + paypro: txPayproData, - if (tx.feeLevel == newFeeLevel && !usingCustomFee) return; + feeLevel: configFeeLevel, + spendUnconfirmed: config.wallet.spendUnconfirmed, - tx.feeLevel = newFeeLevel; - if (usingCustomFee) tx.feeRate = parseInt(customFeePerKB); + // Vanity tx info (not in the real tx) + recipientType: vm.destination.kind || null, + toName: vm.destination.name || null, + toEmail: vm.destination.email || null, + toColor: vm.destination.color || null, + network: false, + coin: vm.originWallet.coin, + txp: {}, + }; - updateTx(tx, vm.originWallet, { - clearCache: true, - dryRun: true - }, function() {}); - }; - }; + if (vm.thirdParty && vm.thirdParty.id === "shapeshift") { + tx.toAddress = vm.thirdParty.toAddress; + } - function createVanityTransaction(data) { - var configFeeLevel = config.wallet.settings.feeLevel ? config.wallet.settings.feeLevel : 'normal'; + if (data.stateParams.requiredFeeRate) { + vm.usingMerchantFee = true; + tx.feeRate = parseInt(data.stateParams.requiredFeeRate); + } - // Grab stateParams - tx = { - amount: parseInt(sendFlowData.amount), - sendMax: sendFlowData.sendMax, - fromWalletId: sendFlowData.fromWalletId, - toAddress: sendFlowData.toAddress, - paypro: txPayproData, + if (tx.coin && tx.coin === 'bch') { + tx.feeLevel = 'normal'; + } - feeLevel: configFeeLevel, - spendUnconfirmed: config.wallet.spendUnconfirmed, - - // Vanity tx info (not in the real tx) - recipientType: vm.destination.kind || null, - toName: vm.destination.name || null, - toEmail: vm.destination.email || null, - toColor: vm.destination.color || null, - network: false, - coin: vm.originWallet.coin, - txp: {}, - }; - - if (vm.thirdParty && vm.thirdParty.id === "shapeshift") { - tx.toAddress = vm.thirdParty.toAddress; - } - - if (data.stateParams.requiredFeeRate) { - vm.usingMerchantFee = true; - tx.feeRate = parseInt(data.stateParams.requiredFeeRate); - } - - if (tx.coin && tx.coin === 'bch') { - tx.feeLevel = 'normal'; - } - - var B = tx.coin === 'bch' ? bitcoreCash : bitcore; - var networkName; - try { - // Final destination is a wallet, but this transaction must go to an address for the first stage of the exchange. - if (sendFlowData.thirdParty && sendFlowData.thirdParty.id === 'shapeshift') { - networkName = (new B.Address(tx.toAddress)).network.name; - tx.network = networkName; - setupTx(tx); - - } else if (vm.destination.kind === 'wallet') { // This is a wallet-to-wallet transfer - ongoingProcess.set('generatingNewAddress', true); - var toWallet = profileService.getWallet(destinationWalletId); - - // We need an address to send to, so we ask the walletService to create a new address for the toWallet. - walletService.getAddress(toWallet, true, function onWalletAddress(err, addr) { - if (err) { - $log.error('Error getting address for wallet.', err); - throw new Error(err.message); - } - ongoingProcess.set('generatingNewAddress', false); - tx.toAddress = addr; + var B = tx.coin === 'bch' ? bitcoreCash : bitcore; + var networkName; + try { + // Final destination is a wallet, but this transaction must go to an address for the first stage of the exchange. + if (sendFlowData.thirdParty && sendFlowData.thirdParty.id === 'shapeshift') { networkName = (new B.Address(tx.toAddress)).network.name; tx.network = networkName; setupTx(tx); - }); - } else { // This is a Wallet-to-address transfer - networkName = (new B.Address(tx.toAddress)).network.name; - tx.network = networkName; - setupTx(tx); - } - } catch (e) { - $log.error('Error setting up tx', e); - var message = gettextCatalog.getString('Invalid address'); - popupService.showAlert(null, message, function () { - $ionicHistory.nextViewOptions({ - disableAnimate: true, - historyRoot: true - }); - $state.go('tabs.send').then(function () { - $ionicHistory.clearHistory(); - }); - }); - return; - } - } - function getOriginWalletBalance(originWallet) { - var balanceText = getWalletBalanceDisplayText(vm.originWallet); - vm.origin.balanceAmount = balanceText.amount; - vm.origin.balanceCurrency = balanceText.currency; - } - function getSendMaxInfo(tx, wallet, cb) { - if (!tx.sendMax) return cb(); + } else if (vm.destination.kind === 'wallet') { // This is a wallet-to-wallet transfer + ongoingProcess.set('generatingNewAddress', true); + var toWallet = profileService.getWallet(destinationWalletId); - //ongoingProcess.set('retrievingInputs', true); - walletService.getSendMaxInfo(wallet, { - feePerKb: tx.feeRate, - excludeUnconfirmedUtxos: !tx.spendUnconfirmed, - returnInputs: true, - }, cb); - }; - - function getTxp(tx, wallet, dryRun, cb) { - - // ToDo: use a credential's (or fc's) function for this - if (tx.description && !wallet.credentials.sharedEncryptingKey) { - var msg = gettextCatalog.getString('Could not add message to imported wallet without shared encrypting key'); - $log.warn(msg); - return setSendError(msg); - } - - if (tx.amount > Number.MAX_SAFE_INTEGER) { - var msg = gettextCatalog.getString('Amount too big'); - $log.warn(msg); - return setSendError(msg); - } - - var txp = {}; - - txp.outputs = [{ - 'toAddress': tx.toAddress, - 'amount': tx.amount, - 'message': vm.memo - }]; - - if (tx.sendMaxInfo) { - txp.inputs = tx.sendMaxInfo.inputs; - txp.fee = tx.sendMaxInfo.fee; - } else { - if (usingCustomFee || usingMerchantFee) { - txp.feePerKb = tx.feeRate; - } else txp.feeLevel = tx.feeLevel; - } - - txp.message = vm.memo; - - if (tx.paypro) { - txp.payProUrl = tx.paypro.url; - } - txp.excludeUnconfirmedUtxos = !tx.spendUnconfirmed; - txp.dryRun = dryRun; - walletService.createTx(wallet, txp, function onCreateTx(err, ctxp) { - if (err) { - setSendError(err); - return cb(err); - } - return cb(null, ctxp); - }); - }; - - function getWalletBalanceDisplayText(wallet) { - var balanceCryptoAmount = ''; - var balanceCryptoCurrencyCode = ''; - var balanceFiatAmount = ''; - var balanceFiatCurrency = '' - var displayAmount = ''; - var displayCurrency = ''; - - var walletStatus = null; - if (wallet.status.isValid) { - walletStatus = wallet.status; - } else if (wallet.cachedStatus.isValid) { - walletStatus = wallet.cachedStatus; - } - - if (walletStatus) { - var cryptoBalanceParts = walletStatus.spendableBalanceStr.split(' '); - balanceCryptoAmount = cryptoBalanceParts[0]; - balanceCryptoCurrencyCode = cryptoBalanceParts.length > 1 ? cryptoBalanceParts[1] : ''; - - if (walletStatus.alternativeBalanceAvailable) { - balanceFiatAmount = walletStatus.spendableBalanceAlternative; - balanceFiatCurrency = walletStatus.alternativeIsoCode; - } - } - - if (priceDisplayIsFiat) { - displayAmount = balanceFiatAmount ? balanceFiatAmount : balanceCryptoAmount; - displayCurrency = balanceFiatAmount ? balanceFiatCurrency : balanceCryptoCurrencyCode; - } else { - displayAmount = balanceCryptoAmount; - displayCurrency = balanceCryptoCurrencyCode; - } - - return { - amount: displayAmount, - currency: displayCurrency - }; - } - - function goBack() { - sendFlowService.router.goBack(); - } - - function handleDestinationAsAddress(address, originCoin) { - if (!address) { - return; - } - - // Check if the recipient is a contact - addressbookService.get(originCoin + address, function onGetContact(err, contact) { - if (!err && contact) { - handleDestinationAsAddressOfContact(contact); - } else { - if (originCoin === 'bch') { - vm.destination.address = bitcoinCashJsService.readAddress(address).cashaddr; - } else { - vm.destination.address = address; + // We need an address to send to, so we ask the walletService to create a new address for the toWallet. + walletService.getAddress(toWallet, true, function onWalletAddress(err, addr) { + if (err) { + $log.error('Error getting address for wallet.', err); + throw new Error(err.message); + } + ongoingProcess.set('generatingNewAddress', false); + tx.toAddress = addr; + networkName = (new B.Address(tx.toAddress)).network.name; + tx.network = networkName; + setupTx(tx); + }); + } else { // This is a Wallet-to-address transfer + networkName = (new B.Address(tx.toAddress)).network.name; + tx.network = networkName; + setupTx(tx); } - vm.destination.kind = 'address'; + } catch (e) { + $log.error('Error setting up tx', e); + var message = gettextCatalog.getString('Invalid address'); + popupService.showAlert(null, message, function () { + $ionicHistory.nextViewOptions({ + disableAnimate: true, + historyRoot: true + }); + $state.go('tabs.send').then(function () { + $ionicHistory.clearHistory(); + }); + }); + return; } - }); - - } - - function handleDestinationAsAddressOfContact(contact) { - vm.destination.kind = 'contact'; - vm.destination.name = contact.name; - vm.destination.email = contact.email; - vm.destination.color = contact.coin === 'btc' ? defaults.bitcoinWalletColor : defaults.bitcoinCashWalletColor; - vm.destination.currency = contact.coin.toUpperCase(); - vm.destination.currencyColor = vm.destination.color; - } - - function handleDestinationAsWallet(walletId) { - destinationWalletId = walletId; - if (!destinationWalletId) { - return; + } + function getOriginWalletBalance(originWallet) { + var balanceText = getWalletBalanceDisplayText(vm.originWallet); + vm.origin.balanceAmount = balanceText.amount; + vm.origin.balanceCurrency = balanceText.currency; } - var destinationWallet = profileService.getWallet(destinationWalletId); - vm.destination.coin = destinationWallet.coin; - vm.destination.color = destinationWallet.color; - vm.destination.currency = destinationWallet.coin.toUpperCase(); - vm.destination.kind = 'wallet'; - vm.destination.name = destinationWallet.name; + function getSendMaxInfo(tx, wallet, cb) { + if (!tx.sendMax) return cb(); - if (defaults) { - vm.destination.currencyColor = vm.destination.coin === 'btc' ? defaults.bitcoinWalletColor : defaults.bitcoinCashWalletColor; - } - - var balanceText = getWalletBalanceDisplayText(destinationWallet); - vm.destination.balanceAmount = balanceText.amount; - vm.destination.balanceCurrency = balanceText.currency; - } - - function initBip70() { - vm.sendingTitle = gettextCatalog.getString('You are paying'); - vm.memo = vm.thirdParty.memo; - vm.memoExpanded = !!vm.memo; - vm.destination.name = vm.thirdParty.name; - - txPayproData = { - caTrusted: vm.thirdParty.caTrusted, - domain: vm.thirdParty.domain, - expires: vm.thirdParty.expires, - toAddress: toAddress, - url: vm.thirdParty.url, - verified: vm.thirdParty.verified, + //ongoingProcess.set('retrievingInputs', true); + walletService.getSendMaxInfo(wallet, { + feePerKb: tx.feeRate, + excludeUnconfirmedUtxos: !tx.spendUnconfirmed, + returnInputs: true, + }, cb); }; - } - function initShapeshift(cb) { - vm.sendingTitle = gettextCatalog.getString('You are shifting'); + function getTxp(tx, wallet, dryRun, cb) { - var toWallet = profileService.getWallet(destinationWalletId); - vm.destination.name = toWallet.name; - vm.destination.color = toWallet.color; - vm.destination.currency = toWallet.coin.toUpperCase(); + // ToDo: use a credential's (or fc's) function for this + if (tx.description && !wallet.credentials.sharedEncryptingKey) { + var msg = gettextCatalog.getString('Could not add message to imported wallet without shared encrypting key'); + $log.warn(msg); + return setSendError(msg); + } + + if (tx.amount > Number.MAX_SAFE_INTEGER) { + var msg = gettextCatalog.getString('Amount too big'); + $log.warn(msg); + return setSendError(msg); + } + + var txp = {}; + + txp.outputs = [{ + 'toAddress': tx.toAddress, + 'amount': tx.amount, + 'message': vm.memo + }]; + + if (tx.sendMaxInfo) { + txp.inputs = tx.sendMaxInfo.inputs; + txp.fee = tx.sendMaxInfo.fee; + } else { + if (usingCustomFee || usingMerchantFee) { + txp.feePerKb = tx.feeRate; + } else txp.feeLevel = tx.feeLevel; + } + + txp.message = vm.memo; + + if (tx.paypro) { + txp.payProUrl = tx.paypro.url; + } + txp.excludeUnconfirmedUtxos = !tx.spendUnconfirmed; + txp.dryRun = dryRun; + walletService.createTx(wallet, txp, function onCreateTx(err, ctxp) { + if (err) { + setSendError(err); + return cb(err); + } + return cb(null, ctxp); + }); + }; + + function getWalletBalanceDisplayText(wallet) { + var balanceCryptoAmount = ''; + var balanceCryptoCurrencyCode = ''; + var balanceFiatAmount = ''; + var balanceFiatCurrency = '' + var displayAmount = ''; + var displayCurrency = ''; + + var walletStatus = null; + if (wallet.status.isValid) { + walletStatus = wallet.status; + } else if (wallet.cachedStatus.isValid) { + walletStatus = wallet.cachedStatus; + } + + if (walletStatus) { + var cryptoBalanceParts = walletStatus.spendableBalanceStr.split(' '); + balanceCryptoAmount = cryptoBalanceParts[0]; + balanceCryptoCurrencyCode = cryptoBalanceParts.length > 1 ? cryptoBalanceParts[1] : ''; + + if (walletStatus.alternativeBalanceAvailable) { + balanceFiatAmount = walletStatus.spendableBalanceAlternative; + balanceFiatCurrency = walletStatus.alternativeIsoCode; + } + } + + if (priceDisplayIsFiat) { + displayAmount = balanceFiatAmount ? balanceFiatAmount : balanceCryptoAmount; + displayCurrency = balanceFiatAmount ? balanceFiatCurrency : balanceCryptoCurrencyCode; + } else { + displayAmount = balanceCryptoAmount; + displayCurrency = balanceCryptoCurrencyCode; + } + + return { + amount: displayAmount, + currency: displayCurrency + }; + } + + function goBack() { + sendFlowService.router.goBack(); + } + + function handleDestinationAsAddress(address, originCoin) { + if (!address) { + return; + } + + // Check if the recipient is a contact + addressbookService.get(originCoin + address, function onGetContact(err, contact) { + if (!err && contact) { + handleDestinationAsAddressOfContact(contact); + } else { + if (originCoin === 'bch') { + vm.destination.address = bitcoinCashJsService.readAddress(address).cashaddr; + } else { + vm.destination.address = address; + } + vm.destination.kind = 'address'; + } + }); + + } + + function handleDestinationAsAddressOfContact(contact) { + vm.destination.kind = 'contact'; + vm.destination.name = contact.name; + vm.destination.email = contact.email; + vm.destination.color = contact.coin === 'btc' ? defaults.bitcoinWalletColor : defaults.bitcoinCashWalletColor; + vm.destination.currency = contact.coin.toUpperCase(); + vm.destination.currencyColor = vm.destination.color; + } + + function handleDestinationAsWallet(walletId) { + destinationWalletId = walletId; + if (!destinationWalletId) { + return; + } + + var destinationWallet = profileService.getWallet(destinationWalletId); + vm.destination.coin = destinationWallet.coin; + vm.destination.color = destinationWallet.color; + vm.destination.currency = destinationWallet.coin.toUpperCase(); + vm.destination.kind = 'wallet'; + vm.destination.name = destinationWallet.name; + + if (defaults) { + vm.destination.currencyColor = vm.destination.coin === 'btc' ? defaults.bitcoinWalletColor : defaults.bitcoinCashWalletColor; + } + + var balanceText = getWalletBalanceDisplayText(destinationWallet); + vm.destination.balanceAmount = balanceText.amount; + vm.destination.balanceCurrency = balanceText.currency; + } + + function initBip70() { + vm.sendingTitle = gettextCatalog.getString('You are paying'); + vm.memo = vm.thirdParty.memo; + vm.memoExpanded = !!vm.memo; + vm.destination.name = vm.thirdParty.name; + + txPayproData = { + caTrusted: vm.thirdParty.caTrusted, + domain: vm.thirdParty.domain, + expires: vm.thirdParty.expires, + toAddress: toAddress, + url: vm.thirdParty.url, + verified: vm.thirdParty.verified, + }; + } + + function initShapeshift(cb) { + vm.sendingTitle = gettextCatalog.getString('You are shifting'); + + var toWallet = profileService.getWallet(destinationWalletId); + vm.destination.name = toWallet.name; + vm.destination.color = toWallet.color; + vm.destination.currency = toWallet.coin.toUpperCase(); - ongoingProcess.set('connectingShapeshift', true); - walletService.getAddress(vm.originWallet, false, function onReturnWalletAddress(err, returnAddr) { - if (err) { - return cb(err); - } - walletService.getAddress(toWallet, false, function onWithdrawalWalletAddress(err, withdrawalAddr) { + ongoingProcess.set('connectingShapeshift', true); + walletService.getAddress(vm.originWallet, false, function onReturnWalletAddress(err, returnAddr) { if (err) { return cb(err); } - - // Need to use the correct service to do it. - var amount = parseFloat(satoshis / 100000000); - - shapeshiftService.shiftIt(vm.originWallet.coin, toWallet.coin, withdrawalAddr, returnAddr, amount, function onShiftIt(err, shapeshiftData) { + walletService.getAddress(toWallet, false, function onWithdrawalWalletAddress(err, withdrawalAddr) { if (err) { return cb(err); - } else { - // Want it to appear like a wallet-to-wallet transfer, so don't set the main toAddress. - vm.thirdParty.toAddress = shapeshiftData.toAddress; - vm.memo = 'ShapeShift Order:\nhttps://www.shapeshift.io/#/status/' + shapeshiftData.orderId; - vm.memoExpanded = !!vm.memo; - ongoingProcess.set('connectingShapeshift', false); - cb(); - } + } + + // Need to use the correct service to do it. + var amount = parseFloat(satoshis / 100000000); + + shapeshiftService.shiftIt(vm.originWallet.coin, toWallet.coin, withdrawalAddr, returnAddr, amount, function onShiftIt(err, shapeshiftData) { + if (err) { + return cb(err); + } else { + // Want it to appear like a wallet-to-wallet transfer, so don't set the main toAddress. + vm.thirdParty.toAddress = shapeshiftData.toAddress; + vm.memo = 'ShapeShift Order:\nhttps://www.shapeshift.io/#/status/' + shapeshiftData.orderId; + vm.memoExpanded = !!vm.memo; + ongoingProcess.set('connectingShapeshift', false); + cb(); + } + }); }); }); - }); - } - - function onShareTransaction() { - var explorerTxUrl = 'https://explorer.bitcoin.com/' + tx.coin + '/tx/' + lastTxId; - if (platformInfo.isCordova) { - var text = gettextCatalog.getString('Take a look at this Bitcoin Cash transaction here: ') + explorerTxUrl; - if (coin === 'btc') { - text = gettextCatalog.getString('Take a look at this Bitcoin transaction here: ') + explorerTxUrl; - } - window.plugins.socialsharing.share(text, null, null, null); - } else { - ionicToast.show(gettextCatalog.getString('Copied to clipboard'), 'bottom', false, 3000); - clipboardService.copyToClipboard(explorerTxUrl); } - - } - function startExpirationTimer(expirationTime) { - vm.paymentExpired = false; - setExpirationTime(); + function onShareTransaction() { + var explorerTxUrl = 'https://explorer.bitcoin.com/' + tx.coin + '/tx/' + lastTxId; + if (platformInfo.isCordova) { + var text = gettextCatalog.getString('Take a look at this Bitcoin Cash transaction here: ') + explorerTxUrl; + if (coin === 'btc') { + text = gettextCatalog.getString('Take a look at this Bitcoin transaction here: ') + explorerTxUrl; + } + window.plugins.socialsharing.share(text, null, null, null); + } else { + ionicToast.show(gettextCatalog.getString('Copied to clipboard'), 'bottom', false, 3000); + clipboardService.copyToClipboard(explorerTxUrl); + } + + } - countDown = $interval(function() { + function startExpirationTimer(expirationTime) { + vm.paymentExpired = false; setExpirationTime(); - }, 1000); - function setExpirationTime() { - var now = Math.floor(Date.now() / 1000); + countDown = $interval(function() { + setExpirationTime(); + }, 1000); - if (now > expirationTime) { - setExpiredValues(); + function setExpirationTime() { + var now = Math.floor(Date.now() / 1000); + + if (now > expirationTime) { + setExpiredValues(); + return; + } + + var totalSecs = expirationTime - now; + var m = Math.floor(totalSecs / 60); + var s = totalSecs % 60; + vm.remainingTimeStr = m + ":" + ('0' + s).slice(-2); + }; + + function setExpiredValues() { + vm.paymentExpired = true; + vm.remainingTimeStr = gettextCatalog.getString('Expired'); + vm.readyToSend = false; + if (countDown) $interval.cancel(countDown); + $timeout(function() { + $scope.$apply(); + }); + }; + }; + + function updateSendAmounts() { + if (typeof satoshis !== 'number') { return; } - var totalSecs = expirationTime - now; - var m = Math.floor(totalSecs / 60); - var s = totalSecs % 60; - vm.remainingTimeStr = m + ":" + ('0' + s).slice(-2); + var cryptoAmount = ''; + var cryptoCurrencyCode = ''; + var amountStr = txFormatService.formatAmountStr(coin, satoshis); + if (amountStr) { + var amountParts = amountStr.split(' '); + cryptoAmount = amountParts[0]; + cryptoCurrencyCode = amountParts.length > 1 ? amountParts[1] : ''; + } + // Want to avoid flashing of amount strings so do all formatting after this has returned. + txFormatService.formatAlternativeStr(coin, satoshis, function(v) { + if (!v) { + vm.primaryAmount = cryptoAmount; + vm.primaryCurrency = cryptoCurrencyCode; + vm.secondaryAmount = ''; + vm.secondaryCurrency = ''; + return; + } + vm.secondaryAmount = vm.primaryAmount; + vm.secondaryCurrency = vm.primaryCurrency; + + var fiatParts = v.split(' '); + var fiatAmount = fiatParts[0]; + var fiatCurrency = fiatParts.length > 1 ? fiatParts[1] : ''; + + if (priceDisplayIsFiat) { + vm.primaryAmount = fiatAmount; + vm.primaryCurrency = fiatCurrency; + vm.secondaryAmount = cryptoAmount; + vm.secondaryCurrency = cryptoCurrencyCode; + } else { + vm.primaryAmount = cryptoAmount; + vm.primaryCurrency = cryptoCurrencyCode; + vm.secondaryAmount = fiatAmount; + vm.secondaryCurrency = fiatCurrency; + } + }); + } + + function onSuccessConfirm() { + vm.sendStatus = ''; + $ionicHistory.nextViewOptions({ + disableAnimate: true, + historyRoot: true + }); + $state.go('tabs.send').then(function() { + $ionicHistory.clearHistory(); + $state.transitionTo('tabs.home'); + }); }; - function setExpiredValues() { - vm.paymentExpired = true; - vm.remainingTimeStr = gettextCatalog.getString('Expired'); + function setButtonText(isMultisig, isPayPro) { + if (isPayPro) { + if (vm.isCordova) { + vm.buttonText = gettextCatalog.getString('Slide to pay'); + } else { + vm.buttonText = gettextCatalog.getString('Click to pay'); + } + } else if (isMultisig) { + if (vm.isCordova) { + vm.buttonText = gettextCatalog.getString('Slide to accept'); + } else { + vm.buttonText = gettextCatalog.getString('Click to accept'); + } + } else { + if (vm.isCordova) { + vm.buttonText = gettextCatalog.getString('Slide to send'); + } else { + vm.buttonText = gettextCatalog.getString('Click to send'); + } + } + } + + function setNotReady(msg, criticalError) { + vn.readyToSend = false; + vm.notReadyMessage = msg; + $scope.criticalError = criticalError; + $log.warn('Not ready to make the payment:' + msg); + $timeout(function() { + $scope.$apply(); + }); + }; + + function setSendError(msg) { + $scope.sendStatus = ''; vm.readyToSend = false; - if (countDown) $interval.cancel(countDown); $timeout(function() { $scope.$apply(); }); - }; - }; - - function updateSendAmounts() { - if (typeof satoshis !== 'number') { - return; - } - - var cryptoAmount = ''; - var cryptoCurrencyCode = ''; - var amountStr = txFormatService.formatAmountStr(coin, satoshis); - if (amountStr) { - var amountParts = amountStr.split(' '); - cryptoAmount = amountParts[0]; - cryptoCurrencyCode = amountParts.length > 1 ? amountParts[1] : ''; - } - // Want to avoid flashing of amount strings so do all formatting after this has returned. - txFormatService.formatAlternativeStr(coin, satoshis, function(v) { - if (!v) { - vm.primaryAmount = cryptoAmount; - vm.primaryCurrency = cryptoCurrencyCode; - vm.secondaryAmount = ''; - vm.secondaryCurrency = ''; - return; - } - vm.secondaryAmount = vm.primaryAmount; - vm.secondaryCurrency = vm.primaryCurrency; - - var fiatParts = v.split(' '); - var fiatAmount = fiatParts[0]; - var fiatCurrency = fiatParts.length > 1 ? fiatParts[1] : ''; - - if (priceDisplayIsFiat) { - vm.primaryAmount = fiatAmount; - vm.primaryCurrency = fiatCurrency; - vm.secondaryAmount = cryptoAmount; - vm.secondaryCurrency = cryptoCurrencyCode; - } else { - vm.primaryAmount = cryptoAmount; - vm.primaryCurrency = cryptoCurrencyCode; - vm.secondaryAmount = fiatAmount; - vm.secondaryCurrency = fiatCurrency; - } - }); - } - - function onSuccessConfirm() { - vm.sendStatus = ''; - $ionicHistory.nextViewOptions({ - disableAnimate: true, - historyRoot: true - }); - $state.go('tabs.send').then(function() { - $ionicHistory.clearHistory(); - $state.transitionTo('tabs.home'); - }); - }; - - function setButtonText(isMultisig, isPayPro) { - if (isPayPro) { - if (vm.isCordova) { - vm.buttonText = gettextCatalog.getString('Slide to pay'); - } else { - vm.buttonText = gettextCatalog.getString('Click to pay'); - } - } else if (isMultisig) { - if (vm.isCordova) { - vm.buttonText = gettextCatalog.getString('Slide to accept'); - } else { - vm.buttonText = gettextCatalog.getString('Click to accept'); - } - } else { - if (vm.isCordova) { - vm.buttonText = gettextCatalog.getString('Slide to send'); - } else { - vm.buttonText = gettextCatalog.getString('Click to send'); - } - } - } - - function setNotReady(msg, criticalError) { - vn.readyToSend = false; - vm.notReadyMessage = msg; - $scope.criticalError = criticalError; - $log.warn('Not ready to make the payment:' + msg); - $timeout(function() { - $scope.$apply(); - }); - }; - - function setSendError(msg) { - $scope.sendStatus = ''; - vm.readyToSend = false; - $timeout(function() { - $scope.$apply(); - }); - popupService.showAlert(gettextCatalog.getString('Error at confirm'), bwcError.msg(msg), function onAlert() { - $ionicHistory.goBack(); - }); - }; - - function setupTx(tx) { - if (tx.coin === 'bch') { - tx.displayAddress = bitcoinCashJsService.readAddress(tx.toAddress).cashaddr; - } else { - tx.displayAddress = tx.toAddress; - } - - addressbookService.get(tx.coin+tx.toAddress, function onGetContact(err, addr) { // Check if the recipient is a contact - if (!err && addr) { - tx.toName = addr.name; - tx.toEmail = addr.email; - tx.recipientType = 'contact'; - } - }); - - vm.showAddress = false; - - - setButtonText(vm.originWallet.credentials.m > 1, !!tx.paypro); - - if (tx.paypro) - startExpirationTimer(tx.paypro.expires); - - updateTx(tx, vm.originWallet, { - dryRun: true - }, function(err) { - $timeout(function onTimeout() { - $scope.$apply(); - }, 10); - - }); - - // setWalletSelector(tx.coin, tx.network, tx.amount, function(err) { - // if (err) { - // return exitWithError('Could not update wallets'); - // } - // - // if (vm.wallets.length > 1) { - // vm.showWalletSelector(); - // } else if (vm.wallets.length) { - // setWallet(vm.wallets[0], tx); - // } - // }); - } - - function showSendMaxWarning(wallet, sendMaxInfo) { - var feeAlternative = '', - msg = ''; - - function verifyExcludedUtxos() { - var warningMsg = []; - if (sendMaxInfo.utxosBelowFee > 0) { - warningMsg.push(gettextCatalog.getString("A total of {{amountBelowFeeStr}} were excluded. These funds come from UTXOs smaller than the network fee provided.", { - amountBelowFeeStr: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.amountBelowFee) - })); - } - - if (sendMaxInfo.utxosAboveMaxSize > 0) { - warningMsg.push(gettextCatalog.getString("A total of {{amountAboveMaxSizeStr}} were excluded. The maximum size allowed for a transaction was exceeded.", { - amountAboveMaxSizeStr: txFormatService.formatAmountStr(vm.originWallet.coin, sendMaxInfo.amountAboveMaxSize) - })); - } - return warningMsg.join('\n'); + popupService.showAlert(gettextCatalog.getString('Error at confirm'), bwcError.msg(msg), function onAlert() { + $ionicHistory.goBack(); + }); }; - feeAlternative = txFormatService.formatAlternativeStr(vm.originWallet.coin, sendMaxInfo.fee); - if (feeAlternative) { - msg = gettextCatalog.getString("{{feeAlternative}} will be deducted for bitcoin networking fees ({{fee}}).", { - fee: txFormatService.formatAmountStr(vm.originWallet.coin, sendMaxInfo.fee), - feeAlternative: feeAlternative - }); - } else { - msg = gettextCatalog.getString("{{fee}} will be deducted for bitcoin networking fees).", { - fee: txFormatService.formatAmountStr(vm.originWallet.coin, sendMaxInfo.fee) - }); - } - - var warningMsg = verifyExcludedUtxos(); - - if (!lodash.isEmpty(warningMsg)) - msg += '\n' + warningMsg; - - popupService.showAlert(null, msg, function() {}); - }; - - function statusChangeHandler(processName, showName, isOn) { - $log.debug('statusChangeHandler: ', processName, showName, isOn); - if ( - ( - processName === 'broadcastingTx' || - ((processName === 'signingTx') && vm.originWallet.m > 1) || - (processName == 'sendingTx' && !vm.originWallet.canSign() && !vm.originWallet.isPrivKeyExternal()) - ) && !isOn) { - // Show the popup - vm.sendStatus = 'success'; - - // Clear the send flow service state - sendFlowService.state.clear(); - - if ($state.current.name === "tabs.send.review") { // XX SP: Otherwise all open wallets on other devices play this sound if you have been in a send flow before on that device. - soundService.play('misc/payment_sent.mp3'); - } - - var channel = "firebase"; - if (platformInfo.isNW) { - channel = "ga"; - } - // When displaying Fiat, if the formatting fails, the crypto will be the primary amount. - var amount = unitFromSat * satoshis; - var log = new window.BitAnalytics.LogEvent("transfer_success", [{ - "coin": vm.originWallet.coin, - "type": "outgoing", - "amount": amount, - "fees": vm.feeCrypto - }], [channel, "adjust"]); - window.BitAnalytics.LogEventHandlers.postEvent(log); - - $timeout(function() { - $scope.$digest(); - }, 100); - } else if (showName) { - vm.sendStatus = showName; - } - }; - - function updateTx(tx, wallet, opts, cb) { - ongoingProcess.set('calculatingFee', true); - - if (opts.clearCache) { - tx.txp = {}; - } - - // $scope.tx = tx; - - // function updateAmount() { - // if (!tx.amount) return; - // - // // Amount - // tx.amountStr = txFormatService.formatAmountStr(originWallet.coin, tx.amount); - // tx.amountValueStr = tx.amountStr.split(' ')[0]; - // tx.amountUnitStr = tx.amountStr.split(' ')[1]; - // txFormatService.formatAlternativeStr(wallet.coin, tx.amount, function(v) { - // var parts = v.split(' '); - // tx.alternativeAmountStr = v; - // tx.alternativeAmountValueStr = parts[0]; - // tx.alternativeAmountUnitStr = (parts.length > 0) ? parts[1] : ''; - // }); - // } - // - // updateAmount(); - // refresh(); - - var feeServiceLevel = usingMerchantFee && vm.originWallet.coin == 'btc' ? 'urgent' : tx.feeLevel; - feeService.getFeeRate(vm.originWallet.coin, tx.network, feeServiceLevel, function onGetFeeRate(err, feeRate) { - if (err) { - ongoingProcess.set('calculatingFee', false); - return cb(err); - } - - var msg; - if (usingCustomFee) { - msg = gettextCatalog.getString('Custom'); - tx.feeLevelName = msg; - } else if (usingMerchantFee) { - $log.info('Using Merchant Fee:' + tx.feeRate + ' vs. Urgent level:' + feeRate); - msg = gettextCatalog.getString('Suggested by Merchant'); - tx.feeLevelName = msg; + function setupTx(tx) { + if (tx.coin === 'bch') { + tx.displayAddress = bitcoinCashJsService.readAddress(tx.toAddress).cashaddr; } else { - tx.feeLevelName = feeService.feeOpts[tx.feeLevel]; - tx.feeRate = feeRate; + tx.displayAddress = tx.toAddress; } - getSendMaxInfo(lodash.clone(tx), wallet, function onGetSendmaxInfo(err, sendMaxInfo) { + addressbookService.get(tx.coin+tx.toAddress, function onGetContact(err, addr) { // Check if the recipient is a contact + if (!err && addr) { + tx.toName = addr.name; + tx.toEmail = addr.email; + tx.recipientType = 'contact'; + } + }); + + vm.showAddress = false; + + + setButtonText(vm.originWallet.credentials.m > 1, !!tx.paypro); + + if (tx.paypro) + startExpirationTimer(tx.paypro.expires); + + updateTx(tx, vm.originWallet, { + dryRun: true + }, function(err) { + $timeout(function onTimeout() { + $scope.$apply(); + }, 10); + + }); + + // setWalletSelector(tx.coin, tx.network, tx.amount, function(err) { + // if (err) { + // return exitWithError('Could not update wallets'); + // } + // + // if (vm.wallets.length > 1) { + // vm.showWalletSelector(); + // } else if (vm.wallets.length) { + // setWallet(vm.wallets[0], tx); + // } + // }); + } + + function showSendMaxWarning(wallet, sendMaxInfo) { + var feeAlternative = '', + msg = ''; + + function verifyExcludedUtxos() { + var warningMsg = []; + if (sendMaxInfo.utxosBelowFee > 0) { + warningMsg.push(gettextCatalog.getString("A total of {{amountBelowFeeStr}} were excluded. These funds come from UTXOs smaller than the network fee provided.", { + amountBelowFeeStr: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.amountBelowFee) + })); + } + + if (sendMaxInfo.utxosAboveMaxSize > 0) { + warningMsg.push(gettextCatalog.getString("A total of {{amountAboveMaxSizeStr}} were excluded. The maximum size allowed for a transaction was exceeded.", { + amountAboveMaxSizeStr: txFormatService.formatAmountStr(vm.originWallet.coin, sendMaxInfo.amountAboveMaxSize) + })); + } + return warningMsg.join('\n'); + }; + + feeAlternative = txFormatService.formatAlternativeStr(vm.originWallet.coin, sendMaxInfo.fee); + if (feeAlternative) { + msg = gettextCatalog.getString("{{feeAlternative}} will be deducted for bitcoin networking fees ({{fee}}).", { + fee: txFormatService.formatAmountStr(vm.originWallet.coin, sendMaxInfo.fee), + feeAlternative: feeAlternative + }); + } else { + msg = gettextCatalog.getString("{{fee}} will be deducted for bitcoin networking fees).", { + fee: txFormatService.formatAmountStr(vm.originWallet.coin, sendMaxInfo.fee) + }); + } + + var warningMsg = verifyExcludedUtxos(); + + if (!lodash.isEmpty(warningMsg)) + msg += '\n' + warningMsg; + + popupService.showAlert(null, msg, function() {}); + }; + + function statusChangeHandler(processName, showName, isOn) { + $log.debug('statusChangeHandler: ', processName, showName, isOn); + if ( + ( + processName === 'broadcastingTx' || + ((processName === 'signingTx') && vm.originWallet.m > 1) || + (processName == 'sendingTx' && !vm.originWallet.canSign() && !vm.originWallet.isPrivKeyExternal()) + ) && !isOn) { + // Show the popup + vm.sendStatus = 'success'; + + // Clear the send flow service state + sendFlowService.state.clear(); + + if ($state.current.name === "tabs.send.review") { // XX SP: Otherwise all open wallets on other devices play this sound if you have been in a send flow before on that device. + soundService.play('misc/payment_sent.mp3'); + } + + var channel = "firebase"; + if (platformInfo.isNW) { + channel = "ga"; + } + // When displaying Fiat, if the formatting fails, the crypto will be the primary amount. + var amount = unitFromSat * satoshis; + var log = new window.BitAnalytics.LogEvent("transfer_success", [{ + "coin": vm.originWallet.coin, + "type": "outgoing", + "amount": amount, + "fees": vm.feeCrypto + }], [channel, "adjust"]); + window.BitAnalytics.LogEventHandlers.postEvent(log); + + $timeout(function() { + $scope.$digest(); + }, 100); + } else if (showName) { + vm.sendStatus = showName; + } + }; + + function updateTx(tx, wallet, opts, cb) { + ongoingProcess.set('calculatingFee', true); + + if (opts.clearCache) { + tx.txp = {}; + } + + // $scope.tx = tx; + + // function updateAmount() { + // if (!tx.amount) return; + // + // // Amount + // tx.amountStr = txFormatService.formatAmountStr(originWallet.coin, tx.amount); + // tx.amountValueStr = tx.amountStr.split(' ')[0]; + // tx.amountUnitStr = tx.amountStr.split(' ')[1]; + // txFormatService.formatAlternativeStr(wallet.coin, tx.amount, function(v) { + // var parts = v.split(' '); + // tx.alternativeAmountStr = v; + // tx.alternativeAmountValueStr = parts[0]; + // tx.alternativeAmountUnitStr = (parts.length > 0) ? parts[1] : ''; + // }); + // } + // + // updateAmount(); + // refresh(); + + var feeServiceLevel = usingMerchantFee && vm.originWallet.coin == 'btc' ? 'urgent' : tx.feeLevel; + feeService.getFeeRate(vm.originWallet.coin, tx.network, feeServiceLevel, function onGetFeeRate(err, feeRate) { if (err) { ongoingProcess.set('calculatingFee', false); - var msg = gettextCatalog.getString('Error getting SendMax information'); - return setSendError(msg); + return cb(err); } - if (sendMaxInfo) { - - $log.debug('Send max info', sendMaxInfo); - - if (tx.sendMax && sendMaxInfo.amount == 0) { - ongoingProcess.set('calculatingFee', false); - setNotReady(gettextCatalog.getString('Insufficient confirmed funds')); - popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee'), function onAlert() { - $ionicHistory.goBack(); - }); - return cb('no_funds'); - } - - tx.sendMaxInfo = sendMaxInfo; - tx.amount = tx.sendMaxInfo.amount; - satoshis = tx.amount; - updateSendAmounts(); - ongoingProcess.set('calculatingFee', false); - $timeout(function() { - showSendMaxWarning(wallet, sendMaxInfo); - }, 200); + var msg; + if (usingCustomFee) { + msg = gettextCatalog.getString('Custom'); + tx.feeLevelName = msg; + } else if (usingMerchantFee) { + $log.info('Using Merchant Fee:' + tx.feeRate + ' vs. Urgent level:' + feeRate); + msg = gettextCatalog.getString('Suggested by Merchant'); + tx.feeLevelName = msg; + } else { + tx.feeLevelName = feeService.feeOpts[tx.feeLevel]; + tx.feeRate = feeRate; } - // txp already generated for this wallet? - if (tx.txp[wallet.id]) { - ongoingProcess.set('calculatingFee', false); - vm.readyToSend = true; - updateSendAmounts(); - $scope.$apply(); - return cb(); - } - - getTxp(lodash.clone(tx), wallet, opts.dryRun, function onGetTxp(err, txp) { - ongoingProcess.set('calculatingFee', false); + getSendMaxInfo(lodash.clone(tx), wallet, function onGetSendmaxInfo(err, sendMaxInfo) { if (err) { - if (err.message == 'Insufficient funds') { - setNotReady(gettextCatalog.getString('Insufficient funds')); - popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee')); - return cb('no_funds'); - } else - return cb(err); + ongoingProcess.set('calculatingFee', false); + var msg = gettextCatalog.getString('Error getting SendMax information'); + return setSendError(msg); } - txp.feeStr = txFormatService.formatAmountStr(wallet.coin, txp.fee); - txFormatService.formatAlternativeStr(wallet.coin, txp.fee, function onFormatAlternativeStr(v) { - // txp.alternativeFeeStr = v; - // if (txp.alternativeFeeStr.substring(0, 4) == '0.00') - // txp.alternativeFeeStr = '< ' + txp.alternativeFeeStr; - vm.feeFiat = v; - vm.fiatCurrency = config.wallet.settings.alternativeIsoCode; - if (v.substring(0, 1) === "<") { - vm.feeLessThanACent = true; + if (sendMaxInfo) { + + $log.debug('Send max info', sendMaxInfo); + + if (tx.sendMax && sendMaxInfo.amount == 0) { + ongoingProcess.set('calculatingFee', false); + setNotReady(gettextCatalog.getString('Insufficient confirmed funds')); + popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee'), function onAlert() { + $ionicHistory.goBack(); + }); + return cb('no_funds'); } + tx.sendMaxInfo = sendMaxInfo; + tx.amount = tx.sendMaxInfo.amount; + satoshis = tx.amount; + updateSendAmounts(); + ongoingProcess.set('calculatingFee', false); + $timeout(function() { + showSendMaxWarning(wallet, sendMaxInfo); + }, 200); + } + + // txp already generated for this wallet? + if (tx.txp[wallet.id]) { + ongoingProcess.set('calculatingFee', false); + vm.readyToSend = true; + updateSendAmounts(); + $scope.$apply(); + return cb(); + } + + getTxp(lodash.clone(tx), wallet, opts.dryRun, function onGetTxp(err, txp) { + ongoingProcess.set('calculatingFee', false); + if (err) { + if (err.message == 'Insufficient funds') { + setNotReady(gettextCatalog.getString('Insufficient funds')); + popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee')); + return cb('no_funds'); + } else + return cb(err); + } + + txp.feeStr = txFormatService.formatAmountStr(wallet.coin, txp.fee); + txFormatService.formatAlternativeStr(wallet.coin, txp.fee, function onFormatAlternativeStr(v) { + // txp.alternativeFeeStr = v; + // if (txp.alternativeFeeStr.substring(0, 4) == '0.00') + // txp.alternativeFeeStr = '< ' + txp.alternativeFeeStr; + vm.feeFiat = v; + vm.fiatCurrency = config.wallet.settings.alternativeIsoCode; + if (v.substring(0, 1) === "<") { + vm.feeLessThanACent = true; + } + + }); + + var per = (txp.fee / (txp.amount + txp.fee) * 100); + var perString = per.toFixed(2); + txp.feeRatePerStr = (perString == '0.00' ? '< ' : '') + perString + '%'; + txp.feeToHigh = per > FEE_TOO_HIGH_LIMIT_PERCENTAGE; + vm.feeCrypto = (unitFromSat * txp.fee).toFixed(8); + vm.feeIsHigh = txp.feeToHigh; + + tx.txp[wallet.id] = txp; + $log.debug('Confirm. TX Fully Updated for wallet:' + wallet.id, tx); + vm.readyToSend = true; + updateSendAmounts(); + $scope.$apply(); + + return cb(); }); - - var per = (txp.fee / (txp.amount + txp.fee) * 100); - var perString = per.toFixed(2); - txp.feeRatePerStr = (perString == '0.00' ? '< ' : '') + perString + '%'; - txp.feeToHigh = per > FEE_TOO_HIGH_LIMIT_PERCENTAGE; - vm.feeCrypto = (unitFromSat * txp.fee).toFixed(8); - vm.feeIsHigh = txp.feeToHigh; - - tx.txp[wallet.id] = txp; - $log.debug('Confirm. TX Fully Updated for wallet:' + wallet.id, tx); - vm.readyToSend = true; - updateSendAmounts(); - $scope.$apply(); - - return cb(); }); }); - }); - } + } -} + } +})(); From a426a5abc0a14e4dfbf413b812712aaa42c7cdcf Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 26 Sep 2018 16:27:47 +1200 Subject: [PATCH 20/32] Now handles URIs that begin with 'bch:'. --- src/js/services/bitcoin-uri.service.js | 10 ++++++++-- src/js/services/bitcoin-uri.service.spec.js | 22 ++++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index 3da84f3da..669c14bf3 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -151,6 +151,7 @@ hasPassphrase: false, type: 1, }, + isTestnet: false, isValid: false, label: '', message: '', @@ -170,7 +171,6 @@ "req-param0": '', "req-param1": '' }, - testnet: false, url: '' // For BIP70 } @@ -204,7 +204,13 @@ } else if (/^(?:bitcoincash)|(?:bitcoin-cash)$/.test(preColonLower)) { parsed.coin = 'bch'; - parsed.test = false; + parsed.isTestnet = false; + addressAndParams = colonSplit[2].trim(); + console.log('Is bch'); + + } else if (/^(?:bch)$/.test(preColonLower)) { + parsed.coin = 'bch'; + parsed.isTestnet = false; addressAndParams = colonSplit[2].trim(); console.log('Is bch'); diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index 2ddbd0d2e..bf6c9ce9e 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -30,7 +30,7 @@ describe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('bch'); expect(parsed.publicAddress).toBeUndefined(); - expect(parsed.isTestnet).toBeUndefined(); + expect(parsed.isTestnet).toBe(false); expect(parsed.url).toBe('https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu'); }); @@ -171,6 +171,16 @@ describe('bitcoinUriService', function() { expect(parsed.isTestnet).toBe(false); }); + it('legacy address with bch prefix', function() { + + var parsed = bitcoinUriService.parse('bch:19yUdM2H7sADrabR6Afu9zTpmwqr6WYprX'); + + expect(parsed.isValid).toBe(true); + expect(parsed.coin).toBe('bch'); + expect(parsed.publicAddress.legacy).toBe('19yUdM2H7sADrabR6Afu9zTpmwqr6WYprX'); + expect(parsed.isTestnet).toBe(false); + }); + it('cashAddr testnet with prefix', function() { var parsed = bitcoinUriService.parse('bchtest:qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt'); @@ -191,6 +201,16 @@ describe('bitcoinUriService', function() { expect(parsed.isTestnet).toBe(false); }); + it('cashAddr with bch prefix', function() { + + var parsed = bitcoinUriService.parse('bch:qpqzqtjqqc00nsxj0e3kevz65ujg4yt5z5w99jap5f'); + + expect(parsed.isValid).toBe(true); + expect(parsed.coin).toBe('bch'); + expect(parsed.publicAddress.cashAddr).toBe('qpqzqtjqqc00nsxj0e3kevz65ujg4yt5z5w99jap5f'); + expect(parsed.isTestnet).toBe(false); + }); + it('cashAddr with dash', function() { var parsed = bitcoinUriService.parse('bitcoin-cash:qpshfu3dk5s3e7zdcgdcun6xgxtra6uyxs7g580js0'); From 4f7004f5a3ab63836519ba7af3512dd8c4eb13b0 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Dominguez Date: Wed, 26 Sep 2018 15:31:48 +0200 Subject: [PATCH 21/32] Set variables before enter in the screen. (Fix lost after merging) --- src/js/controllers/review.controller.js | 103 +++++++++++++----------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index 36e734c81..d7d69f611 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -9,49 +9,6 @@ angular function reviewController(addressbookService, bitcoinCashJsService, bitcore, bitcoreCash, bwcError, clipboardService, configService, feeService, gettextCatalog, $interval, $ionicHistory, $ionicModal, ionicToast, lodash, $log, ongoingProcess, platformInfo, popupService, profileService, $scope, sendFlowService, shapeshiftService, soundService, $state, $timeout, txConfirmNotification, txFormatService, walletService) { var vm = this; - vm.buttonText = ''; - vm.destination = { - address: '', - balanceAmount: '', - balanceCurrency: '', - coin: '', - color: '', - currency: '', - currencyColor: '', - kind: '', // 'address', 'contact', 'wallet' - name: '' - }; - vm.displayAddress = ''; - vm.feeCrypto = ''; - vm.feeFiat = ''; - vm.fiatCurrency = ''; - vm.feeIsHigh = false; - vm.feeLessThanACent = false; - vm.isCordova = platformInfo.isCordova; - vm.notReadyMessage = ''; - vm.origin = { - balanceAmount: '', - balanceCurrency: '', - currency: '', - currencyColor: '', - }; - vm.originWallet = null; - vm.paymentExpired = false; - vm.personalNotePlaceholder = gettextCatalog.getString('Enter text here'); - vm.primaryAmount = ''; - vm.primaryCurrency = ''; - vm.usingMerchantFee = false; - vm.readyToSend = false; - vm.remainingTimeStr = ''; - vm.secondaryAmount = ''; - vm.secondaryCurrency = ''; - vm.sendingTitle = gettextCatalog.getString('You are sending'); - vm.sendStatus = ''; - vm.showAddress = true; - vm.thirdParty = null; - vm.wallet = null; - vm.memoExpanded = false; - // Functions vm.goBack = goBack; vm.onSuccessConfirm = onSuccessConfirm; @@ -78,9 +35,58 @@ angular $scope.$on("$ionicView.beforeEnter", onBeforeEnter); + function initVariables() { + vm.buttonText = ''; + vm.destination = { + address: '', + balanceAmount: '', + balanceCurrency: '', + coin: '', + color: '', + currency: '', + currencyColor: '', + kind: '', // 'address', 'contact', 'wallet' + name: '' + }; + vm.displayAddress = ''; + vm.feeCrypto = ''; + vm.feeFiat = ''; + vm.fiatCurrency = ''; + vm.feeIsHigh = false; + vm.feeLessThanACent = false; + vm.isCordova = platformInfo.isCordova; + vm.memo = ''; + vm.notReadyMessage = ''; + vm.origin = { + balanceAmount: '', + balanceCurrency: '', + currency: '', + currencyColor: '', + }; + vm.originWallet = null; + vm.paymentExpired = false; + vm.personalNotePlaceholder = gettextCatalog.getString('Enter text here'); + vm.primaryAmount = ''; + vm.primaryCurrency = ''; + vm.usingMerchantFee = false; + vm.readyToSend = false; + vm.remainingTimeStr = ''; + vm.secondaryAmount = ''; + vm.secondaryCurrency = ''; + vm.sendingTitle = gettextCatalog.getString('You are sending'); + vm.sendStatus = ''; + vm.showAddress = true; + vm.thirdParty = null; + vm.wallet = null; + vm.memoExpanded = false; + } function onBeforeEnter(event, data) { $log.debug('reviewController onBeforeEnter sendflow ', sendFlowService.state); + + // Reset from last time + initVariables(); + defaults = configService.getDefaults(); sendFlowData = sendFlowService.state.getClone(); originWalletId = sendFlowData.fromWalletId; @@ -397,14 +403,14 @@ angular var balanceCryptoAmount = ''; var balanceCryptoCurrencyCode = ''; var balanceFiatAmount = ''; - var balanceFiatCurrency = '' + var balanceFiatCurrency = ''; var displayAmount = ''; var displayCurrency = ''; var walletStatus = null; - if (wallet.status.isValid) { + if (wallet.status && wallet.status.isValid) { walletStatus = wallet.status; - } else if (wallet.cachedStatus.isValid) { + } else if (wallet.cachedStatus && wallet.cachedStatus.isValid) { walletStatus = wallet.cachedStatus; } @@ -507,13 +513,15 @@ angular function initShapeshift(cb) { vm.sendingTitle = gettextCatalog.getString('You are shifting'); + if (!vm.thirdParty.data) { + vm.thirdParty.data = {}; + } var toWallet = profileService.getWallet(destinationWalletId); vm.destination.name = toWallet.name; vm.destination.color = toWallet.color; vm.destination.currency = toWallet.coin.toUpperCase(); - ongoingProcess.set('connectingShapeshift', true); walletService.getAddress(vm.originWallet, false, function onReturnWalletAddress(err, returnAddr) { if (err) { @@ -942,6 +950,5 @@ angular }); }); } - } })(); From 621c60a2cedc4e7aebef25370b2c3f692e909318 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Dominguez Date: Wed, 26 Sep 2018 20:15:25 +0200 Subject: [PATCH 22/32] Set thirdParty on the sendflow --- src/js/controllers/amount.controller.js | 7 +- src/js/controllers/review.controller.js | 92 +++++++++---------- .../controllers/wallet-selector.controller.js | 9 +- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/js/controllers/amount.controller.js b/src/js/controllers/amount.controller.js index 07f31bb3e..f837ff2f3 100644 --- a/src/js/controllers/amount.controller.js +++ b/src/js/controllers/amount.controller.js @@ -30,7 +30,7 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory, vm.sendableFunds = ''; vm.showSendMaxButton = false; vm.showSendLimitMaxButton = false; - vm.thirdParty = false; + vm.thirdParty = null; vm.unit = ''; // Functions @@ -88,11 +88,16 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory, sendFlowService.state.pop(); } + + initCurrencies(); passthroughParams = sendFlowService.state.getClone(); console.log('amount onBeforeEnter after back sendflow ', passthroughParams); + // Init thirdParty, should be done for all the variable + vm.thirdParty = null; + vm.fromWalletId = passthroughParams.fromWalletId; vm.toWalletId = passthroughParams.toWalletId; vm.minAmount = parseFloat(passthroughParams.minAmount); diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index d7d69f611..67a4164c8 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -9,6 +9,50 @@ angular function reviewController(addressbookService, bitcoinCashJsService, bitcore, bitcoreCash, bwcError, clipboardService, configService, feeService, gettextCatalog, $interval, $ionicHistory, $ionicModal, ionicToast, lodash, $log, ongoingProcess, platformInfo, popupService, profileService, $scope, sendFlowService, shapeshiftService, soundService, $state, $timeout, txConfirmNotification, txFormatService, walletService) { var vm = this; + vm.buttonText = ''; + vm.destination = { + address: '', + balanceAmount: '', + balanceCurrency: '', + coin: '', + color: '', + currency: '', + currencyColor: '', + kind: '', // 'address', 'contact', 'wallet' + name: '' + }; + vm.displayAddress = ''; + vm.feeCrypto = ''; + vm.feeFiat = ''; + vm.fiatCurrency = ''; + vm.feeIsHigh = false; + vm.feeLessThanACent = false; + vm.isCordova = platformInfo.isCordova; + vm.memo = ''; + vm.notReadyMessage = ''; + vm.origin = { + balanceAmount: '', + balanceCurrency: '', + currency: '', + currencyColor: '', + }; + vm.originWallet = null; + vm.paymentExpired = false; + vm.personalNotePlaceholder = gettextCatalog.getString('Enter text here'); + vm.primaryAmount = ''; + vm.primaryCurrency = ''; + vm.usingMerchantFee = false; + vm.readyToSend = false; + vm.remainingTimeStr = ''; + vm.secondaryAmount = ''; + vm.secondaryCurrency = ''; + vm.sendingTitle = gettextCatalog.getString('You are sending'); + vm.sendStatus = ''; + vm.showAddress = true; + vm.thirdParty = null; + vm.wallet = null; + vm.memoExpanded = false; + // Functions vm.goBack = goBack; vm.onSuccessConfirm = onSuccessConfirm; @@ -35,57 +79,11 @@ angular $scope.$on("$ionicView.beforeEnter", onBeforeEnter); - function initVariables() { - vm.buttonText = ''; - vm.destination = { - address: '', - balanceAmount: '', - balanceCurrency: '', - coin: '', - color: '', - currency: '', - currencyColor: '', - kind: '', // 'address', 'contact', 'wallet' - name: '' - }; - vm.displayAddress = ''; - vm.feeCrypto = ''; - vm.feeFiat = ''; - vm.fiatCurrency = ''; - vm.feeIsHigh = false; - vm.feeLessThanACent = false; - vm.isCordova = platformInfo.isCordova; - vm.memo = ''; - vm.notReadyMessage = ''; - vm.origin = { - balanceAmount: '', - balanceCurrency: '', - currency: '', - currencyColor: '', - }; - vm.originWallet = null; - vm.paymentExpired = false; - vm.personalNotePlaceholder = gettextCatalog.getString('Enter text here'); - vm.primaryAmount = ''; - vm.primaryCurrency = ''; - vm.usingMerchantFee = false; - vm.readyToSend = false; - vm.remainingTimeStr = ''; - vm.secondaryAmount = ''; - vm.secondaryCurrency = ''; - vm.sendingTitle = gettextCatalog.getString('You are sending'); - vm.sendStatus = ''; - vm.showAddress = true; - vm.thirdParty = null; - vm.wallet = null; - vm.memoExpanded = false; - } - function onBeforeEnter(event, data) { $log.debug('reviewController onBeforeEnter sendflow ', sendFlowService.state); // Reset from last time - initVariables(); + vm.thirdParty = null; defaults = configService.getDefaults(); sendFlowData = sendFlowService.state.getClone(); diff --git a/src/js/controllers/wallet-selector.controller.js b/src/js/controllers/wallet-selector.controller.js index 036727333..3d954821c 100644 --- a/src/js/controllers/wallet-selector.controller.js +++ b/src/js/controllers/wallet-selector.controller.js @@ -52,9 +52,6 @@ angular $scope.specificAmount = $scope.specificAlternativeAmount = ''; $scope.isPaymentRequest = true; } - if ($scope.params.thirdParty) { - $scope.thirdParty = $scope.params.thirdParty; - } }; function onEnter (event, data) { @@ -62,7 +59,7 @@ angular $scope.selectedPriceDisplay = config.wallet.settings.priceDisplay; }); - if ($scope.thirdParty) { + if ($scope.params.thirdParty) { // Third party services specific logic handleThirdPartyIfShapeshift(); } @@ -102,8 +99,8 @@ angular } function handleThirdPartyIfShapeshift() { - console.log($scope.thirdParty, $scope.coin); - if ($scope.thirdParty.id === 'shapeshift' && $scope.type === 'destination') { // Shapeshift wants to know the + console.log($scope.params.thirdParty, $scope.coin); + if ($scope.params.thirdParty.id === 'shapeshift' && $scope.type === 'destination') { // Shapeshift wants to know the $scope.coin = profileService.getWallet(fromWalletId).coin; if ($scope.coin === 'bch') { $scope.coin = 'btc'; From 8f67fde60b2b6b517245968047c3f36758839acf Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Dominguez Date: Wed, 26 Sep 2018 21:02:03 +0200 Subject: [PATCH 23/32] Fix - init wallet list --- src/js/controllers/wallet-selector.controller.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/js/controllers/wallet-selector.controller.js b/src/js/controllers/wallet-selector.controller.js index 3d954821c..0ccb3bd1f 100644 --- a/src/js/controllers/wallet-selector.controller.js +++ b/src/js/controllers/wallet-selector.controller.js @@ -114,6 +114,8 @@ angular var walletsAll = []; var walletsSufficientFunds = []; $scope.walletsInsufficientFunds = []; // For origin screen + $scope.walletsBtc = []; + $scope.walletsBch = []; if ($scope.type === 'origin') { $scope.headerTitle = gettextCatalog.getString('Choose a wallet to send from'); From 6c0cf4d62231531843be19201091fbb7f56dac0f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Dominguez Date: Wed, 26 Sep 2018 21:35:30 +0200 Subject: [PATCH 24/32] Add a link to shapeshift in case it doesn't work --- src/js/controllers/review.controller.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index 67a4164c8..40fe81eba 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -6,7 +6,7 @@ angular .module('copayApp.controllers') .controller('reviewController', reviewController); - function reviewController(addressbookService, bitcoinCashJsService, bitcore, bitcoreCash, bwcError, clipboardService, configService, feeService, gettextCatalog, $interval, $ionicHistory, $ionicModal, ionicToast, lodash, $log, ongoingProcess, platformInfo, popupService, profileService, $scope, sendFlowService, shapeshiftService, soundService, $state, $timeout, txConfirmNotification, txFormatService, walletService) { + function reviewController(addressbookService, externalLinkService, bitcoinCashJsService, bitcore, bitcoreCash, bwcError, clipboardService, configService, feeService, gettextCatalog, $interval, $ionicHistory, $ionicModal, ionicToast, lodash, $log, ongoingProcess, platformInfo, popupService, profileService, $scope, sendFlowService, shapeshiftService, soundService, $state, $timeout, txConfirmNotification, txFormatService, walletService) { var vm = this; vm.buttonText = ''; @@ -105,8 +105,12 @@ angular if (err) { // Error stop here ongoingProcess.set('connectingShapeshift', false); - popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString(), function onAlert() { - $ionicHistory.goBack(); + popupService.showConfirm(gettextCatalog.getString('Shapeshift Error'), err.toString(), gettextCatalog.getString('Open') + " Shapeshift", gettextCatalog.getString('Go Back'), function onConfirm(hasConfirm) { + if (hasConfirm) { + externalLinkService.open("https://shapeshift.io"); + } else { + $ionicHistory.goBack(); + } }); } else { _next(data); From dd61e72bcfaac15b2b9e66c0c12f8021e609e421 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Dominguez Date: Wed, 26 Sep 2018 21:42:35 +0200 Subject: [PATCH 25/32] Fix go back anyway --- src/js/controllers/review.controller.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index 40fe81eba..c4923a5f4 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -108,9 +108,8 @@ angular popupService.showConfirm(gettextCatalog.getString('Shapeshift Error'), err.toString(), gettextCatalog.getString('Open') + " Shapeshift", gettextCatalog.getString('Go Back'), function onConfirm(hasConfirm) { if (hasConfirm) { externalLinkService.open("https://shapeshift.io"); - } else { - $ionicHistory.goBack(); } + $ionicHistory.goBack(); }); } else { _next(data); From e14ab5dab3450d796004b045d9d74a2fdfc8166d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Dominguez Date: Wed, 26 Sep 2018 21:46:07 +0200 Subject: [PATCH 26/32] Update appConfig.json rc4 --- app-template/bitcoincom/appConfig.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app-template/bitcoincom/appConfig.json b/app-template/bitcoincom/appConfig.json index 5818a4006..48298ea0c 100644 --- a/app-template/bitcoincom/appConfig.json +++ b/app-template/bitcoincom/appConfig.json @@ -24,9 +24,9 @@ "windowsAppId": "804636ee-b017-4cad-8719-e58ac97ffa5c", "pushSenderId": "1036948132229", "description": "A Secure Bitcoin Wallet", - "version": "5.1.4", - "fullVersion": "5.1-rc3", - "androidVersion": "501400", + "version": "5.1.5", + "fullVersion": "5.1-rc4", + "androidVersion": "501500", "_extraCSS": "", "_enabledExtensions": { "coinbase": false, From 898b7d03fe938599c4f6e6a99e6d2b1bdfaef708 Mon Sep 17 00:00:00 2001 From: A R Hansen Date: Mon, 8 Oct 2018 01:55:55 +0200 Subject: [PATCH 27/32] 0 fee transaction feature added. --- .vscode/launch.json | 14 ++ src/js/controllers/confirm.js | 3 +- src/js/controllers/review.controller.js | 32 +++- src/js/services/feeService.js | 4 +- www/css/main.css | 209 +++++++++++++++--------- 5 files changed, 175 insertions(+), 87 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..29f920f4f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/www/index.html" + } + ] +} \ No newline at end of file diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index 76950df73..ae4bc7153 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -517,7 +517,8 @@ angular.module('copayApp.controllers').controller('confirmController', function( if (!lodash.isEmpty(warningMsg)) msg += '\n' + warningMsg; - popupService.showAlert(null, msg, function() {}); + popupService.showAlert(null, msg, function() {}); + }; $scope.onWalletSelect = function(wallet) { diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index 2311b2cb6..ae30382fb 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -718,14 +718,23 @@ angular if (tx.paypro) startExpirationTimer(tx.paypro.expires); - updateTx(tx, vm.originWallet, { - dryRun: true - }, function(err) { - $timeout(function() { - $scope.$apply(); - }, 10); + popupService.showConfirm(null, 'Do you want this transaction to be sent without a fee?', 'Yes', 'No', function(ok) { + if(ok){ + tx.feeRate = 0; + // tx.feeLevel = 'free'; + usingCustomFee = true; + } + updateTx(tx, vm.originWallet, { + dryRun: true + }, function(err) { + $timeout(function() { + $scope.$apply(); + }, 10); + + }); + }); + - }); // setWalletSelector(tx.coin, tx.network, tx.amount, function(err) { // if (err) { @@ -778,6 +787,7 @@ angular msg += '\n' + warningMsg; popupService.showAlert(null, msg, function() {}); + //popupService.showConfirm(null, msg, null, null, function() {}); }; function statusChangeHandler(processName, showName, isOn) { @@ -855,7 +865,11 @@ angular } var msg; - if (usingCustomFee) { + // if (tx.feeLevel == 'free'){ + // tx.feeRate = 0; + // } + // else + if (usingCustomFee) { msg = gettextCatalog.getString('Custom'); tx.feeLevelName = msg; } else if (usingMerchantFee) { @@ -865,7 +879,9 @@ angular } else { tx.feeLevelName = feeService.feeOpts[tx.feeLevel]; tx.feeRate = feeRate; + } + getSendMaxInfo(lodash.clone(tx), wallet, function(err, sendMaxInfo) { if (err) { diff --git a/src/js/services/feeService.js b/src/js/services/feeService.js index bba972b81..edb1800a4 100644 --- a/src/js/services/feeService.js +++ b/src/js/services/feeService.js @@ -12,6 +12,7 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou normal: gettext('Normal'), economy: gettext('Economy'), superEconomy: gettext('Super Economy'), +// free: gettext('No fee (works only for BCH)'), custom: gettext('Custom') }; @@ -31,6 +32,7 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou root.getFeeRate = function(coin, network, feeLevel, cb) { if (feeLevel == 'custom') return cb(); + if (feeLevel == 'free') return cb(null, 0); network = network || 'livenet'; @@ -48,9 +50,9 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou }) }); } - var feeRate = feeLevelRate.feePerKb; + if (!fromCache) $log.debug('Dynamic fee: ' + feeLevel + '/' + network + ' ' + (feeLevelRate.feePerKb / 1000).toFixed() + ' SAT/B'); return cb(null, feeRate); diff --git a/www/css/main.css b/www/css/main.css index 8602ba35d..83c50fe17 100644 --- a/www/css/main.css +++ b/www/css/main.css @@ -10083,7 +10083,7 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm .onboarding .button.button-white.button-standard, .onboarding .button.button-green.button-standard, .onboarding .button.button-assertive.button-standard, #shapeshift .button-shapeshift { - width: 85%; + width: 90%; max-width: 300px; margin-left: auto; margin-right: auto; @@ -10195,6 +10195,7 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm .button { border-radius: 6px; } .button.button-full { + border-radius: 0; display: block; } .button-green { border-color: #FFF; @@ -10263,8 +10264,8 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm color: #FFFFFF; text-decoration: none; } .button-white-outline.active, .button-white-outline.activated { - border-color: #FFF; - background-color: #FAFAFA; } + border-color: #FFFFFF; + background-color: #FFFFFF; } .button-white-outline.button-outline { border-color: #FFFFFF; background: transparent; @@ -10632,7 +10633,7 @@ qrcode { #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long .primary-amount-display { font-size: 2em; } } #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .primary-amount-display { - font-size: 0.9em; } + font-size: 1.2em; } @media (min-width: 375px) { #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .primary-amount-display { font-size: 1.3em; } } @@ -10659,7 +10660,8 @@ qrcode { line-height: 1em; } #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount .primary-amount-display { margin-right: 5px; - word-break: break-all; } + word-break: break-all; + width: 100%; } #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .alternative-amount { color: #6F6F70; } #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .switch-currencies { @@ -10680,30 +10682,33 @@ qrcode { justify-content: space-between; margin: 0 14px; } #view-amount .scroll-content .send-amount-extras .available-funds { - color: #6F6F70; } + color: #6F6F70; + text-align: left; } + #view-amount .scroll-content .send-amount-extras .change-currency { + text-align: right; } #view-amount .scroll-content .send-amount-extras .warning { color: #b7664d; } - #view-amount .scroll-content .send-amount-extras .extra, - #view-amount .scroll-content .send-amount-extras button.extra { - /*display: flex;*/ - flex: 0 1 auto; } - #view-amount .scroll-content .send-amount-extras button.extra { - background: none; - border: none; - color: #000; - font-family: 'ProximaNova'; - font-size: 14px; - line-height: normal; - min-height: auto; - min-width: auto; - padding: 0; } - #view-amount .scroll-content .send-amount-extras .button .icon:before { - font-size: 14px; + #view-amount .scroll-content .send-amount-extras .extra { + flex: 1; line-height: normal; } - #view-amount .scroll-content .send-amount-extras .button span { - display: flex; - align-items: center; - justify-content: center; } + #view-amount .scroll-content .send-amount-extras .extra .button { + background: none; + border: none; + border-radius: 0; + color: #000; + font-family: 'ProximaNova'; + font-size: 14px; + line-height: normal; + min-height: auto; + min-width: auto; + padding: 0; } + #view-amount .scroll-content .send-amount-extras .extra .button .icon:before { + font-size: 14px; + line-height: normal; } + #view-amount .scroll-content .send-amount-extras .extra .button span { + display: flex; + align-items: center; + justify-content: center; } #view-amount .scroll-content .button.no-margin { margin: 0; } #view-amount .scroll-content .notification-warning { @@ -10939,14 +10944,14 @@ qrcode { #tab-home .buttons .button { border: 2px solid; border-radius: 47px; - padding: 0 15px 0 15px; + padding: 8px 2px 8px 2px; text-align: center; width: 100%; max-width: 300px; font-size: 19px; font-weight: bolder; - min-height: auto; - line-height: 36px; } + min-height: 0; + line-height: 19px; } #tab-home .wallet-coin-logo { vertical-align: middle; @@ -11013,6 +11018,10 @@ qrcode { #tab-home .release .title { font-weight: 700; color: #444; } + #tab-home .release .release-notes { + white-space: pre; + white-space: pre-line; + text-align: left; } #tab-home .release .button { width: 100%; border: none; } @@ -11025,6 +11034,14 @@ qrcode { #tab-home .badge { top: 11px; } +.popup-update .popup-buttons { + display: block; } + +.popup-update .popup-buttons .button { + display: block; + min-width: 100% !important; + margin-top: 4px; } + #tab-receive .button-share { color: #fff; box-shadow: none; @@ -11101,6 +11118,8 @@ qrcode { #tab-receive .payment-received-container { margin: 0 20px; } + #tab-receive .payment-received-container svg { + max-height: 400px; } #tab-receive .payment-received-container .payment-received-amount { font-size: 1.8em; display: block; @@ -11207,7 +11226,7 @@ qrcode { margin: auto; margin-top: 18px; } #tab-send .send-wrapper .buttons .button { - height: 60px; + min-height: 65px; line-height: 16px; margin-right: 0px; width: 95%; @@ -11284,7 +11303,9 @@ qrcode { margin-top: 18px; } #tab-send .sendTip .buttons .button { font-weight: bold; - font-size: 19px; } + font-size: 19px; + line-height: 26px; + padding: 8px 6px; } #tab-send .sendTip .button-first-contact img { height: 19px; width: 19px; @@ -11920,6 +11941,13 @@ qrcode { #walletDetails .bp-content.status-bar { margin-top: 20px; margin-top: env(safe-area-inset-top); } + #walletDetails .bp-content.collapse ion-content { + margin-top: 40px; } + #walletDetails .bp-content.collapse .amount__scale, #walletDetails .bp-content.collapse .amount__error { + -webkit-transform: scale3d(0.5, 0.5, 0.5) translateY(0px); + transform: scale3d(0.5, 0.5, 0.5) translateY(0px); } + #walletDetails .bp-content.collapse .amount-alternative, #walletDetails .bp-content.collapse .send-receive-buttons, #walletDetails .bp-content.collapse .wallet-details-wallet-info { + opacity: 0; } #walletDetails .bar-header { border: 0; background: #eeb640; } @@ -11932,9 +11960,12 @@ qrcode { #walletDetails ion-content { padding-top: 0; top: 0; + transition: all 0.25s ease-in-out; + margin-top: 185px; margin-bottom: 16px; } - #walletDetails ion-content.collapsible { - margin-top: 230px; } + @media only screen and (max-height: 500px) { + #walletDetails ion-content { + margin-top: 165px; } } #walletDetails ion-content .scroll { background: #f8f8f9; min-height: 300px; } @@ -11965,38 +11996,45 @@ qrcode { justify-content: space-evenly; width: 100%; position: absolute; - bottom: 20px; } + bottom: 20px; + transition: all 0.25s ease-in-out; } #walletDetails .amount-wrapper .send-receive-buttons > .col { padding: 5px 10px; margin-bottom: 0; } #walletDetails .amount-wrapper .send-receive-buttons .button { border: 2px solid; border-radius: 47px; - padding: 0 15px 0 15px; + padding: 6px 2px 6px 2px; text-align: center; width: 100%; max-width: 300px; font-size: 19px; font-weight: bolder; - min-height: auto; - line-height: 36px; } + min-height: 0; + line-height: 19px; } #walletDetails .amount { - width: 100%; - text-align: center; - color: #fff; - height: 230px; - padding-top: 40px; - display: block; align-items: center; - justify-content: center; } + color: #fff; + display: block; + height: 230px; + justify-content: center; + padding-top: 40px; + text-align: center; + transition: all 0.25s ease-in-out; + width: 100%; } + @media only screen and (max-height: 500px) { + #walletDetails .amount { + height: 210px; } } #walletDetails .amount__balance { -webkit-transform: scale3d(1, 1, 1) translateY(45px); - transform: scale3d(1, 1, 1) translateY(45px); } + transform: scale3d(1, 1, 1) translateY(45px); + transition: all 0.25s ease-in-out; } #walletDetails .amount__updating { z-index: 999; margin-top: -2.1rem; } #walletDetails .amount-alternative { - line-height: 36px; } + line-height: 36px; + transition: all 0.25s ease-in-out; } #walletDetails .amount__button-balance { background-color: transparent; border: 1px solid rgba(255, 255, 255, 0.25); @@ -12006,7 +12044,8 @@ qrcode { vertical-align: middle; } #walletDetails .amount__error { font-size: 14px; - padding: 35px 20px; } + padding: 35px 20px; + opacity: 1; } #walletDetails .no-alternative { padding-top: 45px; } #walletDetails .item.item-footer { @@ -12071,7 +12110,9 @@ a.item { font-size: 0.9em; } .loading-wallet svg { - margin-top: 0; } + margin-top: 0; + width: 16px; + height: 16px; } #advanced-settings .list .item:before { display: block; @@ -12125,7 +12166,9 @@ a.item { margin-top: 18px; } #shapeshift .empty-case .buttons .button { font-weight: bold; - font-size: 19px; } + font-size: 19px; + line-height: 26px; + padding: 8px 6px; } #shapeshift .empty-case .button-first-contact img { height: 19px; width: 19px; @@ -13812,6 +13855,7 @@ click-to-accept { height: 92px; width: 100%; } click-to-accept .click-to-accept__button.button.button-primary.button-standard { + border-radius: 0; height: 100%; max-width: 9999px; width: 100%; } @@ -13918,6 +13962,8 @@ slide-to-accept { height: 92px; width: 100%; background: #494949; } + slide-to-accept .slide .button { + border-radius: 0; } slide-to-accept .slide__listener { height: 100%; width: 100%; @@ -15349,20 +15395,25 @@ log-options #check-bar .checkbox-icon { #cash-scan a { cursor: pointer; } -#view-review { - background-color: #494949; } - #view-review slide-to-accept, #view-review slide-to-accept-success { - margin-bottom: constant(safe-area-inset-bottom); - /* iOS 11.0 */ - margin-bottom: env(safe-area-inset-bottom); - /* iOS 11.2 */ } - #view-review .fee-summary { - position: absolute; - bottom: 92px; } - #view-review .shapeshift-banner, #view-review .bitpay-banner, #view-review .egifter-banner { - box-shadow: none; } - #view-review .warning { - color: #b7664d; } +#view-review slide-to-accept, #view-review slide-to-accept-success { + margin-bottom: constant(safe-area-inset-bottom); + /* iOS 11.0 */ + margin-bottom: env(safe-area-inset-bottom); + /* iOS 11.2 */ } + +#view-review .fee-summary { + bottom: 92px; + bottom: calc(92px + constant(safe-area-inset-bottom)); + /* iOS 11.0 */ + bottom: calc(92px + env(safe-area-inset-bottom)); + /* iOS 11.2 */ + position: absolute; } + +#view-review .shapeshift-banner, #view-review .bitpay-banner, #view-review .egifter-banner { + box-shadow: none; } + +#view-review .warning { + color: #b7664d; } .gravatar { border-radius: 3px; @@ -15395,6 +15446,7 @@ log-options #check-bar .checkbox-icon { left: 13px; top: 50%; padding: 0; + -webkit-transform: translate(0, -50%); transform: translate(0, -50%); } .item .item-content.item-content-compact { @@ -15463,8 +15515,8 @@ ion-content.padded-bottom-cta-with-summary { overflow: hidden; text-overflow: ellipsis; } .address-frame.expanded { - white-space: pre-wrap; - word-break: break-all; } + white-space: normal; + text-overflow: clip; } .address-frame .prefix { color: #000000; } .address-frame .mid { @@ -15507,13 +15559,13 @@ ion-content.padded-bottom-cta-with-summary { transform: scale(1, 1); } .fee-summary { - position: relative; + background-color: #F2F2F2; + box-sizing: border-box; display: flex; flex-direction: column; - width: 100%; padding: 5px 12px 15px; - box-sizing: border-box; - background-color: #F2F2F2; } + position: relative; + width: 100%; } .fee-summary:before { content: ''; position: absolute; @@ -15523,16 +15575,16 @@ ion-content.padded-bottom-cta-with-summary { height: 15px; background: linear-gradient(to bottom, rgba(242, 242, 242, 0) 0%, #f2f2f2 100%); } .fee-summary .amount { - display: flex; - flex-direction: row; - justify-content: space-between; width: 100%; } - .fee-summary .amount .fee-fiat.positive { - color: #70955F; } - .fee-summary .amount .fee-fiat.negative { - color: #C24633; } + .fee-summary .amount .fee-fiat { + display: inline; } + .fee-summary .amount .fee-fiat.positive { + color: #70955F; } + .fee-summary .amount .fee-fiat.negative { + color: #C24633; } .fee-summary .amount .fee-crypto { - color: #A7A7A7; } + color: #A7A7A7; + float: right; } .formatted-amount { display: inline-block; } @@ -15557,6 +15609,9 @@ ion-content.padded-bottom-cta-with-summary { margin-left: 5px; text-transform: uppercase; } +.wallet-balance-directive { + display: inline-block; } + /* This is for rules that don't yet have a home. * Our goal is to delete this file. Search the regex: /class=".*CLASS.*?"/ */ From d47393f16025571502a4bc418541799f1206e445 Mon Sep 17 00:00:00 2001 From: A R Hansen Date: Mon, 8 Oct 2018 02:00:36 +0200 Subject: [PATCH 28/32] .. From 4e524a7500abda0067c23132397870a771d22b64 Mon Sep 17 00:00:00 2001 From: A R Hansen Date: Mon, 8 Oct 2018 02:16:43 +0200 Subject: [PATCH 29/32] Not needed. --- src/js/services/feeService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/services/feeService.js b/src/js/services/feeService.js index edb1800a4..5189527b2 100644 --- a/src/js/services/feeService.js +++ b/src/js/services/feeService.js @@ -32,7 +32,7 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou root.getFeeRate = function(coin, network, feeLevel, cb) { if (feeLevel == 'custom') return cb(); - if (feeLevel == 'free') return cb(null, 0); +// if (feeLevel == 'free') return cb(null, 0); network = network || 'livenet'; From f1d2b00b2baf6df56a293e2b6b452bc2f7ae93de Mon Sep 17 00:00:00 2001 From: A R Hansen Date: Fri, 12 Oct 2018 11:53:16 +0200 Subject: [PATCH 30/32] set up my own bitcore server and changed the url. for some reason Bitcoin.com's server won't accept 0 fee txs. --- src/js/services/configService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/services/configService.js b/src/js/services/configService.js index 72cc4825f..6f2c0ec67 100644 --- a/src/js/services/configService.js +++ b/src/js/services/configService.js @@ -18,7 +18,7 @@ angular.module('copayApp.services').factory('configService', function(storageSer }, bwscash: { - url: 'https://bwscash.bitcoin.com/bws/api' + url: 'https://bws.freepages.dk/bws/api' }, download: { From 70f520ea43b99365d65083960720309a93675313 Mon Sep 17 00:00:00 2001 From: A R Hansen Date: Fri, 12 Oct 2018 12:46:09 +0200 Subject: [PATCH 31/32] adding instructions --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 75ced5808..9224c2b34 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,24 @@ +This is a fork of the Bitcoin.com wallet to add additional features. +Features included: + + - Zero fee transactions (only works for Bitcoin Cash). You will be asked for, if you want to send a transaction as zero fee on the confirmation page. + + ## Zero fee transactions: + Because most network nodes on the Bitcoin Cash network don't relay zero fee txs, you will experience some strange issues, but don't worry: for me personally the Bitcoin.com pool has included all my zero fee transactions, but please beware that the receiver probably won't see your tx before it has been confirmed and please do also keep in mind, that the transactions coming after it won't confirm or be seen before the zero fee one has been confirmed. + + If you do already have a Bitcoin.com wallet, you need to create a new one to use this feature or change the wallet URL to: https://bws.freepages.dk/bws/api + + ## Disclaimer + + Please beware this is my personal experimental project. You are more than welcome to play with it, but I don't take any responsibility of loss of funds due to errors in the code, so please make sure you made a backup before running this software. + + ## Builds + + You can build the software yourself using the instructions below or use prebuilt binaries which can be found here (currently Windows and Linux only): https://ipfs.io/ipfs/QmR1DaS3QsDS48SzAWKUWFfmtMfJc4tgMtkSk3JFmuzewe + + + + The Bitcoin.com wallet is a fork of the Copay Wallet (https://github.com/bitpay/copay). The Bitcoin.com wallet is a secure bitcoin wallet platform for both desktop and mobile devices. It uses [Bitcore Wallet Service](https://github.com/Bitcoin-com/bitcore-wallet-service) (our fork of the [Bitpay Bitcore Wallet Service](https://github.com/bitpay/bitcore-wallet-service)) (BWS) for peer synchronization and network interfacing. From 06f1f4df91b95133c2d8a9cf9c43a3285b84b59b Mon Sep 17 00:00:00 2001 From: A R Hansen Date: Fri, 12 Oct 2018 12:48:15 +0200 Subject: [PATCH 32/32] - ll - --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9224c2b34..ae2788217 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Features included: You can build the software yourself using the instructions below or use prebuilt binaries which can be found here (currently Windows and Linux only): https://ipfs.io/ipfs/QmR1DaS3QsDS48SzAWKUWFfmtMfJc4tgMtkSk3JFmuzewe - + ## The Bitcoin.com wallet is a fork of the Copay Wallet (https://github.com/bitpay/copay).