diff --git a/public/views/glideraUri.html b/public/views/glideraUri.html
index 361c856e1..20c661c87 100644
--- a/public/views/glideraUri.html
+++ b/public/views/glideraUri.html
@@ -23,7 +23,7 @@

+ ng-click="index.updateGlidera()" width="100">
diff --git a/public/views/modals/destination-address.html b/public/views/modals/destination-address.html
index ee9cf1e51..4d94ab27a 100644
--- a/public/views/modals/destination-address.html
+++ b/public/views/modals/destination-address.html
@@ -64,9 +64,9 @@
{{w.name || w.id}}
-
+
- Needs backup
+ {{errorSelectedWallet[w.id] }}
{{w.m}} of {{w.n}}
diff --git a/src/js/controllers/backup.js b/src/js/controllers/backup.js
index c5653b3e9..b205d63bb 100644
--- a/src/js/controllers/backup.js
+++ b/src/js/controllers/backup.js
@@ -1,13 +1,21 @@
'use strict';
angular.module('copayApp.controllers').controller('backupController',
- function($rootScope, $scope, $timeout, $log, $state, $compile, go, lodash, profileService, gettext, bwcService, bwsError) {
+ function($rootScope, $scope, $timeout, $log, $state, $compile, go, lodash, profileService, gettext, bwcService, bwsError, walletService) {
var self = this;
var fc = profileService.focusedClient;
var customWords = [];
self.walletName = fc.credentials.walletName;
+ var handleEncryptedWallet = function(client, cb) {
+ if (!walletService.isEncrypted(client)) return cb();
+ $rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
+ if (err) return cb(err);
+ return cb(walletService.unlock(client, password));
+ });
+ };
+
function init() {
$scope.passphrase = '';
resetAllButtons();
@@ -46,7 +54,7 @@ angular.module('copayApp.controllers').controller('backupController',
function initWords() {
var words = fc.getMnemonic();
self.xPrivKey = fc.credentials.xPrivKey;
- profileService.lockFC();
+ walletService.lock(fc);
self.mnemonicWords = words.split(/[\u3000\s]+/);
self.shuffledMnemonicWords = lodash.sortBy(self.mnemonicWords);;
self.mnemonicHasPassphrase = fc.mnemonicHasPassphrase();
@@ -74,7 +82,7 @@ angular.module('copayApp.controllers').controller('backupController',
$scope.$apply();
}, 1);
- profileService.unlockFC({}, function(err) {
+ handleEncryptedWallet(fc, function(err) {
if (err) {
self.error = bwsError.msg(err, gettext('Could not decrypt'));
$log.warn('Error decrypting credentials:', self.error); //TODO
diff --git a/src/js/controllers/buyCoinbase.js b/src/js/controllers/buyCoinbase.js
index 00596faae..51d37ffa8 100644
--- a/src/js/controllers/buyCoinbase.js
+++ b/src/js/controllers/buyCoinbase.js
@@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('buyCoinbaseController',
- function($scope, $modal, $log, $timeout, lodash, profileService, coinbaseService, animationService, txService, bwsError, addressService) {
+ function($scope, $modal, $log, $timeout, lodash, profileService, coinbaseService, animationService, bwsError, addressService, walletService) {
window.ignoreMobilePause = true;
var self = this;
@@ -71,17 +71,17 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController',
};
$scope.selectWallet = function(walletId, walletName) {
- if (!profileService.getClient(walletId).isComplete()) {
- self.error = bwsError.msg({
- 'code': 'WALLET_NOT_COMPLETE'
- }, 'Could not choose the wallet');
- self.error = {errors: [{ message: 'The Wallet could not be selected' }]};
- $modalInstance.dismiss('cancel');
- return;
- }
- $modalInstance.close({
- 'walletId': walletId,
- 'walletName': walletName,
+ var client = profileService.getClient(walletId);
+ walletService.isReady(client, function(err) {
+ if (err) {
+ self.error = {errors: [{ message: err }]};
+ $modalInstance.dismiss('cancel');
+ } else {
+ $modalInstance.close({
+ 'walletId': walletId,
+ 'walletName': walletName,
+ });
+ }
});
};
};
diff --git a/src/js/controllers/buyGlidera.js b/src/js/controllers/buyGlidera.js
index d3e163e8c..2add687cb 100644
--- a/src/js/controllers/buyGlidera.js
+++ b/src/js/controllers/buyGlidera.js
@@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('buyGlideraController',
- function($scope, $timeout, $modal, profileService, addressService, glideraService, bwsError, lodash, isChromeApp, animationService) {
+ function($scope, $timeout, $modal, profileService, addressService, glideraService, bwsError, lodash, isChromeApp, animationService, walletService) {
var self = this;
this.show2faCodeInput = null;
@@ -50,14 +50,17 @@ angular.module('copayApp.controllers').controller('buyGlideraController',
};
$scope.selectWallet = function(walletId, walletName) {
- if (!profileService.getClient(walletId).isComplete()) {
- self.error = bwsError.msg({'code': 'WALLET_NOT_COMPLETE'}, 'Could not choose the wallet');
- $modalInstance.dismiss('cancel');
- return;
- }
- $modalInstance.close({
- 'walletId': walletId,
- 'walletName': walletName,
+ var client = profileService.getClient(walletId);
+ walletService.isReady(client, function(err) {
+ if (err) {
+ self.error = err;
+ $modalInstance.dismiss('cancel');
+ return;
+ }
+ $modalInstance.close({
+ 'walletId': walletId,
+ 'walletName': walletName,
+ });
});
};
};
@@ -94,26 +97,24 @@ angular.module('copayApp.controllers').controller('buyGlideraController',
self.gettingBuyPrice = false;
if (err) {
self.error = 'Could not get exchange information. Please, try again.';
+ return;
}
- else {
- self.buyPrice = buyPrice;
- }
+ self.buyPrice = buyPrice;
});
};
this.get2faCode = function(token) {
var self = this;
- this.loading = 'Sending 2FA code...';
+ self.error = null;
+ self.loading = 'Sending 2FA code...';
$timeout(function() {
glideraService.get2faCode(token, function(err, sent) {
self.loading = null;
if (err) {
self.error = 'Could not send confirmation code to your phone';
+ return;
}
- else {
- self.error = null;
- self.show2faCodeInput = sent;
- }
+ self.show2faCodeInput = sent;
});
}, 100);
};
@@ -139,11 +140,10 @@ angular.module('copayApp.controllers').controller('buyGlideraController',
self.loading = null;
if (err) {
self.error = err;
+ return;
}
- else {
- self.success = data;
- $scope.$emit('Local/GlideraTx');
- }
+ self.success = data;
+ $scope.$emit('Local/GlideraTx');
});
});
}, 100);
diff --git a/src/js/controllers/glidera.js b/src/js/controllers/glidera.js
index df876cf9a..fbce3e95d 100644
--- a/src/js/controllers/glidera.js
+++ b/src/js/controllers/glidera.js
@@ -3,8 +3,6 @@
angular.module('copayApp.controllers').controller('glideraController',
function($rootScope, $scope, $timeout, $modal, profileService, configService, storageService, glideraService, isChromeApp, animationService, lodash) {
- window.ignoreMobilePause = true;
-
this.getAuthenticateUrl = function() {
return glideraService.getOauthCodeUrl();
};
diff --git a/src/js/controllers/index.js b/src/js/controllers/index.js
index dc4f08ce0..177aa4a21 100644
--- a/src/js/controllers/index.js
+++ b/src/js/controllers/index.js
@@ -1,6 +1,6 @@
'use strict';
-angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, latestReleaseService, bwcService, pushNotificationsService, lodash, go, profileService, configService, isCordova, rateService, storageService, addressService, gettext, gettextCatalog, amMoment, nodeWebkit, addonManager, isChromeApp, bwsError, txFormatService, uxLanguage, $state, glideraService, coinbaseService, isMobile, addressbookService) {
+angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, latestReleaseService, bwcService, pushNotificationsService, lodash, go, profileService, configService, isCordova, rateService, storageService, addressService, gettext, gettextCatalog, amMoment, nodeWebkit, addonManager, isChromeApp, bwsError, txFormatService, uxLanguage, $state, glideraService, coinbaseService, isMobile, addressbookService, walletService) {
var self = this;
var SOFT_CONFIRMATION_LIMIT = 12;
var errors = bwcService.getErrors();
@@ -161,7 +161,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r
}
}
- profileService.isBackupNeeded(self.walletId, function(needsBackup) {
+ walletService.isBackupNeeded(fc, function(needsBackup) {
self.needsBackup = needsBackup;
self.openWallet(function() {
if (!self.isComplete) {
diff --git a/src/js/controllers/preferences.js b/src/js/controllers/preferences.js
index ba2e7ea75..fb3e98b31 100644
--- a/src/js/controllers/preferences.js
+++ b/src/js/controllers/preferences.js
@@ -1,37 +1,39 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesController',
- function($scope, $rootScope, $timeout, $log, configService, profileService, txService) {
+ function($scope, $rootScope, $timeout, $log, configService, profileService, fingerprintService, walletService) {
var fc = profileService.focusedClient;
+ var config = configService.getSync();
$scope.deleted = false;
if (fc.credentials && !fc.credentials.mnemonicEncrypted && !fc.credentials.mnemonic) {
$scope.deleted = true;
}
this.init = function() {
- var config = configService.getSync();
- var fc = profileService.focusedClient;
if (fc) {
- $scope.encrypt = fc.hasPrivKeyEncrypted();
+ $scope.encrypt = walletService.isEncrypted(fc);
this.externalSource = fc.getPrivKeyExternalSourceName() == 'ledger' ? "Ledger" : null;
// TODO externalAccount
//this.externalIndex = fc.getExternalIndex();
}
- var walletId = fc.credentials.walletId;
- config.touchIdFor = config.touchIdFor || {};
- $scope.touchid = config.touchIdFor[walletId];
+ this.touchidAvailable = fingerprintService.isAvailable();
+ $scope.touchid = config.touchIdFor ? config.touchIdFor[fc.credentials.walletId] : null;
+ };
- if (window.touchidAvailable)
- this.touchidAvailable = true;
+ var handleEncryptedWallet = function(client, cb) {
+ $rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
+ if (err) return cb(err);
+ return cb(walletService.unlock(client, password));
+ });
};
var unwatchEncrypt = $scope.$watch('encrypt', function(val) {
var fc = profileService.focusedClient;
if (!fc) return;
- if (val && !fc.hasPrivKeyEncrypted()) {
+ if (val && !walletService.isEncrypted(fc)) {
$rootScope.$emit('Local/NeedsPassword', true, function(err, password) {
if (err || !password) {
$scope.encrypt = false;
@@ -43,8 +45,8 @@ angular.module('copayApp.controllers').controller('preferencesController',
});
});
} else {
- if (!val && fc.hasPrivKeyEncrypted()) {
- profileService.unlockFC({}, function(err) {
+ if (!val && walletService.isEncrypted(fc)) {
+ handleEncryptedWallet(fc, function(err) {
if (err) {
$scope.encrypt = true;
return;
@@ -68,29 +70,29 @@ angular.module('copayApp.controllers').controller('preferencesController',
$scope.touchidError = false;
return;
}
- var walletId = profileService.focusedClient.credentials.walletId;
+ var walletId = fc.credentials.walletId;
var opts = {
touchIdFor: {}
};
opts.touchIdFor[walletId] = newVal;
- txService.setTouchId(function(err) {
+ fingerprintService.check(fc, function(err) {
if (err) {
$log.debug(err);
$timeout(function() {
$scope.touchidError = true;
$scope.touchid = oldVal;
}, 100);
- } else {
- configService.set(opts, function(err) {
- if (err) {
- $log.debug(err);
- $scope.touchidError = true;
- $scope.touchid = oldVal;
- }
- });
+ return;
}
+ configService.set(opts, function(err) {
+ if (err) {
+ $log.debug(err);
+ $scope.touchidError = true;
+ $scope.touchid = oldVal;
+ }
+ });
});
});
diff --git a/src/js/controllers/sellCoinbase.js b/src/js/controllers/sellCoinbase.js
index 0aeb7f959..607244eb5 100644
--- a/src/js/controllers/sellCoinbase.js
+++ b/src/js/controllers/sellCoinbase.js
@@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('sellCoinbaseController',
- function($scope, $modal, $log, $timeout, lodash, profileService, coinbaseService, animationService, txService, bwsError) {
+ function($scope, $modal, $log, $timeout, lodash, profileService, coinbaseService, animationService, bwsError, configService, walletService, fingerprintService) {
window.ignoreMobilePause = true;
var self = this;
@@ -38,6 +38,14 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController',
});
};
+ var handleEncryptedWallet = function(client, cb) {
+ if (!walletService.isEncrypted(client)) return cb();
+ $rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
+ if (err) return cb(err);
+ return cb(walletService.unlock(client, password));
+ });
+ };
+
this.init = function(testnet) {
self.otherWallets = otherWallets(testnet);
// Choose focused wallet
@@ -179,6 +187,9 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController',
var accountId = account.id;
var dataSrc = { name : 'Received from Copay: ' + self.selectedWalletName };
var outputs = [];
+ var config = configService.getSync();
+ var configWallet = config.wallet;
+ var walletSettings = configWallet.settings;
self.loading = 'Creating transaction...';
@@ -202,17 +213,18 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController',
'amount': amount,
'message': comment
});
-
- var opts = {
- selectedClient: fc,
+
+ var txp = {
toAddress: address,
amount: amount,
outputs: outputs,
message: comment,
- payProUrl: null
+ payProUrl: null,
+ excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true,
+ feeLevel: walletSettings.feeLevel || 'normal'
};
- txService.createTx(opts, function(err, txp) {
+ walletService.createTx(fc, txp, function(err, createdTxp) {
if (err) {
$log.debug(err);
self.loading = null;
@@ -220,10 +232,10 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController',
$scope.$apply();
return;
}
- $scope.$emit('Local/NeedsConfirmation', txp, function(accept) {
+ $scope.$emit('Local/NeedsConfirmation', createdTxp, function(accept) {
self.loading = null;
if (accept) {
- self.confirmTx(txp, function(err, tx) {
+ self.confirmTx(createdTxp, function(err, tx) {
if (err) {
self.error = {errors: [{ message: 'Could not create transaction: ' + err.message }]};
return;
@@ -266,34 +278,54 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController',
};
this.confirmTx = function(txp, cb) {
- txService.prepare({selectedClient: fc}, function(err) {
+
+ fingerprintService.check(fc, function(err) {
if (err) {
$log.debug(err);
return cb(err);
}
- self.loading = 'Sending bitcoin to Coinbase...';
- txService.publishTx(txp, {selectedClient: fc}, function(err, txpPublished) {
+
+ handleEncryptedWallet(fc, function(err) {
if (err) {
- self.loading = null;
$log.debug(err);
- return cb({errors: [{ message: 'Transaction could not be published: ' + err.message }]});
- } else {
- txService.signAndBroadcast(txpPublished, {selectedClient: fc}, function(err, txp) {
+ return cb(err);
+ }
+
+ self.loading = 'Sending bitcoin to Coinbase...';
+ walletService.publishTx(fc, txp, function(err, publishedTxp) {
+ if (err) {
+ self.loading = null;
+ $log.debug(err);
+ return cb({errors: [{ message: 'Transaction could not be published: ' + err.message }]});
+ }
+
+ walletService.signTx(fc, publishedTxp, function(err, signedTxp) {
+ walletService.lock(fc);
if (err) {
self.loading = null;
$log.debug(err);
- txService.removeTx(txp, function(err) {
+ walletService.removeTx(fc, signedTxp, function(err) {
if (err) $log.debug(err);
});
return cb({errors: [{ message: 'The payment was created but could not be completed: ' + err.message }]});
- } else {
+ }
+
+ walletService.broadcastTx(fc, signedTxp, function(err, broadcastedTxp) {
+ if (err) {
+ self.loading = null;
+ $log.debug(err);
+ walletService.removeTx(fc, broadcastedTxp, function(err) {
+ if (err) $log.debug(err);
+ });
+ return cb({errors: [{ message: 'The payment was created but could not be broadcasted: ' + err.message }]});
+ }
$timeout(function() {
self.loading = null;
return cb(null, txp);
}, 5000);
- }
+ });
});
- }
+ });
});
});
};
diff --git a/src/js/controllers/sellGlidera.js b/src/js/controllers/sellGlidera.js
index 94e6901b0..129f2fb53 100644
--- a/src/js/controllers/sellGlidera.js
+++ b/src/js/controllers/sellGlidera.js
@@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('sellGlideraController',
- function($scope, $timeout, $log, $modal, configService, profileService, addressService, feeService, glideraService, bwsError, lodash, isChromeApp, animationService, txService) {
+ function($rootScope, $scope, $timeout, $log, $modal, configService, profileService, addressService, feeService, glideraService, bwsError, lodash, isChromeApp, animationService, walletService, fingerprintService) {
var self = this;
var config = configService.getSync();
@@ -10,7 +10,6 @@ angular.module('copayApp.controllers').controller('sellGlideraController',
this.success = null;
this.error = null;
this.loading = null;
- this.currentSpendUnconfirmed = config.wallet.spendUnconfirmed;
var fc;
window.ignoreMobilePause = true;
@@ -22,6 +21,14 @@ angular.module('copayApp.controllers').controller('sellGlideraController',
});
};
+ var handleEncryptedWallet = function(client, cb) {
+ if (!walletService.isEncrypted(client)) return cb();
+ $rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
+ if (err) return cb(err);
+ return cb(walletService.unlock(client, password));
+ });
+ };
+
this.init = function(testnet) {
self.otherWallets = otherWallets(testnet);
// Choose focused wallet
@@ -92,20 +99,19 @@ angular.module('copayApp.controllers').controller('sellGlideraController',
this.getSellPrice = function(token, price) {
var self = this;
- this.error = null;
+ self.error = null;
if (!price || (price && !price.qty && !price.fiat)) {
- this.sellPrice = null;
+ self.sellPrice = null;
return;
}
- this.gettingSellPrice = true;
+ self.gettingSellPrice = true;
glideraService.sellPrice(token, price, function(err, sellPrice) {
self.gettingSellPrice = false;
if (err) {
self.error = 'Could not get exchange information. Please, try again.';
- } else {
- self.error = null;
- self.sellPrice = sellPrice;
+ return;
}
+ self.sellPrice = sellPrice;
});
};
@@ -127,87 +133,112 @@ angular.module('copayApp.controllers').controller('sellGlideraController',
this.createTx = function(token, permissions, twoFaCode) {
var self = this;
self.error = null;
-
-
- txService.prepare({selectedClient: fc}, function(err) {
- if (err) {
- self.error = err;
+ var outputs = [];
+ var configWallet = config.wallet;
+ var walletSettings = configWallet.settings;
+
+ addressService.getAddress(fc.credentials.walletId, null, function(err, refundAddress) {
+ if (!refundAddress) {
+ self.loading = null;
+ self.error = bwsError.msg(err, 'Could not create address');
return;
}
- self.loading = 'Selling Bitcoin...';
- $timeout(function() {
- addressService.getAddress(fc.credentials.walletId, null, function(err, refundAddress) {
- if (!refundAddress) {
- self.loading = null;
- self.error = bwsError.msg(err, 'Could not create address');
+ glideraService.getSellAddress(token, function(error, sellAddress) {
+ if (!sellAddress) {
+ self.loading = null;
+ self.error = 'Could not get the destination bitcoin address';
+ return;
+ }
+ var amount = parseInt((self.sellPrice.qty * 100000000).toFixed(0));
+ var comment = 'Glidera transaction';
+
+ outputs.push({
+ 'toAddress': sellAddress,
+ 'amount': amount,
+ 'message': comment
+ });
+
+ var txp = {
+ toAddress: sellAddress,
+ amount: amount,
+ outputs: outputs,
+ message: comment,
+ payProUrl: null,
+ excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true,
+ feeLevel: walletSettings.feeLevel || 'normal',
+ customData: {
+ 'glideraToken': token
+ }
+ };
+
+ self.loading = 'Creating transaction...';
+ walletService.createTx(fc, txp, function(err, createdTxp) {
+ self.loading = null;
+ if (err) {
+ self.error = err.message || bwsError.msg(err);
return;
}
- glideraService.getSellAddress(token, function(error, sellAddress) {
- if (!sellAddress) {
- self.loading = null;
- self.error = 'Could not get the destination bitcoin address';
- return;
- }
- var amount = parseInt((self.sellPrice.qty * 100000000).toFixed(0));
-
- feeService.getCurrentFeeValue(function(err, feePerKb) {
- if (err) $log.debug(err);
- fc.sendTxProposal({
- toAddress: sellAddress,
- amount: amount,
- message: 'Glidera transaction',
- customData: {
- 'glideraToken': token
- },
- payProUrl: null,
- feePerKb: feePerKb,
- excludeUnconfirmedUtxos: self.currentSpendUnconfirmed ? false : true
- }, function(err, txp) {
+ $scope.$emit('Local/NeedsConfirmation', createdTxp, function(accept) {
+ if (accept) {
+ fingerprintService.check(fc, function(err) {
if (err) {
- profileService.lockFC();
- $log.error(err);
- $timeout(function() {
- self.loading = null;
- self.error = bwsError.msg(err, 'Error');
- }, 1);
+ self.error = err.message || bwsError.msg(err);
return;
}
- txService.sign(txp, {selectedClient: fc}, function(err, txp) {
+ handleEncryptedWallet(fc, function(err) {
if (err) {
- self.loading = null;
- self.error = err;
- $scope.$apply();
- } else {
- var rawTx = txp.raw;
- var data = {
- refundAddress: refundAddress,
- signedTransaction: rawTx,
- priceUuid: self.sellPrice.priceUuid,
- useCurrentPrice: self.sellPrice.priceUuid ? false : true,
- ip: null
- };
- glideraService.sell(token, twoFaCode, data, function(err, data) {
+ self.error = err.message || bwsError.msg(err);
+ return;
+ }
+
+ self.loading = 'Signing transaction...';
+
+ walletService.publishTx(fc, createdTxp, function(err, publishedTxp) {
+ if (err) {
self.loading = null;
+ self.error = err.message || bwsError.msg(err);
+ }
+
+ walletService.signTx(fc, publishedTxp, function(err, signedTxp) {
+ walletService.lock(fc);
+ walletService.removeTx(fc, signedTxp, function(err) {
+ if (err) $log.debug(err);
+ });
if (err) {
- self.error = err;
- fc.removeTxProposal(txp, function(err, txp) {
+ self.loading = null;
+ self.error = err.message || bwsError.msg(err);
+ return;
+ }
+ var rawTx = signedTxp.raw;
+ var data = {
+ refundAddress: refundAddress,
+ signedTransaction: rawTx,
+ priceUuid: self.sellPrice.priceUuid,
+ useCurrentPrice: self.sellPrice.priceUuid ? false : true,
+ ip: null
+ };
+ self.loading = 'Selling bitcoin...';
+ glideraService.sell(token, twoFaCode, data, function(err, data) {
+ self.loading = null;
+ if (err) {
+ self.error = err.message || bwsError.msg(err);
$timeout(function() {
$scope.$emit('Local/GlideraError');
}, 100);
- });
- } else {
+ return;
+ }
self.success = data;
$scope.$emit('Local/GlideraTx');
- }
+ });
});
- }
+ });
});
});
- });
+ }
});
});
- }, 100);
+ });
});
};
});
diff --git a/src/js/controllers/walletHome.js b/src/js/controllers/walletHome.js
index 02aff04e5..5108e4b09 100644
--- a/src/js/controllers/walletHome.js
+++ b/src/js/controllers/walletHome.js
@@ -1,6 +1,6 @@
'use strict';
-angular.module('copayApp.controllers').controller('walletHomeController', function($scope, $rootScope, $interval, $timeout, $filter, $modal, $log, notification, txStatus, isCordova, isMobile, profileService, lodash, configService, rateService, storageService, bitcore, isChromeApp, gettext, gettextCatalog, nodeWebkit, addressService, ledger, bwsError, confirmDialog, txFormatService, animationService, addressbookService, go, feeService, txService) {
+angular.module('copayApp.controllers').controller('walletHomeController', function($scope, $rootScope, $interval, $timeout, $filter, $modal, $log, notification, txStatus, isCordova, isMobile, profileService, lodash, configService, rateService, storageService, bitcore, isChromeApp, gettext, gettextCatalog, nodeWebkit, addressService, ledger, bwsError, confirmDialog, txFormatService, animationService, addressbookService, go, feeService, walletService, fingerprintService) {
var self = this;
window.ignoreMobilePause = false;
@@ -127,6 +127,14 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
});
};
+ var handleEncryptedWallet = function(client, cb) {
+ if (!walletService.isEncrypted(client)) return cb();
+ $rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
+ if (err) return cb(err);
+ return cb(walletService.unlock(client, password));
+ });
+ };
+
var accept_msg = gettextCatalog.getString('Accept');
var cancel_msg = gettextCatalog.getString('Cancel');
var confirm_msg = gettextCatalog.getString('Confirm');
@@ -251,28 +259,30 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
};
$scope.selectWallet = function(walletId, walletName) {
- profileService.isBackupNeeded(walletId, function(needsBackup) {
- $scope.needsBackup = {};
- $scope.needsBackup[walletId] = needsBackup;
- if (needsBackup) return;
+ var client = profileService.getClient(walletId);
+ $scope.errorSelectedWallet = {};
- $scope.gettingAddress = true;
- $scope.selectedWalletName = walletName;
- $timeout(function() {
- $scope.$apply();
- });
+ walletService.isReady(client, function(err) {
+ if (err) $scope.errorSelectedWallet[walletId] = err;
+ else {
+ $scope.gettingAddress = true;
+ $scope.selectedWalletName = walletName;
+ $timeout(function() {
+ $scope.$apply();
+ });
- addressService.getAddress(walletId, false, function(err, addr) {
- $scope.gettingAddress = false;
+ addressService.getAddress(walletId, false, function(err, addr) {
+ $scope.gettingAddress = false;
- if (err) {
- self.error = err;
- $modalInstance.dismiss('cancel');
- return;
- }
+ if (err) {
+ self.error = err;
+ $modalInstance.dismiss('cancel');
+ return;
+ }
- $modalInstance.close(addr);
- });
+ $modalInstance.close(addr);
+ });
+ }
});
};
};
@@ -413,36 +423,60 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
};
$scope.sign = function(txp) {
- var fc = profileService.focusedClient;
+ var client = profileService.focusedClient;
$scope.error = null;
$scope.loading = true;
-
- txService.prepareAndSignAndBroadcast(txp, {
- reporterFn: self.setOngoingProcess.bind(self)
- }, function(err, txp) {
- $scope.loading = false;
- $scope.$emit('UpdateTx');
-
+
+ fingerprintService.check(client, function(err) {
if (err) {
+ $scope.loading = false;
$scope.error = err;
- $timeout(function() {
- $scope.$digest();
- });
return;
}
- $modalInstance.close(txp);
- return;
+
+ handleEncryptedWallet(client, function(err) {
+ if (err) {
+ $scope.loading = false;
+ $scope.error = err;
+ return;
+ }
+
+ walletService.signTx(client, txp, function(err, signedTxp) {
+ walletService.lock(client);
+ if (err) {
+ $scope.loading = false;
+ $scope.error = err;
+ return;
+ }
+
+ if (signedTxp.status == 'accepted') {
+ walletService.broadcastTx(client, signedTxp, function(err, broadcastedTxp) {
+ $scope.loading = false;
+ $scope.$emit('UpdateTx');
+ $modalInstance.close(broadcastedTxp);
+ if (err) {
+ $scope.loading = false;
+ $scope.error = err;
+ }
+ });
+ } else {
+ $scope.loading = false;
+ $scope.$emit('UpdateTx');
+ $modalInstance.close(signedTxp);
+ }
+ });
+ });
});
};
$scope.reject = function(txp) {
+ var client = profileService.focusedClient;
self.setOngoingProcess(gettextCatalog.getString('Rejecting payment'));
$scope.loading = true;
$scope.error = null;
- // TODO: This should be in txService
$timeout(function() {
- fc.rejectTxProposal(txp, null, function(err, txpr) {
+ walletService.rejectTx(client, txp, function(err, txpr) {
self.setOngoingProcess();
$scope.loading = false;
if (err) {
@@ -458,11 +492,12 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
$scope.remove = function(txp) {
+ var client = profileService.focusedClient;
self.setOngoingProcess(gettextCatalog.getString('Deleting payment'));
$scope.loading = true;
$scope.error = null;
$timeout(function() {
- fc.removeTxProposal(txp, function(err, txpb) {
+ walletService.removeTx(client, txp, function(err) {
self.setOngoingProcess();
$scope.loading = false;
@@ -479,23 +514,20 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
};
$scope.broadcast = function(txp) {
+ var client = profileService.focusedClient;
self.setOngoingProcess(gettextCatalog.getString('Broadcasting Payment'));
$scope.loading = true;
$scope.error = null;
$timeout(function() {
- fc.broadcastTxProposal(txp, function(err, txpb, memo) {
+ walletService.broadcastTx(client, txp, function(err, txpb) {
self.setOngoingProcess();
$scope.loading = false;
if (err) {
$scope.error = bwsError.msg(err, gettextCatalog.getString('Could not broadcast payment'));
$scope.$digest();
- } else {
-
- if (memo)
- $log.info(memo);
-
- $modalInstance.close(txpb);
+ return;
}
+ $modalInstance.close(txpb);
});
}, 100);
};
@@ -876,7 +908,7 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
this.submitForm = function() {
if (!$scope._amount || !$scope._address) return;
- var fc = profileService.focusedClient;
+ var client = profileService.focusedClient;
var unitToSat = this.unitToSatoshi;
var currentSpendUnconfirmed = configWallet.spendUnconfirmed;
@@ -893,7 +925,7 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
var comment = form.comment.$modelValue;
// ToDo: use a credential's (or fc's) function for this
- if (comment && !fc.credentials.sharedEncryptingKey) {
+ if (comment && !client.credentials.sharedEncryptingKey) {
var msg = 'Could not add message to imported wallet without shared encrypting key';
$log.warn(msg);
return self.setSendError(gettext(msg));
@@ -912,90 +944,100 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
'message': comment
});
- var opts = {};
+ var txp = {};
if (!lodash.isEmpty(self.sendMaxInfo)) {
- opts.sendMax = true;
- opts.inputs = self.sendMaxInfo.inputs;
- opts.fee = self.sendMaxInfo.fee;
+ txp.sendMax = true;
+ txp.inputs = self.sendMaxInfo.inputs;
+ txp.fee = self.sendMaxInfo.fee;
}else {
- opts.amount = amount;
+ txp.amount = amount;
}
- opts.toAddress = address;
- opts.outputs = outputs;
- opts.message = comment;
- opts.payProUrl = paypro ? paypro.url : null;
- opts.lockedCurrentFeePerKb = self.lockedCurrentFeePerKb;
+ txp.toAddress = address;
+ txp.outputs = outputs;
+ txp.message = comment;
+ txp.payProUrl = paypro ? paypro.url : null;
+ txp.excludeUnconfirmedUtxos = configWallet.spendUnconfirmed ? false : true;
+ txp.feeLevel = walletSettings.feeLevel || 'normal';
self.setOngoingProcess(gettextCatalog.getString('Creating transaction'));
- txService.createTx(opts, function(err, txp) {
+ walletService.createTx(client, txp, function(err, createdTxp) {
self.setOngoingProcess();
if (err) {
return self.setSendError(err);
}
- if (!fc.canSign() && !fc.isPrivKeyExternal()) {
- self.setOngoingProcess();
+ if (!client.canSign() && !client.isPrivKeyExternal()) {
$log.info('No signing proposal: No private key');
self.resetForm();
- txStatus.notify(txp, function() {
+ txStatus.notify(createdTxp, function() {
return $scope.$emit('Local/TxProposalAction');
});
- return;
} else {
- $rootScope.$emit('Local/NeedsConfirmation', txp, function(accept) {
- if (accept) self.confirmTx(txp);
+ $rootScope.$emit('Local/NeedsConfirmation', createdTxp, function(accept) {
+ if (accept) self.confirmTx(createdTxp);
else self.resetForm();
});
}
});
}, 100);
- };
+ };
this.confirmTx = function(txp) {
+ var client = profileService.focusedClient;
var self = this;
- $log.info('at .confirmTx');
- txService.prepare({}, function(err) {
- $log.info('after .prepare:', err);
+ fingerprintService.check(client, function(err) {
if (err) {
- self.setOngoingProcess();
- $log.warn('confirmTx/Prepare error:', err);
return self.setSendError(err);
}
- self.setOngoingProcess(gettextCatalog.getString('Sending transaction'));
- txService.publishTx(txp, {}, function(err, txpPublished) {
-
- $log.info('after .publishTx:', err);
+ handleEncryptedWallet(client, function(err) {
if (err) {
- self.setOngoingProcess();
- self.setSendError(err);
- return;
- }
-
- txService.signAndBroadcast(txpPublished, {
- reporterFn: self.setOngoingProcess.bind(self)
- }, function(err, txp) {
- $log.info('after .signAndBroadcast:', err);
- self.resetForm();
- self.setOngoingProcess();
+ return self.setSendError(err);
+ }
+ self.setOngoingProcess(gettextCatalog.getString('Sending transaction'));
+ walletService.publishTx(client, txp, function(err, publishedTxp) {
if (err) {
- self.error = bwsError.msg(err, gettextCatalog.getString('The payment was created but could not be completed. Please try again from home screen'));
- $scope.$emit('Local/TxProposalAction');
- $timeout(function() {
- $scope.$digest();
- }, 1);
- return;
- }
+ self.setOngoingProcess();
+ return self.setSendError(err);
+ }
- $log.info('Transaction status:', txp.status);
- go.walletHome();
- txStatus.notify(txp, function() {
- $scope.$emit('Local/TxProposalAction', txp.status == 'broadcasted');
+ self.setOngoingProcess(gettextCatalog.getString('Signing transaction'));
+ walletService.signTx(client, publishedTxp, function(err, signedTxp) {
+ self.setOngoingProcess();
+ walletService.lock(client);
+ if (err) {
+ $scope.$emit('Local/TxProposalAction');
+ return self.setSendError(
+ err.message ?
+ err.message :
+ gettext('The payment was created but could not be completed. Please try again from home screen'));
+ }
+
+ if (signedTxp.status == 'accepted') {
+ self.setOngoingProcess(gettextCatalog.getString('Broadcasting transaction'));
+ walletService.broadcastTx(client, signedTxp, function(err, broadcastedTxp) {
+ self.setOngoingProcess();
+ if (err) {
+ return self.setSendError(err);
+ }
+ self.resetForm();
+ go.walletHome();
+ txStatus.notify(broadcastedTxp, function() {
+ $scope.$emit('Local/TxProposalAction', broadcastedTxp.status == 'broadcasted');
+ });
+ });
+ } else {
+ self.resetForm();
+ go.walletHome();
+ txStatus.notify(signedTxp, function() {
+ $scope.$emit('Local/TxProposalAction');
+ });
+ }
});
});
});
@@ -1030,7 +1072,6 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
this.sendMaxInfo = {};
if (this.countDown) $interval.cancel(this.countDown);
this._paypro = null;
- this.lockedCurrentFeePerKb = null;
this.lockAddress = false;
this.lockAmount = false;
diff --git a/src/js/services/bwsError.js b/src/js/services/bwsError.js
index 4443439cf..3c96fbfcd 100644
--- a/src/js/services/bwsError.js
+++ b/src/js/services/bwsError.js
@@ -127,6 +127,18 @@ angular.module('copayApp.services')
case 'WALLET_NOT_COMPLETE':
body = gettextCatalog.getString('Wallet is not complete');
break;
+ case 'WALLET_NEEDS_BACKUP':
+ body = gettextCatalog.getString('Wallet needs backup');
+ break;
+ case 'MISSING_PARAMETER':
+ body = gettextCatalog.getString('Missing parameter');
+ break;
+ case 'NO_PASSWORD_GIVEN':
+ body = gettextCatalog.getString('Spending Password needed');
+ break;
+ case 'PASSWORD_INCORRECT':
+ body = gettextCatalog.getString('Wrong spending password');
+ break;
case 'ERROR':
body = (err.message || err.error);
break;
@@ -147,7 +159,7 @@ angular.module('copayApp.services')
};
root.cb = function(err, prefix, cb) {
- return cb(root.msg(err, prefix))
+ return cb(root.msg(err, prefix));
};
return root;
diff --git a/src/js/services/fingerprintService.js b/src/js/services/fingerprintService.js
new file mode 100644
index 000000000..3d708e7f2
--- /dev/null
+++ b/src/js/services/fingerprintService.js
@@ -0,0 +1,40 @@
+'use strict';
+
+angular.module('copayApp.services').factory('fingerprintService', function(gettextCatalog, configService) {
+ var root = {};
+
+ var requestTouchId = function(cb) {
+ try {
+ window.plugins.touchid.verifyFingerprint(
+ gettextCatalog.getString('Scan your fingerprint please'),
+ function(msg) {
+ $log.debug('Touch ID OK');
+ return cb();
+ },
+ function(msg) {
+ $log.debug('Touch ID Failed:' + JSON.stringify(msg));
+ return cb(gettextCatalog.getString('Touch ID Failed') + ': ' + msg.localizedDescription);
+ }
+ );
+ } catch (e) {
+ $log.debug('Touch ID Failed:' + JSON.stringify(e));
+ return cb(gettextCatalog.getString('Touch ID Failed'));
+ };
+ };
+
+ root.isAvailable = function(client) {
+ var config = configService.getSync();
+ config.touchIdFor = config.touchIdFor || {};
+ return (window.touchidAvailable && config.touchIdFor[client.credentials.walletId]);
+ };
+
+ root.check = function(client, cb) {
+ if (root.isAvailable()) {
+ requestTouchId(cb);
+ } else {
+ return cb();
+ }
+ };
+
+ return root;
+});
diff --git a/src/js/services/glideraService.js b/src/js/services/glideraService.js
index ece0723a0..dd13bcd53 100644
--- a/src/js/services/glideraService.js
+++ b/src/js/services/glideraService.js
@@ -9,13 +9,13 @@ angular.module('copayApp.services').factory('glideraService', function($http, $l
credentials.HOST = 'https://sandbox.glidera.io';
if (isCordova) {
credentials.REDIRECT_URI = 'copay://glidera';
- credentials.CLIENT_ID = 'dfc56e4336e32bb8ba46dde34f3d7d6d';
- credentials.CLIENT_SECRET = '5eb679058f6c7eb81123162323d4fba5';
+ credentials.CLIENT_ID = '6163427a2f37d1b2022ececd6d6c9cdd';
+ credentials.CLIENT_SECRET = '599cc3af26108c6fece8ab17c3f35867';
}
else {
credentials.REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob';
- credentials.CLIENT_ID = '9915b6ffa6dc3baffb87135ed3873d49';
- credentials.CLIENT_SECRET = 'd74eda05b9c6a228fd5c85cfbd0eb7eb';
+ credentials.CLIENT_ID = 'c402f4a753755456e8c384fb65b7be1d';
+ credentials.CLIENT_SECRET = '3ce826198e3618d0b8ed341ab91fe4e5';
}
}
else {
diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js
index 8fa3cf6e9..a9a07da19 100644
--- a/src/js/services/profileService.js
+++ b/src/js/services/profileService.js
@@ -638,7 +638,7 @@ angular.module('copayApp.services')
$log.debug('Encrypting private key for', fc.credentials.walletName);
fc.setPrivateKeyEncryption(password);
- root.lockFC();
+ fc.lock();
root.updateCredentialsFC(function() {
$log.debug('Wallet encrypted');
return cb();
@@ -661,51 +661,6 @@ angular.module('copayApp.services')
});
};
- root.lockFC = function() {
- var fc = root.focusedClient;
- try {
- fc.lock();
- } catch (e) {};
- };
-
- root.unlockFC = function(opts, cb) {
- opts = opts || {};
- var fc = opts.selectedClient || root.focusedClient;
-
- if (!fc.isPrivKeyEncrypted())
- return cb();
-
- $log.debug('Wallet is encrypted');
- $rootScope.$emit('Local/NeedsPassword', false, function(err2, password) {
-
- if (err2)
- return cb(err2);
-
- if (!password)
- return cb(gettext('Spending Password needed'));
-
- try {
- fc.unlock(password);
- } catch (e) {
- $log.warn('Error decrypting wallet:', e);
- return cb(gettext('Wrong spending password'));
- }
- return cb();
- });
- };
-
- root.isBackupNeeded = function(walletId, cb) {
- var c = root.getClient(walletId);
- if (c.isPrivKeyExternal()) return cb(false);
- if (!c.credentials.mnemonic) return cb(false);
- if (c.credentials.network == 'testnet') return cb(false);
-
- storageService.getBackupFlag(walletId, function(err, val) {
- if (err || val) return cb(false);
- return cb(true);
- });
- };
-
root.getWallets = function(network) {
if (!root.profile) return [];
diff --git a/src/js/services/txService.js b/src/js/services/txService.js
deleted file mode 100644
index 7eb340f27..000000000
--- a/src/js/services/txService.js
+++ /dev/null
@@ -1,267 +0,0 @@
-'use strict';
-
-angular.module('copayApp.services').factory('txService', function($rootScope, profileService, gettextCatalog, lodash, trezor, ledger, configService, bwsError, $log, feeService) {
- var root = {};
-
- var reportSigningStatus = function(opts) {
- opts = opts || {};
- if (!opts.reporterFn) return;
-
- var fc = opts.selectedClient || profileService.focusedClient;
-
- if (fc.isPrivKeyExternal()) {
- if (fc.getPrivKeyExternalSourceName() == 'ledger') {
- opts.reporterFn(gettextCatalog.getString('Requesting Ledger Wallet to sign'));
- } else if (fc.getPrivKeyExternalSourceName() == 'trezor') {
- opts.reporterFn(gettextCatalog.getString('Requesting Trezor Wallet to sign'));
- }
- } else {
- opts.reporterFn(gettextCatalog.getString('Signing payment'));
- }
- };
-
- var reportBroadcastingStatus = function(opts) {
- if (!opts.reporterFn) return;
-
- opts.reporterFn(gettextCatalog.getString('Broadcasting transaction'));
- };
-
- var stopReport = function(opts) {
- if (!opts.reporterFn) return;
-
- opts.reporterFn();
- };
-
- var requestTouchId = function(cb) {
- try {
- window.plugins.touchid.verifyFingerprint(
- gettextCatalog.getString('Scan your fingerprint please'),
- function(msg) {
- $log.debug('Touch ID OK');
- return cb();
- },
- function(msg) {
- $log.debug('Touch ID Failed:' + JSON.stringify(msg));
- return cb(gettextCatalog.getString('Touch ID Failed') + ': ' + msg.localizedDescription);
- }
- );
- } catch (e) {
- $log.debug('Touch ID Failed:' + JSON.stringify(e));
- return cb(gettextCatalog.getString('Touch ID Failed'));
- };
- };
-
- root.setTouchId = function(cb) {
- if (window.touchidAvailable) {
- requestTouchId(cb);
- } else {
- return cb();
- }
- };
-
- root.checkTouchId = function(opts, cb) {
- opts = opts || {};
- var config = configService.getSync();
- var fc = opts.selectedClient || profileService.focusedClient;
- config.touchIdFor = config.touchIdFor || {};
- if (window.touchidAvailable && config.touchIdFor[fc.credentials.walletId]) {
- requestTouchId(cb);
- } else {
- return cb();
- }
- };
-
- root.prepare = function(opts, cb) {
- $log.info("at .prepare");
- opts = opts || {};
- var fc = opts.selectedClient || profileService.focusedClient;
- $log.info('FC Client Dump:' + fc);
- if (!fc.canSign() && !fc.isPrivKeyExternal())
- return cb('Cannot sign'); // should never happen, no need to translate
-
- root.checkTouchId(opts, function(err) {
- if (err) {
- $log.warn('CheckTouchId error:', err);
- return cb(err);
- };
-
- profileService.unlockFC(opts, function(err) {
- if (err) {
- $log.warn("prepare/unlockFC error:", err);
- return cb(err);
- };
-
- return cb();
-
- });
- });
- };
-
- root.removeTx = function(txp, opts, cb) {
- opts = opts || {};
- var fc = opts.selectedClient || profileService.focusedClient;
-
- fc.removeTxProposal(txp, function(err) {
- return cb(err);
- });
- };
-
- root.createTx = function(opts, cb) {
- opts = opts || {};
- var fc = opts.selectedClient || profileService.focusedClient;
- var currentSpendUnconfirmed = configService.getSync().wallet.spendUnconfirmed;
-
- var getFee = function(cb) {
- if (opts.lockedCurrentFeePerKb) {
- cb(null, opts.lockedCurrentFeePerKb);
- } else {
- feeService.getCurrentFeeValue(cb);
- }
- };
-
- if (opts.sendMax) {
- fc.createTxProposal(opts, function(err, txp) {
- if (err) return cb(err);
- else return cb(null, txp);
- });
- }else {
- getFee(function(err, feePerKb) {
- if (err) return cb(err);
-
- opts.feePerKb = feePerKb;
- opts.excludeUnconfirmedUtxos = currentSpendUnconfirmed ? false : true;
- fc.createTxProposal(opts, function(err, txp) {
- if (err) return cb(err);
- else return cb(null, txp);
- });
- });
- }
- };
-
- root.publishTx = function(txp, opts, cb) {
- opts = opts || {};
- var fc = opts.selectedClient || profileService.focusedClient;
- fc.publishTxProposal({txp: txp}, function(err, txp) {
- if (err) return cb(err);
- else return cb(null, txp);
- });
- };
-
- var _signWithLedger = function(txp, opts, cb) {
- opts = opts || {};
- var fc = opts.selectedClient || profileService.focusedClient;
- $log.info('Requesting Ledger Chrome app to sign the transaction');
-
- ledger.signTx(txp, fc.credentials.account, function(result) {
- $log.debug('Ledger response', result);
- if (!result.success)
- return cb(result.message || result.error);
-
- txp.signatures = lodash.map(result.signatures, function(s) {
- return s.substring(0, s.length - 2);
- });
- return fc.signTxProposal(txp, cb);
- });
- };
-
- var _signWithTrezor = function(txp, opts, cb) {
- opts = opts || {};
- var fc = opts.selectedClient || profileService.focusedClient;
- $log.info('Requesting Trezor to sign the transaction');
-
- var xPubKeys = lodash.pluck(fc.credentials.publicKeyRing, 'xPubKey');
- trezor.signTx(xPubKeys, txp, fc.credentials.account, function(err, result) {
- if (err) return cb(err);
-
- $log.debug('Trezor response', result);
- txp.signatures = result.signatures;
- return fc.signTxProposal(txp, cb);
- });
- };
-
- root.sign = function(txp, opts, cb) {
- $log.info('at .sign');
- opts = opts || {};
- var fc = opts.selectedClient || profileService.focusedClient;
-
- if (fc.isPrivKeyExternal()) {
- switch (fc.getPrivKeyExternalSourceName()) {
- case 'ledger':
- return _signWithLedger(txp, opts, cb);
- case 'trezor':
- return _signWithTrezor(txp, opts, cb);
- default:
- var msg = 'Unsupported External Key:' + fc.getPrivKeyExternalSourceName();
- $log.error(msg);
- return cb(msg);
- }
- } else {
-
- txp.signatures = null;
- $log.info('at .sign: (isEncrypted):', fc.isPrivKeyEncrypted());
- $log.info('txp BEFORE .signTxProposal:', txp);
-
- fc.signTxProposal(txp, function(err, signedTxp) {
- $log.info('txp AFTER .signTxProposal:',err, signedTxp);
- profileService.lockFC();
- return cb(err, signedTxp);
- });
- }
- };
-
- root.signAndBroadcast = function(txp, opts, cb) {
- $log.info('at .signAndBroadcast');
- opts = opts || {};
- reportSigningStatus(opts);
-
- var fc = opts.selectedClient || profileService.focusedClient;
- root.sign(txp, opts, function(err, txp) {
- if (err) {
- stopReport(opts);
- return bwsError.cb(err, gettextCatalog.getString('Could not accept payment'), cb);
- };
-
- if (txp.status != 'accepted') {
- stopReport(opts);
- return cb(null, txp);
- }
-
- reportBroadcastingStatus(opts);
- fc.broadcastTxProposal(txp, function(err, txp, memo) {
- stopReport(opts);
-
- if (err) {
- return bwsError.cb(err, gettextCatalog.getString('Could not broadcast payment'), cb);
- };
-
- $log.debug('Transaction signed and broadcasted')
-
- if (memo)
- $log.info(memo);
-
- return cb(null, txp);
- });
- });
- };
-
- root.prepareAndSignAndBroadcast = function(txp, opts, cb) {
- opts = opts || {};
- $log.info('at .prepareSignAndBroadcast');
-
- root.prepare(opts, function(err) {
- if (err) {
- $log.warn('Prepare error:' + err);
- stopReport(opts);
- return cb(err);
- };
- root.signAndBroadcast(txp, opts, function(err, txp) {
- if (err) {
- stopReport(opts);
- return cb(err);
- };
- return cb(null, txp);
- });
- });
- };
- return root;
-});
diff --git a/src/js/services/walletService.js b/src/js/services/walletService.js
new file mode 100644
index 000000000..c2d060a53
--- /dev/null
+++ b/src/js/services/walletService.js
@@ -0,0 +1,201 @@
+'use strict';
+
+angular.module('copayApp.services').factory('walletService', function($log, lodash, trezor, ledger, storageService) {
+ var root = {};
+
+ var _signWithLedger = function(client, txp, cb) {
+ $log.info('Requesting Ledger Chrome app to sign the transaction');
+
+ ledger.signTx(txp, client.credentials.account, function(result) {
+ $log.debug('Ledger response', result);
+ if (!result.success)
+ return cb(result.message || result.error);
+
+ txp.signatures = lodash.map(result.signatures, function(s) {
+ return s.substring(0, s.length - 2);
+ });
+ return client.signTxProposal(txp, cb);
+ });
+ };
+
+ var _signWithTrezor = function(client, txp, cb) {
+ $log.info('Requesting Trezor to sign the transaction');
+
+ var xPubKeys = lodash.pluck(client.credentials.publicKeyRing, 'xPubKey');
+ trezor.signTx(xPubKeys, txp, client.credentials.account, function(err, result) {
+ if (err) return cb(err);
+
+ $log.debug('Trezor response', result);
+ txp.signatures = result.signatures;
+ return client.signTxProposal(txp, cb);
+ });
+ };
+
+ root.isBackupNeeded = function(client, cb) {
+ if (client.isPrivKeyExternal()) return cb(false);
+ if (!client.credentials.mnemonic) return cb(false);
+ if (client.credentials.network == 'testnet') return cb(false);
+
+ storageService.getBackupFlag(client.credentials.walletId, function(err, val) {
+ if (err) $log.error(err);
+ if (val) return cb(false);
+ return cb(true);
+ });
+ };
+
+ root.isReady = function(client, cb) {
+ if(!client.isComplete())
+ return cb('WALLET_NOT_COMPLETE');
+ root.isBackupNeeded(client, function(needsBackup) {
+ if (needsBackup)
+ return cb('WALLET_NEEDS_BACKUP');
+ return cb();
+ });
+ };
+
+ root.isEncrypted = function(client) {
+ if (lodash.isEmpty(client)) return;
+ var isEncrypted = client.isPrivKeyEncrypted();
+ if (isEncrypted) $log.debug('Wallet is encrypted');
+ return isEncrypted;
+ };
+
+ root.lock = function(client) {
+ try {
+ client.lock();
+ } catch (e) {
+ $log.warn('Encrypting wallet:', e);
+ };
+ };
+
+ root.unlock = function(client, password) {
+ if (lodash.isEmpty(client))
+ return 'MISSING_PARAMETER';
+ if (lodash.isEmpty(password))
+ return 'NO_PASSWORD_GIVEN';
+ try {
+ client.unlock(password);
+ } catch (e) {
+ $log.warn('Decrypting wallet:', e);
+ return 'PASSWORD_INCORRECT';
+ }
+ };
+
+ root.createTx = function(client, txp, cb) {
+ if (lodash.isEmpty(txp) || lodash.isEmpty(client))
+ return cb('MISSING_PARAMETER');
+
+ if (txp.sendMax) {
+ client.createTxProposal(txp, function(err, createdTxp) {
+ if (err) return cb(err);
+ else return cb(null, createdTxp);
+ });
+ } else {
+ client.getFeeLevels(client.credentials.network, function(err, levels) {
+ if (err) return cb(err);
+
+ var feeLevelValue = lodash.find(levels, {
+ level: txp.feeLevel
+ });
+
+ if (!feeLevelValue || !feeLevelValue.feePerKB)
+ return cb({
+ message: 'Could not get dynamic fee for level: ' + feeLevel
+ });
+
+ $log.debug('Dynamic fee: ' + txp.feeLevel + ' ' + feeLevelValue.feePerKB + ' SAT');
+
+ txp.feePerKb = feeLevelValue.feePerKB;
+ client.createTxProposal(txp, function(err, createdTxp) {
+ if (err) return cb(err);
+ else {
+ $log.debug('Transaction created');
+ return cb(null, createdTxp);
+ }
+ });
+ });
+ }
+ };
+
+ root.publishTx = function(client, txp, cb) {
+ if (lodash.isEmpty(txp) || lodash.isEmpty(client))
+ return cb('MISSING_PARAMETER');
+
+ client.publishTxProposal({txp: txp}, function(err, publishedTx) {
+ if (err) return cb(err);
+ else {
+ $log.debug('Transaction published');
+ return cb(null, publishedTx);
+ }
+ });
+ };
+
+ root.signTx = function(client, txp, cb) {
+ if (lodash.isEmpty(txp) || lodash.isEmpty(client))
+ return cb('MISSING_PARAMETER');
+
+ if (client.isPrivKeyExternal()) {
+ switch (client.getPrivKeyExternalSourceName()) {
+ case 'ledger':
+ return _signWithLedger(client, txp, cb);
+ case 'trezor':
+ return _signWithTrezor(client, txp, cb);
+ default:
+ var msg = 'Unsupported External Key:' + client.getPrivKeyExternalSourceName();
+ $log.error(msg);
+ return cb(msg);
+ }
+ } else {
+
+ try {
+ client.signTxProposal(txp, function(err, signedTxp) {
+ $log.debug('Transaction signed');
+ return cb(err, signedTxp);
+ });
+ } catch (e) {
+ $log.warn('Error at signTxProposal:', e);
+ return cb(e);
+ }
+ }
+ };
+
+ root.broadcastTx = function(client, txp, cb) {
+ if (lodash.isEmpty(txp) || lodash.isEmpty(client))
+ return cb('MISSING_PARAMETER');
+
+ if (txp.status != 'accepted')
+ return cb('TX_NOT_ACCEPTED');
+
+ client.broadcastTxProposal(txp, function(err, broadcastedTxp, memo) {
+ if (err)
+ return cb(err);
+
+ $log.debug('Transaction broadcasted');
+ if (memo) $log.info(memo);
+
+ return cb(null, broadcastedTxp);
+ });
+ };
+
+ root.rejectTx = function(client, txp, cb) {
+ if (lodash.isEmpty(txp) || lodash.isEmpty(client))
+ return cb('MISSING_PARAMETER');
+
+ client.rejectTxProposal(txp, null, function(err, rejectedTxp) {
+ $log.debug('Transaction rejected');
+ return cb(err, rejectedTxp);
+ });
+ };
+
+ root.removeTx = function(client, txp, cb) {
+ if (lodash.isEmpty(txp) || lodash.isEmpty(client))
+ return cb('MISSING_PARAMETER');
+
+ client.removeTxProposal(txp, function(err) {
+ $log.debug('Transaction removed');
+ return cb(err);
+ });
+ };
+
+ return root;
+});
diff --git a/test/controllers/sidebar.test.js b/test/controllers/sidebar.test.js
deleted file mode 100644
index ffa7d7137..000000000
--- a/test/controllers/sidebar.test.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-describe('sidebarController', function(){
- var scope, controller;
-
- beforeEach(angular.mock.module('copayApp.controllers'));
-
- it('wallet selection', function(){
- expect(true).not.toBeUndefined();
- });
-});
diff --git a/test/karma.conf.js b/test/karma.conf.js
index 881d6b189..0c252bd3f 100644
--- a/test/karma.conf.js
+++ b/test/karma.conf.js
@@ -10,7 +10,7 @@ module.exports = function(config) {
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
- frameworks: ['jasmine'],
+ frameworks: ['mocha', 'chai'],
// list of files / patterns to load in the browser
@@ -20,16 +20,20 @@ module.exports = function(config) {
'bower_components/moment/min/moment-with-locales.js',
'bower_components/angular/angular.js',
'bower_components/angular-ui-router/release/angular-ui-router.js',
- 'bower_components/angular-foundation/mm-foundation.js',
'bower_components/angular-foundation/mm-foundation-tpls.js',
- 'bower_components/angular-animate/angular-animate.js',
+ 'bower_components/angular-touch/angular-touch.js',
+ 'bower_components/fastclick/lib/fastclick.js',
+ 'bower_components/stateful-fastclick/dist/stateful-fastclick.js',
+ 'bower_components/angular-stateful-fastclick/lib/angular-stateful-fastclick.js',
'bower_components/angular-moment/angular-moment.js',
'bower_components/ng-lodash/build/ng-lodash.js',
- 'bower_components/angular-qrcode/qrcode.js',
+ 'bower_components/angular-qrcode/angular-qrcode.js',
'bower_components/angular-gettext/dist/angular-gettext.js',
- 'bower_components/angular-touch/angular-touch.js',
'bower_components/angular-ui-switch/angular-ui-switch.js',
- 'node_modules/angular-mocks/angular-mocks.js',
+ 'bower_components/angular-sanitize/angular-sanitize.js',
+ 'bower_components/ng-csv/build/ng-csv.js',
+ 'bower_components/angular-mocks/angular-mocks.js',
+ 'angular-bitcore-wallet-client/angular-bitcore-wallet-client.js',
'src/js/**/*.js',
'test/**/*.js'
],
@@ -38,7 +42,7 @@ module.exports = function(config) {
// list of files to exclude
exclude: [
'src/js/translations.js',
- 'src/js/version.js',
+ // 'src/js/version.js',
'test/karma.conf.js',
'test/old/*'
],
@@ -54,7 +58,7 @@ module.exports = function(config) {
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
- reporters: ['progress', , 'coverage'],
+ reporters: ['mocha', 'coverage'],
// optionally, configure the reporter
coverageReporter: {
@@ -91,6 +95,16 @@ module.exports = function(config) {
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
+ plugins: [
+ 'karma-mocha-reporter',
+ 'karma-coverage',
+ 'karma-mocha',
+ 'karma-chai',
+ 'karma-sinon',
+ 'karma-phantomjs-launcher',
+ 'karma-chrome-launcher',
+ ],
+
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
diff --git a/test/mocks/stateMocks.js b/test/mocks/stateMocks.js
deleted file mode 100644
index 5a71a5469..000000000
--- a/test/mocks/stateMocks.js
+++ /dev/null
@@ -1,30 +0,0 @@
-angular.module('stateMock',[]);
-angular.module('stateMock').service("$state", function($q){
- this.expectedTransitions = [];
- this.transitionTo = function(stateName){
- if(this.expectedTransitions.length > 0){
- var expectedState = this.expectedTransitions.shift();
- if(expectedState !== stateName){
- throw Error("Expected transition to state: " + expectedState + " but transitioned to " + stateName );
- }
- }else{
- throw Error("No more transitions were expected! Tried to transition to "+ stateName );
- }
- console.log("Mock transition to: " + stateName);
- var deferred = $q.defer();
- var promise = deferred.promise;
- deferred.resolve();
- return promise;
- }
- this.go = this.transitionTo;
- this.expectTransitionTo = function(stateName){
- this.expectedTransitions.push(stateName);
- }
-
-
- this.ensureAllTransitionsHappened = function(){
- if(this.expectedTransitions.length > 0){
- throw Error("Not all transitions happened!");
- }
- }
-});
diff --git a/test/old/sidebar.test.js b/test/old/sidebar.test.js
new file mode 100644
index 000000000..d3a197e69
--- /dev/null
+++ b/test/old/sidebar.test.js
@@ -0,0 +1,11 @@
+// 'use strict';
+//
+// describe('sidebarController', function(){
+// var scope, controller;
+//
+// beforeEach(angular.mock.module('copayApp.controllers'));
+//
+// it('dummy test', function(){
+// should.exist(true);
+// });
+// });
diff --git a/test/walletService.test.js b/test/walletService.test.js
new file mode 100644
index 000000000..006fddcf0
--- /dev/null
+++ b/test/walletService.test.js
@@ -0,0 +1,25 @@
+describe('walletService', function() {
+
+ var walletService;
+
+
+ // Adds walletService's module dependencies
+ beforeEach(function() {
+ module('ngLodash');
+ module('gettext');
+ module('bwcModule');
+ module('copayApp.services');
+ });
+
+
+ beforeEach(inject(function(_walletService_) {
+ walletService = _walletService_;
+ }));
+
+ describe('walletService', function() {
+ it('should be defined', function() {
+ should.exist(walletService);
+ });
+ });
+
+});