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