diff --git a/angular-bitcore-wallet-client/index.js b/angular-bitcore-wallet-client/index.js index 0765b6df0..84d851ce0 100644 --- a/angular-bitcore-wallet-client/index.js +++ b/angular-bitcore-wallet-client/index.js @@ -13,6 +13,10 @@ bwcModule.provider("bwcService", function() { return Client.Bitcore; }; + service.getBitcoreCash = function() { + return Client.BitcoreCash; + }; + service.getErrors = function() { return Client.errors; }; diff --git a/app-template/package-template.json b/app-template/package-template.json index cd74ad7e6..b323223c9 100644 --- a/app-template/package-template.json +++ b/app-template/package-template.json @@ -56,7 +56,7 @@ "bezier-easing": "^2.0.3", "bhttp": "1.2.1", "bitauth": "^0.2.1", - "bitcore-wallet-client": "6.0.0", + "bitcore-wallet-client": "6.2.0", "bower": "^1.7.9", "cordova-android": "5.1.1", "cordova-custom-config": "^3.0.5", @@ -107,7 +107,7 @@ "run:android": "cordova run android --device", "run:android-release": "cordova run android --device --release", "log:android": "adb logcat | grep chromium", - "sign:android": "rm -f platforms/android/build/outputs/apk/android-release-signed-aligned.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../copay.keystore -signedjar platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-unsigned.apk copay_play && ../android-sdk-macosx/build-tools/25.0.3/zipalign -v 4 platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-signed-aligned.apk", + "sign:android": "rm -f platforms/android/build/outputs/apk/android-release-signed-aligned.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../copay.keystore -signedjar platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-unsigned.apk copay_play && $ANDROID_HOME/build-tools/26.0.1/zipalign -v 4 platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-signed-aligned.apk", "apply:copay": "npm i fs-extra && cd app-template && node apply.js copay && npm i && cordova prepare", "apply:bitpay": "npm i fs-extra && cd app-template && node apply.js bitpay && npm i && cordova prepare", "test": "echo \"no package tests configured\"", diff --git a/src/js/controllers/advancedSettings.js b/src/js/controllers/advancedSettings.js index 94f9a2553..cd5604395 100644 --- a/src/js/controllers/advancedSettings.js +++ b/src/js/controllers/advancedSettings.js @@ -14,9 +14,6 @@ angular.module('copayApp.controllers').controller('advancedSettingsController', $scope.hideNextSteps = { value: config.hideNextSteps.enabled }; - $scope.cashSupport = { - value: config.cashSupport.enabled - }; }; @@ -31,19 +28,6 @@ angular.module('copayApp.controllers').controller('advancedSettingsController', }); }; - - $scope.cashSupportChange = function() { - var opts = { - cashSupport: { - enabled: $scope.cashSupport.value - } - }; - configService.set(opts, function(err) { - if (err) $log.debug(err); - }); - }; - - $scope.nextStepsChange = function() { var opts = { hideNextSteps: { @@ -66,16 +50,6 @@ angular.module('copayApp.controllers').controller('advancedSettingsController', }); }; - $scope.openBitcoinCashWeb = function() { - var url = 'https://www.bitcoincash.org/'; - var optIn = true; - var title = null; - var message = gettextCatalog.getString('Open bitcoincash.org?'); - var okText = gettextCatalog.getString('Open'); - var cancelText = gettextCatalog.getString('Go Back'); - externalLinkService.open(url, optIn, title, message, okText, cancelText); - }; - $scope.$on("$ionicView.beforeEnter", function(event, data) { $scope.isWindowsPhoneApp = platformInfo.isCordova && platformInfo.isWP; updateConfig(); diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index c1b105068..d96483aab 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -309,7 +309,11 @@ angular.module('copayApp.controllers').controller('amountController', function($ if (a) { $scope.alternativeAmount = txFormatService.formatAmount(a * unitToSatoshi, true); } else { - $scope.alternativeAmount = 'N/A'; //TODO + if (result) { + $scope.alternativeAmount = 'N/A'; + } else { + $scope.alternativeAmount = null; + } $scope.allowSend = false; } } else { @@ -369,7 +373,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ id: _id, amount: $scope.useSendMax ? null : _amount, currency: unit.id.toUpperCase(), - coin: $scope.useSendMax ? null : coin, + coin: coin, useSendMax: $scope.useSendMax }); } else { @@ -388,7 +392,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ toName: $scope.toName, toEmail: $scope.toEmail, toColor: $scope.toColor, - coin: $scope.useSendMax ? null : coin, + coin: coin, useSendMax: $scope.useSendMax }); } diff --git a/src/js/controllers/cashScan.js b/src/js/controllers/cashScan.js new file mode 100644 index 000000000..f00ed3ebf --- /dev/null +++ b/src/js/controllers/cashScan.js @@ -0,0 +1,176 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('cashScanController', + function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, $ionicHistory, $window, gettextCatalog, lodash, popupService, ongoingProcess, profileService, walletService, configService, $log, txFormatService, bwcError, pushNotificationsService, bwcService) { + var wallet; + var errors = bwcService.getErrors(); + $scope.error = null; + $scope.walletDisabled = '#667'; + + $scope.$on("$ionicView.beforeEnter", function(event, data) { + updateAllWallets(); + }); + + var goHome = function() { + $ionicHistory.nextViewOptions({ + disableAnimate: true, + historyRoot: true + }); + $ionicHistory.clearHistory(); + $state.go('tabs.settings').then(function() { + $state.transitionTo('tabs.home'); + }); + } + + var updateAllWallets = function() { + var walletsBTC = profileService.getWallets({ + coin: 'btc', + onlyComplete: true, + network: 'livenet' + }); + + if (lodash.isEmpty(walletsBTC)) { + goHome(); + return; + } + + // Filter out already duplicated wallets + var walletsBCH = profileService.getWallets({ + coin: 'bch', + network: 'livenet' + }); + var xPubKeyIndex = lodash.indexBy(walletsBCH, "credentials.xPubKey"); + + walletsBTC = lodash.filter(walletsBTC, function(w) { + return !xPubKeyIndex[w.credentials.xPubKey]; + }); + + // Filter out non BIP44 wallets + var wallets = lodash.filter(walletsBTC, function(w) { + return w.credentials.derivationStrategy == 'BIP44' + }); + + $scope.wallets = wallets; + $scope.nonBIP44Wallets = lodash.filter(walletsBTC, function(w) { + return w.credentials.derivationStrategy != 'BIP44'; + }); + + var i = wallets.length; + var j = 0; + lodash.each(wallets, function(wallet) { + walletService.getBalance(wallet, { + coin: 'bch' + }, function(err, balance) { + if (err) { + wallet.error = (err === 'WALLET_NOT_REGISTERED') ? gettextCatalog.getString('Wallet not registered') : bwcError.msg(err); + $log.error(err); + return; + } + + wallet.error = null; + wallet.bchBalance = txFormatService.formatAmountStr('bch', balance.availableAmount); + if (++j == i) { + //Done + $timeout(function() { + $rootScope.$apply(); + }, 10); + } + }); + }); + }; + + $scope.duplicate = function(wallet) { + $scope.error = null; + $log.debug('Duplicating wallet for BCH:' + wallet.id + ':' + wallet.name); + + var opts = {}; + opts.name = wallet.name + '[BCH]'; + opts.m = wallet.m; + opts.n = wallet.n; + opts.myName = wallet.credentials.copayerName; + opts.networkName = wallet.network; + opts.coin = 'bch'; + opts.walletPrivKey = wallet.credentials.walletPrivKey; + opts.compliantDerivation = wallet.credentials.compliantDerivation; + + + function setErr(err, cb) { + + if (!cb) cb = function() {}; + + $scope.error = bwcError.cb(err, gettextCatalog.getString('Could not duplicate'), function() { + return cb(err); + }); + $timeout(function() { + $rootScope.$apply(); + }, 10); + } + + function importOrCreate(cb) { + walletService.getStatus(wallet, {}, function(err, status) { + if (err) return cb(err); + + opts.singleAddress = status.wallet.singleAddress; + + // first try to import + profileService.importExtendedPrivateKey(opts.extendedPrivateKey, opts, function(err, newWallet) { + if (err && !(err instanceof errors.NOT_AUTHORIZED)) { + return setErr(err, cb); + } + if (err) { + // create and store a wallet + return profileService.createWallet(opts, function(err, newWallet) { + if (err) return setErr(err, cb); + return cb(null, newWallet, true); + }); + } + return cb(null, newWallet); + }); + }); + }; + + // Multisig wallets? add Copayers + function addCopayers(newWallet, isNew, cb) { + if (!isNew) return cb(); + if (wallet.n == 1) return cb(); + + $log.info('Adding copayers for BCH wallet config:' + wallet.m + '-' + wallet.n); + + walletService.copyCopayers(wallet, newWallet, function(err) { + if (err) return setErr(err, cb); + + return cb(); + }); + }; + + walletService.getKeys(wallet, function(err, keys) { + if (err) { + $scope.error = err; + return $timeout(function() { + $rootScope.$apply(); + }, 10); + } + opts.extendedPrivateKey = keys.xPrivKey; + ongoingProcess.set('duplicatingWallet', true); + importOrCreate(function(err, newWallet, isNew) { + if (err) { + ongoingProcess.set('duplicatingWallet', false); + return; + } + walletService.updateRemotePreferences(newWallet); + pushNotificationsService.updateSubscription(newWallet); + + addCopayers(newWallet, isNew, function(err) { + ongoingProcess.set('duplicatingWallet', false); + if (err) + return setErr(err); + + if (isNew) + walletService.startScan(newWallet, function() {}); + + goHome(); + }); + }); + }); + } + }); diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index b85105a06..60cbc0244 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, $stateParams, $window, $state, $log, profileService, bitcore, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, bwcError, txConfirmNotification) { +angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, $stateParams, $window, $state, $log, profileService, bitcore, bitcoreCash, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, bwcError, txConfirmNotification) { var countDown = null; var CONFIRM_LIMIT_USD = 20; @@ -58,9 +58,10 @@ angular.module('copayApp.controllers').controller('confirmController', function( }); }; - function setNoWallet(msg) { + function setNoWallet(msg, criticalError) { $scope.wallet = null; $scope.noWalletMessage = msg; + $scope.criticalError = criticalError; $log.warn('Not ready to make the payment:' + msg); $timeout(function() { $scope.$apply(); @@ -81,7 +82,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( }); if (!$scope.wallets || !$scope.wallets.length) { - setNoWallet(gettextCatalog.getString('No wallets available')); + setNoWallet(gettextCatalog.getString('No wallets available'), true); return cb(); } @@ -110,7 +111,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( return cb('Could not update any wallet'); if (lodash.isEmpty(filteredWallets)) { - setNoWallet(gettextCatalog.getString('Insufficient funds')); + setNoWallet(gettextCatalog.getString('Insufficient funds'), true); } $scope.wallets = lodash.clone(filteredWallets); return cb(); @@ -120,6 +121,9 @@ angular.module('copayApp.controllers').controller('confirmController', function( }; // Setup $scope + + + var B = data.stateParams.coin == 'bch' ? bitcoreCash : bitcore; // Grab stateParams tx = { @@ -137,7 +141,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( toName: data.stateParams.toName, toEmail: data.stateParams.toEmail, toColor: data.stateParams.toColor, - network: (new bitcore.Address(data.stateParams.toAddress)).network.name, + network: (new B.Address(data.stateParams.toAddress)).network.name, coin: data.stateParams.coin, txp: {}, }; diff --git a/src/js/controllers/create.js b/src/js/controllers/create.js index 6bb2bd78a..d47dba87f 100644 --- a/src/js/controllers/create.js +++ b/src/js/controllers/create.js @@ -30,7 +30,7 @@ angular.module('copayApp.controllers').controller('createController', $scope.formData.derivationPath = derivationPathHelper.default; $scope.formData.coin = 'btc'; - if (config.cashSupport.enabled) $scope.enableCash = true; + if (config.cashSupport) $scope.enableCash = true; $scope.setTotalCopayers(tc); updateRCSelect(tc); diff --git a/src/js/controllers/preferencesCash.js b/src/js/controllers/preferencesCash.js new file mode 100644 index 000000000..eb81e0394 --- /dev/null +++ b/src/js/controllers/preferencesCash.js @@ -0,0 +1,42 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('preferencesCashController', function($scope, $log, $timeout, appConfigService, configService, gettextCatalog, externalLinkService) { + var updateConfig = function() { + + var config = configService.getSync(); + $scope.appName = appConfigService.nameCase; + + $scope.cashSupport = { + value: config.cashSupport + }; + + $timeout(function() { + $scope.$apply(); + }); + }; + + $scope.cashSupportChange = function() { + var opts = { + cashSupport: $scope.cashSupport.value + }; + configService.set(opts, function(err) { + if (err) $log.debug(err); + }); + }; + + + $scope.openBitcoinCashWeb = function() { + var url = 'https://www.bitcoincash.org/'; + var optIn = true; + var title = null; + var message = gettextCatalog.getString('Open bitcoincash.org?'); + var okText = gettextCatalog.getString('Open'); + var cancelText = gettextCatalog.getString('Go Back'); + externalLinkService.open(url, optIn, title, message, okText, cancelText); + }; + + + $scope.$on("$ionicView.beforeEnter", function(event, data) { + updateConfig(); + }); +}); diff --git a/src/js/controllers/tab-settings.js b/src/js/controllers/tab-settings.js index f806b0f84..75cce161b 100644 --- a/src/js/controllers/tab-settings.js +++ b/src/js/controllers/tab-settings.js @@ -25,6 +25,11 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct }, 10); }); + $scope.cashSupport = { + value: config.cashSupport + }; + + // TODO move this to a generic service bitpayCardService.getCards(function(err, cards) { if (err) $log.error(err); @@ -62,6 +67,8 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct }); }); + + $scope.$on("$ionicView.enter", function(event, data) { updateConfig(); }); diff --git a/src/js/routes.js b/src/js/routes.js index 0fe72181d..f3de8ba00 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -374,6 +374,16 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr * */ + .state('tabs.preferencesCash', { + url: '/preferencesCash', + views: { + 'tab-settings@tabs': { + controller: 'preferencesCashController', + templateUrl: 'views/preferencesCash.html' + } + } + }) + .state('tabs.notifications', { url: '/notifications', views: { @@ -465,6 +475,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) + /* * * Wallet preferences @@ -580,6 +591,16 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) + .state('tabs.preferencesCash.scan', { + url: '/cashScan', + views: { + 'tab-settings@tabs': { + controller: 'cashScanController', + templateUrl: 'views/cashScan.html' + } + } + }) + /* * * Addressbook diff --git a/src/js/services/bitcoreCash.js b/src/js/services/bitcoreCash.js new file mode 100644 index 000000000..484c2a6e9 --- /dev/null +++ b/src/js/services/bitcoreCash.js @@ -0,0 +1,6 @@ +'use strict'; +angular.module('copayApp.services') + .factory('bitcoreCash', function bitcoreFactory(bwcService) { + var bitcoreCash = bwcService.getBitcoreCash(); + return bitcoreCash; + }); diff --git a/src/js/services/incomingData.js b/src/js/services/incomingData.js index a805fac29..ae7d97609 100644 --- a/src/js/services/incomingData.js +++ b/src/js/services/incomingData.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.services').factory('incomingData', function($log, $state, $timeout, $ionicHistory, bitcore, $rootScope, payproService, scannerService, appConfigService, popupService, gettextCatalog) { +angular.module('copayApp.services').factory('incomingData', function($log, $state, $timeout, $ionicHistory, bitcore, bitcoreCash, $rootScope, payproService, scannerService, appConfigService, popupService, gettextCatalog) { var root = {}; @@ -84,14 +84,9 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat data = sanitizeUri(data); - // Bitcoin or Bitcoin Cash URL - if ((/^bitcoin(cash)?:/).exec(data)) { - var coin = 'btc'; - if ((/^bitcoincash:/).exec(data)) { - coin = 'bch'; - data = data.replace(/bitcoincash:/, 'bitcoin:'); - } - if (bitcore.URI.isValid(data)) { + // Bitcoin URL + if (bitcore.URI.isValid(data)) { + var coin = 'btc'; var parsed = new bitcore.URI(data); var addr = parsed.address ? parsed.address.toString() : ''; @@ -110,12 +105,76 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat goSend(addr, amount, message, coin); } return true; + // Cash URI + } else if (bitcoreCash.URI.isValid(data)) { + var coin = 'bch'; + var parsed = new bitcoreCash.URI(data); - } else { - $log.error('Invalid Bitcoin URL'); - return false; - } + var addr = parsed.address ? parsed.address.toString() : ''; + var message = parsed.message; + var amount = parsed.amount ? parsed.amount : ''; + + // paypro not yet supported on cash + if (parsed.r) { + payproService.getPayProDetails(parsed.r, function(err, details) { + if (err) { + if (addr && amount) + goSend(addr, amount, message, coin); + else + popupService.showAlert(gettextCatalog.getString('Error'), err); + } + handlePayPro(details, coin); + }); + } else { + goSend(addr, amount, message, coin); + } + return true; + + // Cash URI with bitcoin core address version number? + } else if (bitcore.URI.isValid(data.replace(/^bitcoincash:/,'bitcoin:'))) { + $log.debug('Handling bitcoincash URI with legacy address'); + var coin = 'bch'; + var parsed = new bitcore.URI(data.replace(/^bitcoincash:/,'bitcoin:')); + + var oldAddr = parsed.address ? parsed.address.toString() : ''; + if (!oldAddr) return false; + + var addr = ''; + + var a = bitcore.Address(oldAddr).toObject(); + addr = bitcoreCash.Address.fromObject(a).toString(); + + // Translate address + $log.debug('address transalated to:' + addr); + popupService.showConfirm( + gettextCatalog.getString('Bitcoin cash Payment'), + gettextCatalog.getString('Payment address was translated to new Bitcoin Cash address format: ' + addr), + gettextCatalog.getString('OK'), + gettextCatalog.getString('Cancel'), + function(ret) { + if (!ret) return false; + + var message = parsed.message; + var amount = parsed.amount ? parsed.amount : ''; + + // paypro not yet supported on cash + if (parsed.r) { + payproService.getPayProDetails(parsed.r, function(err, details) { + if (err) { + if (addr && amount) + goSend(addr, amount, message, coin); + else + popupService.showAlert(gettextCatalog.getString('Error'), err); + } + handlePayPro(details, coin); + }); + } else { + goSend(addr, amount, message, coin); + } + } + ); + return true; // Plain URL } else if (/^https?:\/\//.test(data)) { @@ -140,6 +199,16 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat } else { goToAmountPage(data); } + } else if (bitcoreCash.Address.isValid(data, 'livenet')) { + if ($state.includes('tabs.scan')) { + root.showMenu({ + data: data, + type: 'bitcoinAddress', + coin: 'bch', + }); + } else { + goToAmountPage(data, 'bch'); + } } else if (data && data.indexOf(appConfigService.name + '://glidera') === 0) { var code = getParameterByName('code', data); $ionicHistory.nextViewOptions({ @@ -249,29 +318,29 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat }); } } - return false; - }; - function goToAmountPage(toAddress) { + function goToAmountPage(toAddress, coin) { $state.go('tabs.send', {}, { 'reload': true, 'notify': $state.current.name == 'tabs.send' ? false : true }); $timeout(function() { $state.transitionTo('tabs.send.amount', { - toAddress: toAddress + toAddress: toAddress, + coin: coin, }); }, 100); } - function handlePayPro(payProDetails) { + function handlePayPro(payProDetails, coin) { var stateParams = { toAmount: payProDetails.amount, toAddress: payProDetails.toAddress, description: payProDetails.memo, - paypro: payProDetails + paypro: payProDetails, + coin: coin, }; scannerService.pausePreview(); $state.go('tabs.send', {}, { diff --git a/src/js/services/onGoingProcess.js b/src/js/services/onGoingProcess.js index e0f82fc40..94d746202 100644 --- a/src/js/services/onGoingProcess.js +++ b/src/js/services/onGoingProcess.js @@ -45,7 +45,8 @@ angular.module('copayApp.services').factory('ongoingProcess', function($log, $ti 'cancelingGiftCard': 'Canceling Gift Card...', 'creatingGiftCard': 'Creating Gift Card...', 'buyingGiftCard': 'Buying Gift Card...', - 'topup': gettext('Top up in progress...') + 'topup': gettext('Top up in progress...'), + 'duplicatingWallet': gettext('Duplicating wallet...'), }; root.clear = function() { diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js index 36a7d938e..109fee8b2 100644 --- a/src/js/services/profileService.js +++ b/src/js/services/profileService.js @@ -322,6 +322,7 @@ angular.module('copayApp.services') var walletClient = bwcService.getClient(null, opts); var network = opts.networkName || 'livenet'; +console.log('[profileService.js.324]'); //TODO if (opts.mnemonic) { try { opts.mnemonic = root._normalizeMnemonic(opts.mnemonic); @@ -339,7 +340,12 @@ angular.module('copayApp.services') } } else if (opts.extendedPrivateKey) { try { - walletClient.seedFromExtendedPrivateKey(opts.extendedPrivateKey); + walletClient.seedFromExtendedPrivateKey(opts.extendedPrivateKey, { + network: network, + account: opts.account || 0, + derivationStrategy: opts.derivationStrategy || 'BIP44', + coin: opts.coin, + }); } catch (ex) { $log.warn(ex); return cb(gettextCatalog.getString('Could not create using the specified extended private key')); @@ -389,6 +395,7 @@ angular.module('copayApp.services') $log.debug('Creating Wallet:', opts); $timeout(function() { seedWallet(opts, function(err, walletClient) { +console.log('[profileService.js.395:walletClient:]',walletClient); //TODO if (err) return cb(err); var name = opts.name || gettextCatalog.getString('Personal Wallet'); @@ -400,6 +407,7 @@ angular.module('copayApp.services') walletPrivKey: opts.walletPrivKey, coin: opts.coin }, function(err, secret) { +console.log('[profileService.js.407:err:]',err); //TODO if (err) return bwcError.cb(err, gettextCatalog.getString('Error creating wallet'), cb); return cb(null, walletClient, secret); }); diff --git a/src/js/services/walletService.js b/src/js/services/walletService.js index 3768cecaa..15be5ecc0 100644 --- a/src/js/services/walletService.js +++ b/src/js/services/walletService.js @@ -259,6 +259,8 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim }; function cacheStatus(status) { + if (status.wallet && status.wallet.scanStatus == 'running') return; + wallet.cachedStatus = status ||  {}; var cache = wallet.cachedStatus; cache.statusUpdatedOn = Date.now(); @@ -303,6 +305,8 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim cacheStatus(status); + wallet.scanning = status.wallet && status.wallet.scanStatus == 'running'; + return cb(null, status); }); }; @@ -816,13 +820,10 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim $log.debug('Scanning wallet ' + wallet.id); if (!wallet.isComplete()) return; - wallet.updating = true; - ongoingProcess.set('scanning', true); + wallet.scanning = true; wallet.startScan({ includeCopayerBranches: true, }, function(err) { - wallet.updating = false; - ongoingProcess.set('scanning', false); return cb(err); }); }; @@ -931,15 +932,17 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim // Approx utxo amount, from which the uxto is economically redeemable root.getLowAmount = function(wallet, feeLevels, nbOutputs) { - var minFee = root.getMinFee(wallet,feeLevels, nbOutputs); - return parseInt( minFee / LOW_AMOUNT_RATIO); + var minFee = root.getMinFee(wallet, feeLevels, nbOutputs); + return parseInt(minFee / LOW_AMOUNT_RATIO); }; root.getLowUtxos = function(wallet, levels, cb) { - wallet.getUtxos({coin: wallet.coin}, function(err, resp) { + wallet.getUtxos({ + coin: wallet.coin + }, function(err, resp) { if (err || !resp || !resp.length) return cb(); var minFee = root.getMinFee(wallet, levels, resp.length); @@ -955,7 +958,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim var totalLow = lodash.sum(lowUtxos, 'satoshis'); return cb(err, { - allUtxos: resp || [], + allUtxos: resp || [], lowUtxos: lowUtxos || [], warning: minFee / balance > TOTAL_LOW_WARNING_RATIO, minFee: minFee, @@ -1237,5 +1240,33 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim else return 'bitcoin'; } + + root.copyCopayers = function(wallet, newWallet, cb) { + var c = wallet.credentials; + + var walletPrivKey = bitcore.PrivateKey.fromString(c.walletPrivKey); + + var copayer = 1, + i = 0, + l = c.publicKeyRing.length; + var mainErr = null; + + lodash.each(c.publicKeyRing, function(item) { + var name = item.copayerName || ('copayer ' + copayer++); + newWallet._doJoinWallet(newWallet.credentials.walletId, walletPrivKey, item.xPubKey, item.requestPubKey, name, { + coin: newWallet.credentials.coin, + }, function(err) { + //Ignore error is copayer already in wallet + if (err && !(err instanceof errors.COPAYER_IN_WALLET)) { + mainErr = err; + } + + if (++i == l) { + return cb(mainErr); + } + }); + }); + }; + return root; }); diff --git a/src/sass/views/cashScan.scss b/src/sass/views/cashScan.scss new file mode 100644 index 000000000..5ae76ef53 --- /dev/null +++ b/src/sass/views/cashScan.scss @@ -0,0 +1,39 @@ +#cash-scan { + .comment { + color: #667; + font-size: 0.9em; + } + + .item { + color: $v-dark-gray; + padding-top: 1.3rem; + padding-bottom: 1.3rem; + } + + .heading { + font-size: 17px; + color: $v-dark-gray; + margin: 1rem 0; + padding-top: 5px; + padding-bottom: 5px; + border: none; + } + + .text-disabled { + color: $v-light-gray; + } + + .supported { + display: flex; + + .wallet-content { + padding-left: 7px; + } + } + + .duplicate-button { + position: absolute; + right: 15px; + padding-top: .5rem; + } +} diff --git a/src/sass/views/views.scss b/src/sass/views/views.scss index 722c97e1d..37754970f 100644 --- a/src/sass/views/views.scss +++ b/src/sass/views/views.scss @@ -50,3 +50,4 @@ @import "includes/pin"; @import "includes/logOptions"; @import "includes/checkBar"; +@import "cashScan"; diff --git a/www/views/advancedSettings.html b/www/views/advancedSettings.html index 58031d981..253c35c8f 100644 --- a/www/views/advancedSettings.html +++ b/www/views/advancedSettings.html @@ -8,16 +8,6 @@
- - Support Bitcoin Cash - -
- Enable Bitcoin Cash wallet creation and operation within the App. - Learn more -
- - - Use Unconfirmed Funds diff --git a/www/views/cashScan.html b/www/views/cashScan.html new file mode 100644 index 000000000..2e62297ba --- /dev/null +++ b/www/views/cashScan.html @@ -0,0 +1,60 @@ + + + + Bitcoin Cash (BCH) Balances + + + + + + + +
+
+ No wallets eligible for Bitcoin Cash support +
+ +
+ {{error}} +
+ +
+ BTC Wallets +
+ +
+ + + +
+
{{wallet.name || wallet.id}}
+
{{wallet.bchBalance || ('Checking...' | translate)}}
+
{{wallet.m}}-of-{{wallet.n}}
+
+ +
+ +
+
+ +
+
+ +
+ Non eligible BTC wallets +
+ +
+ + + + {{wallet.name || wallet.id}} +
+ +
+ Some of you wallets are not eligible for Bitcon Cash support because there where created before Copay v1.2. Please use our recovery tool to access your Bitcoin Cash balance for those wallets +
+
+
+ +
diff --git a/www/views/confirm.html b/www/views/confirm.html index 85f18e130..da6aca0f8 100644 --- a/www/views/confirm.html +++ b/www/views/confirm.html @@ -9,7 +9,7 @@
-
+
Sending @@ -27,7 +27,7 @@ Expired
-
+
To diff --git a/www/views/preferencesCash.html b/www/views/preferencesCash.html new file mode 100644 index 000000000..b03acec3c --- /dev/null +++ b/www/views/preferencesCash.html @@ -0,0 +1,25 @@ + + + {{'Bitcoin Cash Support' | translate}} + + + + + +
+ + Support Bitcoin Cash + +
+ +
+
+ Enable Bitcoin Cash wallet creation and operation within the App. + Learn more +
+
+ +
+
diff --git a/www/views/preferencesInformation.html b/www/views/preferencesInformation.html index 728edb40b..4fe0012f2 100644 --- a/www/views/preferencesInformation.html +++ b/www/views/preferencesInformation.html @@ -13,6 +13,12 @@ {{walletName}}
+
+ Coin + + {{wallet.coin}} + +
Wallet Id diff --git a/www/views/tab-home.html b/www/views/tab-home.html index 1a30620da..be67c7440 100644 --- a/www/views/tab-home.html +++ b/www/views/tab-home.html @@ -91,9 +91,10 @@ Incomplete - {{wallet.status.totalBalanceStr ? wallet.status.totalBalanceStr : ( wallet.cachedBalance ? wallet.cachedBalance + (wallet.cachedBalanceUpdatedOn ? ' · ' + ( wallet.cachedBalanceUpdatedOn * 1000 | amTimeAgo) : '') : '' ) }} + {{wallet.status.totalBalanceStr ? wallet.status.totalBalanceStr : ( wallet.cachedBalance ? wallet.cachedBalance + (wallet.cachedBalanceUpdatedOn ? ' · ' + ( wallet.cachedBalanceUpdatedOn * 1000 | amTimeAgo) : '') : '' ) }} + Scanning funds... - [Balance Hidden] + [Balance Hidden] {{wallet.m}}-of-{{wallet.n}} diff --git a/www/views/tab-settings.html b/www/views/tab-settings.html index f5768dfd2..395c79891 100644 --- a/www/views/tab-settings.html +++ b/www/views/tab-settings.html @@ -39,6 +39,20 @@
{{'Preferences' | translate}}
+ + + + + {{'Bitcoin Cash Support' | translate}} + + Enabled + Disabled + + + + + + diff --git a/www/views/walletDetails.html b/www/views/walletDetails.html index 2a094e233..7136db6a7 100644 --- a/www/views/walletDetails.html +++ b/www/views/walletDetails.html @@ -31,7 +31,7 @@
@@ -46,7 +46,7 @@
[Balance Hidden]
@@ -54,7 +54,18 @@
-
+ +
+ [Scanning Funds] +
+ Please wait +
+
+ + +