diff --git a/app-template/package-template.json b/app-template/package-template.json index 849d1e6a3..cd74ad7e6 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": "5.3.0", + "bitcore-wallet-client": "6.0.0", "bower": "^1.7.9", "cordova-android": "5.1.1", "cordova-custom-config": "^3.0.5", diff --git a/src/js/controllers/addresses.js b/src/js/controllers/addresses.js index 47ec668ce..c83279f84 100644 --- a/src/js/controllers/addresses.js +++ b/src/js/controllers/addresses.js @@ -1,13 +1,8 @@ 'use strict'; -angular.module('copayApp.controllers').controller('addressesController', function($scope, $log, $stateParams, $state, $timeout, $ionicHistory, $ionicScrollDelegate, configService, popupService, gettextCatalog, ongoingProcess, lodash, profileService, walletService, bwcError, platformInfo, appConfigService, txFormatService, feeService) { +angular.module('copayApp.controllers').controller('addressesController', function($scope, $log, $stateParams, $state, $timeout, $ionicHistory, $ionicScrollDelegate, popupService, gettextCatalog, ongoingProcess, lodash, profileService, walletService, bwcError, platformInfo, appConfigService, txFormatService, feeService) { var UNUSED_ADDRESS_LIMIT = 5; var BALANCE_ADDRESS_LIMIT = 5; - var config = configService.getSync().wallet.settings; - var unitName = config.unitName; - var unitToSatoshi = config.unitToSatoshi; - var satToUnit = 1 / unitToSatoshi; - var unitDecimals = config.unitDecimals; var withBalance, cachedWallet; $scope.isCordova = platformInfo.isCordova; @@ -55,7 +50,7 @@ angular.module('copayApp.controllers').controller('addressesController', functio $scope.latestWithBalance = lodash.slice(withBalance, 0, BALANCE_ADDRESS_LIMIT); lodash.each(withBalance, function(a) { - a.balanceStr = txFormatService.formatAmount(a.amount); + a.balanceStr = txFormatService.formatAmountStr($scope.wallet.coin, a.amount); }); $scope.viewAll = { @@ -75,11 +70,11 @@ angular.module('copayApp.controllers').controller('addressesController', functio - feeService.getFeeLevels(function(err, levels){ + feeService.getFeeLevels($scope.wallet.coin, function(err, levels){ walletService.getLowUtxos($scope.wallet, levels, function(err, resp) { if (err) return; - if (resp.allUtxos && resp.allUtxos.length) { + if (resp && resp.allUtxos && resp.allUtxos.length) { var allSum = lodash.sum(resp.allUtxos || 0, 'satoshis'); @@ -88,9 +83,9 @@ angular.module('copayApp.controllers').controller('addressesController', functio $scope.lowWarning = resp.warning; $scope.lowUtxosNb = resp.lowUtxos.length; $scope.allUtxosNb = resp.allUtxos.length; - $scope.lowUtxosSum = txFormatService.formatAmountStr(lodash.sum(resp.lowUtxos || 0, 'satoshis')); - $scope.allUtxosSum = txFormatService.formatAmountStr(allSum); - $scope.minFee = txFormatService.formatAmountStr(resp.minFee || 0); + $scope.lowUtxosSum = txFormatService.formatAmountStr($scope.wallet.coin, lodash.sum(resp.lowUtxos || 0, 'satoshis')); + $scope.allUtxosSum = txFormatService.formatAmountStr($scope.wallet.coin, allSum); + $scope.minFee = txFormatService.formatAmountStr($scope.wallet.coin, resp.minFee || 0); $scope.minFeePer = per.toFixed(2) + '%'; diff --git a/src/js/controllers/advancedSettings.js b/src/js/controllers/advancedSettings.js index cd065200c..b9bf65449 100644 --- a/src/js/controllers/advancedSettings.js +++ b/src/js/controllers/advancedSettings.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('advancedSettingsController', function($scope, $log, configService, platformInfo) { +angular.module('copayApp.controllers').controller('advancedSettingsController', function($scope, $log, configService, platformInfo, externalLinkService, gettextCatalog) { var updateConfig = function() { var config = configService.getSync(); @@ -14,6 +14,10 @@ angular.module('copayApp.controllers').controller('advancedSettingsController', $scope.hideNextSteps = { value: config.hideNextSteps.enabled }; + $scope.cashSupport = { + value: config.cashSupport.enabled + }; + }; $scope.spendUnconfirmedChange = function() { @@ -27,6 +31,19 @@ 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: { @@ -49,6 +66,16 @@ angular.module('copayApp.controllers').controller('advancedSettingsController', }); }; + $scope.learnMore = 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 99912b99f..c759f0dfb 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -9,6 +9,14 @@ angular.module('copayApp.controllers').controller('amountController', function($ var SMALL_FONT_SIZE_LIMIT = 10; var LENGTH_EXPRESSION_LIMIT = 19; var isNW = platformInfo.isNW; + + var unitIndex = 0; + var altUnitIndex = 0; + var availableUnits = []; + var fiatCode; + + var fixedUnit; + $scope.isChromeApp = platformInfo.isChromeApp; $scope.$on('$ionicView.leave', function() { @@ -16,11 +24,82 @@ angular.module('copayApp.controllers').controller('amountController', function($ }); $scope.$on("$ionicView.beforeEnter", function(event, data) { + + var config = configService.getSync().wallet.settings; + + function setAvailableUnits() { + + availableUnits = [{ + name: 'Bitcoin', + id: 'btc', + shortName: 'BTC', + }]; + + + var anyCashWallet = true; // TODO!! + if (anyCashWallet) { + availableUnits.push({ + name: 'Bitcoin Cash', + id: 'bch', + shortName: 'BCH', + }); + }; + + unitIndex = 0; + + if (data.stateParams.coin) { + var coins = data.stateParams.coin.split(','); + var newAvailableUnits = []; + + lodash.each(coins, function(c) { + var coin = lodash.find(availableUnits, { + id: c + }); + if (!coin) { + $log.warn('Could not find desired coin:' + data.stateParams.coin) + } else { + newAvailableUnits.push(coin); + } + }); + + if (newAvailableUnits.length>0) { + availableUnits = newAvailableUnits; + } + } + + + // currency have preference + var fiat; + if (data.stateParams.currency) { + fiat = data.stateParams.currency; + altUnitIndex = unitIndex + unitIndex = availableUnits.length; + } else { + fiat = config.fiat || 'USD'; + altUnitIndex = availableUnits.length; + } + + availableUnits.push({ + name: fiat, // TODO + id: fiat, + shortName: fiat, + isFiat: true, + }); + + fiatCode = fiat; + + if (data.stateParams.fixedUnit) { + fixedUnit = true; + } + }; + // Go to... _id = data.stateParams.id; // Optional (BitPay Card ID or Wallet ID) $scope.nextStep = data.stateParams.nextStep; - $scope.currency = data.stateParams.currency; - $scope.forceCurrency = data.stateParams.forceCurrency; + + + setAvailableUnits(); + updateUnitUI(); $scope.showMenu = $ionicHistory.backView() && ($ionicHistory.backView().stateName == 'tabs.send' || $ionicHistory.backView().stateName == 'tabs.bitpayCard'); @@ -28,7 +107,6 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.toAddress = data.stateParams.toAddress; $scope.toName = data.stateParams.toName; $scope.toEmail = data.stateParams.toEmail; - $scope.showAlternativeAmount = !!$scope.nextStep; $scope.toColor = data.stateParams.toColor; $scope.showSendMax = false; @@ -61,14 +139,6 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.$apply(); }); }); - - var config = configService.getSync().wallet.settings; - $scope.unitName = config.unitName; - if (data.stateParams.currency) { - $scope.alternativeIsoCode = data.stateParams.currency; - } else { - $scope.alternativeIsoCode = config.alternativeIsoCode || 'USD'; - } $scope.specificAmount = $scope.specificAlternativeAmount = ''; $scope.isCordova = platformInfo.isCordova; unitToSatoshi = config.unitToSatoshi; @@ -114,16 +184,59 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.finish(); }; - $scope.toggleAlternative = function() { - if ($scope.forceCurrency) return; - $scope.showAlternativeAmount = !$scope.showAlternativeAmount; + $scope.toggleAlternative = function() { if ($scope.amount && isExpression($scope.amount)) { var amount = evaluate(format($scope.amount)); $scope.globalResult = '= ' + processResult(amount); } }; + function updateUnitUI() { + $scope.unit = availableUnits[unitIndex].shortName; + $scope.alternativeUnit = availableUnits[altUnitIndex].shortName; + + processAmount(); + $log.debug('Update unit coin @amount unit:' + $scope.unit + " alternativeUnit:" + $scope.alternativeUnit); + }; + + $scope.changeUnit = function() { + if (fixedUnit) return; + + unitIndex++; + if (unitIndex >= availableUnits.length) unitIndex = 0; + + + if (availableUnits[unitIndex].isFiat) { + // Always return to BTC... TODO? + altUnitIndex = 0; + } else { + altUnitIndex = lodash.findIndex(availableUnits, { + isFiat: true + }); + } + + updateUnitUI(); + }; + + + $scope.changeAlternativeUnit = function() { + + // Do nothing is fiat is not main unit + if (!availableUnits[unitIndex].isFiat) return; + + var nextCoin = lodash.findIndex(availableUnits, function(x) { + if (x.isFiat) return false; + if (x.id == availableUnits[altUnitIndex].id) return false; + return true; + }); + + if (nextCoin >= 0) { + altUnitIndex = nextCoin; + updateUnitUI(); + } + }; + function checkFontSize() { if ($scope.amount && $scope.amount.length >= SMALL_FONT_SIZE_LIMIT) $scope.smallFont = true; else $scope.smallFont = false; @@ -132,7 +245,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.pushDigit = function(digit) { if ($scope.amount && $scope.amount.length >= LENGTH_EXPRESSION_LIMIT) return; if ($scope.amount.indexOf('.') > -1 && digit == '.') return; - if ($scope.showAlternativeAmount && $scope.amount.indexOf('.') > -1 && $scope.amount[$scope.amount.indexOf('.') + 2]) return; + if (availableUnits[unitIndex].isFiat && $scope.amount.indexOf('.') > -1 && $scope.amount[$scope.amount.indexOf('.') + 2]) return; $scope.amount = ($scope.amount + digit).replace('..', '.'); checkFontSize(); @@ -169,7 +282,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ }; $scope.resetAmount = function() { - $scope.amount = $scope.alternativeResult = $scope.amountResult = $scope.globalResult = ''; + $scope.amount = $scope.alternativeAmount = $scope.globalResult = ''; $scope.allowSend = false; checkFontSize(); }; @@ -180,24 +293,28 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.allowSend = lodash.isNumber(result) && +result > 0; if (lodash.isNumber(result)) { $scope.globalResult = isExpression($scope.amount) ? '= ' + processResult(result) : ''; - $scope.amountResult = $filter('formatFiatAmount')(toFiat(result)); - $scope.alternativeResult = txFormatService.formatAmount(fromFiat(result) * unitToSatoshi, true); + + if (availableUnits[unitIndex].isFiat) { + $scope.alternativeAmount = txFormatService.formatAmount(fromFiat(result) * unitToSatoshi, true); + } else { + $scope.alternativeAmount = $filter('formatFiatAmount')(toFiat(result)); + } } }; function processResult(val) { - if ($scope.showAlternativeAmount) + if (availableUnits[unitIndex].isFiat) return $filter('formatFiatAmount')(val); else return txFormatService.formatAmount(val.toFixed(unitDecimals) * unitToSatoshi, true); }; function fromFiat(val) { - return parseFloat((rateService.fromFiat(val, $scope.alternativeIsoCode) * satToUnit).toFixed(unitDecimals)); + return parseFloat((rateService.fromFiat(val, fiatCode, availableUnits[altUnitIndex].id) * satToUnit).toFixed(unitDecimals)); }; function toFiat(val) { - return parseFloat((rateService.toFiat(val * unitToSatoshi, $scope.alternativeIsoCode)).toFixed(2)); + return parseFloat((rateService.toFiat(val * unitToSatoshi, fiatCode, availableUnits[unitIndex].id)).toFixed(2)); }; function evaluate(val) { @@ -212,6 +329,8 @@ angular.module('copayApp.controllers').controller('amountController', function($ }; function format(val) { + if (!val) return; + var result = val.toString(); if (isOperator(lodash.last(val))) @@ -221,24 +340,42 @@ angular.module('copayApp.controllers').controller('amountController', function($ }; $scope.finish = function() { + + var unit = availableUnits[unitIndex]; var _amount = evaluate(format($scope.amount)); if ($scope.nextStep) { + + var coin = unit.id; + if (unit.isFiat) { + coin = availableUnits[altUnitIndex].id; + } + $state.transitionTo($scope.nextStep, { id: _id, amount: $scope.useSendMax ? null : _amount, - currency: $scope.showAlternativeAmount ? $scope.alternativeIsoCode : $scope.unitName, + currency: unit.id.toUpperCase(), + coin: coin, useSendMax: $scope.useSendMax }); } else { - var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; + var amount = _amount; + + if (unit.isFiat) { + amount = fromFiat(_amount); + } else { + amount = (amount * unitToSatoshi).toFixed(0); + } + + $state.transitionTo('tabs.send.confirm', { recipientType: $scope.recipientType, - toAmount: $scope.useSendMax ? null : (amount * unitToSatoshi).toFixed(0), + toAmount: amount, toAddress: $scope.toAddress, toName: $scope.toName, toEmail: $scope.toEmail, toColor: $scope.toColor, + coin: unit.id, useSendMax: $scope.useSendMax }); } diff --git a/src/js/controllers/buyAmazon.js b/src/js/controllers/buyAmazon.js index 986b2c908..856888e68 100644 --- a/src/js/controllers/buyAmazon.js +++ b/src/js/controllers/buyAmazon.js @@ -2,6 +2,7 @@ angular.module('copayApp.controllers').controller('buyAmazonController', function($scope, $log, $state, $timeout, $filter, $ionicHistory, $ionicConfig, lodash, amazonService, popupService, profileService, ongoingProcess, configService, walletService, payproService, bwcError, externalLinkService, platformInfo, gettextCatalog, txFormatService) { + var coin = 'btc'; var amount; var currency; var createdTx; @@ -64,7 +65,7 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio }; var satToFiat = function(sat, cb) { - txFormatService.toFiat(sat, $scope.currencyIsoCode, function(value) { + txFormatService.toFiat(coin, sat, $scope.currencyIsoCode, function(value) { return cb(value); }); }; @@ -216,7 +217,7 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio }); var initialize = function(wallet) { - var parsedAmount = txFormatService.parseAmount(amount, currency); + var parsedAmount = txFormatService.parseAmount(coin, amount, currency); $scope.currencyIsoCode = parsedAmount.currency; $scope.amountUnitStr = parsedAmount.amountUnitStr; var dataSrc = { @@ -260,7 +261,7 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio invoiceUrl: invoice.url, invoiceTime: invoice.invoiceTime }; - $scope.totalAmountStr = txFormatService.formatAmountStr(ctxp.amount); + $scope.totalAmountStr = txFormatService.formatAmountStr(coin, ctxp.amount); setTotalAmount(parsedAmount.amountSat, invoiceFeeSat, ctxp.fee); }); }); @@ -292,7 +293,8 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio $scope.wallets = profileService.getWallets({ onlyComplete: true, network: $scope.network, - hasFunds: true + hasFunds: true, + coin: coin }); if (lodash.isEmpty($scope.wallets)) { showErrorAndBack(null, gettextCatalog.getString('No wallets available')); diff --git a/src/js/controllers/buyCoinbase.js b/src/js/controllers/buyCoinbase.js index 57a95a0c7..10e6e35a0 100644 --- a/src/js/controllers/buyCoinbase.js +++ b/src/js/controllers/buyCoinbase.js @@ -2,6 +2,7 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicScrollDelegate, $ionicConfig, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService, txFormatService) { + var coin = 'btc'; var amount; var currency; @@ -33,6 +34,52 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct } }; + var processPaymentInfo = function() { + ongoingProcess.set('connectingCoinbase', true); + coinbaseService.init(function(err, res) { + if (err) { + ongoingProcess.set('connectingCoinbase', false); + showErrorAndBack(err); + return; + } + var accessToken = res.accessToken; + + coinbaseService.buyPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, b) { + $scope.buyPrice = b.data || null; + }); + + $scope.paymentMethods = []; + $scope.selectedPaymentMethodId = { value : null }; + coinbaseService.getPaymentMethods(accessToken, function(err, p) { + if (err) { + ongoingProcess.set('connectingCoinbase', false); + showErrorAndBack(err); + return; + } + + var hasPrimary; + var pm; + for(var i = 0; i < p.data.length; i++) { + pm = p.data[i]; + if (pm.allow_buy) { + $scope.paymentMethods.push(pm); + } + if (pm.allow_buy && pm.primary_buy) { + hasPrimary = true; + $scope.selectedPaymentMethodId.value = pm.id; + } + } + if (lodash.isEmpty($scope.paymentMethods)) { + ongoingProcess.set('connectingCoinbase', false); + showErrorAndBack('No payment method available to buy'); + return; + } + if (!hasPrimary) $scope.selectedPaymentMethodId.value = $scope.paymentMethods[0].id; + $scope.buyRequest(); + }); + }); + }; + $scope.$on("$ionicView.beforeLeave", function(event, data) { $ionicConfig.views.swipeBackEnabled(true); }); @@ -42,81 +89,22 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct }); $scope.$on("$ionicView.beforeEnter", function(event, data) { - $scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false; - var parsedAmount = txFormatService.parseAmount( - data.stateParams.amount, - data.stateParams.currency); + $scope.isFiat = data.stateParams.currency != 'BTC' ? true : false; + amount = data.stateParams.amount; + currency = data.stateParams.currency; - // Buy always in BTC - amount = (parsedAmount.amountSat / 100000000).toFixed(8); - currency = 'BTC'; - - $scope.amountUnitStr = parsedAmount.amountUnitStr; - - ongoingProcess.set('calculatingFee', true); - coinbaseService.checkEnoughFundsForFee(amount, function(err) { - ongoingProcess.set('calculatingFee', false); - if (err) { - showErrorAndBack(err); - return; - } - - $scope.network = coinbaseService.getNetwork(); - $scope.wallets = profileService.getWallets({ - onlyComplete: true, - network: $scope.network - }); - - if (lodash.isEmpty($scope.wallets)) { - showErrorAndBack('No wallets available'); - return; - } - $scope.wallet = $scope.wallets[0]; // Default first wallet - - ongoingProcess.set('connectingCoinbase', true); - coinbaseService.init(function(err, res) { - if (err) { - ongoingProcess.set('connectingCoinbase', false); - showErrorAndBack(err); - return; - } - var accessToken = res.accessToken; - - coinbaseService.buyPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, b) { - $scope.buyPrice = b.data || null; - }); - - $scope.paymentMethods = []; - $scope.selectedPaymentMethodId = { value : null }; - coinbaseService.getPaymentMethods(accessToken, function(err, p) { - if (err) { - ongoingProcess.set('connectingCoinbase', false); - showErrorAndBack(err); - return; - } - - var hasPrimary; - var pm; - for(var i = 0; i < p.data.length; i++) { - pm = p.data[i]; - if (pm.allow_buy) { - $scope.paymentMethods.push(pm); - } - if (pm.allow_buy && pm.primary_buy) { - hasPrimary = true; - $scope.selectedPaymentMethodId.value = pm.id; - } - } - if (lodash.isEmpty($scope.paymentMethods)) { - ongoingProcess.set('connectingCoinbase', false); - showErrorAndBack('No payment method available to buy'); - return; - } - if (!hasPrimary) $scope.selectedPaymentMethodId.value = $scope.paymentMethods[0].id; - $scope.buyRequest(); - }); - }); + $scope.network = coinbaseService.getNetwork(); + $scope.wallets = profileService.getWallets({ + onlyComplete: true, + network: $scope.network, + coin: coin }); + + if (lodash.isEmpty($scope.wallets)) { + showErrorAndBack('No wallets available'); + return; + } + $scope.onWalletSelect($scope.wallets[0]); // Default first wallet }); $scope.buyRequest = function() { @@ -248,6 +236,25 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct $scope.onWalletSelect = function(wallet) { $scope.wallet = wallet; + var parsedAmount = txFormatService.parseAmount( + coin, + amount, + currency); + + // Buy always in BTC + amount = (parsedAmount.amountSat / 100000000).toFixed(8); + currency = 'BTC'; + + $scope.amountUnitStr = parsedAmount.amountUnitStr; + ongoingProcess.set('calculatingFee', true); + coinbaseService.checkEnoughFundsForFee(amount, function(err) { + ongoingProcess.set('calculatingFee', false); + if (err) { + showErrorAndBack(err); + return; + } + processPaymentInfo(); + }); }; $scope.goBackHome = function() { diff --git a/src/js/controllers/buyGlidera.js b/src/js/controllers/buyGlidera.js index 46d0c3238..a2040bc3c 100644 --- a/src/js/controllers/buyGlidera.js +++ b/src/js/controllers/buyGlidera.js @@ -2,6 +2,7 @@ angular.module('copayApp.controllers').controller('buyGlideraController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicConfig, lodash, glideraService, popupService, profileService, ongoingProcess, walletService, platformInfo, txFormatService) { + var coin = 'btc'; var amount; var currency; @@ -35,36 +36,7 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi } }; - $scope.$on("$ionicView.beforeLeave", function(event, data) { - $ionicConfig.views.swipeBackEnabled(true); - }); - - $scope.$on("$ionicView.enter", function(event, data) { - $ionicConfig.views.swipeBackEnabled(false); - }); - - $scope.$on("$ionicView.beforeEnter", function(event, data) { - $scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false; - var parsedAmount = txFormatService.parseAmount( - data.stateParams.amount, - data.stateParams.currency); - - amount = parsedAmount.amount; - currency = parsedAmount.currency; - $scope.amountUnitStr = parsedAmount.amountUnitStr; - - $scope.network = glideraService.getNetwork(); - $scope.wallets = profileService.getWallets({ - onlyComplete: true, - network: $scope.network - }); - - if (lodash.isEmpty($scope.wallets)) { - showErrorAndBack('No wallets available'); - return; - } - $scope.wallet = $scope.wallets[0]; // Default first wallet - + var processPaymentInfo = function() { ongoingProcess.set('connectingGlidera', true); glideraService.init(function(err, data) { if (err) { @@ -88,6 +60,33 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi $scope.buyInfo = buy; }); }); + }; + + $scope.$on("$ionicView.beforeLeave", function(event, data) { + $ionicConfig.views.swipeBackEnabled(true); + }); + + $scope.$on("$ionicView.enter", function(event, data) { + $ionicConfig.views.swipeBackEnabled(false); + }); + + $scope.$on("$ionicView.beforeEnter", function(event, data) { + $scope.isFiat = data.stateParams.currency != 'BTC' ? true : false; + amount = data.stateParams.amount; + currency = data.stateParams.currency; + + $scope.network = glideraService.getNetwork(); + $scope.wallets = profileService.getWallets({ + onlyComplete: true, + network: $scope.network, + coin: coin + }); + + if (lodash.isEmpty($scope.wallets)) { + showErrorAndBack('No wallets available'); + return; + } + $scope.onWalletSelect($scope.wallets[0]); // Default first wallet }); var ask2FaCode = function(mode, cb) { @@ -105,7 +104,7 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi popupService.showPrompt(title, message, null, function(twoFaCode) { if (typeof twoFaCode == 'undefined') return cb(); return cb(twoFaCode); - }); + }); } else { return cb(); } @@ -116,7 +115,7 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi var okText = 'Confirm'; var cancelText = 'Cancel'; popupService.showConfirm(null, message, okText, cancelText, function(ok) { - if (!ok) return; + if (!ok) return; ongoingProcess.set('buyingBitcoin', true, statusChangeHandler); glideraService.get2faCode($scope.token, function(err, tfa) { if (err) { @@ -162,6 +161,15 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi $scope.onWalletSelect = function(wallet) { $scope.wallet = wallet; + var parsedAmount = txFormatService.parseAmount( + coin, + amount, + currency); + + amount = parsedAmount.amount; + currency = parsedAmount.currency; + $scope.amountUnitStr = parsedAmount.amountUnitStr; + processPaymentInfo(); }; $scope.goBackHome = function() { diff --git a/src/js/controllers/buyMercadoLibre.js b/src/js/controllers/buyMercadoLibre.js index fa01a6143..2bd96ed55 100644 --- a/src/js/controllers/buyMercadoLibre.js +++ b/src/js/controllers/buyMercadoLibre.js @@ -2,6 +2,7 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', function($scope, $log, $state, $timeout, $filter, $ionicHistory, $ionicConfig, lodash, mercadoLibreService, popupService, profileService, ongoingProcess, configService, walletService, payproService, bwcError, externalLinkService, platformInfo, txFormatService, gettextCatalog) { + var coin = 'btc'; var amount; var currency; var createdTx; @@ -64,7 +65,7 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f }; var satToFiat = function(sat, cb) { - txFormatService.toFiat(sat, $scope.currencyIsoCode, function(value) { + txFormatService.toFiat(coin, sat, $scope.currencyIsoCode, function(value) { return cb(value); }); }; @@ -214,7 +215,7 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f }); var initialize = function(wallet) { - var parsedAmount = txFormatService.parseAmount(amount, currency); + var parsedAmount = txFormatService.parseAmount(coin, amount, currency); $scope.currencyIsoCode = parsedAmount.currency; $scope.amountUnitStr = parsedAmount.amountUnitStr; var dataSrc = { @@ -258,7 +259,7 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f invoiceUrl: invoice.url, invoiceTime: invoice.invoiceTime }; - $scope.totalAmountStr = txFormatService.formatAmountStr(ctxp.amount); + $scope.totalAmountStr = txFormatService.formatAmountStr(coin, ctxp.amount); setTotalAmount(parsedAmount.amountSat, invoiceFeeSat, ctxp.fee); }); }); @@ -284,7 +285,8 @@ angular.module('copayApp.controllers').controller('buyMercadoLibreController', f $scope.network = mercadoLibreService.getNetwork(); $scope.wallets = profileService.getWallets({ onlyComplete: true, - network: $scope.network + network: $scope.network, + coin: coin }); if (lodash.isEmpty($scope.wallets)) { showErrorAndBack(null, gettextCatalog.getString('No wallets available')); diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index eff5aa4b2..45605f719 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, rateService, $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, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, bwcError, txConfirmNotification) { var countDown = null; var CONFIRM_LIMIT_USD = 20; @@ -69,14 +69,15 @@ angular.module('copayApp.controllers').controller('confirmController', function( $scope.$on("$ionicView.beforeEnter", function(event, data) { - function setWalletSelector(network, minAmount, cb) { + function setWalletSelector(coin, network, minAmount, cb) { // no min amount? (sendMax) => look for no empty wallets minAmount = minAmount || 1; $scope.wallets = profileService.getWallets({ onlyComplete: true, - network: network + network: network, + coin: coin }); if (!$scope.wallets || !$scope.wallets.length) { @@ -118,6 +119,11 @@ angular.module('copayApp.controllers').controller('confirmController', function( }); }; + // TODO: Default fee level for BCH + if (data.stateParams.coin == 'bch') { + configFeeLevel = 'normal'; + } + // Setup $scope // Grab stateParams @@ -137,32 +143,29 @@ angular.module('copayApp.controllers').controller('confirmController', function( toEmail: data.stateParams.toEmail, toColor: data.stateParams.toColor, network: (new bitcore.Address(data.stateParams.toAddress)).network.name, + coin: data.stateParams.coin, txp: {}, }; - // Other Scope vars $scope.isCordova = isCordova; $scope.isWindowsPhoneApp = isWindowsPhoneApp; $scope.showAddress = false; - updateTx(tx, null, {}, function() { + $scope.walletSelectorTitle = gettextCatalog.getString('Send from'); - $scope.walletSelectorTitle = gettextCatalog.getString('Send from'); - - setWalletSelector(tx.network, tx.toAmount, function(err) { - if (err) { - return exitWithError('Could not update wallets'); - } - - if ($scope.wallets.length > 1) { - $scope.showWalletSelector(); - } else if ($scope.wallets.length) { - setWallet($scope.wallets[0], tx); - } - }); + setWalletSelector(tx.coin, tx.network, tx.toAmount, function(err) { + if (err) { + return exitWithError('Could not update wallets'); + } + if ($scope.wallets.length > 1) { + $scope.showWalletSelector(); + } else if ($scope.wallets.length) { + setWallet($scope.wallets[0], tx); + } }); + }); @@ -238,10 +241,10 @@ angular.module('copayApp.controllers').controller('confirmController', function( if (!tx.toAmount) return; // Amount - tx.amountStr = txFormatService.formatAmountStr(tx.toAmount); + tx.amountStr = txFormatService.formatAmountStr(wallet.coin, tx.toAmount); tx.amountValueStr = tx.amountStr.split(' ')[0]; tx.amountUnitStr = tx.amountStr.split(' ')[1]; - txFormatService.formatAlternativeStr(tx.toAmount, function(v) { + txFormatService.formatAlternativeStr(wallet.coin, tx.toAmount, function(v) { tx.alternativeAmountStr = v; }); } @@ -252,7 +255,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( // End of quick refresh, before wallet is selected. if (!wallet) return cb(); - feeService.getFeeRate(tx.network, tx.feeLevel, function(err, feeRate) { + feeService.getFeeRate(wallet.coin, tx.network, tx.feeLevel, function(err, feeRate) { if (err) return cb(err); if (!usingCustomFee) tx.feeRate = feeRate; @@ -280,7 +283,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( tx.sendMaxInfo = sendMaxInfo; tx.toAmount = tx.sendMaxInfo.amount; updateAmount(); - showSendMaxWarning(sendMaxInfo); + showSendMaxWarning(wallet, sendMaxInfo); } // txp already generated for this wallet? @@ -292,8 +295,8 @@ angular.module('copayApp.controllers').controller('confirmController', function( getTxp(lodash.clone(tx), wallet, opts.dryRun, function(err, txp) { if (err) return cb(err); - txp.feeStr = txFormatService.formatAmountStr(txp.fee); - txFormatService.formatAlternativeStr(txp.fee, function(v) { + txp.feeStr = txFormatService.formatAmountStr(wallet.coin, txp.fee); + txFormatService.formatAlternativeStr(wallet.coin, txp.fee, function(v) { txp.alternativeFeeStr = v; }); @@ -337,26 +340,26 @@ angular.module('copayApp.controllers').controller('confirmController', function( }; - function showSendMaxWarning(sendMaxInfo) { + function showSendMaxWarning(wallet, sendMaxInfo) { 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(sendMaxInfo.amountBelowFee) + 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(sendMaxInfo.amountAboveMaxSize) + amountAboveMaxSizeStr: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.amountAboveMaxSize) })); } return warningMsg.join('\n'); }; var msg = gettextCatalog.getString("{{fee}} will be deducted for bitcoin networking fees.", { - fee: txFormatService.formatAmountStr(sendMaxInfo.fee) + fee: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.fee) }); var warningMsg = verifyExcludedUtxos(); @@ -483,7 +486,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( if (walletService.isEncrypted(wallet)) return cb(); - var amountUsd = parseFloat(txFormatService.formatToUSD(txp.amount)); + var amountUsd = parseFloat(txFormatService.formatToUSD(wallet.coin, txp.amount)); if (amountUsd <= CONFIRM_LIMIT_USD) return cb(); @@ -563,10 +566,13 @@ angular.module('copayApp.controllers').controller('confirmController', function( $scope.chooseFeeLevel = function(tx, wallet) { + if (wallet.coin == 'bch') return; + var scope = $rootScope.$new(true); scope.network = tx.network; scope.feeLevel = tx.feeLevel; scope.noSave = true; + scope.coin = wallet.coin; if (usingCustomFee) { scope.customFeePerKB = tx.feeRate; diff --git a/src/js/controllers/create.js b/src/js/controllers/create.js index c43bf406b..e1d84bfed 100644 --- a/src/js/controllers/create.js +++ b/src/js/controllers/create.js @@ -22,11 +22,16 @@ angular.module('copayApp.controllers').controller('createController', $scope.$on("$ionicView.beforeEnter", function(event, data) { $scope.formData = {}; var defaults = configService.getDefaults(); + var config = configService.getSync(); var tc = $state.current.name == 'tabs.add.create-personal' ? 1 : defaults.wallet.totalCopayers; $scope.formData.account = 1; $scope.formData.bwsurl = defaults.bws.url; $scope.TCValues = lodash.range(2, defaults.limits.totalCopayers + 1); $scope.formData.derivationPath = derivationPathHelper.default; + $scope.formData.coin = 'btc'; + + if (config.cashSupport.enabled) $scope.enableCash = true; + $scope.setTotalCopayers(tc); updateRCSelect(tc); resetPasswordFields(); @@ -137,6 +142,7 @@ angular.module('copayApp.controllers').controller('createController', bwsurl: $scope.formData.bwsurl, singleAddress: $scope.formData.singleAddressEnabled, walletPrivKey: $scope.formData._walletPrivKey, // Only for testing + coin: $scope.formData.coin }; var setSeed = $scope.formData.seedSource.id == 'set'; diff --git a/src/js/controllers/customAmount.js b/src/js/controllers/customAmount.js index 9a2b46f6c..a57ea8c3b 100644 --- a/src/js/controllers/customAmount.js +++ b/src/js/controllers/customAmount.js @@ -15,7 +15,7 @@ angular.module('copayApp.controllers').controller('customAmountController', func showErrorAndBack('Error', 'No wallet selected'); return; } - + $scope.showShareButton = platformInfo.isCordova ? (platformInfo.isIOS ? 'iOS' : 'Android') : null; $scope.wallet = profileService.getWallet(walletId); @@ -25,11 +25,13 @@ angular.module('copayApp.controllers').controller('customAmountController', func showErrorAndBack('Error', 'Could not get the address'); return; } - + $scope.address = addr; - + + $scope.coin = data.stateParams.coin; var parsedAmount = txFormatService.parseAmount( - data.stateParams.amount, + $scope.wallet.coin, + data.stateParams.amount, data.stateParams.currency); // Amount in USD or BTC @@ -37,17 +39,17 @@ angular.module('copayApp.controllers').controller('customAmountController', func var currency = parsedAmount.currency; $scope.amountUnitStr = parsedAmount.amountUnitStr; - if (currency != 'BTC') { - // Convert to BTC + if (currency != 'BTC' && currency != 'BCH') { + // Convert to BTC or BCH var config = configService.getSync().wallet.settings; var amountUnit = txFormatService.satToUnit(parsedAmount.amountSat); - var btcParsedAmount = txFormatService.parseAmount(amountUnit, config.unitName); - + var btcParsedAmount = txFormatService.parseAmount($scope.wallet.coin, amountUnit, $scope.wallet.coin); + $scope.amountBtc = btcParsedAmount.amount; $scope.altAmountStr = btcParsedAmount.amountUnitStr; } else { - $scope.amountBtc = amount; // BTC - $scope.altAmountStr = txFormatService.formatAlternativeStr(parsedAmount.amountSat); + $scope.amountBtc = amount; // BTC or BCH + $scope.altAmountStr = txFormatService.formatAlternativeStr($scope.wallet.coin, parsedAmount.amountSat); } }); }); @@ -61,12 +63,12 @@ angular.module('copayApp.controllers').controller('customAmountController', func $scope.shareAddress = function() { if (!platformInfo.isCordova) return; - var data = 'bitcoin:' + $scope.address + '?amount=' + $scope.amountBtc; + var data = 'bitcoin:' + $scope.address + '?amount=' + $scope.amountBtc + '&coin=' + $scope.wallet.coin; window.plugins.socialsharing.share(data, null, null, null); } $scope.copyToClipboard = function() { - return 'bitcoin:' + $scope.address + '?amount=' + $scope.amountBtc; + return 'bitcoin:' + $scope.address + '?amount=' + $scope.amountBtc + '&coin=' + $scope.wallet.coin; }; }); diff --git a/src/js/controllers/import.js b/src/js/controllers/import.js index 3b34839ad..c66cb4a3f 100644 --- a/src/js/controllers/import.js +++ b/src/js/controllers/import.js @@ -5,6 +5,7 @@ angular.module('copayApp.controllers').controller('importController', var reader = new FileReader(); var defaults = configService.getDefaults(); + var config = configService.getSync(); var errors = bwcService.getErrors(); $scope.init = function() { @@ -15,10 +16,13 @@ angular.module('copayApp.controllers').controller('importController', $scope.formData.bwsurl = defaults.bws.url; $scope.formData.derivationPath = derivationPathHelper.default; $scope.formData.account = 1; + $scope.formData.coin = 'btc'; $scope.importErr = false; $scope.isCopay = appConfigService.name == 'copay'; $scope.fromHardwareWallet = { value: false }; + if (config.cashSupport.enabled) $scope.enableCash = true; + if ($stateParams.code) $scope.processWalletInfo($stateParams.code); @@ -203,6 +207,7 @@ angular.module('copayApp.controllers').controller('importController', if (evt.target.readyState == FileReader.DONE) { // DONE == 2 var opts = {}; opts.bwsurl = $scope.formData.bwsurl; + opts.coin = $scope.formData.coin; _importBlob(evt.target.result, opts); } } @@ -228,6 +233,7 @@ angular.module('copayApp.controllers').controller('importController', } else { var opts = {}; opts.bwsurl = $scope.formData.bwsurl; + opts.coin = $scope.formData.coin; _importBlob(backupText, opts); } }; @@ -273,6 +279,7 @@ angular.module('copayApp.controllers').controller('importController', } opts.passphrase = $scope.formData.passphrase || null; + opts.coin = $scope.formData.coin; if ($scope.fromHardwareWallet.value) { $log.debug('Importing seed from hardware wallet'); diff --git a/src/js/controllers/join.js b/src/js/controllers/join.js index 2728b6eb0..2a209701d 100644 --- a/src/js/controllers/join.js +++ b/src/js/controllers/join.js @@ -5,11 +5,14 @@ angular.module('copayApp.controllers').controller('joinController', $scope.$on("$ionicView.beforeEnter", function(event, data) { var defaults = configService.getDefaults(); + var config = configService.getSync(); $scope.formData = {}; $scope.formData.bwsurl = defaults.bws.url; $scope.formData.derivationPath = derivationPathHelper.default; $scope.formData.account = 1; $scope.formData.secret = null; + $scope.formData.coin = 'btc'; + if (config.cashSupport.enabled) $scope.enableCash = true; resetPasswordFields(); updateSeedSourceSelect(); }); @@ -103,7 +106,8 @@ angular.module('copayApp.controllers').controller('joinController', var opts = { secret: $scope.formData.secret, myName: $scope.formData.myName, - bwsurl: $scope.formData.bwsurl + bwsurl: $scope.formData.bwsurl, + coin: $scope.formData.coin } var setSeed = $scope.formData.seedSource.id == 'set'; diff --git a/src/js/controllers/modals/feeLevels.js b/src/js/controllers/modals/feeLevels.js index 72a3e2abf..691ce7d32 100644 --- a/src/js/controllers/modals/feeLevels.js +++ b/src/js/controllers/modals/feeLevels.js @@ -18,14 +18,14 @@ angular.module('copayApp.controllers').controller('feeLevelsController', functio var value = lodash.find($scope.feeLevels[$scope.network], { level: 'superEconomy' }); - return parseInt((value.feePerKB / 1000).toFixed()); + return parseInt((value.feePerKb / 1000).toFixed()); }; var getMaxRecommended = function() { var value = lodash.find($scope.feeLevels[$scope.network], { level: 'urgent' }); - return parseInt((value.feePerKB / 1000).toFixed()); + return parseInt((value.feePerKb / 1000).toFixed()); }; $scope.ok = function() { @@ -61,7 +61,7 @@ angular.module('copayApp.controllers').controller('feeLevelsController', functio // If no custom fee if (value) { $scope.customFeePerKB = null; - $scope.feePerSatByte = (value.feePerKB / 1000).toFixed(); + $scope.feePerSatByte = (value.feePerKb / 1000).toFixed(); $scope.avgConfirmationTime = value.nbBlocks * 10; } else { $scope.avgConfirmationTime = null; @@ -102,7 +102,7 @@ angular.module('copayApp.controllers').controller('feeLevelsController', functio $scope.feeOpts = feeService.feeOpts; $scope.loadingFee = true; - feeService.getFeeLevels(function(err, levels) { + feeService.getFeeLevels($scope.coin, function(err, levels) { $scope.loadingFee = false; if (err || lodash.isEmpty(levels)) { showErrorAndClose(null, err); diff --git a/src/js/controllers/modals/txpDetails.js b/src/js/controllers/modals/txpDetails.js index 68c6d0778..a901871e8 100644 --- a/src/js/controllers/modals/txpDetails.js +++ b/src/js/controllers/modals/txpDetails.js @@ -23,7 +23,7 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi }; function displayFeeValues() { - txFormatService.formatAlternativeStr($scope.tx.fee, function(v) { + txFormatService.formatAlternativeStr($scope.wallet.coin, $scope.tx.fee, function(v) { $scope.tx.feeFiatStr = v; }); $scope.tx.feeRateStr = ($scope.tx.fee / ($scope.tx.amount + $scope.tx.fee) * 100).toFixed(2) + '%'; @@ -219,7 +219,7 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi copayerId: $scope.wallet.credentials.copayerId }); - $scope.tx = txFormatService.processTx(tx); + $scope.tx = txFormatService.processTx($scope.wallet.coin, tx); if (!action && tx.status == 'pending') $scope.tx.pendingForUs = true; diff --git a/src/js/controllers/onboarding/tour.js b/src/js/controllers/onboarding/tour.js index bcea8c405..5781ec22a 100644 --- a/src/js/controllers/onboarding/tour.js +++ b/src/js/controllers/onboarding/tour.js @@ -27,7 +27,7 @@ angular.module('copayApp.controllers').controller('tourController', rateService.whenAvailable(function() { var localCurrency = 'USD'; var btcAmount = 1; - var rate = rateService.toFiat(btcAmount * 1e8, localCurrency); + var rate = rateService.toFiat(btcAmount * 1e8, localCurrency, 'btc'); $scope.localCurrencySymbol = '$'; $scope.localCurrencyPerBtc = $filter('formatFiatAmount')(parseFloat(rate.toFixed(2), 10)); $timeout(function() { diff --git a/src/js/controllers/paperWallet.js b/src/js/controllers/paperWallet.js index 83346fbbc..72a1d961a 100644 --- a/src/js/controllers/paperWallet.js +++ b/src/js/controllers/paperWallet.js @@ -45,8 +45,7 @@ angular.module('copayApp.controllers').controller('paperWalletController', $scope.balanceSat = balance; if ($scope.balanceSat <= 0) popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not funds found')); - var config = configService.getSync().wallet.settings; - $scope.balance = txFormatService.formatAmount(balance) + ' ' + config.unitName; + $scope.balance = txFormatService.formatAmountStr($scope.wallet.coin, balance); } $scope.$apply(); }); @@ -60,9 +59,9 @@ angular.module('copayApp.controllers').controller('paperWalletController', $scope.wallet.buildTxFromPrivateKey($scope.privateKey, destinationAddress, null, function(err, testTx) { if (err) return cb(err); var rawTxLength = testTx.serialize().length; - feeService.getCurrentFeeRate('livenet', function(err, feePerKB) { + feeService.getCurrentFeeRate('btc', 'livenet', function(err, feePerKb) { var opts = {}; - opts.fee = Math.round((feePerKB * rawTxLength) / 2000); + opts.fee = Math.round((feePerKb * rawTxLength) / 2000); $scope.wallet.buildTxFromPrivateKey($scope.privateKey, destinationAddress, opts, function(err, tx) { if (err) return cb(err); $scope.wallet.broadcastRawTx({ diff --git a/src/js/controllers/preferencesFee.js b/src/js/controllers/preferencesFee.js index 9339b413e..686c78460 100644 --- a/src/js/controllers/preferencesFee.js +++ b/src/js/controllers/preferencesFee.js @@ -32,11 +32,12 @@ angular.module('copayApp.controllers').controller('preferencesFeeController', fu }); $scope.init = function() { + var coin = 'btc'; // TODO: only BTC in preferences $scope.network = $scope.network || 'livenet'; $scope.feeOpts = feeService.feeOpts; $scope.currentFeeLevel = $scope.feeLevel || feeService.getCurrentFeeLevel(); $scope.loadingFee = true; - feeService.getFeeLevels(function(err, levels) { + feeService.getFeeLevels(coin, function(err, levels) { $scope.loadingFee = false; if (err) { //Error is already formatted @@ -66,7 +67,7 @@ angular.module('copayApp.controllers').controller('preferencesFeeController', fu return; } - $scope.feePerSatByte = (value.feePerKB / 1000).toFixed(); + $scope.feePerSatByte = (value.feePerKb / 1000).toFixed(); $scope.avgConfirmationTime = value.nbBlocks * 10; $scope.invalidCustomFeeEntered = false; setMinWarning(); @@ -97,7 +98,7 @@ angular.module('copayApp.controllers').controller('preferencesFeeController', fu var value = lodash.find($scope.feeLevels[$scope.network], { level: 'superEconomy' }); - return parseInt((value.feePerKB / 1000).toFixed()); + return parseInt((value.feePerKb / 1000).toFixed()); }; var setMinWarning = function() { diff --git a/src/js/controllers/preferencesUnit.js b/src/js/controllers/preferencesUnit.js deleted file mode 100644 index 235deb3b2..000000000 --- a/src/js/controllers/preferencesUnit.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -angular.module('copayApp.controllers').controller('preferencesUnitController', function($scope, $log, configService, $ionicHistory, gettextCatalog, walletService, profileService) { - - var config = configService.getSync(); - $scope.unitList = [{ - name: 'bits (1,000,000 bits = 1BTC)', - shortName: 'bits', - value: 100, - decimals: 2, - code: 'bit', - }, { - name: 'BTC', - shortName: 'BTC', - value: 100000000, - decimals: 8, - code: 'btc', - }]; - - $scope.save = function(newUnit) { - var opts = { - wallet: { - settings: { - unitName: newUnit.shortName, - unitToSatoshi: newUnit.value, - unitDecimals: newUnit.decimals, - unitCode: newUnit.code, - } - } - }; - - configService.set(opts, function(err) { - if (err) $log.warn(err); - - $ionicHistory.goBack(); - walletService.updateRemotePreferences(profileService.getWallets()) - }); - }; - - $scope.$on("$ionicView.enter", function(event, data){ - $scope.currentUnit = config.wallet.settings.unitCode; - }); -}); diff --git a/src/js/controllers/sellCoinbase.js b/src/js/controllers/sellCoinbase.js index c6fd31f58..b90b1e610 100644 --- a/src/js/controllers/sellCoinbase.js +++ b/src/js/controllers/sellCoinbase.js @@ -2,6 +2,7 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicScrollDelegate, $ionicConfig, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService, appConfigService, configService, txFormatService) { + var coin = 'btc'; var amount; var currency; @@ -34,124 +35,7 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func }, onSendStatusChange); }; - var checkTransaction = lodash.throttle(function(count, txp) { - $log.warn('Check if transaction has been received by Coinbase. Try ' + count + '/5'); - // TX amount in BTC - var satToBtc = 1 / 100000000; - var amountBTC = (txp.amount * satToBtc).toFixed(8); - coinbaseService.init(function(err, res) { - if (err) { - $log.error(err); - checkTransaction(count, txp); - return; - } - var accessToken = res.accessToken; - var accountId = res.accountId; - var sellPrice = null; - - coinbaseService.sellPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, sell) { - if (err) { - $log.debug(err); - checkTransaction(count, txp); - return; - } - sellPrice = sell.data; - - coinbaseService.getTransactions(accessToken, accountId, function(err, ctxs) { - if (err) { - $log.debug(err); - checkTransaction(count, txp); - return; - } - - var coinbaseTransactions = ctxs.data; - var txFound = false; - var ctx; - for(var i = 0; i < coinbaseTransactions.length; i++) { - ctx = coinbaseTransactions[i]; - if (ctx.type == 'send' && ctx.from && ctx.amount.amount == amountBTC ) { - $log.warn('Transaction found!', ctx); - txFound = true; - $log.debug('Saving transaction to process later...'); - ctx['payment_method'] = $scope.selectedPaymentMethodId.value; - ctx['status'] = 'pending'; // Forcing "pending" status to process later - ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data; - ctx['sell_price_amount'] = sellPrice ? sellPrice.amount : ''; - ctx['sell_price_currency'] = sellPrice ? sellPrice.currency : 'USD'; - ctx['description'] = appConfigService.nameCase + ' Wallet: ' + $scope.wallet.name; - coinbaseService.savePendingTransaction(ctx, null, function(err) { - ongoingProcess.set('sellingBitcoin', false, statusChangeHandler); - if (err) $log.debug(err); - }); - return; - } - } - if (!txFound) { - // Transaction sent, but could not be verified by Coinbase.com - $log.warn('Transaction not found in Coinbase.'); - if (count < 5) { - checkTransaction(count + 1, txp); - } else { - ongoingProcess.set('sellingBitcoin', false, statusChangeHandler); - showError('No transaction found'); - return; - } - } - }); - }); - }); - }, 8000, { - 'leading': true - }); - - var statusChangeHandler = function (processName, showName, isOn) { - $log.debug('statusChangeHandler: ', processName, showName, isOn); - if ( processName == 'sellingBitcoin' && !isOn) { - $scope.sendStatus = 'success'; - $timeout(function() { - $scope.$digest(); - }, 100); - } else if (showName) { - $scope.sendStatus = showName; - } - }; - - $scope.$on("$ionicView.beforeLeave", function(event, data) { - $ionicConfig.views.swipeBackEnabled(true); - }); - - $scope.$on("$ionicView.enter", function(event, data) { - $ionicConfig.views.swipeBackEnabled(false); - }); - - $scope.$on("$ionicView.beforeEnter", function(event, data) { - $scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false; - var parsedAmount = txFormatService.parseAmount( - data.stateParams.amount, - data.stateParams.currency); - - amount = parsedAmount.amount; - currency = parsedAmount.currency; - $scope.amountUnitStr = parsedAmount.amountUnitStr; - - $scope.priceSensitivity = coinbaseService.priceSensitivity; - $scope.selectedPriceSensitivity = { data: coinbaseService.selectedPriceSensitivity }; - - $scope.network = coinbaseService.getNetwork(); - $scope.wallets = profileService.getWallets({ - m: 1, // Only 1-signature wallet - onlyComplete: true, - network: $scope.network, - hasFunds: true, - minAmount: parsedAmount.amountSat - }); - - if (lodash.isEmpty($scope.wallets)) { - showErrorAndBack('Insufficient funds'); - return; - } - $scope.wallet = $scope.wallets[0]; // Default first wallet - + var processPaymentInfo = function() { ongoingProcess.set('connectingCoinbase', true); coinbaseService.init(function(err, res) { if (err) { @@ -193,7 +77,121 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func if (!hasPrimary) $scope.selectedPaymentMethodId.value = $scope.paymentMethods[0].id; $scope.sellRequest(); }); - }); + }); + }; + + var checkTransaction = lodash.throttle(function(count, txp) { + $log.warn('Check if transaction has been received by Coinbase. Try ' + count + '/5'); + // TX amount in BTC + var satToBtc = 1 / 100000000; + var amountBTC = (txp.amount * satToBtc).toFixed(8); + coinbaseService.init(function(err, res) { + if (err) { + $log.error(err); + checkTransaction(count, txp); + return; + } + var accessToken = res.accessToken; + var accountId = res.accountId; + var sellPrice = null; + + coinbaseService.sellPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, sell) { + if (err) { + $log.debug(err); + checkTransaction(count, txp); + return; + } + sellPrice = sell.data; + + coinbaseService.getTransactions(accessToken, accountId, function(err, ctxs) { + if (err) { + $log.debug(err); + checkTransaction(count, txp); + return; + } + + var coinbaseTransactions = ctxs.data; + var txFound = false; + var ctx; + for(var i = 0; i < coinbaseTransactions.length; i++) { + ctx = coinbaseTransactions[i]; + if (ctx.type == 'send' && ctx.from && ctx.amount.amount == amountBTC ) { + $log.warn('Transaction found!', ctx); + txFound = true; + $log.debug('Saving transaction to process later...'); + ctx['payment_method'] = $scope.selectedPaymentMethodId.value; + ctx['status'] = 'pending'; // Forcing "pending" status to process later + ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data; + ctx['sell_price_amount'] = sellPrice ? sellPrice.amount : ''; + ctx['sell_price_currency'] = sellPrice ? sellPrice.currency : 'USD'; + ctx['description'] = appConfigService.nameCase + ' Wallet: ' + $scope.wallet.name; + coinbaseService.savePendingTransaction(ctx, null, function(err) { + ongoingProcess.set('sellingBitcoin', false, statusChangeHandler); + if (err) $log.debug(err); + }); + return; + } + } + if (!txFound) { + // Transaction sent, but could not be verified by Coinbase.com + $log.warn('Transaction not found in Coinbase.'); + if (count < 5) { + checkTransaction(count + 1, txp); + } else { + ongoingProcess.set('sellingBitcoin', false, statusChangeHandler); + showError('No transaction found'); + return; + } + } + }); + }); + }); + }, 8000, { + 'leading': true + }); + + var statusChangeHandler = function (processName, showName, isOn) { + $log.debug('statusChangeHandler: ', processName, showName, isOn); + if ( processName == 'sellingBitcoin' && !isOn) { + $scope.sendStatus = 'success'; + $timeout(function() { + $scope.$digest(); + }, 100); + } else if (showName) { + $scope.sendStatus = showName; + } + }; + + $scope.$on("$ionicView.beforeLeave", function(event, data) { + $ionicConfig.views.swipeBackEnabled(true); + }); + + $scope.$on("$ionicView.enter", function(event, data) { + $ionicConfig.views.swipeBackEnabled(false); + }); + + $scope.$on("$ionicView.beforeEnter", function(event, data) { + $scope.isFiat = data.stateParams.currency != 'BTC' ? true : false; + amount = data.stateParams.amount; + currency = data.stateParams.currency; + + $scope.priceSensitivity = coinbaseService.priceSensitivity; + $scope.selectedPriceSensitivity = { data: coinbaseService.selectedPriceSensitivity }; + + $scope.network = coinbaseService.getNetwork(); + $scope.wallets = profileService.getWallets({ + m: 1, // Only 1-signature wallet + onlyComplete: true, + network: $scope.network, + hasFunds: true, + coin: coin + }); + + if (lodash.isEmpty($scope.wallets)) { + showErrorAndBack('Insufficient funds'); + return; + } + $scope.onWalletSelect($scope.wallets[0]); // Default first wallet }); $scope.sellRequest = function() { @@ -236,7 +234,7 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func var cancelText = 'Cancel'; popupService.showConfirm(null, message, okText, cancelText, function(ok) { if (!ok) return; - + ongoingProcess.set('sellingBitcoin', true, statusChangeHandler); coinbaseService.init(function(err, res) { if (err) { @@ -294,8 +292,8 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func checkTransaction(1, txSent); }); }); - }); - }); + }); + }); }); }; @@ -306,6 +304,15 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func $scope.onWalletSelect = function(wallet) { $scope.wallet = wallet; + var parsedAmount = txFormatService.parseAmount( + coin, + amount, + currency); + + amount = parsedAmount.amount; + currency = parsedAmount.currency; + $scope.amountUnitStr = parsedAmount.amountUnitStr; + processPaymentInfo(); }; $scope.goBackHome = function() { diff --git a/src/js/controllers/sellGlidera.js b/src/js/controllers/sellGlidera.js index 90a6ed027..039286980 100644 --- a/src/js/controllers/sellGlidera.js +++ b/src/js/controllers/sellGlidera.js @@ -2,6 +2,7 @@ angular.module('copayApp.controllers').controller('sellGlideraController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicConfig, lodash, glideraService, popupService, profileService, ongoingProcess, walletService, configService, platformInfo, txFormatService) { + var coin = 'btc'; var amount; var currency; @@ -35,39 +36,7 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct } }; - $scope.$on("$ionicView.beforeLeave", function(event, data) { - $ionicConfig.views.swipeBackEnabled(true); - }); - - $scope.$on("$ionicView.enter", function(event, data) { - $ionicConfig.views.swipeBackEnabled(false); - }); - - $scope.$on("$ionicView.beforeEnter", function(event, data) { - $scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false; - var parsedAmount = txFormatService.parseAmount( - data.stateParams.amount, - data.stateParams.currency); - - amount = parsedAmount.amount; - currency = parsedAmount.currency; - $scope.amountUnitStr = parsedAmount.amountUnitStr; - - $scope.network = glideraService.getNetwork(); - $scope.wallets = profileService.getWallets({ - m: 1, // Only 1-signature wallet - onlyComplete: true, - network: $scope.network, - hasFunds: true, - minAmount: parsedAmount.amountSat - }); - - if (lodash.isEmpty($scope.wallets)) { - showErrorAndBack('Insufficient funds'); - return; - } - $scope.wallet = $scope.wallets[0]; // Default first wallet - + var processPaymentInfo = function() { ongoingProcess.set('connectingGlidera', true); glideraService.init(function(err, data) { if (err) { @@ -91,6 +60,35 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct $scope.sellInfo = sell; }); }); + }; + + $scope.$on("$ionicView.beforeLeave", function(event, data) { + $ionicConfig.views.swipeBackEnabled(true); + }); + + $scope.$on("$ionicView.enter", function(event, data) { + $ionicConfig.views.swipeBackEnabled(false); + }); + + $scope.$on("$ionicView.beforeEnter", function(event, data) { + $scope.isFiat = data.stateParams.currency != 'BTC' ? true : false; + amount = data.stateParams.amount; + currency = data.stateParams.currency; + + $scope.network = glideraService.getNetwork(); + $scope.wallets = profileService.getWallets({ + m: 1, // Only 1-signature wallet + onlyComplete: true, + network: $scope.network, + hasFunds: true, + coin: coin + }); + + if (lodash.isEmpty($scope.wallets)) { + showErrorAndBack('Insufficient funds'); + return; + } + $scope.onWalletSelect($scope.wallets[0]); // Default first wallet }); var ask2FaCode = function(mode, cb) { @@ -108,7 +106,7 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct popupService.showPrompt(title, message, null, function(twoFaCode) { if (typeof twoFaCode == 'undefined') return cb(); return cb(twoFaCode); - }); + }); } else { return cb(); } @@ -119,7 +117,7 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct var okText = 'Confirm'; var cancelText = 'Cancel'; popupService.showConfirm(null, message, okText, cancelText, function(ok) { - if (!ok) return; + if (!ok) return; ongoingProcess.set('sellingBitcoin', true, statusChangeHandler); glideraService.get2faCode($scope.token, function(err, tfa) { if (err) { @@ -231,6 +229,15 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct $scope.onWalletSelect = function(wallet) { $scope.wallet = wallet; + var parsedAmount = txFormatService.parseAmount( + coin, + amount, + currency); + + amount = parsedAmount.amount; + currency = parsedAmount.currency; + $scope.amountUnitStr = parsedAmount.amountUnitStr; + processPaymentInfo(); }; $scope.goBackHome = function() { diff --git a/src/js/controllers/tab-receive.js b/src/js/controllers/tab-receive.js index 39446924f..43785ab8b 100644 --- a/src/js/controllers/tab-receive.js +++ b/src/js/controllers/tab-receive.js @@ -8,7 +8,8 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi $scope.requestSpecificAmount = function() { $state.go('tabs.paymentRequest.amount', { - id: $scope.wallet.credentials.walletId + id: $scope.wallet.credentials.walletId, + coin: $scope.wallet.coin }); }; diff --git a/src/js/controllers/tab-send.js b/src/js/controllers/tab-send.js index 830cc167b..77a664632 100644 --- a/src/js/controllers/tab-send.js +++ b/src/js/controllers/tab-send.js @@ -76,6 +76,7 @@ angular.module('copayApp.controllers').controller('tabSendController', function( color: v.color, name: v.name, recipientType: 'wallet', + coin: v.coin, getAddress: function(cb) { walletService.getAddress(v, false, cb); }, @@ -186,7 +187,8 @@ angular.module('copayApp.controllers').controller('tabSendController', function( toAddress: addr, toName: item.name, toEmail: item.email, - toColor: item.color + toColor: item.color, + coin: item.coin }) }); }); diff --git a/src/js/controllers/tab-settings.js b/src/js/controllers/tab-settings.js index 01ffacd42..f806b0f84 100644 --- a/src/js/controllers/tab-settings.js +++ b/src/js/controllers/tab-settings.js @@ -10,7 +10,6 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct $scope.buyAndSellServices = buyAndSellService.getLinked(); configService.whenAvailable(function(config) { - $scope.unitName = config.wallet.settings.unitName; $scope.selectedAlternative = { name: config.wallet.settings.alternativeName, isoCode: config.wallet.settings.alternativeIsoCode diff --git a/src/js/controllers/topup.js b/src/js/controllers/topup.js index 3c66830b1..ee47872cc 100644 --- a/src/js/controllers/topup.js +++ b/src/js/controllers/topup.js @@ -3,6 +3,7 @@ angular.module('copayApp.controllers').controller('topUpController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicConfig, lodash, popupService, profileService, ongoingProcess, walletService, configService, platformInfo, bitpayService, bitpayCardService, payproService, bwcError, txFormatService, sendMaxService, gettextCatalog) { $scope.isCordova = platformInfo.isCordova; + var coin = 'btc'; var cardId; var useSendMax; var amount; @@ -36,7 +37,7 @@ angular.module('copayApp.controllers').controller('topUpController', function($s }; var satToFiat = function(sat, cb) { - txFormatService.toFiat(sat, $scope.currencyIsoCode, function(value) { + txFormatService.toFiat(coin, sat, $scope.currencyIsoCode, function(value) { return cb(value); }); }; @@ -218,7 +219,7 @@ angular.module('copayApp.controllers').controller('topUpController', function($s // Save TX in memory createdTx = ctxp; - $scope.totalAmountStr = txFormatService.formatAmountStr(ctxp.amount); + $scope.totalAmountStr = txFormatService.formatAmountStr(coin, ctxp.amount); setTotalAmount(parsedAmount.amountSat, invoiceFeeSat, ctxp.fee); @@ -256,7 +257,8 @@ angular.module('copayApp.controllers').controller('topUpController', function($s $scope.wallets = profileService.getWallets({ onlyComplete: true, network: bitpayService.getEnvironment().network, - hasFunds: true + hasFunds: true, + coin: coin }); if (lodash.isEmpty($scope.wallets)) { @@ -319,7 +321,7 @@ angular.module('copayApp.controllers').controller('topUpController', function($s }); return; } - var parsedAmount = txFormatService.parseAmount(a, c); + var parsedAmount = txFormatService.parseAmount(coin, a, c); initializeTopUp(wallet, parsedAmount); }); }; diff --git a/src/js/controllers/tx-details.js b/src/js/controllers/tx-details.js index ebd44f6a5..2815878a5 100644 --- a/src/js/controllers/tx-details.js +++ b/src/js/controllers/tx-details.js @@ -5,6 +5,7 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio var txId; var listeners = []; var config = configService.getSync(); + var blockexplorerUrl; $scope.$on("$ionicView.beforeEnter", function(event, data) { txId = data.stateParams.txid; @@ -15,6 +16,12 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio $scope.isShared = $scope.wallet.credentials.n > 1; $scope.txsUnsubscribedForNotifications = config.confirmedTxsNotifications ? !config.confirmedTxsNotifications.enabled : true; + if ($scope.wallet.coin == 'bch') { + blockexplorerUrl = 'cashexplorer.bitcoin.com'; + } else { + blockexplorerUrl = 'insight.bitpay.com'; + } + txConfirmNotification.checkIfEnabled(txId, function(res) { $scope.txNotification = { value: res @@ -112,8 +119,8 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Transaction not available at this time')); } - $scope.btx = txFormatService.processTx(tx); - txFormatService.formatAlternativeStr(tx.fees, function(v) { + $scope.btx = txFormatService.processTx($scope.wallet.coin, tx); + txFormatService.formatAlternativeStr($scope.wallet.coin, tx.fees, function(v) { $scope.btx.feeFiatStr = v; $scope.btx.feeRateStr = ($scope.btx.fees / ($scope.btx.amount + $scope.btx.fees) * 100).toFixed(2) + '%'; }); @@ -131,7 +138,7 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio $scope.$digest(); }); - feeService.getFeeLevels(function(err, levels) { + feeService.getFeeLevels($scope.wallet.coin, function(err, levels) { if (err) return; walletService.getLowAmount($scope.wallet, levels, function(err, amount) { if (err) return; @@ -178,7 +185,7 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio $scope.viewOnBlockchain = function() { var btx = $scope.btx; - var url = 'https://' + ($scope.getShortNetworkName() == 'test' ? 'test-' : '') + 'insight.bitpay.com/tx/' + btx.txid; + var url = 'https://' + ($scope.getShortNetworkName() == 'test' ? 'test-' : '') + blockexplorerUrl + '/tx/' + btx.txid; var optIn = true; var title = null; var message = gettextCatalog.getString('View Transaction on Insight'); diff --git a/src/js/controllers/walletDetails.js b/src/js/controllers/walletDetails.js index 912086c79..2f112d21c 100644 --- a/src/js/controllers/walletDetails.js +++ b/src/js/controllers/walletDetails.js @@ -52,7 +52,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun var analyzeUtxos = function() { if (analyzeUtxosDone) return; - feeService.getFeeLevels(function(err, levels) { + feeService.getFeeLevels($scope.wallet.coin, function(err, levels) { if (err) return; walletService.getLowUtxos($scope.wallet, levels, function(err, resp) { if (err || !resp) return; @@ -169,7 +169,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun }); }; - feeService.getFeeLevels(function(err, levels) { + feeService.getFeeLevels($scope.wallet.coin, function(err, levels) { walletService.getTxHistory($scope.wallet, { progressFn: progressFn, feeLevels: levels, diff --git a/src/js/filters/filters.js b/src/js/filters/filters.js index 8d9a56d10..e074f803b 100644 --- a/src/js/filters/filters.js +++ b/src/js/filters/filters.js @@ -27,12 +27,10 @@ angular.module('copayApp.filters', []) } }) .filter('formatFiatAmount', ['$filter', '$locale', 'configService', - function(filter, locale, configService) { + function(filter, locale) { var numberFilter = filter('number'); var formats = locale.NUMBER_FORMATS; - var config = configService.getSync().wallet.settings; return function(amount) { - if (!config) return amount; var fractionSize = 2; var value = numberFilter(amount, fractionSize); diff --git a/src/js/routes.js b/src/js/routes.js index 59cfad4fa..0fe72181d 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -287,7 +287,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr */ .state('tabs.send.amount', { - url: '/amount/:recipientType/:toAddress/:toName/:toEmail/:toColor', + url: '/amount/:recipientType/:toAddress/:toName/:toEmail/:toColor/:coin/:fixedUnit', views: { 'tab-send@tabs': { controller: 'amountController', @@ -296,7 +296,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) .state('tabs.send.confirm', { - url: '/confirm/:recipientType/:toAddress/:toName/:toAmount/:toEmail/:toColor/:description/:useSendMax', + url: '/confirm/:recipientType/:toAddress/:toName/:toAmount/:toEmail/:toColor/:description/:coin/:useSendMax', views: { 'tab-send@tabs': { controller: 'confirmController', @@ -392,15 +392,6 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } } }) - .state('tabs.unit', { - url: '/unit', - views: { - 'tab-settings@tabs': { - controller: 'preferencesUnitController', - templateUrl: 'views/preferencesUnit.html' - } - } - }) .state('tabs.fee', { url: '/fee', views: { @@ -676,12 +667,12 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr abstract: true, params: { id: null, - nextStep: 'tabs.paymentRequest.confirm' + nextStep: 'tabs.paymentRequest.confirm', } }) .state('tabs.paymentRequest.amount', { - url: '/amount', + url: '/amount/:coin', views: { 'tab-receive@tabs': { controller: 'amountController', @@ -690,7 +681,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) .state('tabs.paymentRequest.confirm', { - url: '/confirm/:amount/:currency', + url: '/confirm/:amount/:currency/:coin', views: { 'tab-receive@tabs': { controller: 'customAmountController', @@ -923,6 +914,9 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr controllerAs: 'glidera', templateUrl: 'views/glidera.html' } + }, + params: { + coin: 'btc', } }) .state('tabs.buyandsell.glidera.amount', { @@ -976,6 +970,9 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr controllerAs: 'coinbase', templateUrl: 'views/coinbase.html' } + }, + params: { + coin: 'btc', } }) .state('tabs.preferences.coinbase', { @@ -1064,7 +1061,8 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr params: { nextStep: 'tabs.giftcards.mercadoLibre.buy', currency: 'BRL', - forceCurrency: true + coin: 'btc', + fixedUnit: 1, } }) .state('tabs.giftcards.mercadoLibre.buy', { @@ -1115,7 +1113,8 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr params: { nextStep: 'tabs.giftcards.amazon.buy', currency: 'USD', - forceCurrency: true + coin: 'btc', + fixedUnit: true, } }) .state('tabs.giftcards.amazon.buy', { @@ -1155,6 +1154,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr params: { id: null, currency: 'USD', + coin: 'btc', useSendMax: null } }) diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 87e732bb1..87d1205a6 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -170,9 +170,9 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ var _getNetAmount = function(amount, cb) { // Fee Normal for a single transaction (450 bytes) var txNormalFeeKB = 450 / 1000; - feeService.getFeeRate(null, 'normal', function(err, feePerKB) { + feeService.getFeeRate('btc', 'livenet', 'normal', function(err, feePerKb) { if (err) return cb(err); - var feeBTC = (feePerKB * txNormalFeeKB / 100000000).toFixed(8); + var feeBTC = (feePerKb * txNormalFeeKB / 100000000).toFixed(8); return cb(null, amount - feeBTC, feeBTC); }); diff --git a/src/js/services/configService.js b/src/js/services/configService.js index a17fcf628..8d3bae013 100644 --- a/src/js/services/configService.js +++ b/src/js/services/configService.js @@ -61,7 +61,10 @@ angular.module('copayApp.services').factory('configService', function(storageSer bannedUntil: null, }, - // External services + cashSupport: { + enabled: false, + }, + recentTransactions: { enabled: true, }, @@ -133,6 +136,11 @@ angular.module('copayApp.services').factory('configService', function(storageSer configCache.hideNextSteps = defaultConfig.hideNextSteps; } + + if (!configCache.cashSupport) { + configCache.cashSupport = defaultConfig.cashSupport; + } + if (!configCache.recentTransactions) { configCache.recentTransactions = defaultConfig.recentTransactions; } @@ -143,6 +151,14 @@ angular.module('copayApp.services').factory('configService', function(storageSer configCache.bitpayAccount = defaultConfig.bitpayAccount; } + if (configCache.wallet.settings.unitCode == 'bit') { + // Convert to BTC. Bits will be disabled + configCache.wallet.settings.unitName = defaultConfig.wallet.settings.unitName; + configCache.wallet.settings.unitToSatoshi = defaultConfig.wallet.settings.unitToSatoshi; + configCache.wallet.settings.unitDecimals = defaultConfig.wallet.settings.unitDecimals; + configCache.wallet.settings.unitCode = defaultConfig.wallet.settings.unitCode; + } + } else { configCache = lodash.clone(defaultConfig); }; diff --git a/src/js/services/feeService.js b/src/js/services/feeService.js index 3a289bfd8..2109d7ea0 100644 --- a/src/js/services/feeService.js +++ b/src/js/services/feeService.js @@ -24,20 +24,20 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou }; - root.getFeeRate = function(network, feeLevel, cb) { + root.getFeeRate = function(coin, network, feeLevel, cb) { if (feeLevel == 'custom') return cb(); network = network || 'livenet'; - root.getFeeLevels(function(err, levels, fromCache) { + root.getFeeLevels(coin, function(err, levels, fromCache) { if (err) return cb(err); var feeLevelRate = lodash.find(levels[network], { level: feeLevel }); - if (!feeLevelRate || !feeLevelRate.feePerKB) { + if (!feeLevelRate || !feeLevelRate.feePerKb) { return cb({ message: gettextCatalog.getString("Could not get dynamic fee for level: {{feeLevel}}", { feeLevel: feeLevel @@ -45,29 +45,29 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou }); } - var feeRate = feeLevelRate.feePerKB; + var feeRate = feeLevelRate.feePerKb; - if (!fromCache) $log.debug('Dynamic fee: ' + feeLevel + '/' + network + ' ' + (feeLevelRate.feePerKB / 1000).toFixed() + ' SAT/B'); + if (!fromCache) $log.debug('Dynamic fee: ' + feeLevel + '/' + network + ' ' + (feeLevelRate.feePerKb / 1000).toFixed() + ' SAT/B'); return cb(null, feeRate); }); }; - root.getCurrentFeeRate = function(network, cb) { - return root.getFeeRate(network, root.getCurrentFeeLevel(), cb); + root.getCurrentFeeRate = function(coin, network, cb) { + return root.getFeeRate(coin, network, root.getCurrentFeeLevel(), cb); }; - root.getFeeLevels = function(cb) { + root.getFeeLevels = function(coin, cb) { + coin = coin || 'btc'; if (cache.updateTs > Date.now() - CACHE_TIME_TS * 1000) { return cb(null, cache.data, true); } var walletClient = bwcService.getClient(); - var unitName = configService.getSync().wallet.settings.unitName; - walletClient.getFeeLevels('livenet', function(errLivenet, levelsLivenet) { - walletClient.getFeeLevels('testnet', function(errTestnet, levelsTestnet) { + walletClient.getFeeLevels(coin, 'livenet', function(errLivenet, levelsLivenet) { + walletClient.getFeeLevels('btc', 'testnet', function(errTestnet, levelsTestnet) { if (errLivenet || errTestnet) { return cb(gettextCatalog.getString('Could not get dynamic fee')); } diff --git a/src/js/services/incomingData.js b/src/js/services/incomingData.js index c9d633eb8..8e384b051 100644 --- a/src/js/services/incomingData.js +++ b/src/js/services/incomingData.js @@ -46,7 +46,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat return true; } - function goSend(addr, amount, message) { + function goSend(addr, amount, message, coin) { $state.go('tabs.send', {}, { 'reload': true, 'notify': $state.current.name == 'tabs.send' ? false : true @@ -57,11 +57,13 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat $state.transitionTo('tabs.send.confirm', { toAmount: amount, toAddress: addr, - description: message + description: message, + coin: coin }); } else { $state.transitionTo('tabs.send.amount', { - toAddress: addr + toAddress: addr, + coin: coin }); } }, 100); @@ -90,16 +92,17 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat var message = parsed.message; var amount = parsed.amount ? parsed.amount : ''; + var coin = parsed.extras && parsed.extras.coin ? parsed.extras.coin : ''; if (parsed.r) { payproService.getPayProDetails(parsed.r, function(err, details) { if (err) { - if (addr && amount) goSend(addr, amount, message); + if (addr && amount) goSend(addr, amount, message, coin); else popupService.showAlert(gettextCatalog.getString('Error'), err); } else handlePayPro(details); }); } else { - goSend(addr, amount, message); + goSend(addr, amount, message, coin); } return true; diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js index 9f32b137f..b1b9404fa 100644 --- a/src/js/services/profileService.js +++ b/src/js/services/profileService.js @@ -89,6 +89,7 @@ angular.module('copayApp.services') wallet.copayerId = wallet.credentials.copayerId; wallet.m = wallet.credentials.m; wallet.n = wallet.credentials.n; + wallet.coin = wallet.credentials.coin; root.updateWalletSettings(wallet); root.wallet[walletId] = wallet; @@ -222,11 +223,12 @@ angular.module('copayApp.services') return ((config.bwsFor && config.bwsFor[walletId]) || defaults.bws.url); }; - var client = bwcService.getClient(JSON.stringify(credentials), { bwsurl: getBWSURL(credentials.walletId), }); + + var skipKeyValidation = shouldSkipValidation(credentials.walletId); if (!skipKeyValidation) root.runValidation(client, 500); @@ -328,6 +330,7 @@ angular.module('copayApp.services') passphrase: opts.passphrase, account: opts.account || 0, derivationStrategy: opts.derivationStrategy || 'BIP44', + coin: opts.coin }); } catch (ex) { @@ -346,6 +349,7 @@ angular.module('copayApp.services') walletClient.seedFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, { account: opts.account || 0, derivationStrategy: opts.derivationStrategy || 'BIP44', + coin: opts.coin }); walletClient.credentials.hwInfo = opts.hwInfo; } catch (ex) { @@ -360,6 +364,7 @@ angular.module('copayApp.services') passphrase: opts.passphrase, language: lang, account: 0, + coin: opts.coin }); } catch (e) { $log.info('Error creating recovery phrase: ' + e.message); @@ -369,6 +374,7 @@ angular.module('copayApp.services') network: network, passphrase: opts.passphrase, account: 0, + coin: opts.coin }); } else { return cb(e); @@ -392,6 +398,7 @@ angular.module('copayApp.services') network: opts.networkName, singleAddress: opts.singleAddress, walletPrivKey: opts.walletPrivKey, + coin: opts.coin }, function(err, secret) { if (err) return bwcError.cb(err, gettextCatalog.getString('Error creating wallet'), cb); return cb(null, walletClient, secret); @@ -435,7 +442,7 @@ angular.module('copayApp.services') seedWallet(opts, function(err, walletClient) { if (err) return cb(err); - walletClient.joinWallet(opts.secret, opts.myName || 'me', {}, function(err) { + walletClient.joinWallet(opts.secret, opts.myName || 'me', { coin: opts.coin }, function(err) { if (err) return bwcError.cb(err, gettextCatalog.getString('Could not join wallet'), cb); addAndBindWalletClient(walletClient, { bwsurl: opts.bwsurl @@ -623,6 +630,7 @@ angular.module('copayApp.services') entropySourcePath: opts.entropySourcePath, derivationStrategy: opts.derivationStrategy || 'BIP44', account: opts.account || 0, + coin: opts.coin }, function(err) { if (err) { if (err instanceof errors.NOT_AUTHORIZED) @@ -644,6 +652,7 @@ angular.module('copayApp.services') walletClient.importFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, { account: opts.account || 0, derivationStrategy: opts.derivationStrategy || 'BIP44', + coin: opts.coin }, function(err) { if (err) { @@ -684,6 +693,7 @@ angular.module('copayApp.services') opts.m = 1; opts.n = 1; opts.networkName = 'livenet'; + opts.coin = 'btc'; root.createWallet(opts, cb); }; @@ -749,6 +759,12 @@ angular.module('copayApp.services') var ret = lodash.values(root.wallet); + if (opts.coin) { + ret = lodash.filter(ret, function(x) { + return (x.credentials.coin == opts.coin); + }); + } + if (opts.network) { ret = lodash.filter(ret, function(x) { return (x.credentials.network == opts.network); @@ -861,7 +877,7 @@ angular.module('copayApp.services') x.types = [x.type]; if (x.data && x.data.amount) - x.amountStr = txFormatService.formatAmountStr(x.data.amount); + x.amountStr = txFormatService.formatAmountStr(x.wallet.coin, x.data.amount); x.action = function() { // TODO? diff --git a/src/js/services/rateService.js b/src/js/services/rateService.js index 9cbfe2821..3818120a0 100644 --- a/src/js/services/rateService.js +++ b/src/js/services/rateService.js @@ -25,9 +25,10 @@ var RateService = function(opts) { self._isAvailable = false; self._rates = {}; self._alternatives = []; + self._ratesBCH = {}; self._queued = []; - self._fetchCurrencies(); + self.updateRates(); }; @@ -39,14 +40,20 @@ RateService.singleton = function(opts) { return _instance; }; -RateService.prototype._fetchCurrencies = function() { +RateService.prototype.updateRates = function() { var self = this; var backoffSeconds = 5; var updateFrequencySeconds = 5 * 60; var rateServiceUrl = 'https://bitpay.com/api/rates'; + var bchRateServiceUrl = 'https://api.kraken.com/0/public/Ticker?pair=BCHUSD,BCHEUR'; + + + function getBTC(cb, tries) { + tries = tries || 0; + if (!self.httprequest) return; + if (tries > 5) return cb('could not get BTC rates'); - var retrieve = function() { //log.info('Fetching exchange rates'); self.httprequest.get(rateServiceUrl).success(function(res) { self.lodash.each(res, function(currency) { @@ -57,27 +64,64 @@ RateService.prototype._fetchCurrencies = function() { rate: currency.rate }); }); + + return cb(); + }).error(function() { + //log.debug('Error fetching exchange rates', err); + setTimeout(function() { + backoffSeconds *= 1.5; + getBTC(cb, tries++); + }, backoffSeconds * 1000); + return; + }) + } + + function getBCH(cb, tries) { + tries = tries || 0; + if (!self.httprequest) return; + if (tries > 5) return cb('could not get BCH rates'); + + function retry(tries) { + //log.debug('Error fetching exchange rates', err); + setTimeout(function() { + backoffSeconds *= 1.5; + getBTC(cb, tries++); + }, backoffSeconds * 1000); + return; + } + + self.httprequest.get(bchRateServiceUrl).success(function(res) { + self.lodash.each(res.result, function(data, paircode) { + var code = paircode.substr(3,3); + var rate =data.c[0]; + self._ratesBCH[code] = rate; + }) + return cb(); + }).error(function() { + return retry(tries); + }) + } + + getBTC(function(err) { + if (err) return; + getBCH(function(err) { + if (err) return; + self._isAvailable = true; self.lodash.each(self._queued, function(callback) { setTimeout(callback, 1); }); - setTimeout(retrieve, updateFrequencySeconds * 1000); - }).error(function(err) { - //log.debug('Error fetching exchange rates', err); - setTimeout(function() { - backoffSeconds *= 1.5; - retrieve(); - }, backoffSeconds * 1000); - return; - }); + setTimeout( self.updateRates , updateFrequencySeconds * 1000); + }) + }) - }; - - retrieve(); }; -RateService.prototype.getRate = function(code) { - return this._rates[code]; +RateService.prototype.getRate = function(code, chain) { + if (chain == 'bch') + return this._ratesBCH[code]; + else + return this._rates[code]; }; RateService.prototype.getAlternatives = function() { @@ -90,25 +134,25 @@ RateService.prototype.isAvailable = function() { RateService.prototype.whenAvailable = function(callback) { if (this.isAvailable()) { - setTimeout(callback, 1); + setTimeout(callback, 10); } else { this._queued.push(callback); } }; -RateService.prototype.toFiat = function(satoshis, code) { +RateService.prototype.toFiat = function(satoshis, code, chain) { if (!this.isAvailable()) { return null; } - return satoshis * this.SAT_TO_BTC * this.getRate(code); + return satoshis * this.SAT_TO_BTC * this.getRate(code, chain); }; -RateService.prototype.fromFiat = function(amount, code) { +RateService.prototype.fromFiat = function(amount, code, chain) { if (!this.isAvailable()) { return null; } - return amount / this.getRate(code) * this.BTC_TO_SAT; + return amount / this.getRate(code, chain) * this.BTC_TO_SAT; }; RateService.prototype.listAlternatives = function(sort) { diff --git a/src/js/services/sendMax.js b/src/js/services/sendMax.js index a9c238a1e..9183a3ca2 100644 --- a/src/js/services/sendMax.js +++ b/src/js/services/sendMax.js @@ -10,7 +10,7 @@ angular.module('copayApp.services').service('sendMaxService', function(feeServic * */ this.getInfo = function(wallet, cb) { - feeService.getCurrentFeeRate(wallet.credentials.network, function(err, feePerKb) { + feeService.getCurrentFeeRate(wallet.coin, wallet.credentials.network, function(err, feePerKb) { if (err) return cb(err); var config = configService.getSync().wallet; diff --git a/src/js/services/txFormatService.js b/src/js/services/txFormatService.js index 0df46fe86..a8bc292c6 100644 --- a/src/js/services/txFormatService.js +++ b/src/js/services/txFormatService.js @@ -7,7 +7,7 @@ angular.module('copayApp.services').factory('txFormatService', function($filter, root.formatAmount = function(satoshis, fullPrecision) { - var config = configService.getSync().wallet.settings; + var config = configService.getDefaults().wallet.settings; if (config.unitCode == 'sat') return satoshis; //TODO : now only works for english, specify opts to change thousand separator and decimal separator @@ -17,16 +17,15 @@ angular.module('copayApp.services').factory('txFormatService', function($filter, return this.Utils.formatAmount(satoshis, config.unitCode, opts); }; - root.formatAmountStr = function(satoshis) { + root.formatAmountStr = function(coin, satoshis) { if (isNaN(satoshis)) return; - var config = configService.getSync().wallet.settings; - return root.formatAmount(satoshis) + ' ' + config.unitName; + return root.formatAmount(satoshis) + ' ' + (coin).toUpperCase(); }; - root.toFiat = function(satoshis, code, cb) { + root.toFiat = function(coin, satoshis, code, cb) { if (isNaN(satoshis)) return; var val = function() { - var v1 = rateService.toFiat(satoshis, code); + var v1 = rateService.toFiat(satoshis, code, coin); if (!v1) return null; return v1.toFixed(2); @@ -43,10 +42,10 @@ angular.module('copayApp.services').factory('txFormatService', function($filter, }; }; - root.formatToUSD = function(satoshis, cb) { + root.formatToUSD = function(coin, satoshis, cb) { if (isNaN(satoshis)) return; var val = function() { - var v1 = rateService.toFiat(satoshis, 'USD'); + var v1 = rateService.toFiat(satoshis, 'USD', coin); if (!v1) return null; return v1.toFixed(2); @@ -63,12 +62,12 @@ angular.module('copayApp.services').factory('txFormatService', function($filter, }; }; - root.formatAlternativeStr = function(satoshis, cb) { + root.formatAlternativeStr = function(coin, satoshis, cb) { if (isNaN(satoshis)) return; var config = configService.getSync().wallet.settings; var val = function() { - var v1 = parseFloat((rateService.toFiat(satoshis, config.alternativeIsoCode)).toFixed(2)); + var v1 = parseFloat((rateService.toFiat(satoshis, config.alternativeIsoCode, coin)).toFixed(2)); v1 = $filter('formatFiatAmount')(v1); if (!v1) return null; @@ -86,7 +85,7 @@ angular.module('copayApp.services').factory('txFormatService', function($filter, }; }; - root.processTx = function(tx) { + root.processTx = function(coin, tx) { if (!tx || tx.action == 'invalid') return tx; @@ -101,17 +100,17 @@ angular.module('copayApp.services').factory('txFormatService', function($filter, tx.hasMultiplesOutputs = true; } tx.amount = lodash.reduce(tx.outputs, function(total, o) { - o.amountStr = root.formatAmountStr(o.amount); - o.alternativeAmountStr = root.formatAlternativeStr(o.amount); + o.amountStr = root.formatAmountStr(coin, o.amount); + o.alternativeAmountStr = root.formatAlternativeStr(coin, o.amount); return total + o.amount; }, 0); } tx.toAddress = tx.outputs[0].toAddress; } - tx.amountStr = root.formatAmountStr(tx.amount); - tx.alternativeAmountStr = root.formatAlternativeStr(tx.amount); - tx.feeStr = root.formatAmountStr(tx.fee || tx.fees); + tx.amountStr = root.formatAmountStr(coin, tx.amount); + tx.alternativeAmountStr = root.formatAlternativeStr(coin, tx.amount); + tx.feeStr = root.formatAmountStr(coin, tx.fee || tx.fees); if (tx.amountStr) { tx.amountValueStr = tx.amountStr.split(' ')[0]; @@ -145,8 +144,6 @@ angular.module('copayApp.services').factory('txFormatService', function($filter, lodash.each(txps, function(tx) { - tx = txFormatService.processTx(tx); - // no future transactions... if (tx.createdOn > now) tx.createdOn = now; @@ -157,6 +154,8 @@ angular.module('copayApp.services').factory('txFormatService', function($filter, return; } + tx = txFormatService.processTx(tx.wallet.coin, tx); + var action = lodash.find(tx.actions, { copayerId: tx.wallet.copayerId }); @@ -180,7 +179,7 @@ angular.module('copayApp.services').factory('txFormatService', function($filter, return txps; }; - root.parseAmount = function(amount, currency) { + root.parseAmount = function(coin, amount, currency) { var config = configService.getSync().wallet.settings; var satToBtc = 1 / 100000000; var unitToSatoshi = config.unitToSatoshi; @@ -189,21 +188,21 @@ angular.module('copayApp.services').factory('txFormatService', function($filter, var alternativeIsoCode = config.alternativeIsoCode; // If fiat currency - if (currency != 'bits' && currency != 'BTC' && currency != 'sat') { + if (currency != 'BCH' && currency != 'BTC' && currency != 'sat') { amountUnitStr = $filter('formatFiatAmount')(amount) + ' ' + currency; - amountSat = rateService.fromFiat(amount, currency).toFixed(0); + amountSat = rateService.fromFiat(amount, currency, coin).toFixed(0); } else if (currency == 'sat') { amountSat = amount; - amountUnitStr = root.formatAmountStr(amountSat); - // convert sat to BTC + amountUnitStr = root.formatAmountStr(coin, amountSat); + // convert sat to BTC or BCH amount = (amountSat * satToBtc).toFixed(8); - currency = 'BTC'; + currency = (coin).toUpperCase(); } else { amountSat = parseInt((amount * unitToSatoshi).toFixed(0)); - amountUnitStr = root.formatAmountStr(amountSat); - // convert unit to BTC + amountUnitStr = root.formatAmountStr(coin, amountSat); + // convert unit to BTC or BCH amount = (amountSat * satToBtc).toFixed(8); - currency = 'BTC'; + currency = (coin).toUpperCase(); } return { diff --git a/src/js/services/walletService.js b/src/js/services/walletService.js index 5414da09c..1e7eb6dc8 100644 --- a/src/js/services/walletService.js +++ b/src/js/services/walletService.js @@ -2,8 +2,8 @@ angular.module('copayApp.services').factory('walletService', function($log, $timeout, lodash, trezor, ledger, intelTEE, storageService, configService, rateService, uxLanguage, $filter, gettextCatalog, bwcError, $ionicPopup, fingerprintService, ongoingProcess, gettext, $rootScope, txFormatService, $ionicModal, $state, bwcService, bitcore, popupService) { - // Ratio low amount warning (fee/amount) in incoming TX - var LOW_AMOUNT_RATIO = 0.15; + // Ratio low amount warning (fee/amount) in incoming TX + var LOW_AMOUNT_RATIO = 0.15; // Ratio of "many utxos" warning in total balance (fee/amount) var TOTAL_LOW_WARNING_RATIO = .3; @@ -130,7 +130,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim lodash.each(txps, function(tx) { - tx = txFormatService.processTx(tx); + tx = txFormatService.processTx(wallet.coin, tx); // no future transactions... if (tx.createdOn > now) @@ -213,14 +213,13 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim // Selected unit cache.unitToSatoshi = config.settings.unitToSatoshi; cache.satToUnit = 1 / cache.unitToSatoshi; - cache.unitName = config.settings.unitName; //STR - cache.totalBalanceStr = txFormatService.formatAmount(cache.totalBalanceSat) + ' ' + cache.unitName; - cache.lockedBalanceStr = txFormatService.formatAmount(cache.lockedBalanceSat) + ' ' + cache.unitName; - cache.availableBalanceStr = txFormatService.formatAmount(cache.availableBalanceSat) + ' ' + cache.unitName; - cache.spendableBalanceStr = txFormatService.formatAmount(cache.spendableAmount) + ' ' + cache.unitName; - cache.pendingBalanceStr = txFormatService.formatAmount(cache.pendingAmount) + ' ' + cache.unitName; + cache.totalBalanceStr = txFormatService.formatAmountStr(wallet.coin, cache.totalBalanceSat); + cache.lockedBalanceStr = txFormatService.formatAmountStr(wallet.coin, cache.lockedBalanceSat); + cache.availableBalanceStr = txFormatService.formatAmountStr(wallet.coin, cache.availableBalanceSat); + cache.spendableBalanceStr = txFormatService.formatAmountStr(wallet.coin, cache.spendableAmount); + cache.pendingBalanceStr = txFormatService.formatAmountStr(wallet.coin, cache.pendingAmount); cache.alternativeName = config.settings.alternativeName; cache.alternativeIsoCode = config.settings.alternativeIsoCode; @@ -238,11 +237,11 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim rateService.whenAvailable(function() { - var totalBalanceAlternative = rateService.toFiat(cache.totalBalanceSat, cache.alternativeIsoCode); - var pendingBalanceAlternative = rateService.toFiat(cache.pendingAmount, cache.alternativeIsoCode); - var lockedBalanceAlternative = rateService.toFiat(cache.lockedBalanceSat, cache.alternativeIsoCode); - var spendableBalanceAlternative = rateService.toFiat(cache.spendableAmount, cache.alternativeIsoCode); - var alternativeConversionRate = rateService.toFiat(100000000, cache.alternativeIsoCode); + var totalBalanceAlternative = rateService.toFiat(cache.totalBalanceSat, cache.alternativeIsoCode, wallet.coin); + var pendingBalanceAlternative = rateService.toFiat(cache.pendingAmount, cache.alternativeIsoCode, wallet.coin); + var lockedBalanceAlternative = rateService.toFiat(cache.lockedBalanceSat, cache.alternativeIsoCode, wallet.coin); + var spendableBalanceAlternative = rateService.toFiat(cache.spendableAmount, cache.alternativeIsoCode, wallet.coin); + var alternativeConversionRate = rateService.toFiat(100000000, cache.alternativeIsoCode, wallet.coin); cache.totalBalanceAlternative = $filter('formatFiatAmount')(totalBalanceAlternative); cache.pendingBalanceAlternative = $filter('formatFiatAmount')(pendingBalanceAlternative); @@ -366,7 +365,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim wallet.hasUnsafeConfirmed = false; lodash.each(txs, function(tx) { - tx = txFormatService.processTx(tx); + tx = txFormatService.processTx(wallet.coin, tx); // no future transactions... if (tx.time > now) @@ -400,7 +399,6 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim var LIMIT = 50; var requestLimit = FIRST_LIMIT; var walletId = wallet.credentials.walletId; - var config = configService.getSync().wallet.settings; var opts = opts || {}; var progressFn = opts.progressFn || function() {}; @@ -414,18 +412,16 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim var fixTxsUnit = function(txs) { if (!txs || !txs[0] || !txs[0].amountStr) return; - var cacheUnit = txs[0].amountStr.split(' ')[1]; + var cacheCoin = txs[0].amountStr.split(' ')[1]; - if (cacheUnit == config.unitName) - return; + if (cacheCoin == 'bits') { - var name = ' ' + config.unitName; - - $log.debug('Fixing Tx Cache Unit to:' + name) - lodash.each(txs, function(tx) { - tx.amountStr = txFormatService.formatAmount(tx.amount) + name; - tx.feeStr = txFormatService.formatAmount(tx.fees) + name; - }); + $log.debug('Fixing Tx Cache Unit to: ' + wallet.coin) + lodash.each(txs, function(tx) { + tx.amountStr = txFormatService.formatAmountStr(wallet.coin, tx.amount); + tx.feeStr = txFormatService.formatAmountStr(wallet.coin, tx.fees); + }); + } }; getSavedTxs(walletId, function(err, txsFromLocal) { @@ -788,7 +784,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim //prefs.email (may come from arguments) prefs.email = config.emailNotifications.email; prefs.language = uxLanguage.getCurrentLanguage(); - prefs.unit = walletSettings.unitCode; + // prefs.unit = walletSettings.unitCode; // TODO: remove, not used updateRemotePreferencesFor(lodash.clone(clients), prefs, function(err) { if (err) return cb(err); @@ -922,18 +918,18 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim }; - // Approx utxo amount, from which the uxto is economically redeemable + // Approx utxo amount, from which the uxto is economically redeemable root.getMinFee = function(wallet, feeLevels, nbOutputs) { var lowLevelRate = (lodash.find(feeLevels[wallet.network], { level: 'normal', - }).feePerKB / 1000).toFixed(0); + }).feePerKb / 1000).toFixed(0); var size = root.getEstimatedTxSize(wallet, nbOutputs); return size * lowLevelRate; }; - // Approx utxo amount, from which the uxto is economically redeemable + // 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); @@ -943,7 +939,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim root.getLowUtxos = function(wallet, levels, cb) { - wallet.getUtxos({}, 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); diff --git a/src/sass/views/amount.scss b/src/sass/views/amount.scss index 627f905e8..bd45e524a 100644 --- a/src/sass/views/amount.scss +++ b/src/sass/views/amount.scss @@ -139,6 +139,19 @@ } } .amount { + .icon-toggle { + font-size: 1.2em; + width: auto; + margin: 0.8em auto; + border: 1px solid $v-subtle-gray; + color: $v-dark-gray; + border-radius: 3px; + padding: 0 10px; + cursor: pointer; + @media(max-height: 280px) { + margin: 0.1em auto; + } + } &__editable { &--minimize { font-size: 22px; @@ -187,7 +200,7 @@ &__result { color: $v-light-gray; font-size: .9em; - margin-bottom: -.9em; + //margin-bottom: -.9em; TODO matias line-height: 1; @media(max-height: 480px) { margin-bottom: 0; @@ -196,7 +209,6 @@ &__result-equiv { color: $v-mid-gray; font-size: 1.2em; - margin-top: 2rem; @media(max-height: 480px) { margin-top: 0; font-size: 16px; diff --git a/src/sass/views/walletDetails.scss b/src/sass/views/walletDetails.scss index a3ef99348..85baf42b8 100644 --- a/src/sass/views/walletDetails.scss +++ b/src/sass/views/walletDetails.scss @@ -215,6 +215,11 @@ &__button-balance { background-color: transparent; border: 1px solid rgba(255,255,255,0.25); + margin-top: 10px; + i.icon { + margin-right: 7px; + vertical-align: middle; + } } &__error { diff --git a/www/img/icon-bch.svg b/www/img/icon-bch.svg new file mode 100644 index 000000000..b02172ceb --- /dev/null +++ b/www/img/icon-bch.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/www/img/icon-btc.svg b/www/img/icon-btc.svg new file mode 100644 index 000000000..da480b629 --- /dev/null +++ b/www/img/icon-btc.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/www/img/icon-wallet-testnet.svg b/www/img/icon-wallet-testnet.svg new file mode 100644 index 000000000..c1759c599 --- /dev/null +++ b/www/img/icon-wallet-testnet.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/www/views/advancedSettings.html b/www/views/advancedSettings.html index 68f1f58a5..85dcaf0d4 100644 --- a/www/views/advancedSettings.html +++ b/www/views/advancedSettings.html @@ -7,6 +7,17 @@
+ + + Support Bitcoin Cash + +
+ Enable Bitcoin Cash wallet creation and operation within the App. + Learn more +
+ + + Use Unconfirmed Funds diff --git a/www/views/amount.html b/www/views/amount.html index 5dfee29e5..5d79f8ad7 100644 --- a/www/views/amount.html +++ b/www/views/amount.html @@ -48,16 +48,11 @@
{{amount || "0.00" }} - {{alternativeIsoCode}} - {{unitName}} + {{unit}}
-
-
{{globalResult}} {{unitName}}
-
≈ {{amountResult || '0.00'}} {{alternativeIsoCode}}
-
-
-
{{globalResult}} {{alternativeIsoCode}}
-
{{alternativeResult || '0.00'}} {{unitName}}
+
+
{{globalResult}} {{unit}}
+
≈ {{alternativeAmount || '0.00'}} {{alternativeUnit}}
diff --git a/www/views/buyAmazon.html b/www/views/buyAmazon.html index ded0aca93..3ea4d78c6 100644 --- a/www/views/buyAmazon.html +++ b/www/views/buyAmazon.html @@ -29,7 +29,7 @@
From
- + {{wallet ? wallet.name : '...'}}
diff --git a/www/views/buyCoinbase.html b/www/views/buyCoinbase.html index 8ae1999a0..fe1371ce9 100644 --- a/www/views/buyCoinbase.html +++ b/www/views/buyCoinbase.html @@ -38,7 +38,7 @@
Receive in
- + {{wallet ? wallet.name : '...'}}
diff --git a/www/views/buyGlidera.html b/www/views/buyGlidera.html index df8e386e9..5f1168996 100644 --- a/www/views/buyGlidera.html +++ b/www/views/buyGlidera.html @@ -30,7 +30,7 @@
Receive in
- + {{wallet ? wallet.name : '...'}}
diff --git a/www/views/buyMercadoLibre.html b/www/views/buyMercadoLibre.html index 7791aee82..322259a01 100644 --- a/www/views/buyMercadoLibre.html +++ b/www/views/buyMercadoLibre.html @@ -28,7 +28,7 @@
From
- + {{wallet ? wallet.name : '...'}}
diff --git a/www/views/confirm.html b/www/views/confirm.html index a2b85ad88..ab289f266 100644 --- a/www/views/confirm.html +++ b/www/views/confirm.html @@ -65,7 +65,7 @@ From
- +
{{wallet.name}}
@@ -79,18 +79,18 @@
{{'Fee:' | translate}} {{tx.feeLevelName | translate}} - {{tx.txp[wallet.id].feeStr || '...'}} - - {{tx.txp[wallet.id].alternativeFeeStr || '...'}}  - · -   - {{tx.txp[wallet.id].feeRatePerStr}} of the sending amount + {{tx.txp[wallet.id].feeStr || '...'}} + + {{tx.txp[wallet.id].alternativeFeeStr || '...'}}  + · +   + {{tx.txp[wallet.id].feeRatePerStr}} of the sending amount + + - - - +
Add Memo diff --git a/www/views/customAmount.html b/www/views/customAmount.html index f9d5aafb7..c05c8d704 100644 --- a/www/views/customAmount.html +++ b/www/views/customAmount.html @@ -21,7 +21,7 @@
- +
@@ -39,7 +39,7 @@
- + {{wallet.name}}
diff --git a/www/views/includes/cash.html b/www/views/includes/cash.html new file mode 100644 index 000000000..e67fb182b --- /dev/null +++ b/www/views/includes/cash.html @@ -0,0 +1,9 @@ + diff --git a/www/views/includes/walletItem.html b/www/views/includes/walletItem.html index e087e1410..1d5ba209e 100644 --- a/www/views/includes/walletItem.html +++ b/www/views/includes/walletItem.html @@ -1,6 +1,6 @@
- +
{{wallet.name || wallet.id}} diff --git a/www/views/includes/walletSelector.html b/www/views/includes/walletSelector.html index 28b77830f..3fa8860f1 100644 --- a/www/views/includes/walletSelector.html +++ b/www/views/includes/walletSelector.html @@ -7,7 +7,7 @@ ng-click="selectWallet(w)" > - +
diff --git a/www/views/join.html b/www/views/join.html index 411b8b190..8b858fa80 100644 --- a/www/views/join.html +++ b/www/views/join.html @@ -39,6 +39,9 @@
+ +
+
diff --git a/www/views/modals/txp-details.html b/www/views/modals/txp-details.html index 144202699..c5e2aea91 100644 --- a/www/views/modals/txp-details.html +++ b/www/views/modals/txp-details.html @@ -74,7 +74,7 @@ From
- +
{{wallet.name}}
diff --git a/www/views/paperWallet.html b/www/views/paperWallet.html index 81b84ba41..d92aba85e 100644 --- a/www/views/paperWallet.html +++ b/www/views/paperWallet.html @@ -24,7 +24,7 @@
- + {{wallet.name || wallet.id}} diff --git a/www/views/preferencesUnit.html b/www/views/preferencesUnit.html deleted file mode 100644 index 77edf05d7..000000000 --- a/www/views/preferencesUnit.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - {{'Unit'|translate}} - - - - - - {{unit.shortName}} - - - diff --git a/www/views/sellCoinbase.html b/www/views/sellCoinbase.html index 5827efda6..ae715ebe4 100644 --- a/www/views/sellCoinbase.html +++ b/www/views/sellCoinbase.html @@ -29,7 +29,7 @@
From
- + {{wallet ? wallet.name : '...'}}
diff --git a/www/views/sellGlidera.html b/www/views/sellGlidera.html index cb354bac0..e3ac25c31 100644 --- a/www/views/sellGlidera.html +++ b/www/views/sellGlidera.html @@ -30,7 +30,7 @@
From
- + {{wallet ? wallet.name : '...'}}
diff --git a/www/views/tab-create-personal.html b/www/views/tab-create-personal.html index 9b695aefc..505f854d4 100644 --- a/www/views/tab-create-personal.html +++ b/www/views/tab-create-personal.html @@ -19,6 +19,8 @@ required> +
+
diff --git a/www/views/tab-create-shared.html b/www/views/tab-create-shared.html index 700ac9c2b..cdbb7ceec 100644 --- a/www/views/tab-create-shared.html +++ b/www/views/tab-create-shared.html @@ -29,6 +29,8 @@ ng-blur="formFocus(false)"> +
+
+ +
+
diff --git a/www/views/tab-receive.html b/www/views/tab-receive.html index 7f42ab857..95c35b8de 100644 --- a/www/views/tab-receive.html +++ b/www/views/tab-receive.html @@ -63,7 +63,7 @@